Skip to content

[turtlebot4_gz/Create3] /odom.twist can exceed configured acceleration limits under safe commands even when command-path evidence is not direct #248

Description

@147258369ymc

Description

TurtleBot4 Jazzy simulation can publish /odom.twist.twist velocity samples whose adjacent-sample acceleration exceeds the configured Create3 diffdrive acceleration limits, even when the recorded /diffdrive_controller/cmd_vel evidence is not strong enough to prove that the internal command topic itself is the direct cause.

This is distinct from the command-path issue where /diffdrive_controller/cmd_vel visibly exceeds the configured acceleration envelope. In this class, the externally published /cmd_vel is still safe and the odometry stream still violates the acceleration envelope, but the available command-path observation does not by itself isolate the bug to the limited-command topic. That points to a separate possible root location in the Gazebo physics, wheel simulation, odometry integration, or controller output path.

Impact: Consumers of /odom may observe motion dynamics that violate the Create3/TurtleBot4 controller's declared acceleration limits. Navigation, safety monitors, and downstream estimators can make incorrect assumptions if odometry acceleration is allowed to exceed the configured motion envelope under safe commands.

Environment

  • Operating system: Ubuntu 24.04
  • ROS version: ROS 2 Jazzy
  • TurtleBot4 model: standard
  • Gazebo: Harmonic / ROS-GZ simulation
  • Packages:
    • ros-jazzy-turtlebot4-simulator
    • ros-jazzy-irobot-create-nodes
    • ros_gz_bridge
  • Launch path used by the reproducer:
    • Gazebo server via ros_gz_sim
    • TurtleBot4/Create3 nodes via TurtleBot4 Jazzy bringup
    • RoboFuzz split headless adapter: src_jazzy/launch/turtlebot4_fuzz_spawn.launch.py
  • Discovered via automated fuzzing:
    • RoboFuzz Jazzy fork, target profile turtlebot4_jazzy
    • 1000-round campaigns: tb4_headless_p0_p1_1000 and tb4_headless_semantic3_1000

Declared Limits

The Create3 diffdrive controller configuration declares:

linear.x.has_acceleration_limits: true
linear.x.max_acceleration: 0.9

angular.z.has_acceleration_limits: true
angular.z.max_acceleration: 7.725
angular.z.min_acceleration: -7.725

The same config publishes odometry and wheel data:

publish_rate: 62.0
open_loop: false
publish_cmd: true
publish_wheel_data: true

Therefore /odom.twist.twist should not persistently show dynamics outside the configured acceleration envelope under safe commands.

Steps to Reproduce

  1. Start TurtleBot4 Jazzy simulation with Gazebo and the Create3/TurtleBot4 nodes.
  2. Record:
    • /cmd_vel
    • /diffdrive_controller/cmd_vel
    • /odom
    • /wheel_vels
    • /clock
  3. Publish one of the following safe geometry_msgs/msg/TwistStamped sequences.

Representative linear odom-only repro from frame 1782630920.9406638:

step linear.x (m/s) angular.z (rad/s)
1 -0.116 -0.738
2 +0.050 -0.179
3 -0.074 +0.517
4 +0.030 -0.650
5 -0.040 +0.655
6 -0.106 -0.487
7 +0.010 +0.342
8 +0.030 -0.650

Representative angular odom-only repro from frame 1782633493.955823:

step linear.x (m/s) angular.z (rad/s)
1 -0.017 +0.199
2 +0.030 +0.545
3 +0.041 -0.262
4 +0.050 +0.125
5 +0.030 -0.484
6 +0.086 +0.450
7 +0.097 -0.272
8 -0.076 +0.242

Representative combined odom-only repro from frame 1782643396.2443988:

(+0.100,-0.500) x 8

All inputs are inside the velocity envelope:

  • |linear.x| <= 0.150 m/s, below linear.x.max_velocity = 0.46 m/s
  • |angular.z| <= 0.800 rad/s, below angular.z.max_velocity = 1.9 rad/s

Expected Behavior

The odometry velocity stream should remain consistent with the configured acceleration envelope:

|d(odom.twist.twist.linear.x)/dt| <= 0.9 m/s^2
|d(odom.twist.twist.angular.z)/dt| <= 7.725 rad/s^2

