16 #ifndef NAV2_CONSTRAINED_SMOOTHER__SMOOTHER_HPP_
17 #define NAV2_CONSTRAINED_SMOOTHER__SMOOTHER_HPP_
29 #include "nav2_constrained_smoother/smoother_cost_function.hpp"
30 #include "nav2_constrained_smoother/utils.hpp"
31 #include "nav2_core/smoother_exceptions.hpp"
33 #include "ceres/ceres.h"
36 namespace nav2_constrained_smoother
62 debug_ = params.debug;
64 options_.linear_solver_type = params.solver_types.at(params.linear_solver_type);
66 options_.max_num_iterations = params.max_iterations;
68 options_.function_tolerance = params.fn_tol;
69 options_.gradient_tolerance = params.gradient_tol;
70 options_.parameter_tolerance = params.param_tol;
73 options_.minimizer_progress_to_stdout =
true;
74 options_.logging_type = ceres::LoggingType::PER_MINIMIZER_ITERATION;
76 options_.logging_type = ceres::SILENT;
90 std::vector<Eigen::Vector3d> & path,
91 const Eigen::Vector2d & start_dir,
92 const Eigen::Vector2d & end_dir,
97 if (path.size() < 2) {
101 options_.max_solver_time_in_seconds = params.max_time;
103 ceres::Problem problem;
104 std::vector<Eigen::Vector3d> path_optim;
105 std::vector<bool> optimized;
106 if (buildProblem(path, costmap, params, problem, path_optim, optimized)) {
108 ceres::Solver::Summary summary;
109 ceres::Solve(options_, &problem, &summary);
111 RCLCPP_INFO(rclcpp::get_logger(
"smoother_server"),
"%s", summary.FullReport().c_str());
113 if (!summary.IsSolutionUsable() || summary.initial_cost - summary.final_cost < 0.0) {
117 RCLCPP_INFO(rclcpp::get_logger(
"smoother_server"),
"Path too short to optimize");
120 upsampleAndPopulate(path_optim, optimized, start_dir, end_dir, params, path);
137 const std::vector<Eigen::Vector3d> & path,
140 ceres::Problem & problem,
141 std::vector<Eigen::Vector3d> & path_optim,
142 std::vector<bool> & optimized)
145 costmap_grid_ = std::make_shared<ceres::Grid2D<unsigned char>>(
147 auto costmap_interpolator =
148 std::make_shared<ceres::BiCubicInterpolator<ceres::Grid2D<unsigned char>>>(*costmap_grid_);
151 const double cusp_half_length = params.cusp_zone_length / 2;
152 ceres::LossFunction * loss_function = NULL;
154 optimized = std::vector<bool>(path.size());
158 double last_direction = path_optim[0][2];
159 bool last_was_cusp =
false;
160 bool last_is_reversing =
false;
161 std::deque<std::pair<double, SmootherCostFunction *>> potential_cusp_funcs;
162 double last_segment_len = EPSILON;
163 double potential_cusp_funcs_len = 0;
164 double len_since_cusp = std::numeric_limits<double>::infinity();
166 for (
size_t i = 1; i < path_optim.size(); i++) {
167 auto & pt = path_optim[i];
168 bool is_cusp =
false;
169 if (i != path_optim.size() - 1) {
170 is_cusp = pt[2] * last_direction < 0;
171 last_direction = pt[2];
175 i > (params.keep_start_orientation ? 1 : 0) &&
176 i < path_optim.size() - (params.keep_goal_orientation ? 2 : 1) &&
177 static_cast<int>(i - last_i) < params.path_downsampling_factor)
185 double current_segment_len = (path_optim[i] - path_optim[last_i]).block<2, 1>(0, 0).norm();
188 potential_cusp_funcs_len += current_segment_len;
189 while (!potential_cusp_funcs.empty() && potential_cusp_funcs_len > cusp_half_length) {
190 potential_cusp_funcs_len -= potential_cusp_funcs.front().first;
191 potential_cusp_funcs.pop_front();
196 double len_to_cusp = current_segment_len;
197 for (
int i_cusp = potential_cusp_funcs.size() - 1; i_cusp >= 0; i_cusp--) {
198 auto & f = potential_cusp_funcs[i_cusp];
200 params.cusp_costmap_weight * (1.0 - len_to_cusp / cusp_half_length) +
201 params.costmap_weight * len_to_cusp / cusp_half_length;
202 if (std::abs(new_weight - params.cusp_costmap_weight) <
203 std::abs(f.second->getCostmapWeight() - params.cusp_costmap_weight))
205 f.second->setCostmapWeight(new_weight);
207 len_to_cusp += f.first;
209 potential_cusp_funcs_len = 0;
210 potential_cusp_funcs.clear();
216 if (prelast_i != -1) {
217 double costmap_weight = params.costmap_weight;
218 if (len_since_cusp <= cusp_half_length) {
220 params.cusp_costmap_weight * (1.0 - len_since_cusp / cusp_half_length) +
221 params.costmap_weight * len_since_cusp / cusp_half_length;
223 SmootherCostFunction * cost_function =
new SmootherCostFunction(
224 path[last_i].
template block<2, 1>(
227 (last_was_cusp ? -1 : 1) * last_segment_len / current_segment_len,
230 costmap_interpolator,
234 problem.AddResidualBlock(
235 cost_function->AutoDiff(), loss_function,
236 path_optim[last_i].data(), pt.data(), path_optim[prelast_i].data());
238 potential_cusp_funcs.emplace_back(current_segment_len, cost_function);
242 last_was_cusp = is_cusp;
243 last_is_reversing = last_direction < 0;
246 len_since_cusp += current_segment_len;
247 last_segment_len = std::max(EPSILON, current_segment_len);
250 int posesToOptimize = problem.NumParameterBlocks() - 2;
251 if (params.keep_goal_orientation) {
252 posesToOptimize -= 1;
254 if (params.keep_start_orientation) {
255 posesToOptimize -= 1;
257 if (posesToOptimize <= 0) {
261 problem.SetParameterBlockConstant(path_optim.front().data());
262 if (params.keep_start_orientation) {
263 problem.SetParameterBlockConstant(path_optim[1].data());
265 if (params.keep_goal_orientation) {
266 problem.SetParameterBlockConstant(path_optim[path_optim.size() - 2].data());
268 problem.SetParameterBlockConstant(path_optim.back().data());
281 void upsampleAndPopulate(
282 const std::vector<Eigen::Vector3d> & path_optim,
283 const std::vector<bool> & optimized,
284 const Eigen::Vector2d & start_dir,
285 const Eigen::Vector2d & end_dir,
286 const SmootherParams & params,
287 std::vector<Eigen::Vector3d> & path)
291 if (params.path_upsampling_factor > 1) {
292 path.reserve(params.path_upsampling_factor * (path_optim.size() - 1) + 1);
296 Eigen::Vector2d prelast_dir = {0, 0};
297 for (
int i = 1; i <= static_cast<int>(path_optim.size()); i++) {
298 if (i ==
static_cast<int>(path_optim.size()) || optimized[i]) {
299 if (prelast_i != -1) {
300 Eigen::Vector2d last_dir;
301 auto & prelast = path_optim[prelast_i];
302 auto & last = path_optim[last_i];
305 if (i <
static_cast<int>(path_optim.size())) {
306 auto & current = path_optim[i];
307 Eigen::Vector2d tangent_dir = tangentDir<double>(
308 prelast.block<2, 1>(0, 0),
309 last.block<2, 1>(0, 0),
310 current.block<2, 1>(0, 0),
311 prelast[2] * last[2] < 0);
314 tangent_dir.dot((current - last).block<2, 1>(0, 0) * last[2]) >= 0 ?
317 last_dir.normalize();
318 }
else if (params.keep_goal_orientation) {
321 last_dir = (last - prelast).block<2, 1>(0, 0) * last[2];
322 last_dir.normalize();
324 double last_angle = atan2(last_dir[1], last_dir[0]);
327 int interp_cnt = (last_i - prelast_i) * params.path_upsampling_factor - 1;
328 if (interp_cnt > 0) {
329 Eigen::Vector2d last_pt = last.block<2, 1>(0, 0);
330 Eigen::Vector2d prelast_pt = prelast.block<2, 1>(0, 0);
331 double dist = (last_pt - prelast_pt).norm();
332 Eigen::Vector2d pt1 = prelast_pt + prelast_dir * dist * 0.4 * prelast[2];
333 Eigen::Vector2d pt2 = last_pt - last_dir * dist * 0.4 * prelast[2];
334 for (
int j = 1; j <= interp_cnt; j++) {
335 double interp = j /
static_cast<double>(interp_cnt + 1);
336 Eigen::Vector2d pt = cubicBezier(prelast_pt, pt1, pt2, last_pt, interp);
337 path.emplace_back(pt[0], pt[1], 0.0);
340 path.emplace_back(last[0], last[1], last_angle);
343 for (
size_t j = path.size() - 1 - interp_cnt; j < path.size() - 1; j++) {
344 Eigen::Vector2d tangent_dir = tangentDir<double>(
345 path[j - 1].block<2, 1>(0, 0),
346 path[j].block<2, 1>(0, 0),
347 path[j + 1].block<2, 1>(0, 0),
350 tangent_dir.dot((path[j + 1] - path[j]).block<2, 1>(0, 0) * prelast[2]) >= 0 ?
353 path[j][2] = atan2(tangent_dir[1], tangent_dir[0]);
356 prelast_dir = last_dir;
358 auto & start = path_optim[0];
359 Eigen::Vector2d dir = params.keep_start_orientation ?
361 ((path_optim[i] - start).block<2, 1>(0, 0) * start[2]).normalized();
362 path.emplace_back(start[0], start[1], atan2(dir[1], dir[0]));
376 static Eigen::Vector2d cubicBezier(
377 Eigen::Vector2d & pt0, Eigen::Vector2d & pt1,
378 Eigen::Vector2d & pt2, Eigen::Vector2d & pt3,
double mu)
380 Eigen::Vector2d a, b, c, pt;
382 c[0] = 3 * (pt1[0] - pt0[0]);
383 c[1] = 3 * (pt1[1] - pt0[1]);
384 b[0] = 3 * (pt2[0] - pt1[0]) - c[0];
385 b[1] = 3 * (pt2[1] - pt1[1]) - c[1];
386 a[0] = pt3[0] - pt0[0] - c[0] - b[0];
387 a[1] = pt3[1] - pt0[1] - c[1] - b[1];
389 pt[0] = a[0] * mu * mu * mu + b[0] * mu * mu + c[0] * mu + pt0[0];
390 pt[1] = a[1] * mu * mu * mu + b[1] * mu * mu + c[1] * mu + pt0[1];
396 ceres::Solver::Options options_;
397 std::shared_ptr<ceres::Grid2D<unsigned char>> costmap_grid_;
Smoother()
A constructor for nav2_smac_planner::Smoother.
bool smooth(std::vector< Eigen::Vector3d > &path, const Eigen::Vector2d &start_dir, const Eigen::Vector2d &end_dir, const nav2_costmap_2d::Costmap2D *costmap, const SmootherParams ¶ms)
Smoother method.
void initialize(const OptimizerParams params)
Initialization of the smoother.
~Smoother()
A destructor for nav2_smac_planner::Smoother.
A 2D costmap provides a mapping between points in the world and their associated "costs".
unsigned char * getCharMap() const
Will return a pointer to the underlying unsigned char array used as the costmap.
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.