When a robotic system starts moving real or simulated actuators, it enters one of the most delicate phases of its entire architecture: control. In ROS 2, this responsibility is handled by ros2_control, a framework designed to manage the interaction between high-level software and hardware in a modular, extensible, and safe way.
The goal of ros2_control is to clearly separate hardware description, controller configuration, and runtime activation, avoiding rigid or hard-to-maintain solutions. In this article, we walk through the complete operational flow: configuration using YAML, controller startup, and runtime interaction through the Command Line Interface.
ros2_control was created to solve a very concrete problem: how to manage the control of different robots, with different hardware, without rewriting high-level logic every time. The framework introduces a clear separation between hardware interfaces and controllers, allowing one component to be replaced without affecting the rest of the system.
This approach is essential when working with multiple robots, simulation and real hardware, or when transitioning from a test platform to an industrial setup. Configuration is no longer hard-coded inside nodes but becomes an external, editable, and versionable description of the system.
As systems grow in complexity, passing parameters individually through the CLI is no longer sustainable. This is where YAML configuration files become essential, allowing the entire control setup to be described in a structured and readable way.
In a ros2_control-based system, the YAML configuration defines the controller manager, the controllers to be loaded, and their main properties. This enables the entire control stack to be initialized consistently with a single configuration.
controller_manager:
ros__parameters:
update_rate: 100
joint_state_broadcaster:
type: joint_state_broadcaster/JointStateBroadcaster
diff_drive_controller:
type: diff_drive_controller/DiffDriveController
controller_manager:
This key identifies the controller manager, the core node of ros2_control. It is responsible for loading, managing, and coordinating all controllers in the system.
ros__parameters:
All runtime parameters for the controller manager and its controllers are declared inside this section. ROS 2 uses this namespace to clearly distinguish node parameters.
update_rate: 100
This parameter defines the control loop frequency in hertz. A value of 100 Hz is typical for mobile robots, providing a good balance between responsiveness and computational load.
joint_state_broadcaster:
type: joint_state_broadcaster/JointStateBroadcaster
The joint_state_broadcaster publishes the state of the robot’s joints. Without it, tools such as RViz, TF, and diagnostic nodes would not have access to the robot’s kinematic information.
diff_drive_controller:
type: diff_drive_controller/DiffDriveController
This controller implements the kinematic model of a differential-drive robot, converting velocity commands into coherent wheel motions.
Once the configuration is defined, the controller manager is started as a ROS 2 node. Importantly, controllers are typically loaded but not activated automatically. This design choice allows precise control over system initialization and reduces the risk of unexpected behavior.
One of the major strengths of ros2_control is the ability to manage the control system at runtime, without restarting the robot or the software. This is crucial during testing, debugging, and commissioning.
-ros2 control list_controllers
-ros2 control load_controller diff_drive_controller
-ros2 control set_controller_state diff_drive_controller active
-ros2 control set_controller_state diff_drive_controller inactive
ros2 control list_controllers
This command queries the controller manager and returns a list of all controllers along with their current states. It is the first step to understand the system status.
ros2 control load_controller diff_drive_controller
Here, the specified controller is loaded and initialized but not yet activated. This allows configuration checks before enabling actual control.
ros2 control set_controller_state diff_drive_controller active
This command transitions the controller into the active state. From this moment on, it starts receiving commands and interacting with hardware.
ros2 control set_controller_state diff_drive_controller inactive
This disables the controller without unloading it, which is useful during testing or when temporarily suspending control.
Putting everything together, the workflow becomes clear: the system is first described declaratively, then the controller manager is started, controllers are loaded, and finally activated when needed. This separation of responsibilities results in a modular, robust, and extensible control architecture.
In this article, you explored how a ros2_control-based system is configured, how controllers are defined and launched, and how they are managed at runtime through the CLI. Each step plays a precise role in making robotic control systems reliable and maintainable.
In real applications, additional complexity arises: heterogeneous hardware, custom controllers, navigation integration, and fault handling. Addressing these challenges requires deeper understanding and guided practice.
Assemble your robot and get started to learn Robotics!