Hi ROS community,
I’ve been working on a small ROS 2 package called ros2_kinematic_guard. It is a pre-E-stop guard for ROS 2 AMR/AGV systems.The idea is simple: Most mobile robot stacks already have timeouts. If /cmd_vel stops arriving, the base driver or controller can stop the robot.But timeouts only answer one question:
Did a command arrive recently?
They do not answer:
Is the robot still moving according to the command it was just given?
That gap matters in AMR/AGV deployments.
Why pre-E-stop detection matters
Safety-rated E-stop systems, safety PLCs, and safety lidars are the final protection layer. ros2_kinematic_guard does not replace them. The goal is different:
Detect execution collapse earlier, before the certified safety layer is forced to intervene.
Examples:
- wheel slip on wet or oily floors
- wheel-speed / odometry mismatch
- localization jumps from lidar / SLAM glitches
- stale or replayed command windows
- bad Wi-Fi / 5G command bursts
- robot shaking, spinning, or over-correcting before safety lidar cuts power
Frequent hard stops may contribute to:
- manual recovery time
- production interruption
- payload instability
- mechanical stress on wheels, reducers, and brakes
- unclear root cause during post-incident debugging
Why not just a timeout?
A timeout can detect silence. It cannot detect a physically inconsistent command-feedback episode. For example:
/cmd_vel is still arriving normallybut /odom no longer matches the commanded motion
That can happen during wheel slip, localization jumps, or command bursts after network buffering. ros2_kinematic_guard watches both /cmd_vel and /odom over a short sliding window. When the robot’s measured motion no longer matches the command stream, it emits a compact KinematicStatus JSON and can optionally clamp or brake the outgoing command.
The goal is to provide a local pre-E-stop diagnostic layer. By detecting and self-correcting minor kinematic inconsistencies early, we can significantly reduce the frequency of unforced hard E-stops, thereby improving operational uptime.
Zero-code modification
The package works as an inline ROS 2 topic filter. You do not need to modify Nav2, behavior trees, planners, controllers, or proprietary base drivers.
Nav2 / teleop / planner
↓
/cmd_vel
↓
Kinematic Guard
↓
/safe_cmd_vel
↓
base driver
It supports three modes:
mode:=observe— passive monitoring only, no control interventionmode:=passthrough— wiring test,/safe_cmd_velequals/cmd_velmode:=guard— active mode, can clamp velocity or enterBRAKE_AND_RESYNC
This means first-day deployment can be completely passive.
Example output
Healthy window:
{
"status": "GREEN",
"residual": 0.0009,
"causalAlignment": "ALIGNED",
"dominantCause": "NONE",
"guardAction": "OBSERVE_ONLY",
"safeCmd": {
"linear_vx": 0.8,
"angular_wz": 0.0
}
}
Wheel-slip window in observe mode:
{
"status": "RESYNCING",
"causalAlignment": "BROKEN",
"dominantCause": "WHEEL_SLIP",
"guardAction": "OBSERVE_ONLY",
"mode": "observe",
"controlInterceptionEnabled": false
}
Wheel-slip window in guard mode:
{
"status": "RESYNCING",
"causalAlignment": "BROKEN",
"dominantCause": "WHEEL_SLIP",
"guardAction": "BRAKE_AND_RESYNC",
"mode": "guard",
"controlInterceptionEnabled": true,
"safeCmd": {
"linear_vx": 0.0,
"angular_wz": 0.0
}
}
5-minute demo
The demo does not require Gazebo, Isaac Sim, or a real robot. It runs a lightweight mock AMR/AGV and injects wheel slip:
/cmd_vel
↓
Kinematic Guard
↓
/safe_cmd_vel
↓
Mock Robot
↓
/odom
↑
Kinematic Guard
Build:
source /opt/ros/humble/setup.bash
colcon build --symlink-install
source install/setup.bash
Run observe mode:
ros2 launch ros2_kinematic_guard start_pre_estop_demo.launch.py \
profile:=wheel_slip \
mode:=observe \
slip_start_sec:=10.0 \
slip_duration_sec:=12.0
Publish a smooth command:
ros2 topic pub -r 20 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.8}, angular: {z: 0.0}}"
Watch the guard:
watch -n 0.2 'ros2 topic echo /kinematic_guard/status --field data --once --full-length | awk "/^---$/{exit} {print}" | python3 -m json.tool'
The README also includes a persistent fault debug mode with slip_duration_sec:=9999.0.
Repository:
https://github.com/ZC502/ros2_kinematic_guard.git
I would be very interested in feedback from people working with AMRs/AGVs, Nav2 deployments, base drivers, or post-incident debugging. The core question is:
Would a lightweight command/odom sanity layer be useful before the system escalates to a hard E-stop?