@@ -23,36 +23,17 @@ MPPIOptimizer::MPPIOptimizer(
2323std::vector<std::pair<double , double >>
2424MPPIOptimizer::simulate_trajectory (
2525 double x, double y, double yaw, double v, double w,
26- const nav_msgs::msg::Path & path, int steps)
26+ const nav_msgs::msg::Path & /* path*/ , int steps)
2727{
2828 size_t horizon_steps = static_cast <size_t >(steps);
2929 std::vector<std::pair<double , double >> trajectory;
3030 trajectory.reserve (horizon_steps);
3131
32- bool has_path = !path.poses .empty ();
33-
3432 for (size_t i = 0 ; i < horizon_steps; ++i) {
35- // MPPI noise sampling
36- double v_sample = v + normal_ (rng_);
37- double w_sample = w + normal_ (rng_);
38-
3933 // Differential drive kinematics
40- x += v_sample * std::cos (yaw) * dt_;
41- y += v_sample * std::sin (yaw) * dt_;
42- yaw += w_sample * dt_;
43-
44- if (has_path) {
45- // Calculate the index in the path based on the current step
46- size_t path_idx = std::min (static_cast <size_t >((i * path.poses .size ()) / horizon_steps),
47- path.poses .size () - 1 );
48- double target_x = path.poses [path_idx].pose .position .x ;
49- double target_y = path.poses [path_idx].pose .position .y ;
50-
51- // Smooth motion towards the target
52- double alpha = 0.2 ;
53- x += alpha * (target_x - x);
54- y += alpha * (target_y - y);
55- }
34+ x += v * std::cos (yaw) * dt_;
35+ y += v * std::sin (yaw) * dt_;
36+ yaw += w * dt_;
5637
5738 trajectory.emplace_back (x, y);
5839 }
@@ -87,21 +68,30 @@ double MPPIOptimizer::compute_cost(
8768 double cost = 0.0 ;
8869 size_t path_size = path.poses .size ();
8970
71+ if (path_size == 0 ) return std::numeric_limits<double >::max ();
72+
9073 // --- Path-Tracking Penalties ---
9174 for (size_t i = 0 ; i < trajectory.size (); ++i) {
9275 const auto &[x, y] = trajectory[i];
9376
94- // Cost is distributed along the trajectory
95- double path_alpha = static_cast <double >(i) / trajectory.size ();
96- size_t idx = std::min (static_cast <size_t >(path_alpha * path_size), path_size - 1 );
97-
98- double dx = path.poses [idx].pose .position .x - x;
99- double dy = path.poses [idx].pose .position .y - y;
100- double dist = std::hypot (dx, dy);
77+ // Find closest point on path (path is already cropped to local window)
78+ double min_dist2 = std::numeric_limits<double >::max ();
79+ size_t closest_idx = 0 ;
80+
81+ for (size_t j = 0 ; j < path_size; ++j) {
82+ double dx = path.poses [j].pose .position .x - x;
83+ double dy = path.poses [j].pose .position .y - y;
84+ double dist2 = dx*dx + dy*dy;
85+ if (dist2 < min_dist2) {
86+ min_dist2 = dist2;
87+ closest_idx = j;
88+ }
89+ }
90+ double dist = std::sqrt (min_dist2);
10191
10292 // Heading error is calculated based on the initial yaw
103- double heading_penalty = heading_error (initial_yaw, path.poses [idx ].pose .position .x ,
104- path.poses [idx ].pose .position .y , x, y);
93+ double heading_penalty = heading_error (initial_yaw, path.poses [closest_idx ].pose .position .x ,
94+ path.poses [closest_idx ].pose .position .y , x, y);
10595
10696 // FOV penalty: discourage trajectories outside robot's view
10797 double angle_to_goal = heading_error (initial_yaw, trajectory.back ().first ,
@@ -187,10 +177,39 @@ MPPIResult MPPIOptimizer::compute_control(
187177 double y = current_pose.position .y ;
188178 double yaw = tf2::getYaw (current_pose.orientation );
189179
190- // Select goal pose (within horizon, or last path point)
191- int last_pose = std::min (static_cast <size_t >(horizon_steps_), path.poses .size () - 1 );
192- int sim_steps = std::max (1 , last_pose);
193- const auto & path_pose = path.poses [last_pose].pose ;
180+ // Find closest point on global path
181+ size_t closest_idx = 0 ;
182+ double min_dist_to_robot = std::numeric_limits<double >::max ();
183+ for (size_t i = 0 ; i < path.poses .size (); ++i) {
184+ double dist = std::hypot (path.poses [i].pose .position .x - x, path.poses [i].pose .position .y - y);
185+ if (dist < min_dist_to_robot) {
186+ min_dist_to_robot = dist;
187+ closest_idx = i;
188+ }
189+ }
190+
191+ // Crop the path up to a reasonable lookahead distance
192+ nav_msgs::msg::Path local_path;
193+ local_path.header = path.header ;
194+
195+ double max_lookahead_dist = max_lin_vel_ * horizon_steps_ * dt_ * 1.5 ;
196+ if (max_lookahead_dist < 2.0 ) max_lookahead_dist = 2.0 ;
197+
198+ double path_accum_dist = 0.0 ;
199+ local_path.poses .push_back (path.poses [closest_idx]);
200+ for (size_t i = closest_idx + 1 ; i < path.poses .size (); ++i) {
201+ local_path.poses .push_back (path.poses [i]);
202+ double dx = path.poses [i].pose .position .x - path.poses [i-1 ].pose .position .x ;
203+ double dy = path.poses [i].pose .position .y - path.poses [i-1 ].pose .position .y ;
204+ path_accum_dist += std::hypot (dx, dy);
205+ if (path_accum_dist > max_lookahead_dist) {
206+ break ;
207+ }
208+ }
209+
210+ // Select local goal pose from the cropped path
211+ int sim_steps = horizon_steps_;
212+ const auto & path_pose = local_path.poses .back ().pose ;
194213 double px = path_pose.position .x ;
195214 double py = path_pose.position .y ;
196215 double dist_to_goal = std::hypot (px - x, py - y);
@@ -222,19 +241,22 @@ MPPIResult MPPIOptimizer::compute_control(
222241 double base_v = max_lin_vel_ * std::min (dist_to_goal, 1.0 ) * v_scale;
223242 base_v = std::clamp (base_v, 0.0 , max_lin_vel_);
224243
225- // Angular velocity is proportional to the heading error: turn faster if more misaligned
226- double w_scale = std::min (1.0 , 2.0 * angle_mag / M_PI);
227- double base_w = std::clamp (w_scale * angle_error, -max_ang_vel_, max_ang_vel_);
244+ // Apply a stronger proportional gain for angular tracking to allow sharp turns
245+ double base_w = std::clamp (2.5 * angle_error, -max_ang_vel_, max_ang_vel_);
246+
247+ // Use proper spread variance to allow MPPI to explore turns
248+ std::normal_distribution<double > v_dist (0.0 , max_lin_vel_ * 0.2 );
249+ std::normal_distribution<double > w_dist (0.0 , max_ang_vel_ * 0.4 );
228250
229251 // Generate candidate trajectories
230252 for (int i = 0 ; i < num_samples_; ++i) {
231253 // Sample noise for velocities with Gaussian distribution and clamp
232- double v = std::clamp (base_v + v_noise_ (rng_), -max_lin_vel_, max_lin_vel_);
233- double w = std::clamp (base_w + w_noise_ (rng_), -max_ang_vel_, max_ang_vel_);
254+ double v = std::clamp (base_v + v_dist (rng_), -max_lin_vel_, max_lin_vel_);
255+ double w = std::clamp (base_w + w_dist (rng_), -max_ang_vel_, max_ang_vel_);
234256
235257 // Simulate trajectory and compute its cost
236- auto traj = simulate_trajectory (x, y, yaw, v, w, path , sim_steps);
237- double cost = compute_cost (traj, path , v, w, yaw, points);
258+ auto traj = simulate_trajectory (x, y, yaw, v, w, local_path , sim_steps);
259+ double cost = compute_cost (traj, local_path , v, w, yaw, points);
238260 all_trajs.push_back (traj);
239261
240262 // Store the sample with its cost
@@ -273,18 +295,11 @@ MPPIResult MPPIOptimizer::compute_control(
273295 vrot += sample.w * sample.cost / denom;
274296 }
275297
276- // Calculate the maximum change in velocities based on acceleration limits
277- double max_v_delta = max_lin_acc_ * dt_;
278- double max_w_delta = max_ang_acc_ * dt_;
279-
280- // Limit velocity changes (slew rate)
281- vlin = last_v_ + std::clamp (vlin - last_v_, -max_v_delta, max_v_delta);
282- vrot = last_w_ + std::clamp (vrot - last_w_, -max_w_delta, max_w_delta);
283-
284- // Smooth velocities
285- double alpha_smooth = 0.2 ; // lower is more smooth
286- last_v_ = alpha_smooth * vlin + (1.0 - alpha_smooth) * last_v_;
287- last_w_ = alpha_smooth * vrot + (1.0 - alpha_smooth) * last_w_;
298+ // We remove the internal slew rate and low-pass filters here because MPPIController
299+ // already rigorously applies kinematic constraints (accelerations). Double filtering
300+ // causes severe sluggishness and path divergence.
301+ last_v_ = vlin;
302+ last_w_ = vrot;
288303
289304 // Return final control command and trajectories
290305 return MPPIResult{last_v_, last_w_, all_trajs, best_traj};
0 commit comments