13#include <ceres/ceres.h>
15#if CERES_VERSION_MAJOR >= 2 && CERES_VERSION_MINOR >= 1
16# include <ceres/manifold.h>
18# define SET_CONSTRAINT(problem, param, constraint) \
19 (problem).SetManifold((param), (constraint))
21# include <ceres/local_parameterization.h>
23# define SET_CONSTRAINT(problem, param, constraint) \
24 (problem).SetParameterization((param), (constraint))
53 return ceres::atan2(ceres::sin(diff), ceres::cos(diff));
59 return std::atan2(std::sin(a), std::cos(a));
63 normalizeTrajectoryAngles(std::vector<CenterPoint>& traj)
76 double t = (
x - edge0) / (edge1 - edge0);
77 return t * t * (3.0 - 2.0 * t);
82 double fade_start_clearance,
83 double fade_end_clearance)
86 return 1.0 -
smoothstep(fade_start_clearance, fade_end_clearance, clearance);
95 if constexpr (std::is_arithmetic_v<T>)
101 template <
typename T>
105 if constexpr (std::is_arithmetic_v<T>)
108 return ceres::log(
x);
116 template <
typename T>
122 if (values.size() == 1)
125 T min_val = values[0];
126 for (
const auto& v : values)
131 for (
const auto& v : values)
133 T arg = -alpha * (v - min_val);
143 static std::pair<int, int>
144 getDiscreteOrientationBracket(
double theta_rad,
int num_orientations)
146 double deg = theta_rad * 180.0 /
M_PI;
153 double deg_per = 360.0 / num_orientations;
154 int lower =
static_cast<int>(std::floor(deg / deg_per));
155 int upper = lower + 1;
157 lower += num_orientations;
158 if (upper >= num_orientations)
159 upper -= num_orientations;
160 return {lower, upper};
175 template <
typename T>
179 const T*
const c_next,
182 residual[0] =
T(
pos_w) * (c_prev[0] -
T(2.0) * c_i[0] + c_next[0]);
183 residual[1] =
T(
pos_w) * (c_prev[1] -
T(2.0) * c_i[1] + c_next[1]);
187 residual[2] =
T(
ori_w) * (dprev - dnext);
202 template <
typename T>
204 operator()(
const T*
const theta0,
const T*
const theta1,
T* residuals)
const
208 residuals[0] =
T(
w_) * d;
223 template <
typename T>
243 template <
typename T>
247 T dv = c_ip1[3] - c_i[3];
253 residual[0] =
T(0.0);
270 template <
typename T>
292 template <
typename T>
297 if (v_above_min <
T(0))
299 residual[0] =
T(
w) * v_above_min *
T(
danger_);
315 template <
typename T>
323 residual[0] =
T(
w) * (c_m2[0] -
T(3.0) * c_m1[0] +
T(3.0) * c_i[0] - c_p1[0]);
324 residual[1] =
T(
w) * (c_m2[1] -
T(3.0) * c_m1[1] +
T(3.0) * c_i[1] - c_p1[1]);
326 T a_m2 = c_m2[2], a_m1 = c_m1[2], a_i = c_i[2], a_p1 = c_p1[2];
329 residual[2] =
T(
w) * r;
351 bool use_fuzzy_orientation =
true,
352 int num_orientations = 72,
353 int fuzzy_window_bins = 2,
354 double fuzzy_alpha = 5.0) :
367 template <
typename T>
374 T theta_query = node[2];
384 std::vector<T> samples;
388 T theta_sample = theta_query +
T(
static_cast<double>(k) * bin_size);
390 wrapper_(&node[0], &node[1], &theta_sample, &d_sample);
391 samples.push_back(d_sample);
397 wrapper_(&node[0], &node[1], &theta_query, &obs_dist_raw);
402 if (!ceres::isfinite(obs_dist_raw))
404 residual[0] =
T(
w_) *
T(100.0);
410 if (obs_dist_raw < min_allowed)
411 obs_dist_raw = min_allowed;
417 if (obs_dist >=
T(0.0))
420 residual[0] =
T(0.0);
427 T penetration = -obs_dist;
429 if (penetration > max_pen)
430 penetration = max_pen;
435 T r =
T(0.5) * (ceres::sqrt(penetration * penetration + eps * eps) + penetration)
437 residual[0] =
T(
w_) * r;
464 bool use_fuzzy_orientation =
true,
465 int num_orientations = 72,
466 int fuzzy_window_bins = 2,
467 double fuzzy_alpha = 5.0) :
480 template <
typename T>
484 T mx = (c_i[0] + c_ip1[0]) /
T(2.0);
485 T my = (c_i[1] + c_ip1[1]) /
T(2.0);
486 T mtheta = c_i[2] +
angleDiff(c_ip1[2], c_i[2]) /
T(2.0);
490 T theta_query = mtheta;
499 std::vector<T> samples;
503 T theta_sample = theta_query +
T(
static_cast<double>(k) * bin_size);
505 wrapper_(&mx, &my, &theta_sample, &d_sample);
506 samples.push_back(d_sample);
512 wrapper_(&mx, &my, &theta_query, &obs_dist_raw);
517 if (!ceres::isfinite(obs_dist_raw))
519 residual[0] =
T(
w_) *
T(100.0);
525 if (obs_dist_raw < min_allowed)
526 obs_dist_raw = min_allowed;
530 if (obs_dist >=
T(0.0))
533 residual[0] =
T(0.0);
538 T penetration = -obs_dist;
540 if (penetration > max_pen)
541 penetration = max_pen;
544 T r =
T(0.5) * (ceres::sqrt(penetration * penetration + eps * eps) + penetration)
546 residual[0] =
T(
w_) * r;
577 template <
typename T>
581 residual[0] =
T(
wx_) * (c_i[0] -
T(
xr_));
582 residual[1] =
T(
wy_) * (c_i[1] -
T(
yr_));
584 residual[3] =
T(
wv_) * (c_i[3] -
T(
vr_));
599 template <
typename T>
606 residual[0] =
T(
w) * diff;
608 residual[0] =
T(0.0);
625 template <
typename T>
629 T cx =
c[0], cy =
c[1], cth =
c[2];
630 T ccos = ceres::cos(cth), csin = ceres::sin(cth);
631 rx = cx + ccos *
T(
dx_) - csin *
T(
dy_);
632 ry = cy + csin *
T(
dx_) + ccos *
T(
dy_);
636 template <
typename T>
640 const T*
const c_next,
643 T rpx, rpy, rpth, rix, riy, rith, rnx, rny, rnth;
647 residual[0] =
T(
w) * (rpx -
T(2.0) * rix + rnx);
648 residual[1] =
T(
w) * (rpy -
T(2.0) * riy + rny);
673 template <
typename T>
677 T cx =
c[0], cy =
c[1], cth =
c[2];
678 T ccos = ceres::cos(cth), csin = ceres::sin(cth);
679 rx = cx + ccos *
T(-0.5) - csin *
T(0.0);
680 ry = cy + csin *
T(-0.5) + ccos *
T(0.0);
683 template <
typename T>
694 const T dist_eps =
T(1e-6);
695 T dist = ceres::sqrt(dx * dx + dy * dy + dist_eps * dist_eps);
698 residual[0] =
T(
w_) *
T(0.5)
699 * (ceres::sqrt(y * y +
T(
eps_ *
eps_)) + y);
716 template <
typename T>
720 T dx = c_ip1[0] - c_i[0];
721 T dy = c_ip1[1] - c_i[1];
722 T dist_sq = dx * dx + dy * dy;
724 residual[0] =
T(
w) * (dist_sq - target_sq);
739 double linear_weight,
740 double quadratic_weight,
741 double relative_deviation_threshold) :
749 template <
typename T>
753 T dx = c_ip1[0] - c_i[0];
754 T dy = c_ip1[1] - c_i[1];
755 T dist_sq = dx * dx + dy * dy;
757 T dist = ceres::sqrt(dist_sq +
T(1e-12));
758 T rel_dev = ceres::abs(dist /
T(
d_avg_) -
T(1.0));
782 return std::hypot(b.
x - a.x, b.
y - a.y);
786 computeAverageSpacing(
const std::vector<CenterPoint>& traj)
791 for (
size_t i = 0; i + 1 < traj.size(); ++i)
792 sum += segment_length(traj[i], traj[i + 1]);
793 return sum / double(traj.size() - 1);
800 logResidualBreakdown(
const std::vector<CenterPoint>& traj,
801 const Costmap3DWrapper& wrapper,
802 const std::vector<CenterPoint>&
targets,
803 const std::vector<double>& tracking_scales,
804 const io::SmoothingParams& opts)
806 const int N =
static_cast<int>(traj.size());
807 double c_obs = 0.0, c_mid_obs = 0.0, c_pose_smooth = 0.0, c_start_rot = 0.0,
808 c_vel_smooth = 0.0, c_jerk = 0.0, c_robot_smooth = 0.0, c_spacing = 0.0,
809 c_tracking = 0.0, c_robot_pose_proximity = 0.0;
811 auto evalObsAtTheta = [&](
double x,
double y,
double th) ->
double
814 wrapper(&
x, &y, &th, &d_raw);
815 double d = d_raw - opts.clearance;
819 double max_pen = 3.0 * opts.obs_max_distance;
822 const double eps = opts.obs_barrier_eps_mm;
824 const double r = 0.5 * (std::sqrt(pen * pen + eps * eps) + pen) - 0.5 * eps;
825 return opts.w_obs * r;
830 auto evalObsFuzzy = [&](
double x,
double y,
double theta_query) ->
double
832 if (!opts.use_fuzzy_orientation)
833 return evalObsAtTheta(
x, y, theta_query);
836 if (opts.fuzzy_orientation_window_bins > 0)
840 const double bin_size = 2.0 *
M_PI / 72.0;
841 std::vector<double> samples;
842 samples.reserve(2 * opts.fuzzy_orientation_window_bins + 1);
843 for (
int k = -opts.fuzzy_orientation_window_bins;
844 k <= opts.fuzzy_orientation_window_bins;
847 double theta_sample = theta_query +
static_cast<double>(k) * bin_size;
849 wrapper(&
x, &y, &theta_sample, &d_sample);
850 samples.push_back(d_sample);
852 d_raw =
smoothMin(samples, opts.fuzzy_orientation_alpha);
856 wrapper(&x, &y, &theta_query, &d_raw);
860 const double min_allowed = -3.0 * opts.obs_max_distance;
861 if (d_raw < min_allowed)
864 double d = d_raw - opts.clearance;
868 double max_pen = 3.0 * opts.obs_max_distance;
871 const double eps = opts.obs_barrier_eps_mm;
873 const double r = 0.5 * (std::sqrt(pen * pen + eps * eps) + pen) - 0.5 * eps;
874 return opts.w_obs * r;
882 for (
int i = 0; i < N; ++i)
884 double r = evalObsFuzzy(traj[i].x, traj[i].y, traj[i].theta);
887 for (
int i = 0; i < N - 1; ++i)
891 +
angleDiff(traj[i + 1].theta, traj[i].theta) / 2.0);
892 double mx = (traj[i].x + traj[i + 1].x) / 2.0;
893 double my = (traj[i].y + traj[i + 1].y) / 2.0;
894 double r = evalObsFuzzy(mx, my, midTheta);
900 for (
int i = 1; i <= N - 2; ++i)
902 double rx = traj[i - 1].x - 2.0 * traj[i].x + traj[i + 1].x;
903 double ry = traj[i - 1].y - 2.0 * traj[i].y + traj[i + 1].y;
906 double rth = dprev - dnext;
907 c_pose_smooth += (opts.w_pose_smooth * rx) * (opts.w_pose_smooth * rx)
908 + (opts.w_pose_smooth * ry) * (opts.w_pose_smooth * ry)
909 + (opts.w_orientation_smooth * rth) * (opts.w_orientation_smooth * rth);
916 c_start_rot += (opts.w_boundary * d) * (opts.w_boundary * d);
918 c_start_rot += (opts.w_boundary * d) * (opts.w_boundary * d);
922 for (
int i = 0; i < N - 1; ++i)
924 double dv = traj[i + 1].v - traj[i].v;
925 if (dv > opts.max_accel_per_segment)
926 c_vel_smooth += (opts.w_accel_decel_limit * (dv - opts.max_accel_per_segment))
927 * (opts.w_accel_decel_limit * (dv - opts.max_accel_per_segment));
928 else if (dv < -opts.max_decel_per_segment)
929 c_vel_smooth += (opts.w_accel_decel_limit * (-dv - opts.max_decel_per_segment))
930 * (opts.w_accel_decel_limit * (-dv - opts.max_decel_per_segment));
934 double v_nominal = (N >= 3) ? traj[1].v : traj.front().
v;
935 for (
int i = 1; i < N - 1; ++i)
937 double rv = traj[i].v - v_nominal;
938 c_vel_smooth += (opts.w_nominal_velocity * rv) * (opts.w_nominal_velocity * rv);
942 for (
int i = 1; i < N - 1; ++i)
945 wrapper(&traj[i].x, &traj[i].y, &traj[i].theta, &d);
946 double danger = std::clamp((opts.slow_down_distance - d) / opts.slow_down_distance,
949 double v_above_min = std::max(traj[i].v - opts.min_velocity_near_obstacle, 0.0);
951 (opts.w_velocity_proximity * v_above_min * danger)
952 * (opts.w_velocity_proximity * v_above_min * danger);
956 if (opts.use_jerk && N >= 4)
958 for (int i = 2; i <= N - 2; ++i)
960 double rx = traj[i - 2].x - 3.0 * traj[i - 1].x + 3.0 * traj[i].x - traj[i + 1].x;
961 double ry = traj[i - 2].y - 3.0 * traj[i - 1].y + 3.0 * traj[i].y - traj[i + 1].y;
962 double r = normalizeAngle(traj[i - 2].theta - traj[i].theta)
963 - 3.0 * normalizeAngle(traj[i - 1].theta - traj[i].theta)
964 + 3.0 * normalizeAngle(traj[i].theta - traj[i].theta)
965 - normalizeAngle(traj[i + 1].theta - traj[i].theta);
966 c_jerk += (opts.w_pose_jerk * rx) * (opts.w_pose_jerk * rx)
967 + (opts.w_pose_jerk * ry) * (opts.w_pose_jerk * ry)
968 + (opts.w_pose_jerk * r) * (opts.w_pose_jerk * r);
973 if (opts.use_robot_smooth)
975 for (int i = 1; i <= N - 2; ++i)
977 auto c2r = [](const CenterPoint& c, double& rx, double& ry, double& rth)
979 double ccos = std::cos(c.theta), csin = std::sin(c.theta);
980 rx = c.x + ccos * (-0.5) - csin * (0.0);
981 ry = c.y + csin * (-0.5) + ccos * (0.0);
984 double rpx, rpy, rpth, rix, riy, rith, rnx, rny, rnth;
985 c2r(traj[i - 1], rpx, rpy, rpth);
986 c2r(traj[i], rix, riy, rith);
987 c2r(traj[i + 1], rnx, rny, rnth);
988 double rx = opts.w_robot_smooth * (rpx - 2.0 * rix + rnx);
989 double ry = opts.w_robot_smooth * (rpy - 2.0 * riy + rny);
990 double rth = opts.w_robot_smooth
991 * (normalizeAngle(rpth - rith) - normalizeAngle(rnth - rith));
992 c_robot_smooth += rx * rx + ry * ry + rth * rth;
998 for (
int i = 0; i + 1 < N; ++i)
999 d_avg += std::hypot(traj[i + 1].
x - traj[i].
x, traj[i + 1].y - traj[i].y);
1004 for (
int i = 0; i + 1 < N; ++i)
1006 double dist = std::hypot(traj[i + 1].
x - traj[i].
x, traj[i + 1].y - traj[i].y);
1007 if (opts.use_soft_spacing)
1009 double rel_dev = std::abs(dist / d_avg - 1.0);
1010 double r = opts.w_spacing_linear * rel_dev;
1011 double excess = rel_dev - opts.spacing_relative_deviation_threshold;
1014 r += opts.w_spacing_quadratic * excess * excess;
1020 double r = opts.w_spacing * (dist * dist - d_avg * d_avg);
1026 if (opts.use_tracking &&
static_cast<int>(targets.size()) == N)
1028 for (
int i = 0; i < N; ++i)
1030 double s = tracking_scales[i];
1031 double wx = opts.w_track *
s, wy = opts.w_track *
s,
1032 wth = opts.w_track * 300.0 *
s, wv = opts.w_track * 0.2 *
s;
1033 double rx = wx * (traj[i].x - targets[i].x);
1034 double ry = wy * (traj[i].y - targets[i].y);
1035 double rth = wth *
normalizeAngle(traj[i].theta - targets[i].theta);
1036 double rv = wv * (traj[i].v - targets[i].v);
1037 c_tracking += rx * rx + ry * ry + rth * rth + rv * rv;
1042 if (opts.use_robot_pose_proximity &&
static_cast<int>(targets.size()) == N)
1044 auto c2r = [](
const CenterPoint&
c,
double& rx,
double& ry)
1046 double ccos = std::cos(
c.theta), csin = std::sin(
c.theta);
1047 rx =
c.x + ccos * (-0.5) - csin * (0.0);
1048 ry =
c.y + csin * (-0.5) + ccos * (0.0);
1050 const double eps_sq = opts.robot_pose_proximity_eps_mm
1051 * opts.robot_pose_proximity_eps_mm;
1052 for (
int i = 1; i <= N - 2; ++i)
1054 double rx0, ry0, rx, ry;
1055 c2r(targets[i], rx0, ry0);
1056 c2r(traj[i], rx, ry);
1057 const double dx = rx - rx0;
1058 const double dy = ry - ry0;
1059 const double dist = std::hypot(dx, dy);
1060 const double y = dist - opts.robot_pose_proximity_threshold_mm;
1061 const double r = opts.w_robot_pose_proximity * tracking_scales[i] * 0.5
1062 * (std::sqrt(y * y + eps_sq) + y);
1063 c_robot_pose_proximity += r * r;
1067 double total = c_obs + c_mid_obs + c_pose_smooth + c_start_rot + c_vel_smooth + c_jerk
1068 + c_robot_smooth + c_spacing + c_tracking + c_robot_pose_proximity;
1071 auto pct = [&](
double c) ->
double {
return total > 0.0 ?
c / total * 100.0 : 0.0; };
1072 ARMARX_INFO <<
"Obstacle (waypoint): " << std::scientific << std::setprecision(3)
1073 << c_obs <<
" (" << std::fixed << std::setprecision(1) << pct(c_obs) <<
"%)";
1074 ARMARX_INFO <<
"Obstacle (midpoint): " << std::scientific << std::setprecision(3)
1075 << c_mid_obs <<
" (" << std::fixed << std::setprecision(1) << pct(c_mid_obs)
1077 ARMARX_INFO <<
"Pose smoothness: " << std::scientific << std::setprecision(3)
1078 << c_pose_smooth <<
" (" << std::fixed << std::setprecision(1)
1079 << pct(c_pose_smooth) <<
"%)";
1080 ARMARX_INFO <<
"Start/end rotation: " << std::scientific << std::setprecision(3)
1081 << c_start_rot <<
" (" << std::fixed << std::setprecision(1)
1082 << pct(c_start_rot) <<
"%)";
1083 ARMARX_INFO <<
"Velocity smoothness: " << std::scientific << std::setprecision(3)
1084 << c_vel_smooth <<
" (" << std::fixed << std::setprecision(1)
1085 << pct(c_vel_smooth) <<
"%)";
1086 ARMARX_INFO <<
"Pose jerk: " << std::scientific << std::setprecision(3)
1087 << c_jerk <<
" (" << std::fixed << std::setprecision(1) << pct(c_jerk)
1089 ARMARX_INFO <<
"Robot smoothness: " << std::scientific << std::setprecision(3)
1090 << c_robot_smooth <<
" (" << std::fixed << std::setprecision(1)
1091 << pct(c_robot_smooth) <<
"%)";
1092 ARMARX_INFO <<
"Spacing: " << std::scientific << std::setprecision(3)
1093 << c_spacing <<
" (" << std::fixed << std::setprecision(1) << pct(c_spacing)
1095 ARMARX_INFO <<
"Tracking: " << std::scientific << std::setprecision(3)
1096 << c_tracking <<
" (" << std::fixed << std::setprecision(1)
1097 << pct(c_tracking) <<
"%)";
1098 ARMARX_INFO <<
"Robot-pose proximity: " << std::scientific << std::setprecision(3)
1099 << c_robot_pose_proximity <<
" (" << std::fixed << std::setprecision(1)
1100 << pct(c_robot_pose_proximity) <<
"%)";
1101 ARMARX_INFO <<
"TOTAL: " << std::scientific << std::setprecision(3)
1111 const std::vector<CenterPoint>& traj_targets,
1114 const int N =
static_cast<int>(traj.size());
1117 ARMARX_INFO <<
"[optimizeTrajectoryCeres] need at least 4 points\n";
1121 normalizeTrajectoryAngles(traj);
1126 for (
int i = 1; i < N - 1; ++i)
1128 double dx = traj[i].x - traj[i - 1].x;
1129 double dy = traj[i].y - traj[i - 1].y;
1130 if (std::hypot(dx, dy) < 1.0)
1132 double ndx = (i + 1 < N) ? (traj[i + 1].
x - traj[i].
x) : 1.0;
1133 double ndy = (i + 1 < N) ? (traj[i + 1].y - traj[i].y) : 0.0;
1134 double nlen = std::hypot(ndx, ndy);
1141 traj[i].x += 1.0 * ndx / nlen;
1142 traj[i].y += 1.0 * ndy / nlen;
1150 std::vector<double> tracking_scales(N, 1.0);
1154 for (
int i = 0; i < N; ++i)
1157 costmap_wrapper(&traj_targets[i].
x,
1159 &traj_targets[i].theta,
1171 std::vector<bool> freeze_pose(N,
false);
1174 for (
int i = 1; i <= N - 2; ++i)
1177 costmap_wrapper(&traj_targets[i].
x,
1179 &traj_targets[i].theta,
1184 std::string frozen_indices;
1185 for (
int i = 1; i <= N - 2; ++i)
1189 if (!frozen_indices.empty())
1190 frozen_indices +=
", ";
1191 frozen_indices += std::to_string(i);
1194 ARMARX_INFO <<
"[Pass 1] Frozen waypoints (clearance <= "
1196 << (frozen_indices.empty() ?
"none" : frozen_indices) <<
"]";
1201 ceres::Problem problem;
1202 for (
int i = 0; i < N; ++i)
1203 problem.AddParameterBlock(&traj[i].x, 4);
1206 problem.SetParameterBlockConstant(&traj.front().x);
1207 problem.SetParameterBlockConstant(&traj.back().x);
1211 for (
int i = 1; i < N - 1; ++i)
1215 problem.SetParameterBlockConstant(&traj[i].
x);
1219 std::vector<int> constant_v = {3};
1225 for (
int i = 1; i <= N - 2; ++i)
1227 problem.AddResidualBlock(
1228 new ceres::AutoDiffCostFunction<PoseSmoothResidual, 3, 4, 4, 4>(
1237 problem.AddResidualBlock(
1238 new ceres::AutoDiffCostFunction<StartRotationResidual, 1, 4, 4>(
1243 problem.AddResidualBlock(
1244 new ceres::AutoDiffCostFunction<StartRotationResidual, 1, 4, 4>(
1253 for (
int i = 2; i <= N - 2; ++i)
1255 problem.AddResidualBlock(
1256 new ceres::AutoDiffCostFunction<PoseJerkResidual, 3, 4, 4, 4, 4>(
1269 for (
int i = 1; i <= N - 2; ++i)
1271 problem.AddResidualBlock(
1272 new ceres::AutoDiffCostFunction<RobotSmoothResidual, 3, 4, 4, 4>(
1282 double d_avg = computeAverageSpacing(traj);
1285 for (
int i = 0; i < N - 1; ++i)
1289 problem.AddResidualBlock(
1290 new ceres::AutoDiffCostFunction<SoftSpacingResidual, 1, 4, 4>(
1301 problem.AddResidualBlock(
1302 new ceres::AutoDiffCostFunction<SpacingResidual, 1, 4, 4>(
1313 for (
int i = 0; i < N; ++i)
1315 problem.AddResidualBlock(
1316 new ceres::AutoDiffCostFunction<ObstacleResidual, 1, 4>(
1329 for (
int i = 0; i < N - 1; ++i)
1331 problem.AddResidualBlock(
1332 new ceres::AutoDiffCostFunction<MidpointObstacleResidual, 1, 4, 4>(
1349 for (
int i = 1; i <= N - 2; ++i)
1351 problem.AddResidualBlock(
1352 new ceres::AutoDiffCostFunction<ThetaTrackingResidual, 1, 4>(
1354 traj_targets[i].theta)),
1362 if (
static_cast<int>(traj_targets.size()) != N)
1364 ARMARX_INFO <<
"[Pass 1] traj_targets size mismatch; skipping tracking.\n";
1368 for (
int i = 0; i < N; ++i)
1370 double wx = opts.
w_track * tracking_scales[i];
1371 double wy = opts.
w_track * tracking_scales[i];
1372 double wth = opts.
w_track * 300.0 * tracking_scales[i];
1373 double wv = opts.
w_track * 0.2 * tracking_scales[i];
1374 problem.AddResidualBlock(
1375 new ceres::AutoDiffCostFunction<TrackingResidual, 4, 4>(
1378 traj_targets[i].theta,
1393 if (
static_cast<int>(traj_targets.size()) != N)
1395 ARMARX_INFO <<
"[Pass 1] traj_targets size mismatch; skipping robot-pose proximity.\n";
1399 for (
int i = 1; i <= N - 2; ++i)
1401 const double cth = traj_targets[i].theta;
1402 const double rx0 = traj_targets[i].x + std::cos(cth) * (-0.5);
1403 const double ry0 = traj_targets[i].y + std::sin(cth) * (-0.5);
1405 problem.AddResidualBlock(
1406 new ceres::AutoDiffCostFunction<RobotPoseProximityResidual, 1, 4>(
1411 * tracking_scales[i],
1420 ceres::Solver::Options options;
1422 options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
1423 options.minimizer_progress_to_stdout =
false;
1425 options.function_tolerance = 1e-6;
1426 options.parameter_tolerance = 1e-8;
1427 options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
1431 options.min_relative_decrease = 1e-3;
1435 for (
int i = 0; i < N; ++i)
1441 <<
"] before solve: x=" << traj[i].x <<
" y=" << traj[i].y
1442 <<
" theta=" << traj[i].theta <<
" v=" << traj[i].v;
1448 ARMARX_WARNING <<
"[Pass 1] Pre-solve validation FAILED. Aborting.";
1452 ARMARX_INFO <<
"[Pass 1] Pre-solve residual breakdown:";
1453 logResidualBreakdown(traj, costmap_wrapper, traj_targets, tracking_scales, opts);
1455 ceres::Solver::Summary summary;
1456 ceres::Solve(options, &problem, &summary);
1457 normalizeTrajectoryAngles(traj);
1459 ARMARX_INFO <<
"[Pass 1] Post-solve residual breakdown:";
1460 logResidualBreakdown(traj, costmap_wrapper, traj_targets, tracking_scales, opts);
1462 bool postSolveValid =
true;
1463 for (
int i = 0; i < N; ++i)
1469 <<
"] AFTER solve. Reverting to pre-processed trajectory.";
1470 postSolveValid =
false;
1473 if (!postSolveValid)
1475 for (
int i = 0; i < N; ++i)
1476 traj[i] = traj_targets[i];
1477 ARMARX_WARNING <<
"[Pass 1] Trajectory reverted to pre-processed safe path.";
1482 ARMARX_INFO <<
"[Pass 1] Done. Final cost: " << summary.final_cost
1483 <<
" iterations: " << summary.num_successful_steps <<
"\n";
1489 double v_nominal = (N >= 3) ? traj[1].v : traj.front().v;
1495 std::vector<double> dangers(N);
1496 for (
int i = 0; i < N; ++i)
1499 costmap_wrapper(&traj[i].
x, &traj[i].y, &traj[i].theta, &d);
1500 dangers[i] = std::clamp(
1504 ceres::Problem problem;
1505 for (
int i = 0; i < N; ++i)
1507 problem.AddParameterBlock(&traj[i].
x, 4);
1509 std::vector<int> constant_indices = {0, 1, 2};
1515 problem.SetParameterBlockConstant(&traj.front().x);
1516 problem.SetParameterBlockConstant(&traj.back().x);
1519 for (
int i = 0; i < N - 1; ++i)
1521 problem.AddResidualBlock(
1522 new ceres::AutoDiffCostFunction<AccelDecelLimitResidual, 1, 4, 4>(
1532 for (
int i = 1; i < N - 1; ++i)
1534 problem.AddResidualBlock(
1535 new ceres::AutoDiffCostFunction<NominalVelocityResidual, 1, 4>(
1542 for (
int i = 1; i < N - 1; ++i)
1544 problem.AddResidualBlock(
1545 new ceres::AutoDiffCostFunction<VelocityProximityResidual, 1, 4>(
1554 ceres::Solver::Options options;
1556 options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
1557 options.minimizer_progress_to_stdout =
false;
1559 options.function_tolerance = 1e-6;
1560 options.parameter_tolerance = 1e-8;
1561 options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
1565 options.min_relative_decrease = 1e-3;
1567 ceres::Solver::Summary summary;
1568 ceres::Solve(options, &problem, &summary);
1571 ARMARX_INFO <<
"[Pass 2] Done. Final cost: " << summary.final_cost
1572 <<
" iterations: " << summary.num_successful_steps <<
"\n";
1575 normalizeTrajectoryAngles(traj);
Brief description of class targets.
#define ARMARX_INFO
The normal logging level.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
double s(double t, double s0, double v0, double a0, double j)
double v(double t, double v0, double a0, double j)
T expForSmoothMin(const T &x)
T logForSmoothMin(const T &x)
This file is part of ArmarX.
void optimizeTrajectoryCeres(std::vector< CenterPoint > &traj, const Costmap3DWrapper &costmap_wrapper, const std::vector< CenterPoint > &traj_targets, const io::SmoothingParams &opts)
double smoothstep(double edge0, double edge1, double x)
double normalizeAngle(double a)
T angleDiff(const T &a, const T &b)
T smoothMin(const std::vector< T > &values, double alpha)
double trackingScaleFromClearance(double clearance, double fade_start_clearance, double fade_end_clearance)
This file offers overloads of toIce() and fromIce() functions for STL container types.
bool isfinite(const std::vector< T, Ts... > &v)
ceres::SubsetParameterization SubsetConstraint
This file is part of ArmarX.
#define SET_CONSTRAINT(problem, param, constraint)
AccelDecelLimitResidual(double max_accel, double max_decel, double weight)
bool operator()(const T *const c_i, const T *const c_ip1, T *residual) const
MidpointObstacleResidual(const Costmap3DWrapper &wrapper, double weight, double max_dist, double clearance, double barrier_eps, bool use_fuzzy_orientation=true, int num_orientations=72, int fuzzy_window_bins=2, double fuzzy_alpha=5.0)
const Costmap3DWrapper & wrapper_
bool operator()(const T *const c_i, const T *const c_ip1, T *residual) const
bool use_fuzzy_orientation_
NominalVelocityResidual(double v_nominal, double weight)
bool operator()(const T *const c_i, T *residual) const
const Costmap3DWrapper & wrapper_
ObstacleResidual(const Costmap3DWrapper &wrapper, double weight, double max_dist, double clearance, double barrier_eps, bool use_fuzzy_orientation=true, int num_orientations=72, int fuzzy_window_bins=2, double fuzzy_alpha=5.0)
bool operator()(const T *const node, T *residual) const
bool use_fuzzy_orientation_
bool operator()(const T *const c_m2, const T *const c_m1, const T *const c_i, const T *const c_p1, T *residual) const
PoseJerkResidual(double weight=1.0)
PoseSmoothResidual(double pos_weight=1.0, double orientation_weight=1.0)
bool operator()(const T *const c_prev, const T *const c_i, const T *const c_next, T *residual) const
void centerToRobot(const T *const c, T &rx, T &ry) const
RobotPoseProximityResidual(double rx0, double ry0, double threshold_mm, double weight, double eps_mm)
bool operator()(const T *const c_i, T *residual) const
bool operator()(const T *const c_prev, const T *const c_i, const T *const c_next, T *residual) const
RobotSmoothResidual(double weight=1.0, double dx=-0.5, double dy=0.0)
void centerToRobotTemplated(const T *const c, T &rx, T &ry, T &rtheta) const
bool operator()(const T *const c_i, const T *const c_ip1, T *residual) const
SoftSpacingResidual(double d_avg, double linear_weight, double quadratic_weight, double relative_deviation_threshold)
SpacingResidual(double d_avg, double weight)
bool operator()(const T *const c_i, const T *const c_ip1, T *residual) const
StartRotationResidual(double w)
bool operator()(const T *const theta0, const T *const theta1, T *residuals) const
ThetaTrackingResidual(double w, double theta_target)
bool operator()(const T *const node, T *residual) const
TrackingResidual(double xr, double yr, double thetar, double vr, double wx, double wy, double wth, double wv)
bool operator()(const T *const c_i, T *residual) const
VelLimitResidual(double vmax, double weight=1.0)
bool operator()(const T *const c_i, T *residual) const
VelocityProximityResidual(double danger, double min_velocity, double weight)
bool operator()(const T *const c_i, T *residual) const
double fuzzy_orientation_alpha
bool use_fuzzy_orientation
double slow_down_distance
int fuzzy_orientation_window_bins
double obs_barrier_eps_mm
double tracking_fade_end_clearance
double min_velocity_near_obstacle
double max_decel_per_segment
double w_orientation_smooth
double w_nominal_velocity
double max_trust_region_radius
double spacing_relative_deviation_threshold
double robot_pose_proximity_threshold_mm
double w_accel_decel_limit
bool use_robot_pose_proximity
double w_velocity_proximity
double tracking_fade_start_clearance
bool use_tracking_clearance_adaptive
double robot_pose_proximity_eps_mm
double w_spacing_quadratic
double w_robot_pose_proximity
double max_accel_per_segment
double obstacle_freeze_threshold
double initial_trust_region_radius
double min_trust_region_radius