Controller parameters
What every controller_server parameter on the AMR does, read from the source it configures, with the comments the parameter file carried beside each one.
Read from hmc_nav2_params_tuning_18Sep.yaml (the controller_server block) on 27 Sept 2026. Defaults and behaviour were read from the navigation2_common (nav2_controller, nav2_util) and hmgics_navigation2 (nav2_mppi_controller, nav2_precise_rotation_shim_controller) working trees on the same day, uncommitted changes included. A default is what the code declares when the key is absent from the file.
Parameters
231
in 25 groups
Away from default
115
of 223 with a declared default
Comment lines kept
348
on 82 parameters and 8 group headings
No effect here
33
set, but switched off or never read
Where the comments and the code disagree
Read these before deleting the comments. Each is a comment that describes a value the file no longer has, or a mechanism the code no longer uses. The comment itself is kept on the parameter below.
enforce_path_inversionController server
The FIXME above it says it "never reaches the critics". In this source it does reach the ones that matter, indirectly: the optimizer turns it into
effective_goal, andTerminalGoalCriticandGoalAngleCriticscore against that. What does not reach them isFollowPath.enforce_path_inversion. Each critic reads that key but onlyGoalCriticacts on it, andGoalCriticis disabled. The path itself is cut at the cusp byPathHandler.enforce_path_inversion, a third key.general_goal_checker.xy_goal_toleranceGoal checker
The comment says it is "aligned with FollowPath.vertex_tolerance", but
vertex_toleranceis 0.02 in this file and this is 0.05. What now holds is containment: the shim’s 2 cm terminal gate sits well inside the checker’s 5 cm.FollowPath.static_handoff_vel_thresholdRotation shim: engage and hand-over
The source gives this base’s noise floor as about 0.035 rad/s, which is why the default is 0.05. At 0.02 a quiet base settles quickly. A noisy one waits out the 3 s timeout on every turn. Worth measuring before trusting it (the README’s
measure_rotation_braking.pysuggests a value).FollowPath.GoalAngleCritic.threshold_to_considerGoalAngleCritic
The opening comment says this critic "is active in the last 0.5 m". That was the default. At 1.5 it is active over the last 1.5 m, three times the stretch the argument was made about.
FollowPath.TerminalGoalCritic.terminal_window_timeTerminalGoalCritic
The opening comment says the critic "only scores the last 0.25 s of each rollout". Two things changed: the window is 0.50 s, and it is centred on each rollout’s closest approach to the goal, not on the end of the horizon. The header explains why scoring the end was dropped.
FollowPath.TerminalGoalCritic.stop_ramp_distanceTerminalGoalCritic
The comment above says the stop term "fades in only over the final 0.6 m". It is 1.0 here (Kelvin’s value, per the inline note), so it fades in over the last metre.
FollowPath.TerminalGoalCritic.lateral_weightTerminalGoalCritic
The comment argues "Lateral > longitudinal" because a differential drive cannot fix lateral error close in. At 1.0 against a longitudinal 1.0 there is no longer any anisotropy, so the critic scores position isotropically.
FollowPath.TerminalGoalCritic.stop_vx_weightTerminalGoalCritic
The comment prices these weights "at the near-goal caps (vx 0.20, wz 0.25)". The near-goal ramp is inert while
terminal_brakingis on, and itsvxcap is 0.10 in this file anyway. The speeds a rollout actually has at arrival come from the braking envelope.FollowPath.PathAlignCritic.cost_weightPathAlignCritic
The comment’s measurements pair this weight with
PathFollowCritic3.5 at 45 (17 Sep). It was lowered to 30 on 18 Sep with PathFollow unchanged, and no comment records a measurement of the 30 / 3.5 pair now in the file.FollowPath.PathAngleCritic.offset_from_furthestPathAngleCritic
The comment argues for pushing the target further out, "12 -> 25 indices", to quiet the relay. The value is 4, which is the stock default, and the inline note records 12 as an earlier value. The argument above it describes a setting the file no longer has.
Set, but doing nothing
Lines the file sets that change nothing in this configuration: their mechanism is switched off elsewhere, or nothing reads them. Most of them come from one switch.
| Key | Why |
|---|---|
| general_goal_checker.bypass_y_axis_max_x_error | bypass_y_axis is false. |
| general_goal_checker.bypass_y_axis_max_y_error | bypass_y_axis is false. |
| PathHandler.minimum_rotation_angle | enforce_path_rotation is false, and the handler zeroes this when it is. |
| FollowPath.static_handoff_settle_cycles | Only read when rotation_settle_duration is 0; it is 0.15 here. |
| FollowPath.rotation_breakaway_timeout | rotation_breakaway_angular_vel is 0, so breakaway is off. |
| FollowPath.advanced.near_goal_velocity_scaling | terminal_braking is on, and the optimizer runs it INSTEAD of the near-goal ramp (it logs a warning saying so). |
| FollowPath.advanced.near_goal_distance | terminal_braking is on, and the optimizer runs it INSTEAD of the near-goal ramp (it logs a warning saying so). |
| FollowPath.advanced.near_goal_vx_max | terminal_braking is on, and the optimizer runs it INSTEAD of the near-goal ramp (it logs a warning saying so). |
| FollowPath.advanced.near_goal_vx_min | terminal_braking is on, and the optimizer runs it INSTEAD of the near-goal ramp (it logs a warning saying so). |
| FollowPath.advanced.near_goal_wz_max | terminal_braking is on, and the optimizer runs it INSTEAD of the near-goal ramp (it logs a warning saying so). |
| FollowPath.enforce_path_inversion | The only critic that uses it, GoalCritic, is disabled. |
| FollowPath.transform_tolerance | No getParam for it in the controller server, the shim or MPPI. |
| FollowPath.AckermannConstraints.min_turning_r | The motion model is a differential drive. |
| FollowPath.TrajectoryVisualizer.trajectory_step | visualize is false. |
| FollowPath.TrajectoryVisualizer.time_step | visualize is false. |
| FollowPath.GoalCritic.cost_power | GoalCritic is switched off by its own enabled: false. |
| FollowPath.GoalCritic.cost_weight | GoalCritic is switched off by its own enabled: false. |
| FollowPath.GoalCritic.threshold_to_consider | GoalCritic is switched off by its own enabled: false. |
| FollowPath.TerminalGoalCritic.threshold_to_consider | activation_start and activation_full are both set, and they replace it. |
| FollowPath.PreferForwardCritic.cost_power | PreferForwardCritic is switched off by its own enabled: false. |
| FollowPath.PreferForwardCritic.cost_weight | PreferForwardCritic is switched off by its own enabled: false. |
| FollowPath.PreferForwardCritic.threshold_to_consider | PreferForwardCritic is switched off by its own enabled: false. |
| FollowPath.CurvatureOnlyPathAlignCritic.cost_power | CurvatureOnlyPathAlignCritic is switched off by its own enabled: false. |
| FollowPath.CurvatureOnlyPathAlignCritic.cost_weight | CurvatureOnlyPathAlignCritic is switched off by its own enabled: false. |
| FollowPath.CurvatureOnlyPathAlignCritic.max_path_occupancy_ratio | CurvatureOnlyPathAlignCritic is switched off by its own enabled: false. |
| FollowPath.CurvatureOnlyPathAlignCritic.trajectory_point_step | CurvatureOnlyPathAlignCritic is switched off by its own enabled: false. |
| FollowPath.CurvatureOnlyPathAlignCritic.threshold_to_consider | CurvatureOnlyPathAlignCritic is switched off by its own enabled: false. |
| FollowPath.CurvatureOnlyPathAlignCritic.offset_from_furthest | CurvatureOnlyPathAlignCritic is switched off by its own enabled: false. |
| FollowPath.CurvatureOnlyPathAlignCritic.use_path_orientations | CurvatureOnlyPathAlignCritic is switched off by its own enabled: false. |
| FollowPath.CurvatureOnlyPathAlignCritic.curvature_gain | CurvatureOnlyPathAlignCritic is switched off by its own enabled: false. |
| FollowPath.CurvatureOnlyPathAlignCritic.max_curvature | CurvatureOnlyPathAlignCritic is switched off by its own enabled: false. |
| FollowPath.CurvatureOnlyPathAlignCritic.max_curvature_extra | CurvatureOnlyPathAlignCritic is switched off by its own enabled: false. |
| FollowPath.CurvatureOnlyPathAlignCritic.curvature_influence_distance | CurvatureOnlyPathAlignCritic is switched off by its own enabled: false. |
Contents
- Controller server6/13
- Progress checker1/4
- Goal checker4/10
- Path handler2/9
- Rotation shim: engage and hand-over12/14
- Rotation shim: rotation law3/11
- Rotation shim: goals, vertices and turn direction11/17
- MPPI: horizon and sampling4/10
- MPPI: velocity, acceleration and noise11/12
- MPPI advanced: sampling and warm start4/4
- MPPI advanced: straight wz hold10/14
- MPPI advanced: speed governors8/16
- MPPI: motion model, critics and namespace3/6
- MPPI: diagnostics2/6
- ConstraintCritic0/3
- GoalCriticoff
- GoalAngleCritic3/6
- TerminalGoalCritic9/23
- PreferForwardCriticoff
- CostCritic3/9
- PathAlignCritic4/8
- CurvatureOnlyPathAlignCriticoff
- PathFollowCritic2/6
- PathAngleCritic3/7
- TwirlingCritic1/3
The figure is how many of a group’s parameters are set away from the default, out of how many it has.
Controller server
The node itself: how often it runs the control loop, which plugins fill its four slots, and how it filters the odometry it hands to them. Every key at the top of ros__parameters is here, including one that the server never declares.
- navigation2_common/nav2_controller/src/controller_server.cpp
- navigation2_common/nav2_controller/include/nav2_controller/controller_server.hpp
controller_frequency
20.0Hz
same as the default
Rate of the control loop. Every plugin under
FollowPathsizes its per-cycle quantities on it, and MPPI reads it at start-up to decide whether to shift its control sequence: it does when the period equalsmodel_dt, which at 20 Hz it does.controller_frequency: 20.0 ###################
See alsoFollowPath.model_dtFollowPath.vertex_stop_settle_cyclesFollowPath.static_handoff_settle_cycles
enforce_path_inversion
True
default false
Not a controller-server parameter at all: the server never declares it. MPPI’s optimizer reads it from the node’s ROOT namespace (
getParentParam("")). When true, the terminal approach aims at the last pose of the pruned local path, which is the cusp when the path reverses, instead of the goal. That aim point is whatTerminalGoalCritic,GoalAngleCriticand terminal braking use.## FIXME: enforce_path_inversion never reaches the critics enforce_path_inversion: True
Source check. The FIXME above it says it "never reaches the critics". In this source it does reach the ones that matter, indirectly: the optimizer turns it into
effective_goal, andTerminalGoalCriticandGoalAngleCriticscore against that. What does not reach them isFollowPath.enforce_path_inversion. Each critic reads that key but onlyGoalCriticacts on it, andGoalCriticis disabled. The path itself is cut at the cusp byPathHandler.enforce_path_inversion, a third key.See alsoPathHandler.enforce_path_inversionFollowPath.enforce_path_inversion
costmap_update_timeout
0.30s
same as the default
How long a cycle waits for the local costmap to report itself current before the goal fails with
ControllerTimedOut.min_x_velocity_threshold
0.001m/s
default 0.0001
Odometry
vxat or below this magnitude is replaced with 0 before it reaches the controller and the goal checker. A deadband on the MEASUREMENT, not on the command.min_y_velocity_threshold
0.05m/s
default 0.0001
The same deadband on odometry
vy. On a differential drivevyshould read zero anyway, so this only hides lateral slip from the goal checker’s speed test.min_theta_velocity_threshold
0.001rad/s
default 0.0001
The same deadband on odometry
wz.failure_tolerance
0.3s
default 0.0
How long the controller may keep failing to find a valid command, publishing zero velocity meanwhile, before the goal aborts. 0 aborts on the first failure; -1 never aborts.
progress_checker_plugins
["progress_checker"]
same as the default
Names the progress-checker instance(s). Each name is a namespace whose
pluginkey picks the class.goal_checker_plugins
["general_goal_checker"]
default ["goal_checker"]
Names the goal-checker instance(s). The default name
goal_checkeris replaced here bygeneral_goal_checker.controller_plugins
["FollowPath"]
same as the default
Names the controller instance(s).
FollowPathis also the id the behaviour tree requests.path_handler_plugins
["PathHandler"]
same as the default
Names the path-handler instance(s).
use_realtime_priority
false
same as the default
Runs the action server’s thread at soft real-time priority. The process needs permission to set it, or the server fails to configure.
speed_limit_topic
"speed_limit"
same as the default
Topic the server subscribes to for speed limits (costmap speed filter, operator). The shim passes a limit on to MPPI, and also sets MPPI’s limit itself during a vertex approach, putting the external one back afterwards.
Progress checker
nav2_controller::PoseProgressChecker
Aborts a goal when the robot has not moved far enough, in position or in heading, within a time allowance. Moving either far enough resets the clock.
Opening comment in the file
# Progress checker parameters
- navigation2_common/nav2_controller/plugins/pose_progress_checker.cpp
- navigation2_common/nav2_controller/plugins/simple_progress_checker.cpp
progress_checker.plugin
"nav2_controller::PoseProgressChecker"
default "nav2_controller::SimpleProgressChecker"
Class loaded into the slot.
PoseProgressCheckeradds a heading test toSimpleProgressChecker, so an in-place turn counts as progress.progress_checker.required_movement_radius
0.5m
same as the default
Distance from the last baseline pose that counts as progress and restarts the clock.
progress_checker.required_movement_angle
0.5rad
same as the default
Heading change from the baseline that counts as progress. It is what stops a long in-place turn from being read as a stall.
progress_checker.movement_time_allowance
10.0s
same as the default
Time allowed without either kind of progress before the goal aborts. A shim settle, a correction pass and a vertex stop all have to fit inside it.
Goal checker
nav2_controller::BidirectionalGoalChecker
Decides when a goal is reached: remaining local path under a length, position inside a circle, heading inside a tolerance of EITHER the goal yaw or its opposite, and the robot stopped. Its xy and yaw tolerances are also read by the shim and by several critics, so changing one moves more than the finish line.
Opening comment in the file
# Goal checker parameters
- navigation2_common/nav2_controller/plugins/bidirectional_goal_checker.cpp
- navigation2_common/nav2_controller/plugins/simple_goal_checker.cpp
general_goal_checker.plugin
"nav2_controller::BidirectionalGoalChecker"
no default: must be set
Class loaded into the slot.
BidirectionalGoalCheckeraccepts either heading at the goal, matching the bidirectional shim and the symmetric critics. The server only supplies a default class for the default instance name,goal_checker, so under this name the key must be set.general_goal_checker.xy_goal_tolerance
0.05m
default 0.25
Radius of the position test. It is also the radius inside which the shim starts the goal rotation, and the tolerance
TwirlingCriticandTerminalGoalCriticread back from the checker.xy_goal_tolerance: 0.05 ## aligned with FollowPath.vertex_tolerance
Source check. The comment says it is "aligned with FollowPath.vertex_tolerance", but
vertex_toleranceis 0.02 in this file and this is 0.05. What now holds is containment: the shim’s 2 cm terminal gate sits well inside the checker’s 5 cm.See alsoFollowPath.vertex_toleranceFollowPath.goal_rotation_hysteresis
general_goal_checker.yaw_goal_tolerance
0.0524rad
default 0.25
Heading tolerance, tested against the goal yaw and against its opposite, whichever is nearer. The shim finishes its goal turn at
goal_yaw_acceptance_ratioof this.yaw_goal_tolerance: 0.0524 ## in radian, [02sep26] 0.0524 = 3 degree
general_goal_checker.path_length_tolerance
1.0m
same as the default
The goal is not even tested while the remaining local path is longer than this.
general_goal_checker.trans_stopped_velocity
0.01m/s
default 0.25
Measured translational speed (after
min_x_velocity_threshold) under which the robot counts as stopped. The goal is only accepted once the robot is stopped.# [HMGICS Alex 26Sep] 0.02 -> 0.01. MPPI creeps the last few cm into a goal, and at 0.02 that # creep already counted as stopped: node 2 on bag mew_test was accepted 3.4-4.3 cm short # (inside xy 0.05, creeping at 0.019 m/s), so the shim's 2 cm terminal gate never engaged. # At 0.01 the checker waits for the robot to really stop -- normally the shim's settle at # vertex_tolerance -- and a robot that stalls short is still accepted where it stopped. # CHECK on the real robot: /odom must read under 0.01 m/s at standstill, or goals never finish. trans_stopped_velocity: 0.01 # m/s ## 26Sep Alex: 0.02
general_goal_checker.rot_stopped_velocity
0.02rad/s
default 0.25
Measured yaw rate under which the robot counts as stopped.
See alsomin_theta_velocity_threshold
general_goal_checker.stateful
True
same as the default
Once the position test passes it is latched, and only the yaw and stopped tests are re-run. If the robot is found moving, the latch is dropped.
general_goal_checker.bypass_y_axis
False
same as the default
Accepts a goal that fails the xy test only because of a sideways offset, which a differential drive cannot drive out. Allowed when the error along the goal heading is inside
bypass_y_axis_max_x_errorand the sideways error insidebypass_y_axis_max_y_error. Always logs a warning when it fires.# [NOTE] Disable this during deployment bypass_y_axis: False
general_goal_checker.bypass_y_axis_max_x_error
0.05m
same as the defaultno effect here
Largest along-heading error the y bypass accepts.
No effect here.
bypass_y_axisis false.general_goal_checker.bypass_y_axis_max_y_error
0.5m
same as the defaultno effect here
Largest sideways error the y bypass accepts.
No effect here.
bypass_y_axisis false.
Path handler
nav2_controller::FeasiblePathHandler
Turns the global path into the local one the controller follows: finds the closest pose, prunes what is behind it, cuts the path at the first direction reversal, and keeps the next stretch up to the costmap edge.
- navigation2_common/nav2_controller/plugins/feasible_path_handler.cpp
- navigation2_common/nav2_util/src/path_utils.cpp
PathHandler.plugin
"nav2_controller::FeasiblePathHandler"
same as the default
Class loaded into the slot.
PathHandler.prune_distance
5.0m
default 2.0
How far ahead of the closest pose the local path extends. Also stops at the local costmap’s edge, whichever is nearer.
PathHandler.enforce_path_inversion
True
default false
Cuts the path at the first direction reversal, so the controller must reach the cusp before it sees the rest. The remainder is released once the robot is inside
inversion_xy_toleranceandinversion_yaw_toleranceof the cusp. A reversal is a turn sharper than 135°, not merely one past 90°.See alsoenforce_path_inversion
PathHandler.enforce_path_rotation
False
same as the default
Also cuts the path at the first in-place rotation larger than
minimum_rotation_angle.PathHandler.max_robot_pose_search_dist
2.0m
default: half the local costmap’s larger side
How far along the path the closest-pose search looks, so a looping path cannot match a later stretch.
PathHandler.inversion_xy_tolerance
0.2m
same as the default
Distance to the cusp at which the path beyond it is released.
PathHandler.inversion_yaw_tolerance
0.4rad
same as the default
Heading tolerance for releasing the cusp. It accepts either end of the reversal axis, because the robot arrives facing the old direction.
PathHandler.minimum_rotation_angle
0.785rad
same as the defaultno effect here
Accumulated in-place rotation that counts as a constraint for
enforce_path_rotation.No effect here.
enforce_path_rotationis false, and the handler zeroes this when it is.PathHandler.reject_unit_path
False
same as the default
Rejects a path of a single pose as invalid instead of trying to follow it.
Rotation shim: engage and hand-over
nav2_precise_rotation_shim_controller::RotationShimController
FollowPath is the shim, and MPPI runs inside it as the primary controller, sharing its parameter namespace. The shim takes the robot whenever the path heading is too far off to drive into, turns it in place, and gives it back. These parameters decide when it takes the robot and how it lets go.
- hmgics_navigation2/nav2_precise_rotation_shim_controller/src/nav2_precise_rotation_shim_controller.cpp
- hmgics_navigation2/nav2_precise_rotation_shim_controller/README.md
FollowPath.plugin
"nav2_precise_rotation_shim_controller::RotationShimController"
default "dwb_core::DWBLocalPlanner"
The controller class. The precise-rotation shim is a drop-in fork of
nav2_rotation_shim_controllerthat keeps every feature and replaces only the in-place rotation law. Switching back is changing this string.FollowPath.static_handoff
true
default false
Keeps the robot after a turn until odometry confirms it has stopped, corrects any residual from rest, then hands over. Off, control is released at
angular_disengage_thresholdwith the robot still turning, and MPPI turns the leftover rate into an arc.# ------------------------------------------------------------------ # Existing rotation shim parameters # ------------------------------------------------------------------ static_handoff: true
FollowPath.static_handoff_vel_threshold
0.02rad/s
default 0.05
Measured yaw rate under which the chassis counts as stopped. It has to clear the odometry noise floor, about 3σ of
wzat standstill, or a stop is only ever confirmed byrotation_settle_timeout.Source check. The source gives this base’s noise floor as about 0.035 rad/s, which is why the default is 0.05. At 0.02 a quiet base settles quickly. A noisy one waits out the 3 s timeout on every turn. Worth measuring before trusting it (the README’s
measure_rotation_braking.pysuggests a value).See alsoFollowPath.rotation_settle_durationFollowPath.rotation_settle_timeout
FollowPath.static_handoff_settle_cycles
20cycles
default 3no effect here
Legacy stop-confirmation dwell, counted in control cycles.
# Legacy fallback only when rotation_settle_duration == 0. # At controller_frequency=20 Hz, 20 cycles = 1.00 s. static_handoff_settle_cycles: 20
No effect here. Only read when
rotation_settle_durationis 0; it is 0.15 here.See alsoFollowPath.rotation_settle_durationcontroller_frequency
FollowPath.angular_dist_threshold
0.90rad
default 0.785
Path-heading error above which the shim takes the robot from MPPI and turns it in place. The dividing line for corners too: a gentler corner is left for MPPI to drive through, and is not slowed for. 0.90 rad is 51.6°.
angular_dist_threshold: 0.90 #0.15 ## 18Sep Alex ## 0.90
See alsoFollowPath.angular_disengage_thresholdFollowPath.rest_angular_dist_thresholdFollowPath.vertex_reversal_angular_dist_threshold
FollowPath.angular_disengage_threshold
0.035rad
default 0.3925
Heading error at which a path-heading or vertex turn is accepted. 0.035 rad is 2.0°. The two situational thresholds below are floored at twice this, so a released turn is not grabbed again the next cycle.
angular_disengage_threshold: 0.035 #0.02 ## 18Sep Alex ##0.035
FollowPath.rest_angular_dist_threshold
0.10rad
default 0.0
Engage threshold used instead of
angular_dist_thresholdwhile the robot is measured AT REST: a leg start, or after a stop. From rest an in-place turn costs nothing. Leaving a sub-threshold error to MPPI means turning while accelerating. Bounded to [2 ×angular_disengage_threshold,angular_dist_threshold]; 0 = unset.# [HMGICS Alex 25Sep] Path-heading engage threshold while the robot is measured at rest # (leg start, or after a stop). Leaving node 16 onto the 20.6 deg edge 16->0, the 20.4 deg # error was under angular_dist_threshold (51.6 deg), so MPPI turned it out while # accelerating: 7.2 cm right of the edge, then 2.3 deg past the edge heading at 1.5 m/s to # come back (bag overshoot_on_inplace_turning). 0.10 turns it in place first. While moving, # angular_dist_threshold still applies. Floored at 2 x angular_disengage_threshold, capped # at angular_dist_threshold; 0 = old behaviour. rest_angular_dist_threshold: 0.10
FollowPath.terminal_goal_approach_threshold
0.05rad
same as the default
At the final pose, if the goal heading differs from the incoming leg by at least this, the goal is handled like a vertex: braked onto, settled, then turned. Below it, MPPI keeps the approach.
FollowPath.rotate_to_heading_angular_vel
0.30rad/s
default 1.8
Peak yaw rate of an in-place turn.
FollowPath.max_angular_accel
0.35rad/s²
default 3.2
Peak ramp-up acceleration of an in-place turn. The ramp is softened to 20% near the target.
FollowPath.max_angular_decel
0.10rad/s²
default 0.0
Deceleration the braking curve plans on. It is a promise that the chassis can still stop in the remaining angle, so it must be a rate the base GUARANTEES at full payload. Set too high, the robot stops past the heading; set too low, it only brakes earlier. 0 =
max_angular_accel.# IMPORTANT: # Current code uses this as the braking model's assumed physical deceleration. # Keep 0.10 only if the fully-loaded robot can reliably achieve >= 0.10 rad/s^2. max_angular_decel: 0.10
See alsoFollowPath.max_angular_brake_decelFollowPath.rotation_latency
FollowPath.rotate_to_goal_heading
true
default false
After arriving inside the goal checker’s xy tolerance, turn in place onto the goal yaw. The shim commands
wzonly, so whatever position error exists at that moment is frozen.FollowPath.rotate_to_heading_once
false
same as the default
Only turn to the path heading once per goal rather than on every new path.
FollowPath.closed_loop
false
default true
No longer affects rotation (it warns if set). Only chooses the speed source of the vertex approach ramp: measured odometry when true, the last command when false.
# For vertex-approach velocity source. closed_loop: false
Rotation shim: rotation law
The in-place turn itself. It ramps up, then brakes along the fastest curve from which a base with this much latency can still stop in the angle left, w = 2d / (sqrt(tau² + 2d/a) + tau). It waits for odometry to confirm a stop, and allows at most a set number of slow correction passes.
- hmgics_navigation2/nav2_precise_rotation_shim_controller/include/nav2_precise_rotation_shim_controller/rotation_controller.hpp
- hmgics_navigation2/nav2_precise_rotation_shim_controller/README.md
FollowPath.rotation_latency
0.15s
default 0.20
Command-to-motion latency the braking curve plans for:
wzdead time plus drive lag (a high percentile, not the mean) plus half a control period. Under-estimate it and the robot stops past the heading; over-estimate it and it stops short. The correction pass removes either.# ------------------------------------------------------------------ # precise-rotation parameters # ------------------------------------------------------------------ # Conservative effective cmd -> physical motion / odom delay. # Start with 0.15 s for your ~0.10 s observed delay, then measure properly. rotation_latency: 0.15
See alsoFollowPath.model_delay_wzFollowPath.max_angular_decel
FollowPath.max_angular_brake_decel
0.10rad/s²
default 0.0
Hard cap on how fast braking may pull the command down when the heading says the robot is running long. Braking always sheds at least 0.2 ×
max_angular_decelper second. 0 = 2 ×max_angular_decel.# Maximum rate at which BRAKING may pull the command down. # For initial testing I would keep this equal to max_angular_decel # until actual braking capability is measured. max_angular_brake_decel: 0.10
See alsoFollowPath.max_angular_decel
FollowPath.rotation_stop_margin
0.003rad
same as the default
Angle braking aims to stop short of the heading by.
# Additional angular stopping margin. rotation_stop_margin: 0.003
FollowPath.rotation_momentum_window
0.15s
same as the default
Look-back window for measured momentum. The largest measured rate in it, when above the command, debits the braking distance by its excess times the latency.
# Look-back window for measured angular momentum. rotation_momentum_window: 0.15
FollowPath.rotation_settle_duration
0.15s
default 0.0
Time the measured rate must stay under
static_handoff_vel_thresholdbefore a stop is confirmed. Time-based, so it does not change withcontroller_frequency. 0 = derive it fromstatic_handoff_settle_cycles.# Explicit time-based stop confirmation. # This overrides static_handoff_settle_cycles. rotation_settle_duration: 0.15
See alsoFollowPath.static_handoff_vel_thresholdFollowPath.static_handoff_settle_cycles
FollowPath.rotation_settle_timeout
3.0s
same as the default
Bound on waiting for a confirmed stop, and on a low-speed pass the chassis never takes up. Stops noisy odometry from holding the robot until the progress checker aborts.
# Maximum time allowed in settling / low-speed stall handling. rotation_settle_timeout: 3.0
See alsoprogress_checker.movement_time_allowanceFollowPath.static_handoff_vel_threshold
FollowPath.rotation_max_corrections
1
same as the default
Correction passes allowed after the first confirmed stop. The command can only change sign through one of these, after a measured stop.
# Allow one reduced-speed correction after confirmed physical stop. rotation_max_corrections: 1
FollowPath.rotation_correction_angular_vel
0.08rad/s
same as the default
Peak yaw rate of a correction pass.
FollowPath.rotation_breakaway_angular_vel
0.0rad/s
same as the default
Command applied from rest to break static friction on a heavy base. Only applied while the measured rate is still zero, only within
rotation_breakaway_timeoutof a pass starting, and never while braking. 0 disables.# Keep disabled initially. # Enable only if you confirm static-friction / breakaway problems. rotation_breakaway_angular_vel: 0.0
FollowPath.rotation_breakaway_timeout
0.5s
same as the defaultno effect here
How long after a pass starts breakaway may apply.
No effect here.
rotation_breakaway_angular_velis 0, so breakaway is off.FollowPath.goal_yaw_acceptance_ratio
0.5
same as the default
The goal turn finishes at this fraction of
yaw_goal_tolerance, so the robot is handed over well inside the tolerance rather than on its edge. The engage test stays at the full tolerance, which gives the hysteresis. Clamped to (0, 1].# Goal yaw acceptance = ratio × Nav2 goal checker yaw tolerance. # The goal tolerance is 0.0524 rad (~3°), so 0.5 means ~1.5°. goal_yaw_acceptance_ratio: 0.5
Rotation shim: goals, vertices and turn direction
Where the robot stops to turn. With rotate_at_vertex the shim finds route-graph corners by their geometry, brakes onto them and turns in place on the vertex, not a sampling distance short of it. The same machinery parks the robot on the final goal before the goal turn.
- hmgics_navigation2/nav2_precise_rotation_shim_controller/src/nav2_precise_rotation_shim_controller.cpp
FollowPath.goal_rotation_hysteresis
1.5
same as the default
Once the goal turn has started, it is held against the goal checker’s xy tolerance multiplied by this, not the bare tolerance. Without it, localisation noise at the tolerance edge hands the robot back and forth between the shim and MPPI. Clamped to at least 1.
# ------------------------------------------------------------------ # Existing remaining shim configuration # ------------------------------------------------------------------ goal_rotation_hysteresis: 1.5
FollowPath.rotate_at_vertex
true
default false
Finds route corners by geometry and stops ON them to turn, rather than turning when the sample
forward_sampling_distanceahead crosses the vertex, which is a whole sampling distance short.FollowPath.vertex_search_distance
3.5m
default 3.0
How far ahead the corner search looks, which only decides whether a corner is visible. It is not when the shim takes over. The source records that raising it 2.0 → 3.5 alone made a corner worse, because MPPI also sees the corner earlier and turns into it.
FollowPath.vertex_engage_distance
1.2m
default 0.0
Distance at which the shim commits to a visible corner, whatever the speed. Without it the only trigger is the braking distance, which the shim never wins while MPPI decelerates more gently than
vertex_decel. Size it past where MPPI starts its own turn. Clamped tovertex_search_distance; 0 = braking distance only.FollowPath.vertex_tolerance
0.02m
default 0.05
Distance short of the vertex the approach ramp aims to stop at, and the terminal position gate that captures a goal. Keep it at or under the path’s pose spacing.
See alsoFollowPath.terminal_position_release_tolerancegeneral_goal_checker.xy_goal_tolerancegeneral_goal_checker.trans_stopped_velocity
FollowPath.terminal_position_release_tolerance
0.03m
default 0.0
Hysteresis on the terminal gate: a goal captured inside
vertex_tolerancestays captured until the 2-D error exceeds this. Never belowvertex_tolerance; 0 = no hysteresis.# [HMGICS Alex 26Sep] Terminal goal: captured at vertex_tolerance (2 cm), released only past # this. mew_test node 16: the gate passed at 0.020 m and dropped at 0.021 m a cycle later on # AMCL noise, so the settle never finished and MPPI did the goal turn while creeping (true # robot ended ~10 cm past the goal). 1 cm above the gate covers the noise and the stop # overshoot; a robot really carried past it goes back to MPPI. 0 = no hysteresis. terminal_position_release_tolerance: 0.03
See alsoFollowPath.vertex_tolerance
FollowPath.vertex_decel
0.5m/s²
same as the default
Linear deceleration of the approach ramp, which also sizes how far out the shim must take over to stop on the vertex. Must be a rate the base and the velocity smoother really deliver, or the robot arrives still rolling. Err low.
FollowPath.vertex_stop_vel_threshold
0.02m/s
same as the default
Measured linear speed under which the robot may count as stopped at a vertex.
FollowPath.vertex_stop_settle_cycles
20cycles
default 3
Consecutive low-speed cycles required before the in-place turn at a vertex starts. 20 cycles at 20 Hz is 1.0 s.
See alsocontroller_frequency
FollowPath.vertex_reversal_angular_dist_threshold
0.10rad
default 0.0
Engage threshold at a vertex where travel REVERSES.
bidirectionalfolds such a hairpin to the small turn that backs onto the next leg, whichangular_dist_thresholdthen declines. Bounded likerest_angular_dist_threshold; 0 = unset.# [HMGICS Alex 24Sep] Engage threshold at a vertex where travel REVERSES (forward in and # reverse out, or the other way). bidirectional folds such a hairpin to the small turn that # backs the robot onto the next leg -- node 0's 159.4 deg reads 20.6 deg -- and against # angular_dist_threshold (51.6 deg) the shim never engaged: MPPI stopped 0.16 m short, # reversed at once and swung the heading on the way out (bag too_rush_on_corner). 0.10 makes # the shim stop ON the vertex, turn in place, then hand over. Anything under it (a # near-180 deg turnaround, nothing to turn) stays MPPI's. Floored at # 2 x angular_disengage_threshold, capped at angular_dist_threshold; 0 = old behaviour. vertex_reversal_angular_dist_threshold: 0.10
FollowPath.use_path_orientations
true
default false
Finish the turn onto the path’s own tangent (route poses carry exact tangents) rather than onto the bearing to the pose
forward_sampling_distanceahead.# [HMGICS Alex 17Sep] true: finish the rotation onto the path TANGENT (the route's poses carry # exact tangents), not onto the bearing to the pose forward_sampling_distance ahead. That # bearing left the robot aimed 0.8-1.8 deg across the path at every leg start # (nav2_full_20260917_212048: 1.0-1.4 cm offset / 0.5 m), which MPPI then steered out # (the +0.02-0.03 rad/s start bump) and which FollowPath.advanced.straight_wz_hold cannot # hold. Parallel hand-over + the hold: start wz exactly 0 offline. use_path_orientations: true
FollowPath.forward_sampling_distance
0.50m
same as the default
Distance ahead the heading is sampled at. With
rotate_at_vertexanduse_path_orientationsit mostly sets the baseline over which each leg’s direction is measured.FollowPath.bidirectional
true
default false
Allows turning onto either the path heading or its reverse, whichever is the shorter rotation, so the robot may back along a leg.
FollowPath.bidirectional_threshold
1.50rad
default 1.5708
Heading error the reversed heading must reach before it is weighed at all. At a 90° corner the two rotations tie, so below π/2 square corners reach the tie and
preferred_turn_directionsettles it. The source suggests 2.0 to keep square corners nose-first; 1.50 deliberately puts them on the preference.FollowPath.preferred_turn_direction
"left"
default "none"
Tie-break when both rotations come out within
preferred_turn_max_penaltyof each other:left,rightornone. Only reachable whenbidirectionalis on with a threshold under π/2, which it is.FollowPath.preferred_turn_max_penalty
0.10rad
same as the default
How much longer the preferred rotation may be and still win. At 0.10 rad only corners within about 6° of square are steered by it; a 60° corner keeps the short way round.
FollowPath.primary_controller
"nav2_mppi_controller::MPPIController"
no default: must be set
The controller the shim wraps. It is configured with the same plugin name, so every MPPI key below lives directly in
FollowPath.
MPPI: horizon and sampling
nav2_mppi_controller::MPPIController
MPPI samples batch_size noisy control sequences over a horizon of time_steps × model_dt, rolls each through the motion model, scores it with the critics, and takes a softmax-weighted mean.
- hmgics_navigation2/nav2_mppi_controller/src/optimizer.cpp
- hmgics_navigation2/nav2_mppi_controller/README.md
FollowPath.time_steps
50
default 56
Points per rollout. With
model_dtthis is the horizon: 50 × 0.05 = 2.5 s.FollowPath.model_dt
0.05s
same as the default
Time between rollout points. Equal to the control period at 20 Hz, which switches control-sequence shifting on.
See alsocontroller_frequency
FollowPath.batch_size
600
default 1000
Rollouts sampled per cycle. Must be even with
antithetic_wz_sampling.batch_size: 600 ## 1200
FollowPath.model_delay_vx
0.10s
default 0.0
Command delay the motion model applies to
vxwhen rolling out, as a whole number ofmodel_dtsteps (rounded). 0.10 s is two steps.FollowPath.model_delay_vy
0.0s
same as the default
The same for
vy.FollowPath.model_delay_wz
0.10s
default 0.0
The same for
wz.See alsoFollowPath.rotation_latency
FollowPath.iteration_count
1
same as the default
Optimisation iterations per cycle. The README recommends 1 and more batches.
FollowPath.temperature
0.3
same as the default
Softmax selectiveness over rollout costs. Critics are balanced here on their cost SPREAD against this: a critic whose
costs_stdis 0.5–2.0 × temperature is shaping the result.See alsoFollowPath.advanced.straight_hold_obstacle_influence
FollowPath.gamma
0.015
same as the default
Trade-off between smoothness and low energy in the control cost.
FollowPath.regenerate_noises
false
same as the default
Redraw the noise every cycle rather than reusing one draw from start-up.
MPPI: velocity, acceleration and noise
Hard limits the rollouts and the command are clamped to, and the standard deviation of the noise each axis is sampled with. The robot is a differential drive, so every vy key is zero.
- hmgics_navigation2/nav2_mppi_controller/src/optimizer.cpp
- hmgics_navigation2/nav2_mppi_controller/include/nav2_mppi_controller/models/constraints.hpp
FollowPath.ax_max
2.0m/s²
default 3.0
Largest forward acceleration a rollout or the command may use.
## acceleration ax_max: 2.0 #[15Sep Tete] #3.0
FollowPath.ax_min
-2.0m/s²
default -3.0
Largest braking. Must be negative; a positive value is flipped with a warning.
ax_min: -2.0 #[15Sep Tete] #-2.0
FollowPath.vx_std
0.50m/s
default 0.2
Standard deviation of the
vxsampling noise.## linear velocity x vx_std: 0.50 #### 0.65
FollowPath.vx_max
1.7m/s
default 0.5
Top forward speed.
FollowPath.vx_min
-1.7m/s
default -0.35
Top reverse speed, as a negative number. Full speed both ways, because the robot is bidirectional.
FollowPath.vy_std
0.0m/s
default 0.2
Sampling noise on
vy. Zero: the base cannot move sideways.## linear velocity y vy_std: 0.0
FollowPath.vy_max
0.0m/s
default 0.5
Top lateral speed. Zero, and together with the motion model this is why
isHolonomic()is false.FollowPath.ay_max
0.0m/s²
default 3.0
Lateral acceleration limit. Zero on a differential drive.
FollowPath.ay_min
0.0m/s²
default -3.0
Lateral deceleration limit. Zero on a differential drive.
FollowPath.wz_std
0.25rad/s
default 0.4
Standard deviation of the
wzsampling noise at rest.advanced.wz_std_decay_*shrinks it with speed.## angular velocity wz_std: 0.25
FollowPath.wz_max
1.2rad/s
default 1.9
Top yaw rate for MPPI. The shim has its own,
rotate_to_heading_angular_vel.wz_max: 1.2 #### NOTE: min 0.9 to turn smoothly or 2.0 corner
FollowPath.az_max
3.5rad/s²
same as the default
Largest yaw acceleration.
MPPI advanced: sampling and warm start
Changes to how the noise is drawn and how the previous cycle’s solution is carried into the next. Two of the four are HMGICS additions to the fork.
- hmgics_navigation2/nav2_mppi_controller/src/noise_generator.cpp
- hmgics_navigation2/nav2_mppi_controller/src/optimizer.cpp
FollowPath.advanced.antithetic_wz_sampling
true
default false
Draws the batch in pairs with the same
vxnoise and oppositewznoise. Where the critics cannot tell the two signs apart, the pair gets equal weight and itswznoise cancels instead of leaking into the command. Needs an evenbatch_size. Changing it live triggers a reset.# [HMGICS Alex 17Sep] Samples drawn in pairs: same vx noise, opposite wz noise. # Removes the small wz wiggle on straight paths while accelerating / decelerating # (low speed -> critics can't see wz, only ~40-100 effective samples -> their random # wz leaks into the command). Offline: accel wz 0.098 -> 0.042 rad/s, decel # 0.042 -> 0.023, straight CTE mean halved, same accel / travel time. Needs an even # batch_size. Dynamic: `ros2 param set /controller_server # FollowPath.advanced.antithetic_wz_sampling false` to A/B live (triggers reset). antithetic_wz_sampling: true
See alsoFollowPath.batch_size
FollowPath.advanced.warm_start_timeout
0.5s
default 0.0
Longest gap between evaluations over which the previous solution is kept. Beyond it, the control sequence, smoothing history and model-delay history are zeroed as on a new goal. This is what detects the shim handing back after it owned a vertex. 0 = keep for ever (stock).
# [HMGICS Alex 25Sep] The shim does not call MPPI while it owns a vertex, so leaving a # corner MPPI resumed from the approach it was running before it. overshoot_at_node0_1, # after ~7 s at node 0: +0.24 m/s FORWARD for 1-2 cycles before reversing (0.5-0.7 cm the # wrong way). After a gap longer than this (s) the control sequence, smoothing history # and model-delay history are zeroed as on a new goal. 0.5 s = 10 missed 20 Hz cycles: # far above a cycle overrun, well below the seconds a shim-owned corner takes. # Logs [WARM_START]. 0 = off (stock). warm_start_timeout: 0.5
See alsoFollowPath.advanced.straight_hold_handover_after_pause
FollowPath.advanced.wz_std_decay_strength
2.0
default -1.0
Shrinks
wz_stdas speed rises:(wz_std − decay_to) · e^(−strength · v) + decay_to. At these values,wz_stdis 0.13 at 0.5 m/s and 0.066 at 1.7 m/s. Negative disables.wz_std_decay_strength: 2.0 ## original 2.0 ### set -1.0 to disable
FollowPath.advanced.wz_std_decay_to
0.06rad/s
default 0.0
The
wz_stdthe decay approaches at high speed. Between 0 andwz_std.wz_std_decay_to: 0.06 # 0.06 for 1.0 m/s max
MPPI advanced: straight wz hold
On a straight path, while the lateral offset now and after a projected distance stays inside a band, every rollout is sampled with wz held to zero (or to a small limit). The critics then choose only the speed, and the robot runs parallel to the path. Any offset is corrected in the next bend, where the robot turns anyway. It exists to remove the small wz weave on straights.
- hmgics_navigation2/nav2_mppi_controller/include/nav2_mppi_controller/tools/straight_wz_hold.hpp
- hmgics_navigation2/nav2_mppi_controller/src/optimizer.cpp
- hmgics_navigation2/nav2_mppi_controller/src/straight_wz_hold_visualizer.cpp
FollowPath.advanced.straight_wz_hold
true
default false
Switches the straight-line
wzhold on. Logs[STRAIGHT_WZ_HOLD]engaged / released with the reason.# [HMGICS Alex 17Sep] Straight wz hold (nav2_mppi_controller/tools/straight_wz_hold.hpp). # On a straight path, while the lateral offset (now, and 1 m ahead at the current heading # error) stays inside the band, every rollout keeps wz = 0: the command's wz is EXACTLY 0 # and the critics only choose the speed. The offset is corrected in the next bend, where # the robot turns anyway. What remains after antithetic sampling is correction of real # error (hand-over 1.0-1.4 cm off the path, bend-exit residue), which a diff drive cannot # do without wz. Band 1 cm; 2 cm from the shim's hand-over until the first release, since # hand-over offset + the turn's 0.1-0.3 deg residual need ~2 cm over the 4 m start straight. # Released 2 s of travel before a bend, and by CostCritic wanting a steer. # Offline, route replay forward x10 seeds, hand-over 1.0 / 1.44 cm parallel, AMCL-level noise: # start (first 3 s) wz rms 0.021-0.028 -> 0 (100% of cycles exactly 0) # last 1.4 m: 81-95% of cycles exactly 0, rms 0.0031-0.0041 -> 0.0016-0.0035, # remaining corrections peak 0.020-0.026 (off 0.012-0.022) # max CTE straight 1.0-1.45 -> 1.0-1.64 cm, bend 2.2-2.3 -> 2.2-2.4, goal 0.3 -> 0.7-1.0 # route time unchanged. Logs [STRAIGHT_WZ_HOLD] engaged / released (reason). straight_wz_hold: true
FollowPath.advanced.straight_hold_lateral_tolerance
0.015m
default 0.01
Lateral band left uncorrected on a straight. After any release, re-engaging needs
straight_hold_engage_ratio(0.5) times this, so a correction that has started is finished.straight_hold_lateral_tolerance: 0.015 ## 18Sep Alex ### original 0.01
FollowPath.advanced.straight_hold_handover_lateral_tolerance
0.050m
default -1.0
Wider band used from a controller reset (the shim’s hand-over) until the hold is first released. Lets a hand-over offset ride to the first bend instead of being steered out while accelerating. Not applied within 1.5 m of the path’s end, nor after
straight_hold_handover_distance. At or below the normal band = off.straight_hold_handover_lateral_tolerance: 0.050 ## 24Sep Alex: 0.015
FollowPath.advanced.straight_hold_prediction_distance
1.5m
default 1.0
Distance, capped at the remaining path, over which the heading error is projected into lateral error for the band check. This predicted offset is what usually releases the hold on a long straight.
straight_hold_prediction_distance: 1.5 ## 24Sep Alex: 3.0
FollowPath.advanced.straight_hold_filter_time
0.3s
same as the default
While held, lateral offset and heading are low-pass filtered with this time constant, so localisation noise read through the prediction distance does not release the hold at random. 0 disables.
straight_hold_filter_time: 0.3 ## 18Sep Alex: original 0.3
FollowPath.advanced.straight_hold_obstacle_influence
1.0
default 0.5
Releases the hold, and re-optimises that cycle with
wzsampled, whenCostCritic’s cost range over the held rollouts exceeds this ×temperature, meaning obstacles want a steer the hold forbids. Negative disables.straight_hold_obstacle_influence: 1.0 ## 18Sep Alex ### original 0.5
See alsoFollowPath.temperature
FollowPath.advanced.straight_hold_lookahead_time
2.0s
default 1.0
Straightness is checked over max(0.5 m, |vx| × this) of path ahead, so the hold releases this long before a bend and MPPI settles any offset before entering it.
FollowPath.advanced.straight_hold_max_wz
0.015rad/s
default 0.0
Yaw rate a held cycle may still command. 0 is the original rule, an exactly zero command. A small limit lets the optimiser trim the offset instead of banking it until the hold releases.
FollowPath.advanced.straight_hold_handover_distance
3.0m
default 0.0
Travel after a reset over which the hand-over band applies, integrated from measured |vx|. After it the normal band does, even if the hold has not released. 0 = until the first release.
# [HMGICS Alex 25Sep] The 5 cm hand-over band applies only over the first 3 m of travel # after the shim's hand-over (about the distance to reach cruise), then the 1.5 cm band # does. overshoot_at_node0_1: on the 14 m edge 16 -> 0 the 5 cm band held wz = 0 for 12 m # at the turn's -0.3 deg residual, drifted -3.4 cm, released only 1.5 m before node 0, # and the shim carried 2.6 cm through the vertex into the next edge (the S-correction). # On that bag's readings the 1.5 cm band would have released at ~4.9 m, 0.8 cm off, at # cruise. 0 = no limit (the old behaviour). straight_hold_handover_distance: 3.0
FollowPath.advanced.straight_hold_handover_after_pause
true
default false
When
warm_start_timeoutdiscards the warm start (the shim owned the robot, for example at a vertex), also start a fresh hand-over for the hold. False only re-judges the hold as an engage.# [HMGICS Alex 26Sep] The shim's hand-over after a vertex turn (node 0) gets the same # hand-over band as after a goal. mew_test: after node 0 the hold was on its strict # re-engage band (0.75 cm), so the 0.9-1.6 cm the shim delivered was steered out at # pull-away with a 0.9-1.8 deg yaw swing -- the "early oscillation". Now it rides the 5 cm # band for 3 m and is corrected at cruise, if at all. Needs warm_start_timeout > 0 (that is # what detects the hand-over). false = the hold is only re-judged there. straight_hold_handover_after_pause: true
FollowPath.advanced.straight_hold_publish_debug_markers
true
default falsediagnostic
Publishes an RViz view of the hold on
straight_wz_hold_markers: the band being judged, the offset now and the predicted offset. Nothing is built unless something subscribes. Static.# Shows the band the hold is judging against, the robot's offset from the path line, # and the PREDICTED offset after straight_hold_prediction_distance of travel # which is what usually releases the hold on a long straight, # while the robot itself is still well inside. straight_hold_publish_debug_markers: true
FollowPath.advanced.straight_hold_debug_text_size
0.16m
same as the defaultdiagnostic
Height of the hold’s readout text.
FollowPath.advanced.straight_hold_debug_readout_offset_x
0.0m
same as the defaultdiagnostic
Offset of the readout from the robot, in the local costmap frame.
FollowPath.advanced.straight_hold_debug_readout_offset_y
-2.2m
same as the defaultdiagnostic
Offset of the readout from the robot, in the local costmap frame. The default keeps it clear of the
TerminalGoalCriticreadout.
MPPI advanced: speed governors
Caps on vx computed every cycle before sampling: a latency-aware braking envelope toward the goal, a legacy near-goal ramp, and a curvature limit that slows the robot ahead of arcs. When terminal_braking is on, the optimizer applies the braking envelope INSTEAD of the near-goal ramp. The curvature cap applies on top of whichever one runs.
- hmgics_navigation2/nav2_mppi_controller/src/optimizer.cpp
FollowPath.advanced.near_goal_velocity_scaling
true
default falseno effect here
Legacy ramp: narrows the
vx/wzcaps linearly from full atnear_goal_distanceof remaining path to thenear_goal_*values at the goal.# Terminal translation is governed by the latency-aware MPPI braking envelope below. near_goal_velocity_scaling: true
No effect here.
terminal_brakingis on, and the optimizer runs it INSTEAD of the near-goal ramp (it logs a warning saying so).FollowPath.advanced.near_goal_distance
1.5m
default 0.0no effect here
Remaining path length over which the near-goal ramp runs.
near_goal_distance: 1.5 #1.0 for 1.0 m/s max
No effect here.
terminal_brakingis on, and the optimizer runs it INSTEAD of the near-goal ramp (it logs a warning saying so).FollowPath.advanced.near_goal_vx_max
0.10m/s
default 0.25no effect here
Forward cap at the goal under the near-goal ramp.
No effect here.
terminal_brakingis on, and the optimizer runs it INSTEAD of the near-goal ramp (it logs a warning saying so).FollowPath.advanced.near_goal_vx_min
-0.10m/s
default 0.0no effect here
Reverse cap at the goal under the near-goal ramp.
No effect here.
terminal_brakingis on, and the optimizer runs it INSTEAD of the near-goal ramp (it logs a warning saying so).FollowPath.advanced.near_goal_wz_max
0.25rad/s
default 0.5no effect here
Yaw-rate cap at the goal under the near-goal ramp.
No effect here.
terminal_brakingis on, and the optimizer runs it INSTEAD of the near-goal ramp (it logs a warning saying so).FollowPath.advanced.near_goal_vx_std
# 0.033m/s
commented out, so the code uses -1.0
Narrows the
vxSAMPLING toward the goal to match the narrowed cap, so the terminal batch is not mostly outside the executable band. Keepingnear_goal_vx_std / near_goal_vx_max = vx_std / vx_maxholds the ratio constant. -1 = off.# near_goal_vx_std / near_goal_vx_max == vx_std / vx_max # near_goal_vx_std: 0.033 # as-is: 0.5 * 0.10 / 1.5
FollowPath.advanced.terminal_braking
true
same as the default
Caps |vx| each cycle at the speed from which the robot can still stop at the terminal goal:
a · (sqrt(τ² + 2d/a) − τ)over the remaining distance d, lessterminal_stop_margin. Replaces the near-goal ramp when on.See alsoFollowPath.advanced.near_goal_velocity_scalingFollowPath.TerminalGoalCritic.stop_vx_weight
FollowPath.advanced.terminal_brake_decel
0.5m/s²
same as the default
Deceleration
aof the braking envelope.TerminalGoalCriticcharges rollouts that exceed the same curve.terminal_brake_decel: 0.5 # replace with loaded-robot measurement before final tuning
FollowPath.advanced.terminal_brake_latency
0.10s
same as the default
Latency
τthe envelope plans for: the command already in flight.FollowPath.advanced.terminal_stop_margin
0.01m
same as the default
Distance short of the goal the envelope aims to stop at.
FollowPath.advanced.terminal_heading_lookback
0.10m
same as the default
Path length behind the goal used to measure the approach direction, which defines longitudinal and lateral at the goal.
FollowPath.advanced.curvature_velocity_scaling
true
default false
Caps
vxso the lateral acceleration on any arc ahead stays undermax_lateral_accel, back-propagated throughcurvature_decelso the slowdown starts early enough to be reachable.FollowPath.advanced.max_lateral_accel
0.17m/s²
default 0.0
Lateral acceleration allowed on an arc: the corner speed is
sqrt(max_lateral_accel / curvature). 0 disables the cap.max_lateral_accel: 0.17 # [HMGICS Alex 17Sep] 0.30 = faster arcs, max CTE 3.2 -> 3.8 cm offline
FollowPath.advanced.curvature_decel
0.3m/s²
default 1.0
Deceleration the corner speed is back-propagated with. Lower starts slowing further out.
# [HMGICS Alex 17Sep] 1.0 -> 0.3: start slowing ~2.5 m before an arc instead of ~0.7 m. # At 1.0 the robot reached the arc at 1.2-1.4 m/s; the rollouts (one speed cap for the # whole horizon) then crossed the arc at that speed, and PathFollow's progress reward # made cutting in cheapest: 10 cm off before the arc, 15 cm inside it. Offline on the # route from nav2_full_20260917_202927, with PathAlign 45 / PathFollow 3.5 below. curvature_decel: 0.3
FollowPath.advanced.curvature_sample_distance
0.2m
same as the default
Arc length either side of a point used to measure curvature, so the planner’s discretisation does not read as curvature.
FollowPath.advanced.curvature_vx_min
0.15m/s
same as the default
Floor on the curvature cap, so a sharp corner cannot stop the robot.
MPPI: motion model, critics and namespace
Which motion model the rollouts are integrated with, which critics score them and in what order, plus two keys that sit in this namespace without doing what their names suggest.
- hmgics_navigation2/nav2_mppi_controller/src/optimizer.cpp
- hmgics_navigation2/nav2_mppi_controller/include/nav2_mppi_controller/motion_models.hpp
- hmgics_navigation2/nav2_mppi_controller/src/critic_manager.cpp
FollowPath.enforce_path_inversion
True
default falseno effect here
Read by every critic through its parent namespace (
FollowPath). OnlyGoalCriticacts on it, aiming at the cusp rather than the goal.## FIXME: enforce_path_inversion never reaches the critics enforce_path_inversion: True
No effect here. The only critic that uses it,
GoalCritic, is disabled.See alsoenforce_path_inversion
FollowPath.transform_tolerance
0.1
no default: must be setno effect here
Nothing in this build reads it. The controller server takes its transform tolerance from the local costmap, and this fork of MPPI no longer declares one.
Not read by the code. No
getParamfor it in the controller server, the shim or MPPI.FollowPath.motion_model
"diff_drive"
default "DiffDrive"
Name of the motion-model PLUGIN instance. In this fork the model is loaded through pluginlib from
FollowPath.<motion_model>.plugin, so this name and thediff_driveblock below must agree. The commented-out"DiffDrive"is the stock spelling, which would look forFollowPath.DiffDrive.plugin.# motion_model: "DiffDrive" motion_model: "diff_drive"
See alsoFollowPath.diff_drive.plugin
FollowPath.diff_drive.plugin
"mppi::DiffDriveMotionModel"
no default: must be set
Class of the motion model: non-holonomic, integrates
vxandwzonly.See alsoFollowPath.motion_model
FollowPath.AckermannConstraints.min_turning_r
0.2m
same as the defaultno effect here
Minimum turning radius for the Ackermann model.
No effect here. The motion model is a differential drive.
FollowPath.critics
["ConstraintCritic", "CostCritic", "GoalCritic", "GoalAngleCritic", "TerminalGoalCritic", "PathAlignCritic", "CurvatureOnlyPathAlignCritic", "PathFollowCritic", "PathAngleCritic", "PreferForwardCritic", "TwirlingCritic"]
default []
Critics loaded, in scoring order. A critic listed here still does nothing while its own
enabledis false, which is how the file switches critics off without removing them. Left empty, MPPI loads no critics at all.critics: [ "ConstraintCritic", #0 "CostCritic", #1 "GoalCritic", #2 "GoalAngleCritic", #3 "TerminalGoalCritic", #4 "PathAlignCritic", #5 "CurvatureOnlyPathAlignCritic", #6 "PathFollowCritic", #7 "PathAngleCritic", #8 "PreferForwardCritic", #9 "TwirlingCritic" #10 ]
MPPI: diagnostics
Topics that exist for tuning. None of these change a command, but some cost CPU while they are on, and the file marks two to switch off for deployment.
- hmgics_navigation2/nav2_mppi_controller/src/controller.cpp
- hmgics_navigation2/nav2_mppi_controller/src/critic_manager.cpp
- hmgics_navigation2/nav2_mppi_controller/src/trajectory_visualizer.cpp
FollowPath.visualize
false
same as the defaultdiagnostic
Publishes candidate trajectories. Can slow the controller significantly.
FollowPath.publish_optimal_trajectory
true
default falsediagnostic
Publishes the full optimal trajectory each cycle on
optimal_trajectory, when something subscribes.FollowPath.publish_critics_stats
true
default falsediagnostic
Publishes
critics_stats: per-critic cost sums and spreads, plus this fork’s effective sample size. The measurement every weight in this file was balanced on. Static.FollowPath.publish_path_window
false
same as the defaultdiagnostic
Draws the reference window
PathAlignCriticcan see onpath_align_windowand adds its figures tocritics_stats. Static.# [DIAGNOSTIC] Draws the reference window the path-alignment critic can actually see # on ~/path_align_window, and adds the numbers to ~/critics_stats. PathAlignCritic now # uses the complete controller-local path, not only the furthest rollout projection. # Clamping should therefore occur only when that whole local path is shorter than a # rollout (normally near a goal or feasible-path cusp). # # Watch path_window_length / trajectory_reach_length. Near 1.0 is healthy. The RViz # marker is green at >= 80%, amber at 50-80%, and red below 50%; its readout also says # whether PathAlign contributed and how much of the rollout was clamped. publish_path_window: false # [NOTE] For path_align_window, Disable this during deployment
FollowPath.TrajectoryVisualizer.trajectory_step
5
same as the defaultno effect herediagnostic
Every n-th candidate trajectory is drawn.
No effect here.
visualizeis false.FollowPath.TrajectoryVisualizer.time_step
3
same as the defaultno effect herediagnostic
Every n-th point of a drawn trajectory.
No effect here.
visualizeis false.
ConstraintCritic
mppi::critics::ConstraintCritic
Penalises the part of each rollout that exceeds the velocity limits. Reads vx_max, vx_min and vy_max once at start-up, so a dynamic speed limit never reaches it.
- hmgics_navigation2/nav2_mppi_controller/src/critics/constraint_critic.cpp
FollowPath.ConstraintCritic.enabled
true
same as the default
Switches the critic on or off without removing it from
critics.FollowPath.ConstraintCritic.cost_power
1
same as the default
Power applied to the weighted cost. 1 = linear.
FollowPath.ConstraintCritic.cost_weight
4.0
same as the default
Weight of the over-limit penalty.
GoalCritic
mppi::critics::GoalCritic
Switched off in this file (enabled: false).
Stock position critic: the mean distance to the goal over the horizon, inside threshold_to_consider of the end of the path. Disabled here, and the block comment says why.
Opening comment in the file
# [DISABLED] Superseded by TerminalGoalCritic, which now owns the whole band # below 1.4 m. Both are UPPER gates on local_path_length, so leaving this on # meant two position critics running together below 1.2 m. Three problems with # that, none of them a disagreement about the optimum -- measured, the two rank # rollouts identically in every case: # 1. Double counting. Each critic gets balanced to std/T 0.5-2.0 on its own, # so two overlapping position terms put the COMBINED authority near 4 T. # 2. It dilutes the anisotropy. This critic is isotropic (euclidean), the # terminal one weights lateral 1.5x because a diff drive cannot fix lateral # error. Measured lateral/longitudinal penalty ratio: 1.87 terminal alone, # 1.16 this one alone, 1.65 for the sum -- pulled back toward isotropic. # 3. A second relay. Two gates on a jittery local_path_length means two step # changes in total cost during the terminal approach, which is the same # gating pattern blamed for the weave everywhere else in this file. # # What used to be given up: this critic scores the MEAN distance over the horizon, # so it rewards arriving early. TerminalGoalCritic now restores that property with # prompt_progress_weight, using time-averaged 2-D goal error rather than a second # independently gated position critic. # # To revert: set enabled back to true and drop TerminalGoalCritic's # threshold_to_consider to 1.2 -- but then tune the two AS A PAIR on their # combined costs_std, never separately.
- hmgics_navigation2/nav2_mppi_controller/src/critics/goal_critic.cpp
FollowPath.GoalCritic.enabled
false
default true
Switches the critic on or off without removing it from
critics.FollowPath.GoalCritic.cost_power
1
same as the defaultno effect here
Power applied to the weighted cost. 1 = linear.
No effect here.
GoalCriticis switched off by its ownenabled: false.FollowPath.GoalCritic.cost_weight
5.0
same as the defaultno effect here
Weight of the mean-distance-to-goal term.
No effect here.
GoalCriticis switched off by its ownenabled: false.FollowPath.GoalCritic.threshold_to_consider
1.4m
same as the defaultno effect here
The critic runs only while the remaining local path is shorter than this.
No effect here.
GoalCriticis switched off by its ownenabled: false.
GoalAngleCritic
mppi::critics::GoalAngleCritic
Penalises heading error to the goal yaw near the end of the path. In this fork it can score the path’s arrival heading instead when the goal yaw is far off it.
Opening comment in the file
# [NOTE] This critic may be spending its weight on the wrong axis. Two facts: # # 1. This robot is NONHOLONOMIC -- motion_model is diff_drive and vy_max is 0.0, # so DiffDriveMotionModel::isHolonomic() is false. It has two controls, v and # w, and cannot translate sideways. The only ways it can change yaw are to # curve (v != 0, w != 0), which moves x and y with it, or to stop and spin # (v = 0), which surrenders all forward progress -- and under payload the # wheel scrub of a spin drags the body anyway. There is no way for it to fix # yaw for free. On an omni platform none of this would apply. # # 2. rotate_to_goal_heading is true, so the shim redoes terminal yaw regardless, # and computeRotateToHeadingCommand() only ever sets twist.angular.z -- it # never commands vx. Position error at that handover is therefore FROZEN. # # Together: this critic is active in the last 0.5 m while the robot is still # approaching, so the cheapest way for a rollout to reduce yaw error here is to # curve -- which displaces the very terminal position the goal tolerance is # measured on. It buys yaw, which the shim will redo anyway, with position, # which the shim cannot fix. TerminalGoalCritic carries a small yaw term of its # own (yaw_weight 0.15) for the one job that does matter: keeping the shim's # spin short, since a long spin drags xy through slip. # # With rotate_to_goal_heading enabled, the shim redoes terminal yaw # anyway, and on a diff drive robot this critic buys that yaw with POSITION error # -- which is the one thing the shim cannot fix, because it commands wz only. # TerminalGoalCritic carries a small yaw term of its own for the same job. # Once TerminalGoalCritic is tuned, try dropping this to ~1.5 or disabling it # and compare goal xy p95; do NOT change both in the same run.
- hmgics_navigation2/nav2_mppi_controller/src/critics/goal_angle_critic.cpp
FollowPath.GoalAngleCritic.enabled
true
same as the default
Switches the critic on or off without removing it from
critics.FollowPath.GoalAngleCritic.cost_power
1
same as the default
Power applied to the weighted cost. 1 = linear.
FollowPath.GoalAngleCritic.cost_weight
3.0
same as the default
Weight of the heading term.
FollowPath.GoalAngleCritic.threshold_to_consider
1.5m
default 0.5
The critic runs only while the remaining local path is shorter than this.
Source check. The opening comment says this critic "is active in the last 0.5 m". That was the default. At 1.5 it is active over the last 1.5 m, three times the stretch the argument was made about.
FollowPath.GoalAngleCritic.symmetric_yaw_tolerance
true
default false
Accept either heading at the goal, matching the bidirectional goal checker.
FollowPath.GoalAngleCritic.path_heading_yaw_threshold
0.05rad
default -1.0
When the goal yaw differs from the path’s arrival heading by more than this, score the arrival heading instead: the shim turns onto the goal yaw after arrival anyway. -1 disables.
# [HMGICS Alex 17Sep] Score the path's arrival heading instead of the goal yaw when they # differ by more than this (rad). Same switch as TerminalGoalCritic below; the shim # turns onto the goal yaw in place after arrival. Offline with a goal yaw 90 deg off # the final straight, this critic plus the terminal yaw term turned the chassis up to # 90 deg over the last 0.5 m (14.5 cm off the path, 10 s to arrive); with both # switched: 0.4 cm, 0.6 deg, 3.9 s. 0.05 (~ yaw_goal_tolerance) also keeps a 15 deg # goal from pre-rotating (2.5 -> 0.5 cm). -1 disables. path_heading_yaw_threshold: 0.05
See alsoFollowPath.TerminalGoalCritic.path_heading_yaw_threshold
TerminalGoalCritic
mppi::critics::TerminalGoalCritic
An HMGICS critic that parks the robot on the goal. It scores each rollout around its CLOSEST approach, not its mean or its end. It splits position error into lateral and longitudinal in the goal frame, charges speed after arrival and distance past the goal, and fades in over a band of remaining path so the handover from the path critics is not a step. One cost_weight scales every term, so the other weights here are relative.
Opening comment in the file
# Parks the robot ON the goal over the last metre. # # [!] cost_weight below is a STARTING POINT, not a tuned value. Balance it the # way every other critic here was: read costs_std off critics_stats, divide by # cost_weight to get the raw-unit spread, and pick the weight that lands # std/T = cost_weight * raw_spread / 0.3 # in the 0.5-2.0 band. This critic only scores the last 0.25 s of each rollout, # so its raw spread will NOT resemble GoalCritic's -- measure, do not scale. # # This fork also publishes effective sample size on critics_stats. Watch it # during the terminal approach: collapsing toward 1 means the softmax has # narrowed onto a single sampled rollout and the command is a fresh random draw # each cycle (jitter at the goal); sitting near batch_size means nothing is # steering. That is the fastest signal that this weight is too high or too low.
- hmgics_navigation2/nav2_mppi_controller/src/critics/terminal_goal_critic.cpp
- hmgics_navigation2/nav2_mppi_controller/include/nav2_mppi_controller/critics/terminal_goal_critic.hpp
FollowPath.TerminalGoalCritic.enabled
true
same as the default
Switches the critic on or off without removing it from
critics.FollowPath.TerminalGoalCritic.cost_power
1
same as the default
Power applied to the weighted cost. 1 = linear.
FollowPath.TerminalGoalCritic.cost_weight
8.0
same as the default
One weight over every term below; balance it on the measured
costs_std, not by analogy.FollowPath.TerminalGoalCritic.threshold_to_consider
1.4m
default 1.2no effect here
Legacy handover distance. Only used as the default for
activation_full, which is set explicitly here.# Legacy fallback for older TerminalGoalCritic binaries. The explicit activation # band below is used by this source version. threshold_to_consider: 1.4
No effect here.
activation_startandactivation_fullare both set, and they replace it.FollowPath.TerminalGoalCritic.activation_start
2.5m
default:
threshold_to_consider+ 0.4Remaining-path distance at which the critic starts to fade in.
# Fade terminal position/yaw in while PathFollow is still active, then reach full # authority exactly where PathFollow gates off (1.4 m). This overlap removes the # one-cycle critic handoff without leaving a goal-seeking gap. activation_start: 2.5 ## Beh: 1.8 ## Kelvin: 2.5
FollowPath.TerminalGoalCritic.activation_full
1.4m
default:
threshold_to_considerDistance at which it reaches full authority. Set equal to the path critics’
threshold_to_consider(1.4), so it is at full strength exactly where they switch off.See alsoFollowPath.PathFollowCritic.threshold_to_considerFollowPath.PathAlignCritic.threshold_to_consider
FollowPath.TerminalGoalCritic.terminal_window_time
0.50s
default 0.25
Width of the scoring windows in physical time, independent of
model_dt. Position and yaw use a window centred on closest approach; velocity uses one starting there.# Physical time, independent of model_dt. Position/yaw use a centred window; # velocity uses a forward-only window beginning at closest approach. terminal_window_time: 0.50
Source check. The opening comment says the critic "only scores the last 0.25 s of each rollout". Two things changed: the window is 0.50 s, and it is centred on each rollout’s closest approach to the goal, not on the end of the horizon. The header explains why scoring the end was dropped.
FollowPath.TerminalGoalCritic.stop_ramp_distance
1.0m
default 0.6
Remaining distance over which the post-arrival stop term fades in, reaching full strength at the goal.
# Do not reward crawling from 1.4 m away. The stop/dwell term fades in only over # the final 0.6 m and reaches full strength at the goal. stop_ramp_distance: 1.0 #0.6 ##1.0 kelvin
Source check. The comment above says the stop term "fades in only over the final 0.6 m". It is 1.0 here (Kelvin’s value, per the inline note), so it fades in over the last metre.
FollowPath.TerminalGoalCritic.lateral_weight
1.0
default 1.5
Weight on lateral error in the goal frame.
# Lateral > longitudinal: a diff drive drives longitudinal error out on the # spot, but lateral error needs a manoeuvre it has no room for this close in. lateral_weight: 1.0 ##1.5
Source check. The comment argues "Lateral > longitudinal" because a differential drive cannot fix lateral error close in. At 1.0 against a longitudinal 1.0 there is no longer any anisotropy, so the critic scores position isotropically.
FollowPath.TerminalGoalCritic.longitudinal_weight
1.0
same as the default
Weight on error along the goal heading.
FollowPath.TerminalGoalCritic.yaw_weight
0.15
same as the default
Weight on yaw error. Kept small because the shim owns the terminal yaw; this only keeps its in-place spin short.
# Deliberately small -- the rotation shim owns terminal yaw. This only keeps # the shim's in-place spin short, because that spin drags xy through slip. yaw_weight: 0.15 ## 18Sep Alex ## original 1.0
FollowPath.TerminalGoalCritic.braking_vx_weight
1.0
same as the default
Charges rollout speed above the braking curve (
advanced.terminal_brake_*) before arrival.FollowPath.TerminalGoalCritic.prompt_progress_weight
1.0
same as the default
Weight on time-averaged 2-D goal error, which makes arriving earlier cheaper than scheduling the same arrival at the horizon’s end. Not a minimum-speed target.
# Prevent receding-horizon procrastination: reducing 2-D goal error early is cheaper # than scheduling the same arrival at the horizon end. The 2-D form stays active for # lateral and already-past recovery. This is not a minimum-speed target; braking and # dwell costs still own the smooth stop. prompt_progress_weight: 1.0
FollowPath.TerminalGoalCritic.stop_vx_weight
0.15
default 0.5
Charges forward/backward speed after closest approach, which separates parking from passing through.
# What separates "park here" from "pass through here". Units are m/s and rad/s # against metres of position error; at the near-goal caps (vx 0.20, wz 0.25) # they are worth about 0.10 m and 0.05 m of equivalent position error. stop_vx_weight: 0.15
Source check. The comment prices these weights "at the near-goal caps (vx 0.20, wz 0.25)". The near-goal ramp is inert while
terminal_brakingis on, and itsvxcap is 0.10 in this file anyway. The speeds a rollout actually has at arrival come from the braking envelope.FollowPath.TerminalGoalCritic.stop_wz_weight
0.2
same as the default
Charges yaw rate after closest approach.
FollowPath.TerminalGoalCritic.overshoot_weight
1.0
default 0.0
Charges each sample’s distance PAST the goal along the incoming direction, averaged over the whole horizon. 0 disables.
# Charges the part of each rollout PAST the goal, along the path's incoming # direction, averaged over the whole horizon (metres, like position error). # Nothing above looks beyond a 0.25 s window at closest approach, so the optimal # path ran 0.34-0.38 m past the goal with the robot still 7-8 cm short # (nav2_full_20260915_072506). At 1.0, sitting 1 cm past costs the same as # stopping 1 cm short. Tune on the goal checker's x_error: trending negative # (parking short) -> lower; plan still running past the goal -> raise. 0 disables. overshoot_weight: 1.0
FollowPath.TerminalGoalCritic.symmetric_yaw_tolerance
true
same as the default
Accept either heading at the goal, matching the checker and the shim.
# Match BidirectionalGoalChecker and the shim's selectBidirectionalHeading. symmetric_yaw_tolerance: true
FollowPath.TerminalGoalCritic.path_heading_yaw_threshold
0.05rad
same as the default
Score the path’s arrival heading instead of the goal yaw when they differ by more than this. Set together with
GoalAngleCritic’s. -1 disables.# [HMGICS Alex 17Sep] Yaw term scores the path's arrival heading when the goal yaw is # more than this (rad) off it. See GoalAngleCritic above; set both together. -1 disables. path_heading_yaw_threshold: 0.05
See alsoFollowPath.GoalAngleCritic.path_heading_yaw_threshold
FollowPath.TerminalGoalCritic.publish_debug_markers
false
same as the defaultdiagnostic
Publishes the critic’s terminal diagnostics as RViz markers. Static.
# path_follow_debug_markers: true # [NOTE] Disable this during deployment publish_debug_markers: false # [NOTE] Disable this during deployment
FollowPath.TerminalGoalCritic.debug_readout_offset_x
-1.5m
default 0.0diagnostic
Readout position relative to the robot.
# Large saturated text so the terminal diagnostics remain legible over a white map. debug_readout_offset_x: -1.5
FollowPath.TerminalGoalCritic.debug_readout_offset_y
-1.0m
default -1.5diagnostic
Readout position relative to the robot.
FollowPath.TerminalGoalCritic.debug_readout_text_size
0.22m
same as the defaultdiagnostic
Readout text height.
FollowPath.TerminalGoalCritic.debug_reached_hold_time
-1.0s
default 8.0diagnostic
How long the "reached" readout stays up. Negative keeps the final frame until a new goal clears it.
# Once XY tolerance is reached, keep the complete final marker frame indefinitely. # Use a positive duration for timed expiry; -1 means no automatic RViz expiry. # A later inactive/new-goal update can still clear it explicitly with DELETEALL. debug_reached_hold_time: -1.0
PreferForwardCritic
mppi::critics::PreferForwardCritic
Switched off in this file (enabled: false).
Penalises reversing. Disabled: the robot is bidirectional, and the shim picks the direction.
- hmgics_navigation2/nav2_mppi_controller/src/critics/prefer_forward_critic.cpp
FollowPath.PreferForwardCritic.enabled
false
default true
Switches the critic on or off without removing it from
critics.FollowPath.PreferForwardCritic.cost_power
1
same as the defaultno effect here
Power applied to the weighted cost. 1 = linear.
No effect here.
PreferForwardCriticis switched off by its ownenabled: false.FollowPath.PreferForwardCritic.cost_weight
5.0
same as the defaultno effect here
Weight of the reversing penalty.
No effect here.
PreferForwardCriticis switched off by its ownenabled: false.FollowPath.PreferForwardCritic.threshold_to_consider
0.5m
same as the defaultno effect here
Switches off within this remaining path length.
No effect here.
PreferForwardCriticis switched off by its ownenabled: false.
CostCritic
mppi::critics::CostCritic
Scores rollouts on the local costmap: a collision costs collision_cost, a point at or above near_collision_cost adds critical_cost, and anything else adds the cell cost, except near the goal.
- hmgics_navigation2/nav2_mppi_controller/src/critics/cost_critic.cpp
FollowPath.CostCritic.enabled
true
same as the default
Switches the critic on or off without removing it from
critics.FollowPath.CostCritic.cost_power
1
same as the default
Power applied to the weighted cost. 1 = linear.
FollowPath.CostCritic.cost_weight
3.0
default 3.81
Weight of the costmap term, divided by the number of points scored.
FollowPath.CostCritic.near_collision_cost
253
same as the default
Cell cost at or above which a point adds
critical_cost. 253 is the inscribed-inflated cost; a higher value is accepted with a warning.FollowPath.CostCritic.critical_cost
60.0
default 300.0
Added per point at or above
near_collision_cost.FollowPath.CostCritic.consider_footprint
false
same as the default
Collision-check the full footprint instead of the centre point. Scoring always uses the centre cost.
FollowPath.CostCritic.collision_cost
1000000.0
same as the default
Cost of a rollout that collides.
FollowPath.CostCritic.near_goal_distance
1.0m
default 0.5
Inside this remaining path the ordinary cell cost is no longer added, so the robot can reach a goal near obstacles. Collisions still count.
FollowPath.CostCritic.trajectory_point_step
2
same as the default
Scores every n-th rollout point.
PathAlignCritic
mppi::critics::PathAlignCritic
Penalises how far each rollout’s points lie from the path. In this fork it scores against the whole controller-local path. It is the main term holding the robot on the line.
Opening comment in the file
## Default value # cost_weight: 14.0 # max_path_occupancy_ratio: 0.05 # threshold_to_consider: 0.5 # offset_from_furthest: 20
- hmgics_navigation2/nav2_mppi_controller/src/critics/path_align_critic.cpp
FollowPath.PathAlignCritic.enabled
true
same as the default
Switches the critic on or off without removing it from
critics.FollowPath.PathAlignCritic.cost_power
1
same as the default
Power applied to the weighted cost. 1 = linear.
FollowPath.PathAlignCritic.cost_weight
30.0
default 10.0
Weight of the distance-from-path term. The heaviest in the file.
# Balance critics on the SPREAD of their cost across the batch (critics_stats # costs_std), not costs_sum: MPPI softmaxes over (cost - min), so a term that shifts # every trajectory equally has no effect. Target std ~= 0.5-2.0 x temperature (0.3). # # Clean post-fix A/B with PathFollow=1.7: # weight 23: influence 2.51, CTE 2.35 cm RMS / 3.59 cm p95 / 15.6 s # weight 14: influence 1.44, CTE 4.45 cm RMS / 9.99 cm p95 / 12.2 s # Influence is magnitude, not correctness: 14 looks more conventionally balanced but # cuts the curve badly. The precision-first requirement therefore keeps 23. # # [HMGICS Alex 17Sep] 23 -> 45, with PathFollow 5.0 -> 3.5 and curvature_decel 0.3. # Offline, real MPPI on the Gazebo route (2 x 90 deg arcs R 2.06 m, reverse), 6 seeds, # max CTE: arc 15.0 -> 3.2 cm, curve entry 10.0 -> 1.3, exit 6.4 -> 2.5, straight # 1.3 -> 0.7; route time 37.3 -> 33.7 s (goal-yaw fix included). Arc n_eff 37 -> 136. # PathFollow kept at 5.0 with this weight was not robust (one seed cut 10.8 cm, arc # n_eff 27). Re-check live: CTE by section with tools/analyze_wz.py. cost_weight: 30.0 ## 18Sep Alex ### original 45.0
Source check. The comment’s measurements pair this weight with
PathFollowCritic3.5 at 45 (17 Sep). It was lowered to 30 on 18 Sep with PathFollow unchanged, and no comment records a measurement of the 30 / 3.5 pair now in the file.FollowPath.PathAlignCritic.max_path_occupancy_ratio
0.10
default 0.07
The critic stands down when more than this fraction of the path is in occupied space, so obstacle avoidance can win.
FollowPath.PathAlignCritic.trajectory_point_step
4
same as the default
Scores every n-th rollout point.
FollowPath.PathAlignCritic.threshold_to_consider
1.4m
default 0.5
The critic stops scoring once the goal is nearer than this, by remaining path or remaining motion, whichever is larger.
# threshold_to_consider: 0.15 threshold_to_consider: 1.4
FollowPath.PathAlignCritic.offset_from_furthest
4
default 20
A GATE, not an offset: the critic skips the cycle unless the furthest path point any rollout reached is at least this index.
# NOTE: this is a GATE, not an offset: the critic returns early when # furthest_reached_path_point < offset_from_furthest. That index was measured # oscillating around 20 at 10 Hz, so at 20 a weight-24.8 term switched fully on and # off every control cycle. Keep it well below the usual value of furthest. offset_from_furthest: 4
FollowPath.PathAlignCritic.use_path_orientations
false
same as the default
Also score heading against the path’s orientations.
CurvatureOnlyPathAlignCritic
mppi::critics::CurvatureOnlyPathAlignCritic
Switched off in this file (enabled: false).
An HMGICS critic that adds only a curvature-weighted extra alignment cost, spread along the path either side of each curved point. Disabled here.
- hmgics_navigation2/nav2_mppi_controller/src/critics/curvature_only_path_align_critic.cpp
FollowPath.CurvatureOnlyPathAlignCritic.enabled
false
default true
Switches the critic on or off without removing it from
critics.FollowPath.CurvatureOnlyPathAlignCritic.cost_power
1
same as the defaultno effect here
Power applied to the weighted cost. 1 = linear.
No effect here.
CurvatureOnlyPathAlignCriticis switched off by its ownenabled: false.FollowPath.CurvatureOnlyPathAlignCritic.cost_weight
8.0
same as the defaultno effect here
Weight of the curvature-weighted alignment term.
# Measured raw-unit spread 0.0339, so std/T = cost_weight * 0.0339 / 0.3. # At 3.0 this is 0.34, close to the ~0.2 floor below which a critic stops # influencing the result at all. 8.0 -> 0.90. cost_weight: 8.0
No effect here.
CurvatureOnlyPathAlignCriticis switched off by its ownenabled: false.FollowPath.CurvatureOnlyPathAlignCritic.max_path_occupancy_ratio
0.07
same as the defaultno effect here
As for
PathAlignCritic.No effect here.
CurvatureOnlyPathAlignCriticis switched off by its ownenabled: false.FollowPath.CurvatureOnlyPathAlignCritic.trajectory_point_step
2
default 4no effect here
Scores every n-th rollout point.
No effect here.
CurvatureOnlyPathAlignCriticis switched off by its ownenabled: false.FollowPath.CurvatureOnlyPathAlignCritic.threshold_to_consider
0.5m
same as the defaultno effect here
Stops scoring near the goal.
No effect here.
CurvatureOnlyPathAlignCriticis switched off by its ownenabled: false.FollowPath.CurvatureOnlyPathAlignCritic.offset_from_furthest
8
default 20no effect here
Same gate as
PathAlignCritic’s.No effect here.
CurvatureOnlyPathAlignCriticis switched off by its ownenabled: false.FollowPath.CurvatureOnlyPathAlignCritic.use_path_orientations
false
same as the defaultno effect here
Also score heading against the path’s orientations.
No effect here.
CurvatureOnlyPathAlignCriticis switched off by its ownenabled: false.FollowPath.CurvatureOnlyPathAlignCritic.curvature_gain
2.0
default 3.0no effect here
Extra alignment weight per unit curvature.
No effect here.
CurvatureOnlyPathAlignCriticis switched off by its ownenabled: false.FollowPath.CurvatureOnlyPathAlignCritic.max_curvature
1.51/m
default 2.0no effect here
Curvature is clamped here before the gain applies.
No effect here.
CurvatureOnlyPathAlignCriticis switched off by its ownenabled: false.FollowPath.CurvatureOnlyPathAlignCritic.max_curvature_extra
2.0
default 3.0no effect here
Cap on the extra weight a point can carry.
No effect here.
CurvatureOnlyPathAlignCriticis switched off by its ownenabled: false.FollowPath.CurvatureOnlyPathAlignCritic.curvature_influence_distance
2.4851m
default 0.0no effect here
Arc length either side of a curved point its weight is spread over, tapering to zero, so the run-out after a corner discriminates between rollouts.
No effect here.
CurvatureOnlyPathAlignCriticis switched off by its ownenabled: false.
PathFollowCritic
mppi::critics::PathFollowCritic
Rewards progress: distance from each rollout’s end to a point offset_from_furthest poses beyond the furthest point any rollout reached. The only critic that makes standing still expensive.
Opening comment in the file
## Default value # cost_weight: 5.0 # offset_from_furthest: 5
- hmgics_navigation2/nav2_mppi_controller/src/critics/path_follow_critic.cpp
FollowPath.PathFollowCritic.enabled
true
same as the default
Switches the critic on or off without removing it from
critics.FollowPath.PathFollowCritic.publish_debug_markers
false
same as the defaultdiagnostic
Publishes the carrot and rollout diagnostics on
path_follow_debug_markers. Static.publish_debug_markers: false # [NOTE] Disable this during deployment
FollowPath.PathFollowCritic.cost_power
1
same as the default
Power applied to the weighted cost. 1 = linear.
FollowPath.PathFollowCritic.cost_weight
3.5
default 5.0
Weight of the progress term. Too low and standing still is cheapest; too high and it cuts corners, because it only constrains the rollout’s END point.
# The only critic that rewards making progress along the path, so it must stay strong # enough that standing still is never the cheapest option. But it only constrains the # trajectory's END POINT -- it says nothing about the shape in between, so letting it # dominate makes the trajectory cut corners and drift off the path. # # Clean node18 -> node12 -> node11 live A/B (same route and old loaded PathAlign binary): # weight 4.5: 5.09 cm RMS / 10.95 cm p95 / 9.30 s # weight 3.0: 3.90 cm RMS / 8.42 cm p95 / 11.25 s # weight 1.7: 2.25 cm RMS / 3.64 cm p95 / 15.75 s # The stated requirement prioritises sticking to the path, so keep 1.7. A post-restart # full-reference run measured 2.35 cm RMS / 3.59 cm p95 at PathAlign=23; the time remained # about 15.6 s, so repeat both directions before accepting the throughput trade-off. # [HMGICS Alex 17Sep] 5.0 -> 3.5 together with PathAlign 45 (see there). 1.7 tracks best # (max CTE 5.3 cm with PathAlign 23) but is ~25% slower; 3.5 / 45 is tighter and faster. cost_weight: 3.5
FollowPath.PathFollowCritic.offset_from_furthest
5
default 6
Path points beyond the furthest reached point that the carrot is placed at.
FollowPath.PathFollowCritic.threshold_to_consider
1.4m
same as the default
The critic stops once the goal is nearer than this, where
TerminalGoalCriticis at full authority.
PathAngleCritic
mppi::critics::PathAngleCritic
Penalises heading away from a target point further along the path, but only when the robot’s angle to that point exceeds max_angle_to_furthest. A corrective term for large misalignment, not a tracking term.
Opening comment in the file
## Default value # offset_from_furthest: 4 # threshold_to_consider: 0.5 # max_angle_to_furthest: 1.0 # The only critic that reads trajectory yaws. Without it, at v ~ 0 every remaining # critic gives an identical trajectory for w = -1.0 and w = +1.0, so the cost is flat # in angular velocity and the robot has no gradient to rotate itself out of a stall.
- hmgics_navigation2/nav2_mppi_controller/src/critics/path_angle_critic.cpp
FollowPath.PathAngleCritic.enabled
true
same as the default
Switches the critic on or off without removing it from
critics.FollowPath.PathAngleCritic.cost_power
1
same as the default
Power applied to the weighted cost. 1 = linear.
FollowPath.PathAngleCritic.cost_weight
2.0
default 2.2
Weight of the angle term. Its cost is in radians, so small weights already matter.
# Corrective, not tracking -- keep low. Its cost is an angle in radians and the batch # terminal-yaw spread is ~0.3-0.5 rad, so 2.0 already lands at std/T ~1.3-2.2. # Do NOT raise above ~3: at 5 it saturates the softmax and drives the oscillation. cost_weight: 2.0
FollowPath.PathAngleCritic.offset_from_furthest
4
same as the default
Path points beyond the furthest reached point that the target is placed at.
# The gate below is evaluated once at the robot's CURRENT pose, so this critic is # all-on or all-off for the whole batch and behaves as a relay with no hysteresis -- # the oscillation source. Pushing the target point further out (12 -> 25 indices, so # ~0.6 m -> ~1.25 m past furthest_reached_path_point at 0.05 m path density) makes the # bearing far less sensitive to that index's jitter, which is what chatters the relay. offset_from_furthest: 4 #### 12
Source check. The comment argues for pushing the target further out, "12 -> 25 indices", to quiet the relay. The value is 4, which is the stock default, and the inline note records 12 as an earlier value. The argument above it describes a setting the file no longer has.
FollowPath.PathAngleCritic.threshold_to_consider
0.5m
same as the default
Switches off within this remaining path length.
FollowPath.PathAngleCritic.max_angle_to_furthest
0.6rad
default 0.785398
The critic only fires when the robot’s angle to the target exceeds this. The test is made once, at the robot’s pose, so the whole batch is on or off together.
# Do NOT widen to quiet the oscillation: at the measured stall the robot-to-target # angle was 0.68 rad, so at 0.6 the critic fires and at 0.8 it would never engage. max_angle_to_furthest: 0.6
FollowPath.PathAngleCritic.mode
1
default 0
0 prefers forward, 1 has no directional preference (either heading along the path is fine), 2 follows the path’s own orientations. 1 suits a bidirectional robot.
mode: 1 #### 2: consider feasible path orientation
TwirlingCritic
mppi::critics::TwirlingCritic
Penalises the mean |wz| of each rollout, and switches off once the remaining path is inside the goal checker’s xy tolerance.
- hmgics_navigation2/nav2_mppi_controller/src/critics/twirling_critic.cpp
FollowPath.TwirlingCritic.enabled
true
same as the default
Switches the critic on or off without removing it from
critics.FollowPath.TwirlingCritic.cost_power
1
same as the default
Power applied to the weighted cost. 1 = linear.
FollowPath.TwirlingCritic.cost_weight
2.0
default 10.0
Weight of the mean-|wz| penalty.
cost_weight: 2.0 ## 24Sep Alex: 25.0
Everything on this page describes hmc_nav2_params_tuning_18Sep.yaml as it stood on 27 Sept 2026. A value changed in the file since then is not reflected here. The version log is the record of what the robot actually ran.