FixTheRobot Explore Robotics / Systems Engineering / Engineering Guides / Module 14

Engineering Guides · Module 14

Integrating a controller with ros2_control

ros2_control connects controllers to hardware through interfaces. A controller only works if its parameters match the hardware: names, geometry, frames and timeouts.

  • Intermediate
  • Module 14: Motion Control & Actuator Integration
  • Written for ROS 2 Jazzy where ROS 2 is used
  • Sources checked 2026-11-06

Assumptions: Numbers in the examples are illustrative and the code is executed by the site’s tests. Facts taken from a cited source are marked; other statements are general engineering knowledge and must be confirmed on the datasheet of the part you use.

1. Engineering problem

A controller that is not configured for the hardware either refuses to start or behaves wrongly in ways that look like tuning problems. The diff_drive_controller needs the wheel joint names, the geometry, and sensible frames and timeouts.

2. When is this needed?

  • Bringing up a new differential-drive base with ros2_control.
  • When the controller will not activate.
  • When odometry or velocity commands behave oddly.

3. Selection and design decisions

From the diff_drive_controller parameter file (ros2_controllers, jazzy, read 2026-11-06): required parameters are left_wheel_names and right_wheel_names (not empty), wheel_separation and wheel_radius (greater than 0). Other parameters and defaults: odom_frame_id: odom, base_frame_id: base_link, enable_odom_tf: true, open_loop: false, position_feedback: true, cmd_vel_timeout: 0.5 seconds (“a command received on the cmd_vel topic is considered stale”; 0.0 disables the timeout), publish_rate: 50.0 Hz, velocity_rolling_window_size: 10, plus pose_covariance_diagonal and twist_covariance_diagonal (defaults all zero: tune them to your robot), the radius and separation multipliers (default 1.0) and per-axis velocity, acceleration and jerk limits. The controller reads wheel feedback and writes wheel velocity commands through the hardware interfaces of the named joints.

Overview
ItemCheck
Wheel joint namesMatch the hardware interface and the URDF
GeometryWheel radius and separation measured, then calibrated
Framesodom and base frames match what the rest of the stack expects; only one publisher of odom→base_link
CovariancesNon-zero and realistic, or fusion treats odometry as perfect
cmd_vel_timeoutShort enough to stop on loss of commands, long enough for your publisher rate
LimitsVelocity and acceleration limits match the robot
Open vs closed loopClosed loop needs wheel feedback

4. Advantages and disadvantages

  • enable_odom_tf true is convenient and conflicts with a fusion filter publishing the same transform.
  • Open loop works without feedback and drifts.
  • A short command timeout is safer and requires a steady publisher.

5. Requirements and constraints

  • Names, geometry and frames are consistent between the URDF, the controller and the hardware.
  • Covariances are set from the measured odometry error.
  • Only one node publishes odom→base_link.

6. Architecture

cmd_vel ─▶ diff_drive_controller ─▶ wheel velocity command interfaces ─▶ hardware ─▶ wheel position/velocity states ─▶ odometry (odom→base_link)

7. Implementation

  1. Write the controller configuration from the hardware description.
  2. Check it (below) and load the controller.
  3. Command a small velocity with wheels off the ground and check the sign and magnitude.
  4. Check the odometry and the transform.
  5. Calibrate geometry and set covariances.
REQUIRED = ["left_wheel_names", "right_wheel_names", "wheel_separation", "wheel_radius"]
DEFAULTS = {"odom_frame_id": "odom", "base_frame_id": "base_link", "cmd_vel_timeout": 0.5, "publish_rate": 50.0, "open_loop": False, "enable_odom_tf": True}
def check(cfg):
    out = []
    for k in REQUIRED:
        v = cfg.get(k)
        if v in (None, [], 0, 0.0):
            out.append(f"{k} is missing or empty (the controller's validation rejects it)")
    for k in ("wheel_separation", "wheel_radius"):
        if isinstance(cfg.get(k), (int, float)) and cfg[k] < 0:
            out.append(f"{k} must be greater than 0")
    if len(cfg.get("left_wheel_names", [])) != len(cfg.get("right_wheel_names", [])):
        out.append("left and right wheel lists have different lengths")
    for k, d in DEFAULTS.items():
        if k in cfg and cfg[k] != d:
            out.append(f"{k} = {cfg[k]!r} differs from the default {d!r}")
    return out or ["no findings"]
cfg = {"left_wheel_names": ["left_wheel_joint"], "right_wheel_names": ["right_wheel_joint", "right_rear_joint"], "wheel_separation": 0.4, "wheel_radius": 0, "cmd_vel_timeout": 0.0, "enable_odom_tf": False}
print("\n".join(check(cfg)))

Printed (checked by the site’s tests):

wheel_radius is missing or empty (the controller's validation rejects it)
left and right wheel lists have different lengths
cmd_vel_timeout = 0.0 differs from the default 0.5
enable_odom_tf = False differs from the default True

Reading it: the checker flags the zero wheel radius (rejected by the controller’s validation), the mismatch between one left wheel and two right wheels, the disabled command timeout (0.0 turns the safety stop off) and the disabled odometry transform. The configuration is an invented example.

8. Real examples

  • Commands (Jazzy): ros2 control list_controllers and ros2 control list_hardware_interfaces show what is loaded and which interfaces exist (not executed by the site’s tests).

9. Failure scenarios

  • A joint name typo, so the controller cannot claim its interfaces.
  • Zero covariance, so a fusion filter trusts odometry completely.
  • Two publishers of odom→base_link.

10. Troubleshooting

  1. Run ros2 control list_hardware_interfaces and compare the joint names.
  2. Check the controller state (active or not) and its log.
  3. Check the sign of the wheel directions on a lifted robot.

11. Validation

  • The checker prints the lines shown (executed by the site tests).
  • The controller activates, and a measured 1 m command moves the robot 1 m within the calibration tolerance.

12. Engineering checklist

13. Further references

Related on FixTheRobot