Minor numerical noise or one-sample timing jitter should not create sustained acceleration-limit violations.

Actual Behavior

The odometry stream shows sustained acceleration-envelope violations under safe commands.

Representative frame 1782630920.9406638:

TB4 linear acceleration envelope violated:
  accel = 1.912 m/s^2
  ratio = 2.12x
  limit = 0.900 m/s^2

Representative frame 1782633493.955823:

TB4 angular acceleration envelope violated:
  accel = -10.011 rad/s^2
  ratio = 1.30x
  limit = [-7.725, 7.725] rad/s^2

Representative frame 1782643396.2443988:

TB4 linear acceleration envelope violated:
  accel = 1.822 m/s^2
  ratio = 2.02x
  limit = 0.900 m/s^2

TB4 angular acceleration envelope violated:
  accel = -14.349 rad/s^2
  ratio = 1.86x
  limit = [-7.725, 7.725] rad/s^2

Latest 1000-round campaign (tb4_headless_semantic3_1000) contained these odom-only classes:

Class Count
linear odom-only acceleration 39
angular odom-only acceleration 1
combined odom-only acceleration 3

The previous 1000-round campaign (tb4_headless_p0_p1_1000) contained the same class at larger volume:

Class Count
linear odom-only acceleration 22
angular odom-only acceleration 47
combined odom-only acceleration 44

Across the latest run, the maximum observed odometry acceleration ratios were:

linear odom acceleration ratio  = 2.95x
angular odom acceleration ratio = 2.33x

Why this is not a RoboFuzz input artifact

The input commands remain within the configured velocity bounds. The fuzzing campaign observed:

max |linear.x|  = 0.150 m/s
max |angular.z| = 0.800 rad/s

These are below the runtime limits:

linear.x.max_velocity  = 0.46 m/s
angular.z.max_velocity = 1.9 rad/s

The error is therefore not caused by out-of-range user command velocity.

Why this is unlikely to be only an oracle artifact

The executable oracle computes acceleration from /odom.twist.twist samples using odometry header timestamps when available. It ignores invalid or very large time deltas and reports a fatal acceleration bug only when:

  • the ratio is very strong, or
  • the acceleration stays over the tolerance for at least two adjacent intervals.

The latest 1000-round report also found that error rounds had substantial observations:

minimum /odom sample count in error rounds: 703
minimum /diffdrive_controller/cmd_vel sample count in error rounds: 446

So the finding is not explained by missing topics or an empty rosbag. Timestamp jitter remains a residual risk for borderline cases, but the representative cases above exceed the configured limit by 1.30x to 2.12x and are supported by sustained odometry evidence.

Code-Level Analysis

This issue is intentionally separated from the direct command-path acceleration issue. For the direct command-path bug, /diffdrive_controller/cmd_vel itself shows a strong acceleration spike. Here, the observable symptom is centered on /odom.twist.twist, so the likely root cause may be later in the simulation/control pipeline:

safe /cmd_vel
  -> ROS-GZ / Gazebo model command path
  -> wheel simulation or controller output
  -> odometry integration / publication
  -> /odom.twist.twist acceleration exceeds configured limits

Possible code areas to investigate:

  • Gazebo model command handling for the Create3 base
  • irobot_create_gz_bringup / irobot_create_gz_nodes simulation-side motion path
  • Create3 diffdrive odometry generation when open_loop: false
  • wheel velocity integration and /odom publication

The root cause may still share part of the same pipeline as the command-path issue, but these odom-only cases should be tested independently because fixing the published limited-command topic may not fully fix odometry dynamics.

Suggested Fix / Next Step

Add an integration test that publishes velocity-safe /cmd_vel sequences and asserts that /odom.twist.twist does not exceed the configured acceleration envelope:

|d(odom.linear.x)/dt| <= linear.x.max_acceleration
|d(odom.angular.z)/dt| <= angular.z.max_acceleration

The test should record both /diffdrive_controller/cmd_vel and /wheel_vels so the failure can be localized to:

  1. command limiting,
  2. wheel dynamics,
  3. odometry calculation,
  4. or timestamp/publication behavior.

If /diffdrive_controller/cmd_vel is within limits but /odom still violates the envelope, the bug is downstream of command limiting and should be fixed in the wheel/physics/odometry path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions