Skip to content

Commit 4a9979d

Browse files
maxime-clemkarishma1911
authored andcommitted
feat(path_sampler): make the trajectory smoother, add params, fix crash (autowarefoundation#6939)
Signed-off-by: Maxime CLEMENT <maxime.clement@tier4.jp>
1 parent bae756c commit 4a9979d

File tree

7 files changed

+149
-85
lines changed

7 files changed

+149
-85
lines changed

planning/behavior_path_sampling_planner_module/src/sampling_planner_module.cpp

+8-8
Original file line numberDiff line numberDiff line change
@@ -54,34 +54,34 @@ SamplingPlannerModule::SamplingPlannerModule(
5454
sampler_common::Path & path, const sampler_common::Constraints & constraints,
5555
const MultiPoint2d & footprint) -> bool {
5656
if (!footprint.empty()) {
57-
path.constraint_results.drivable_area =
57+
path.constraint_results.inside_drivable_area =
5858
bg::within(footprint, constraints.drivable_polygons);
5959
}
6060

6161
for (const auto & f : footprint) {
6262
const auto collision_index = constraints.rtree.qbegin(bgi::intersects(f));
6363
if (collision_index != constraints.rtree.qend()) {
64-
path.constraint_results.collision = false;
64+
path.constraint_results.collision_free = false;
6565
break;
6666
}
6767
}
6868

69-
return path.constraint_results.collision && path.constraint_results.drivable_area;
69+
return path.constraint_results.collision_free && path.constraint_results.inside_drivable_area;
7070
});
7171

7272
hard_constraints_.emplace_back(
7373
[](
7474
sampler_common::Path & path, const sampler_common::Constraints & constraints,
7575
[[maybe_unused]] const MultiPoint2d & footprint) -> bool {
7676
if (path.curvatures.empty()) {
77-
path.constraint_results.curvature = false;
77+
path.constraint_results.valid_curvature = false;
7878
return false;
7979
}
8080
const bool curvatures_satisfied =
8181
std::all_of(path.curvatures.begin(), path.curvatures.end(), [&](const auto & v) -> bool {
8282
return (v > constraints.hard.min_curvature) && (v < constraints.hard.max_curvature);
8383
});
84-
path.constraint_results.curvature = curvatures_satisfied;
84+
path.constraint_results.valid_curvature = curvatures_satisfied;
8585
return curvatures_satisfied;
8686
});
8787

@@ -267,9 +267,9 @@ bool SamplingPlannerModule::isReferencePathSafe() const
267267
[](
268268
sampler_common::Path & path, const sampler_common::Constraints & constraints,
269269
const MultiPoint2d & footprint) -> bool {
270-
path.constraint_results.collision =
270+
path.constraint_results.collision_free =
271271
!sampler_common::constraints::has_collision(footprint, constraints.obstacle_polygons);
272-
return path.constraint_results.collision;
272+
return path.constraint_results.collision_free;
273273
});
274274
evaluateHardConstraints(
275275
reference_path, internal_params_->constraints, footprint, hard_constraints_reference_path);
@@ -591,7 +591,7 @@ BehaviorModuleOutput SamplingPlannerModule::plan()
591591
sampler_common::constraints::buildFootprintPoints(path, internal_params_->constraints);
592592
std::vector<bool> hard_constraints_results =
593593
evaluateHardConstraints(path, internal_params_->constraints, footprint, hard_constraints_);
594-
path.constraint_results.curvature = true;
594+
path.constraint_results.valid_curvature = true;
595595
debug_data_.footprints.push_back(footprint);
596596
std::vector<double> soft_constraints_results = evaluateSoftConstraints(
597597
path, internal_params_->constraints, soft_constraints_, soft_constraints_input);

planning/sampling_based_planner/path_sampler/include/path_sampler/node.hpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ class PathSampler : public rclcpp::Node
9595
void copyVelocity(
9696
const std::vector<TrajectoryPoint> & from_traj, std::vector<TrajectoryPoint> & to_traj,
9797
const geometry_msgs::msg::Pose & ego_pose);
98-
std::vector<TrajectoryPoint> generatePath(const PlannerData & planner_data);
98+
sampler_common::Path generatePath(const PlannerData & planner_data);
99+
std::vector<sampler_common::Path> generateCandidatesFromPreviousPath(
100+
const PlannerData & planner_data, const sampler_common::transform::Spline2D & path_spline);
101+
std::vector<TrajectoryPoint> generateTrajectoryPoints(const PlannerData & planner_data);
99102
void publishVirtualWall(const geometry_msgs::msg::Pose & stop_pose) const;
100103
void publishDebugMarker(const std::vector<TrajectoryPoint> & traj_points) const;
101104
};

planning/sampling_based_planner/path_sampler/include/path_sampler/parameters.hpp

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ struct Parameters
4646
bool force_zero_heading{};
4747
bool smooth_reference{};
4848
} preprocessing{};
49+
50+
struct
51+
{
52+
double max_lat_dev{};
53+
double direct_reuse_dist{};
54+
} path_reuse{};
4955
};
5056

5157
#endif // PATH_SAMPLER__PARAMETERS_HPP_

planning/sampling_based_planner/path_sampler/src/node.cpp

+106-61
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,20 @@ PathSampler::PathSampler(const rclcpp::NodeOptions & node_options)
8484
declare_parameter<double>("constraints.hard.max_curvature");
8585
params_.constraints.hard.min_curvature =
8686
declare_parameter<double>("constraints.hard.min_curvature");
87+
params_.constraints.hard.min_dist_from_obstacles =
88+
declare_parameter<double>("constraints.hard.min_distance_from_obstacles");
89+
params_.constraints.hard.limit_footprint_inside_drivable_area =
90+
declare_parameter<bool>("constraints.hard.limit_footprint_inside_drivable_area");
8791
params_.constraints.soft.lateral_deviation_weight =
8892
declare_parameter<double>("constraints.soft.lateral_deviation_weight");
8993
params_.constraints.soft.length_weight =
9094
declare_parameter<double>("constraints.soft.length_weight");
9195
params_.constraints.soft.curvature_weight =
9296
declare_parameter<double>("constraints.soft.curvature_weight");
97+
params_.path_reuse.max_lat_dev =
98+
declare_parameter<double>("path_reuse.maximum_lateral_deviation");
99+
params_.path_reuse.direct_reuse_dist =
100+
declare_parameter<double>("path_reuse.direct_reuse_distance");
93101
params_.sampling.enable_frenet = declare_parameter<bool>("sampling.enable_frenet");
94102
params_.sampling.enable_bezier = declare_parameter<bool>("sampling.enable_bezier");
95103
params_.sampling.resolution = declare_parameter<double>("sampling.resolution");
@@ -144,6 +152,12 @@ rcl_interfaces::msg::SetParametersResult PathSampler::onParam(
144152

145153
updateParam(parameters, "constraints.hard.max_curvature", params_.constraints.hard.max_curvature);
146154
updateParam(parameters, "constraints.hard.min_curvature", params_.constraints.hard.min_curvature);
155+
updateParam(
156+
parameters, "constraints.hard.min_distance_from_obstacles",
157+
params_.constraints.hard.min_dist_from_obstacles);
158+
updateParam(
159+
parameters, "constraints.hard.limit_footprint_inside_drivable_area",
160+
params_.constraints.hard.limit_footprint_inside_drivable_area);
147161
updateParam(
148162
parameters, "constraints.soft.lateral_deviation_weight",
149163
params_.constraints.soft.lateral_deviation_weight);
@@ -243,9 +257,8 @@ void PathSampler::onPath(const Path::SharedPtr path_ptr)
243257
const auto generated_traj_points = generateTrajectory(planner_data);
244258
// 3. extend trajectory to connect the optimized trajectory and the following path smoothly
245259
if (!generated_traj_points.empty()) {
246-
auto full_traj_points = extendTrajectory(planner_data.traj_points, generated_traj_points);
247260
const auto output_traj_msg =
248-
motion_utils::convertToTrajectory(full_traj_points, path_ptr->header);
261+
motion_utils::convertToTrajectory(generated_traj_points, path_ptr->header);
249262
traj_pub_->publish(output_traj_msg);
250263
} else {
251264
auto stopping_traj = trajectory_utils::convertToTrajectoryPoints(planner_data.traj_points);
@@ -367,7 +380,7 @@ std::vector<TrajectoryPoint> PathSampler::generateTrajectory(const PlannerData &
367380

368381
const auto & input_traj_points = planner_data.traj_points;
369382

370-
auto generated_traj_points = generatePath(planner_data);
383+
auto generated_traj_points = generateTrajectoryPoints(planner_data);
371384

372385
copyVelocity(input_traj_points, generated_traj_points, planner_data.ego_pose);
373386
copyZ(input_traj_points, generated_traj_points);
@@ -377,65 +390,89 @@ std::vector<TrajectoryPoint> PathSampler::generateTrajectory(const PlannerData &
377390
return generated_traj_points;
378391
}
379392

380-
std::vector<TrajectoryPoint> PathSampler::generatePath(const PlannerData & planner_data)
393+
std::vector<sampler_common::Path> PathSampler::generateCandidatesFromPreviousPath(
394+
const PlannerData & planner_data, const sampler_common::transform::Spline2D & path_spline)
381395
{
382-
std::vector<TrajectoryPoint> trajectory;
383-
time_keeper_ptr_->tic(__func__);
384-
const auto & p = planner_data;
385-
386-
const auto path_spline = preparePathSpline(p.traj_points, params_.preprocessing.smooth_reference);
396+
std::vector<sampler_common::Path> candidates;
397+
if (!prev_path_ || prev_path_->points.size() < 2) return candidates;
398+
// Update previous path
387399
sampler_common::State current_state;
388400
current_state.pose = {planner_data.ego_pose.position.x, planner_data.ego_pose.position.y};
389401
current_state.heading = tf2::getYaw(planner_data.ego_pose.orientation);
402+
const auto closest_iter = std::min_element(
403+
prev_path_->points.begin(), prev_path_->points.end() - 1,
404+
[&](const auto & p1, const auto & p2) {
405+
return boost::geometry::distance(p1, current_state.pose) <=
406+
boost::geometry::distance(p2, current_state.pose);
407+
});
408+
if (closest_iter != prev_path_->points.end()) {
409+
const auto current_idx = std::distance(prev_path_->points.begin(), closest_iter);
410+
const auto length_offset = prev_path_->lengths[current_idx];
411+
for (auto & l : prev_path_->lengths) l -= length_offset;
412+
constexpr auto behind_dist = -5.0; // [m] TODO(Maxime): param
413+
auto behind_idx = current_idx;
414+
while (behind_idx > 0 && prev_path_->lengths[behind_idx] > behind_dist) --behind_idx;
415+
*prev_path_ = *prev_path_->subset(behind_idx, prev_path_->points.size());
416+
417+
// Use previous path for replanning
418+
const auto prev_path_length = prev_path_->lengths.back();
419+
const auto reuse_step = prev_path_length / params_.sampling.previous_path_reuse_points_nb;
420+
for (double reuse_length = reuse_step; reuse_length <= prev_path_length;
421+
reuse_length += reuse_step) {
422+
sampler_common::State reuse_state;
423+
size_t reuse_idx = 0;
424+
for (reuse_idx = 0; reuse_idx + 1 < prev_path_->lengths.size() &&
425+
prev_path_->lengths[reuse_idx] < reuse_length;
426+
++reuse_idx)
427+
;
428+
if (reuse_idx == 0UL) continue;
429+
const auto reused_path = *prev_path_->subset(0UL, reuse_idx);
430+
reuse_state.curvature = reused_path.curvatures.back();
431+
reuse_state.pose = reused_path.points.back();
432+
reuse_state.heading = reused_path.yaws.back();
433+
reuse_state.frenet = path_spline.frenet(reuse_state.pose);
434+
auto paths = generateCandidatePaths(reuse_state, path_spline, reuse_length, params_);
435+
for (auto & p : paths) candidates.push_back(reused_path.extend(p));
436+
}
437+
}
438+
return candidates;
439+
}
390440

391-
const auto planning_state = getPlanningState(current_state, path_spline);
392-
prepareConstraints(params_.constraints, *in_objects_ptr_, p.left_bound, p.right_bound);
393-
394-
auto candidate_paths = generateCandidatePaths(planning_state, path_spline, 0.0, params_);
395-
if (prev_path_ && prev_path_->lengths.size() > 1) {
396-
// Update previous path
397-
constexpr auto max_deviation = 2.0; // [m] TODO(Maxime): param
398-
const auto closest_iter = std::min_element(
399-
prev_path_->points.begin(), prev_path_->points.end() - 1,
400-
[&](const auto & p1, const auto & p2) {
401-
return boost::geometry::distance(p1, current_state.pose) <=
402-
boost::geometry::distance(p2, current_state.pose);
403-
});
404-
if (
405-
closest_iter != prev_path_->points.end() &&
406-
boost::geometry::distance(*closest_iter, current_state.pose) <= max_deviation) {
407-
const auto current_idx = std::distance(prev_path_->points.begin(), closest_iter);
408-
const auto length_offset = prev_path_->lengths[current_idx];
409-
for (auto & l : prev_path_->lengths) l -= length_offset;
410-
constexpr auto behind_dist = -5.0; // [m] TODO(Maxime): param
411-
auto behind_idx = current_idx;
412-
while (behind_idx > 0 && prev_path_->lengths[behind_idx] > behind_dist) --behind_idx;
413-
*prev_path_ = *prev_path_->subset(behind_idx, prev_path_->points.size());
414-
415-
// Use previous path for replanning
416-
const auto prev_path_length = prev_path_->lengths.back();
417-
const auto reuse_step = prev_path_length / params_.sampling.previous_path_reuse_points_nb;
418-
for (double reuse_length = reuse_step; reuse_length <= prev_path_length;
419-
reuse_length += reuse_step) {
420-
sampler_common::State reuse_state;
421-
size_t reuse_idx = 0;
422-
for (reuse_idx = 0; reuse_idx + 1 < prev_path_->lengths.size() &&
423-
prev_path_->lengths[reuse_idx] < reuse_length;
424-
++reuse_idx)
425-
;
426-
if (reuse_idx == 0UL) continue;
427-
const auto reused_path = *prev_path_->subset(0UL, reuse_idx);
428-
reuse_state.curvature = reused_path.curvatures.back();
429-
reuse_state.pose = reused_path.points.back();
430-
reuse_state.heading = reused_path.yaws.back();
431-
reuse_state.frenet = path_spline.frenet(reuse_state.pose);
432-
auto paths = generateCandidatePaths(reuse_state, path_spline, reuse_length, params_);
433-
for (auto & p : paths) candidate_paths.push_back(reused_path.extend(p));
434-
}
441+
sampler_common::Path PathSampler::generatePath(const PlannerData & planner_data)
442+
{
443+
time_keeper_ptr_->tic(__func__);
444+
sampler_common::Path generated_path{};
445+
446+
if (prev_path_ && prev_path_->points.size() > 1) {
447+
sampler_common::constraints::checkHardConstraints(*prev_path_, params_.constraints);
448+
if (prev_path_->constraint_results.isValid()) {
449+
const auto prev_path_spline =
450+
preparePathSpline(trajectory_utils::convertToTrajectoryPoints(*prev_path_), false);
451+
const auto frenet_p = prev_path_spline.frenet(
452+
{planner_data.ego_pose.position.x, planner_data.ego_pose.position.y});
453+
if (std::abs(frenet_p.d) > params_.path_reuse.max_lat_dev || frenet_p.s < 0.0)
454+
resetPreviousData();
455+
else if (frenet_p.s <= params_.path_reuse.direct_reuse_dist)
456+
return *prev_path_;
435457
} else {
436458
resetPreviousData();
437459
}
438460
}
461+
const auto path_spline =
462+
preparePathSpline(planner_data.traj_points, params_.preprocessing.smooth_reference);
463+
sampler_common::State current_state;
464+
current_state.pose = {planner_data.ego_pose.position.x, planner_data.ego_pose.position.y};
465+
current_state.heading = tf2::getYaw(planner_data.ego_pose.orientation);
466+
467+
const auto planning_state = getPlanningState(current_state, path_spline);
468+
prepareConstraints(
469+
params_.constraints, *in_objects_ptr_, planner_data.left_bound, planner_data.right_bound);
470+
471+
auto candidate_paths = generateCandidatePaths(planning_state, path_spline, 0.0, params_);
472+
const auto candidates_from_prev_path =
473+
generateCandidatesFromPreviousPath(planner_data, path_spline);
474+
candidate_paths.insert(
475+
candidate_paths.end(), candidates_from_prev_path.begin(), candidates_from_prev_path.end());
439476
debug_data_.footprints.clear();
440477
for (auto & path : candidate_paths) {
441478
const auto footprint =
@@ -457,9 +494,7 @@ std::vector<TrajectoryPoint> PathSampler::generatePath(const PlannerData & plann
457494
};
458495
const auto selected_path_idx = best_path_idx(candidate_paths);
459496
if (selected_path_idx) {
460-
const auto & selected_path = candidate_paths[*selected_path_idx];
461-
trajectory = trajectory_utils::convertToTrajectoryPoints(selected_path);
462-
prev_path_ = selected_path;
497+
generated_path = candidate_paths[*selected_path_idx];
463498
} else {
464499
RCLCPP_WARN(
465500
get_logger(), "No valid path found (out of %lu) outputting %s\n", candidate_paths.size(),
@@ -468,18 +503,28 @@ std::vector<TrajectoryPoint> PathSampler::generatePath(const PlannerData & plann
468503
int da = 0;
469504
int k = 0;
470505
for (const auto & p : candidate_paths) {
471-
coll += static_cast<int>(!p.constraint_results.collision);
472-
da += static_cast<int>(!p.constraint_results.drivable_area);
473-
k += static_cast<int>(!p.constraint_results.curvature);
506+
coll += static_cast<int>(!p.constraint_results.collision_free);
507+
da += static_cast<int>(!p.constraint_results.inside_drivable_area);
508+
k += static_cast<int>(!p.constraint_results.valid_curvature);
474509
}
475510
RCLCPP_WARN(get_logger(), "\tInvalid coll/da/k = %d/%d/%d\n", coll, da, k);
476-
if (prev_path_) trajectory = trajectory_utils::convertToTrajectoryPoints(*prev_path_);
511+
if (prev_path_) generated_path = *prev_path_;
477512
}
478513
time_keeper_ptr_->toc(__func__, " ");
479514
debug_data_.previous_sampled_candidates_nb = debug_data_.sampled_candidates.size();
480515
debug_data_.sampled_candidates = candidate_paths;
481516
debug_data_.obstacles = params_.constraints.obstacle_polygons;
482-
return trajectory;
517+
518+
prev_path_ = generated_path;
519+
return generated_path;
520+
}
521+
522+
std::vector<TrajectoryPoint> PathSampler::generateTrajectoryPoints(const PlannerData & planner_data)
523+
{
524+
std::vector<TrajectoryPoint> trajectory;
525+
time_keeper_ptr_->tic(__func__);
526+
const auto path = generatePath(planner_data);
527+
return trajectory_utils::convertToTrajectoryPoints(path);
483528
}
484529

485530
void PathSampler::publishVirtualWall(const geometry_msgs::msg::Pose & stop_pose) const

planning/sampling_based_planner/sampler_common/include/sampler_common/constraints/hard_constraint.hpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ namespace sampler_common::constraints
2222
{
2323
/// @brief Check if the path satisfies the hard constraints
2424
MultiPoint2d checkHardConstraints(Path & path, const Constraints & constraints);
25-
bool has_collision(const MultiPoint2d & footprint, const MultiPolygon2d & obstacles);
25+
bool has_collision(
26+
const MultiPoint2d & footprint, const MultiPolygon2d & obstacles,
27+
const double min_distance = 0.0);
2628
bool satisfyMinMax(const std::vector<double> & values, const double min, const double max);
2729

2830
} // namespace sampler_common::constraints

planning/sampling_based_planner/sampler_common/include/sampler_common/structures.hpp

+11-6
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,16 @@ typedef boost::geometry::index::rtree<BoxIndexPair, boost::geometry::index::rsta
5050
/// @brief data about constraint check results of a given path
5151
struct ConstraintResults
5252
{
53-
bool collision = true;
54-
bool curvature = true;
55-
bool drivable_area = true;
53+
bool collision_free = true;
54+
bool valid_curvature = true;
55+
bool inside_drivable_area = true;
5656

57-
[[nodiscard]] bool isValid() const { return collision && curvature && drivable_area; }
57+
[[nodiscard]] bool isValid() const
58+
{
59+
return collision_free && valid_curvature && inside_drivable_area;
60+
}
5861

59-
void clear() { collision = curvature = drivable_area = true; }
62+
void clear() { collision_free = valid_curvature = inside_drivable_area = true; }
6063
};
6164
struct FrenetPoint
6265
{
@@ -136,9 +139,9 @@ struct Path
136139
dest.insert(dest.end(), std::next(second.begin(), offset), second.end());
137140
};
138141
ext(extended_path.points, points, path.points);
142+
if (!poses.empty()) ext(extended_path.poses, poses, path.poses);
139143
ext(extended_path.curvatures, curvatures, path.curvatures);
140144
ext(extended_path.yaws, yaws, path.yaws);
141-
ext(extended_path.poses, poses, path.poses);
142145
extended_path.lengths.insert(extended_path.lengths.end(), lengths.begin(), lengths.end());
143146
const auto last_base_length = lengths.empty() ? 0.0 : lengths.back() + length_offset;
144147
for (size_t i = offset; i < path.lengths.size(); ++i)
@@ -346,6 +349,8 @@ struct Constraints
346349
{
347350
double min_curvature;
348351
double max_curvature;
352+
double min_dist_from_obstacles;
353+
bool limit_footprint_inside_drivable_area;
349354
} hard{};
350355
LinearRing2d ego_footprint;
351356
double ego_width;

0 commit comments

Comments
 (0)