Skip to content

mathrosas/ros2_control

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 

Repository files navigation

Checkpoint 15 - ROS2 Control

ROS 2 port of Robotnik's RB-1 mobile base wired end-to-end through the ros2_control framework. The URDF/xacro description exposes two independent hardware systems — a differential drive (two rear rubber wheels) and a linear actuator (elevator lifting platform) — through the gazebo_ros2_control/GazeboSystem plugin. A single controller_manager spins three controllers on top: joint_state_broadcaster, rb1_base_controller (diff_drive_controller/DiffDriveController) and elevator_controller (effort_controllers/JointGroupEffortController). Validated in Gazebo Classic with the full RB-1 model (base + wheels + omni casters + elevator + Hokuyo laser + IMU + Orbbec Astra RGB-D camera).

RB-1 mobile base spawned in Gazebo Classic

How It Works

RB-1 controllers active — diff drive and elevator loaded through ros2_control

Hardware Description Phase

  1. rb1_ros2_base.urdf.xacro assembles the base (rb1_base_v3), two rubber wheels, three omni casters, the elevator platform and all sensors under the rb1_robot/ frame prefix
  2. Two <ros2_control> blocks declare the hardware systems — both backed by gazebo_ros2_control/GazeboSystem:
    • diff_drivevelocity command interface on robot_left_wheel_joint and robot_right_wheel_joint (limits ±1), plus position/velocity/effort state interfaces
    • linear_actuatoreffort command interface on robot_elevator_platform_joint (limits ±10), plus position/velocity/effort state interfaces
  3. A <gazebo> block loads libgazebo_ros2_control.so and points it at config/rb1_controller.yaml, which the plugin hands to the controller_manager at startup

Controller Execution Phase

  1. Gazebo spawns the robot from the xacro-expanded robot_description published by robot_state_publisher (with frame_prefix: rb1_robot/)
  2. controller_manager runs at update_rate: 10 Hz with use_sim_time: true
  3. joint_state_broadcaster is loaded first so /joint_states starts flowing
  4. An OnProcessExit event handler then loads rb1_base_controller — the DiffDriveController maps /rb1_base_controller/cmd_vel_unstamped (geometry_msgs/Twist) into per-wheel velocity commands using wheel_separation = 0.436 m, wheel_radius = 0.076 m, limits linear.x ∈ [-1, 1] m/s / angular.z ∈ [-1, 1] rad/s / accel ±1, and publishes /rb1_base_controller/odom at 50 Hz plus the odom → robot_base_link TF
  5. A spawner node brings up elevator_controller (JointGroupEffortController), which forwards std_msgs/Float64MultiArray messages on /elevator_controller/commands straight to the effort command interface of robot_elevator_platform_joint

Tasks Breakdown

Task 1 - Differential Drive with ros2_control

  • Two-joint ros2_control system diff_drive declared in xacro/rb1_ros2_base.urdf.xacro with velocity command interfaces on both driven wheels
  • DiffDriveController configured in config/rb1_controller.yaml:
    • left_wheel_names: [robot_left_wheel_joint], right_wheel_names: [robot_right_wheel_joint]
    • wheel_separation: 0.436, wheel_radius: 0.076
    • publish_rate: 50.0, odom_frame_id: odom, base_frame_id: robot_base_link
    • open_loop: true, enable_odom_tf: true, use_stamped_vel: false, cmd_vel_timeout: 0.5
    • Velocity/acceleration limits enabled on linear.x and angular.z (±1 m/s, ±1 rad/s, ±1 m/s²)
  • Launch file launch/rb1_ros2_xacro.launch.py sequences Gazebo → robot_state_publisherspawn_entity.pyload_controller joint_state_broadcaster → (OnProcessExit) → load_controller rb1_base_controller
  • Command topic: /rb1_base_controller/cmd_vel_unstamped (geometry_msgs/Twist, unstamped)
  • Teleop validated with ros2 topic pub --rate 10 /rb1_base_controller/cmd_vel_unstamped + odom echoed on /rb1_base_controller/odom

Task 2 - Elevator Lifting Unit with ros2_control

  • Single-joint ros2_control system linear_actuator for robot_elevator_platform_joint with an effort command interface (limits ±10 Nm)
  • Controller elevator_controller configured in config/rb1_controller.yaml as effort_controllers/JointGroupEffortController:
    • joints: [robot_elevator_platform_joint]
    • command_interfaces: [effort]
    • state_interfaces: [position, velocity, effort]
  • Loaded through the controller_manager/spawner node with --controller-manager-timeout 500
  • Elevator lifts by publishing a positive effort on /elevator_controller/commands, lowers with a negative effort:
    • ros2 topic pub /elevator_controller/commands std_msgs/msg/Float64MultiArray "data: [5.0]" → platform rises
    • ros2 topic pub /elevator_controller/commands std_msgs/msg/Float64MultiArray "data: [-5.0]" → platform descends

ROS 2 Interface

Name Type Description
/rb1_base_controller/cmd_vel_unstamped geometry_msgs/Twist (sub) Unstamped velocity command for the diff drive
/rb1_base_controller/odom nav_msgs/Odometry (pub) Wheel odometry (50 Hz)
/elevator_controller/commands std_msgs/Float64MultiArray (sub) Effort command for the elevator joint
/joint_states sensor_msgs/JointState (pub) All joint positions/velocities/efforts
/tf, /tf_static tf2_msgs/TFMessage Robot TF tree under rb1_robot/ prefix (includes odom → robot_base_link)
/controller_manager/* service set list_controllers, load_controller, switch_controller, ...
Controllers joint_state_broadcaster, rb1_base_controller, elevator_controller
Hardware interfaces robot_left_wheel_joint/velocity (command), robot_right_wheel_joint/velocity (command), robot_elevator_platform_joint/effort (command); position/velocity/effort state on all three

Project Structure

rb1_ros2_description/
├── xacro/
│   └── rb1_ros2_base.urdf.xacro     # Base + wheels + omni + elevator + sensors + <ros2_control> tags + gazebo plugin
├── urdf/
│   ├── bases/rb1_base_v3.urdf.xacro
│   ├── wheels/rubber_wheel.urdf.xacro
│   ├── wheels/omni_wheel.urdf.xacro
│   ├── elevator/elevator.urdf.xacro
│   ├── all_sensors.urdf.xacro       # Hokuyo UST-20LX + IMU Hector + Orbbec Astra
│   └── common.gazebo.xacro
├── config/
│   └── rb1_controller.yaml          # controller_manager + DiffDriveController + JointGroupEffortController
├── launch/
│   └── rb1_ros2_xacro.launch.py     # Gazebo → rsp → spawn → staged controller loading
├── meshes/
├── media/
├── CMakeLists.txt
└── package.xml

How to Use

Prerequisites

  • ROS 2 Humble
  • ros2_control, ros2_controllers (diff_drive_controller, effort_controllers, joint_state_broadcaster)
  • gazebo_ros2_control, gazebo_ros_pkgs
  • Gazebo Classic 11
  • xacro, robot_state_publisher

Build

cd ~/ros2_ws
colcon build --symlink-install
source install/setup.bash

Simulation

# Terminal 1 - Gazebo + RB-1 + controller_manager + controllers
ros2 launch rb1_ros2_description rb1_ros2_xacro.launch.py

Drive the base

# Forward at 0.3 m/s
ros2 topic pub --rate 10 /rb1_base_controller/cmd_vel_unstamped geometry_msgs/msg/Twist \
  "{linear: {x: 0.3, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}"

# Rotate in place at 0.5 rad/s
ros2 topic pub --rate 10 /rb1_base_controller/cmd_vel_unstamped geometry_msgs/msg/Twist \
  "{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.5}}"

Lift / lower the elevator

# Lift
ros2 topic pub /elevator_controller/commands std_msgs/msg/Float64MultiArray "data: [5.0]"

# Lower
ros2 topic pub /elevator_controller/commands std_msgs/msg/Float64MultiArray "data: [-5.0]"

Sanity checks

# Hardware interfaces exposed by the plugin
ros2 control list_hardware_interfaces
# command:  robot_left_wheel_joint/velocity             [available] [claimed]
# command:  robot_right_wheel_joint/velocity            [available] [claimed]
# command:  robot_elevator_platform_joint/effort        [available] [claimed]
# state:    robot_left_wheel_joint/{position,velocity,effort}
# state:    robot_right_wheel_joint/{position,velocity,effort}
# state:    robot_elevator_platform_joint/{position,velocity,effort}

# Controllers must all be active
ros2 control list_controllers
# joint_state_broadcaster   joint_state_broadcaster/JointStateBroadcaster  active
# rb1_base_controller       diff_drive_controller/DiffDriveController      active
# elevator_controller       effort_controllers/JointGroupEffortController  active

# Odometry and joint states must flow
ros2 topic echo /rb1_base_controller/odom
ros2 topic echo /joint_states

Key Concepts Covered

  • ros2_control framework: <ros2_control> xacro tags, hardware plugin, command_interface vs. state_interface, per-interface limits
  • gazebo_ros2_control: GazeboSystem hardware plugin, <gazebo> plugin loader, YAML parameter injection into controller_manager
  • controller_manager: update_rate, controller types, spawner vs. ros2 control load_controller
  • Diff drive controller: DiffDriveController wiring (left_wheel_names, right_wheel_names, geometry, odom frames, velocity/acceleration limits, unstamped command topic)
  • Joint group effort controller: JointGroupEffortController with effort command interface for lifting actuators
  • Launch orchestration: IncludeLaunchDescription for Gazebo, robot_state_publisher with frame_prefix, spawn_entity.py, ExecuteProcess for ros2 control load_controller, RegisterEventHandler(OnProcessExit(...)) to stage controller loading
  • Robot description pipeline: xacro expansion → robot_description parameter → Gazebo spawn from topic
  • Sensor stack: Hokuyo UST-20LX 2D laser, IMU Hector, Orbbec Astra RGB-D camera wired through the shared description

Technologies

  • ROS 2 Humble
  • ros2_control (controller_manager, diff_drive_controller, effort_controllers, joint_state_broadcaster)
  • gazebo_ros2_control (GazeboSystem)
  • Gazebo Classic 11
  • Robotnik RB-1 mobile base (2× rubber wheels + 3× omni casters + elevator platform)
  • Hokuyo UST-20LX, IMU, Orbbec Astra RGB-D
  • C++ 17 / Python 3 / XML (URDF/SDF/xacro) / YAML

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors