Skip to content

Commit 04ea73c

Browse files
authored
feat(map_based_prediction): enable prediction to/from shoulder lanelet (lanelet not in routing graph) (#4353)
* enable prediction to adjacent lanelet Signed-off-by: yoshiri <yoshiyoshidetteiu@gmail.com> * add function to get unconnected lanelet in graph Signed-off-by: Yoshi, Ri <yoshiyoshidetteiu@gmail.com> * add function to generate possiblepath from isolated lanelets Signed-off-by: Yoshi, Ri <yoshiyoshidetteiu@gmail.com> * precommit fix Signed-off-by: Yoshi, Ri <yoshiyoshidetteiu@gmail.com> * fix typo and unnecessary descriptions Signed-off-by: Yoshi, Ri <yoshiyoshidetteiu@gmail.com> * use lambda function to define lanelet path extractor Signed-off-by: Yoshi, Ri <yoshiyoshidetteiu@gmail.com> * add lambda function to get left/right lanelet Signed-off-by: Yoshi, Ri <yoshiyoshidetteiu@gmail.com> --------- Signed-off-by: yoshiri <yoshiyoshidetteiu@gmail.com> Signed-off-by: Yoshi, Ri <yoshiyoshidetteiu@gmail.com>
1 parent 7e2ef7c commit 04ea73c

File tree

2 files changed

+165
-7
lines changed

2 files changed

+165
-7
lines changed

perception/map_based_prediction/include/map_based_prediction/map_based_prediction_node.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class MapBasedPredictionNode : public rclcpp::Node
139139
// Parameters
140140
bool enable_delay_compensation_;
141141
double prediction_time_horizon_;
142+
double prediction_time_horizon_rate_for_validate_lane_length_;
142143
double prediction_sampling_time_interval_;
143144
double min_velocity_for_map_based_prediction_;
144145
double min_crosswalk_user_velocity_;

perception/map_based_prediction/src/map_based_prediction_node.cpp

+164-7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <algorithm>
3535
#include <chrono>
3636
#include <cmath>
37+
#include <functional>
3738
#include <limits>
3839

3940
namespace map_based_prediction
@@ -207,6 +208,114 @@ double calcAbsYawDiffBetweenLaneletAndObject(
207208
return abs_norm_delta;
208209
}
209210

211+
/**
212+
* @brief Get the Right LineSharing Lanelets object
213+
*
214+
* @param current_lanelet
215+
* @param lanelet_map_ptr
216+
* @return lanelet::ConstLanelets
217+
*/
218+
lanelet::ConstLanelets getRightLineSharingLanelets(
219+
const lanelet::ConstLanelet & current_lanelet, const lanelet::LaneletMapPtr & lanelet_map_ptr)
220+
{
221+
lanelet::ConstLanelets
222+
output_lanelets; // create an empty container of type lanelet::ConstLanelets
223+
224+
// step1: look for lane sharing current right bound
225+
lanelet::Lanelets right_lane_candidates =
226+
lanelet_map_ptr->laneletLayer.findUsages(current_lanelet.rightBound());
227+
for (auto & candidate : right_lane_candidates) {
228+
// exclude self lanelet
229+
if (candidate == current_lanelet) continue;
230+
// if candidate has linestring as leftbound, assign it to output
231+
if (candidate.leftBound() == current_lanelet.rightBound()) {
232+
output_lanelets.push_back(candidate);
233+
}
234+
}
235+
return output_lanelets; // return empty
236+
}
237+
238+
/**
239+
* @brief Get the Left LineSharing Lanelets object
240+
*
241+
* @param current_lanelet
242+
* @param lanelet_map_ptr
243+
* @return lanelet::ConstLanelets
244+
*/
245+
lanelet::ConstLanelets getLeftLineSharingLanelets(
246+
const lanelet::ConstLanelet & current_lanelet, const lanelet::LaneletMapPtr & lanelet_map_ptr)
247+
{
248+
lanelet::ConstLanelets
249+
output_lanelets; // create an empty container of type lanelet::ConstLanelets
250+
251+
// step1: look for lane sharing current left bound
252+
lanelet::Lanelets left_lane_candidates =
253+
lanelet_map_ptr->laneletLayer.findUsages(current_lanelet.leftBound());
254+
for (auto & candidate : left_lane_candidates) {
255+
// exclude self lanelet
256+
if (candidate == current_lanelet) continue;
257+
// if candidate has linestring as rightbound, assign it to output
258+
if (candidate.rightBound() == current_lanelet.leftBound()) {
259+
output_lanelets.push_back(candidate);
260+
}
261+
}
262+
return output_lanelets; // return empty
263+
}
264+
265+
/**
266+
* @brief Check if the lanelet is isolated in routing graph
267+
* @param current_lanelet
268+
* @param lanelet_map_ptr
269+
*/
270+
bool isIsolatedLanelet(
271+
const lanelet::ConstLanelet & lanelet, lanelet::routing::RoutingGraphPtr & graph)
272+
{
273+
const auto & following_lanelets = graph->following(lanelet);
274+
const auto & left_lanelets = graph->lefts(lanelet);
275+
const auto & right_lanelets = graph->rights(lanelet);
276+
return left_lanelets.empty() && right_lanelets.empty() && following_lanelets.empty();
277+
}
278+
279+
/**
280+
* @brief Get the Possible Paths For Isolated Lanelet object
281+
* @param lanelet
282+
* @return lanelet::routing::LaneletPaths
283+
*/
284+
lanelet::routing::LaneletPaths getPossiblePathsForIsolatedLanelet(
285+
const lanelet::ConstLanelet & lanelet)
286+
{
287+
lanelet::ConstLanelets possible_lanelets;
288+
possible_lanelets.push_back(lanelet);
289+
lanelet::routing::LaneletPaths possible_paths;
290+
// need to init path with constlanelets
291+
lanelet::routing::LaneletPath possible_path(possible_lanelets);
292+
possible_paths.push_back(possible_path);
293+
return possible_paths;
294+
}
295+
296+
/**
297+
* @brief validate isolated lanelet length has enough length for prediction
298+
* @param lanelet
299+
* @param object: object information for calc length threshold
300+
* @param prediction_time: time horizon[s] for calc length threshold
301+
* @return bool
302+
*/
303+
bool validateIsolatedLaneletLength(
304+
const lanelet::ConstLanelet & lanelet, const TrackedObject & object, const double prediction_time)
305+
{
306+
// get closest center line point to object
307+
const auto & center_line = lanelet.centerline2d();
308+
const auto & obj_pos = object.kinematics.pose_with_covariance.pose.position;
309+
const lanelet::BasicPoint2d obj_point(obj_pos.x, obj_pos.y);
310+
// get end point of the center line
311+
const auto & end_point = center_line.back();
312+
// calc approx distance between closest point and end point
313+
const double approx_distance = lanelet::geometry::distance2d(obj_point, end_point);
314+
const double min_length =
315+
object.kinematics.twist_with_covariance.twist.linear.x * prediction_time;
316+
return approx_distance > min_length;
317+
}
318+
210319
lanelet::ConstLanelets getLanelets(const map_based_prediction::LaneletsData & data)
211320
{
212321
lanelet::ConstLanelets lanelets;
@@ -522,6 +631,10 @@ MapBasedPredictionNode::MapBasedPredictionNode(const rclcpp::NodeOptions & node_
522631
declare_parameter<int>("lane_change_detection.num_continuous_state_transition");
523632
}
524633
reference_path_resolution_ = declare_parameter("reference_path_resolution", 0.5);
634+
/* prediction path will disabled when the estimated path length exceeds lanelet length. This
635+
* parameter control the estimated path length = vx * th * (rate) */
636+
prediction_time_horizon_rate_for_validate_lane_length_ =
637+
declare_parameter("prediction_time_horizon_rate_for_validate_lane_length", 0.8);
525638

526639
path_generator_ = std::make_shared<PathGenerator>(
527640
prediction_time_horizon_, prediction_sampling_time_interval_, min_crosswalk_user_velocity_);
@@ -1119,25 +1232,69 @@ std::vector<PredictedRefPath> MapBasedPredictionNode::getPredictedReferencePath(
11191232
const double search_dist = prediction_time_horizon_ * obj_vel +
11201233
lanelet::utils::getLaneletLength3d(current_lanelet_data.lanelet);
11211234
lanelet::routing::PossiblePathsParams possible_params{search_dist, {}, 0, false, true};
1235+
const double validate_time_horizon =
1236+
prediction_time_horizon_ * prediction_time_horizon_rate_for_validate_lane_length_;
1237+
1238+
// lambda function to get possible paths for isolated lanelet
1239+
// isolated is often caused by lanelet with no connection e.g. shoulder-lane
1240+
auto getPathsForNormalOrIsolatedLanelet = [&](const lanelet::ConstLanelet & lanelet) {
1241+
// if lanelet is not isolated, return normal possible paths
1242+
if (!isIsolatedLanelet(lanelet, routing_graph_ptr_)) {
1243+
return routing_graph_ptr_->possiblePaths(lanelet, possible_params);
1244+
}
1245+
// if lanelet is isolated, check if it has enough length
1246+
if (!validateIsolatedLaneletLength(lanelet, object, validate_time_horizon)) {
1247+
return lanelet::routing::LaneletPaths{};
1248+
} else {
1249+
// if lanelet has enough length, return possible paths
1250+
return getPossiblePathsForIsolatedLanelet(lanelet);
1251+
}
1252+
};
1253+
1254+
// lambda function to extract left/right lanelets
1255+
auto getLeftOrRightLanelets = [&](
1256+
const lanelet::ConstLanelet & lanelet,
1257+
const bool get_left) -> std::optional<lanelet::ConstLanelet> {
1258+
const auto opt =
1259+
get_left ? routing_graph_ptr_->left(lanelet) : routing_graph_ptr_->right(lanelet);
1260+
if (!!opt) {
1261+
return *opt;
1262+
}
1263+
const auto adjacent = get_left ? routing_graph_ptr_->adjacentLeft(lanelet)
1264+
: routing_graph_ptr_->adjacentRight(lanelet);
1265+
if (!!adjacent) {
1266+
return *adjacent;
1267+
}
1268+
// search for unconnected lanelet
1269+
const auto unconnected_lanelets = get_left
1270+
? getLeftLineSharingLanelets(lanelet, lanelet_map_ptr_)
1271+
: getRightLineSharingLanelets(lanelet, lanelet_map_ptr_);
1272+
// just return first candidate of unconnected lanelet for now
1273+
if (!unconnected_lanelets.empty()) {
1274+
return unconnected_lanelets.front();
1275+
}
1276+
// if no candidate lanelet found, return empty
1277+
return std::nullopt;
1278+
};
11221279

11231280
// Step1. Get the path
11241281
// Step1.1 Get the left lanelet
11251282
lanelet::routing::LaneletPaths left_paths;
1126-
auto opt_left = routing_graph_ptr_->left(current_lanelet_data.lanelet);
1127-
if (!!opt_left) {
1128-
left_paths = routing_graph_ptr_->possiblePaths(*opt_left, possible_params);
1283+
const auto left_lanelet = getLeftOrRightLanelets(current_lanelet_data.lanelet, true);
1284+
if (!!left_lanelet) {
1285+
left_paths = getPathsForNormalOrIsolatedLanelet(left_lanelet.value());
11291286
}
11301287

11311288
// Step1.2 Get the right lanelet
11321289
lanelet::routing::LaneletPaths right_paths;
1133-
auto opt_right = routing_graph_ptr_->right(current_lanelet_data.lanelet);
1134-
if (!!opt_right) {
1135-
right_paths = routing_graph_ptr_->possiblePaths(*opt_right, possible_params);
1290+
const auto right_lanelet = getLeftOrRightLanelets(current_lanelet_data.lanelet, false);
1291+
if (!!right_lanelet) {
1292+
right_paths = getPathsForNormalOrIsolatedLanelet(right_lanelet.value());
11361293
}
11371294

11381295
// Step1.3 Get the centerline
11391296
lanelet::routing::LaneletPaths center_paths =
1140-
routing_graph_ptr_->possiblePaths(current_lanelet_data.lanelet, possible_params);
1297+
getPathsForNormalOrIsolatedLanelet(current_lanelet_data.lanelet);
11411298

11421299
// Skip calculations if all paths are empty
11431300
if (left_paths.empty() && right_paths.empty() && center_paths.empty()) {

0 commit comments

Comments
 (0)