ros2_control Closed-Loop Feedback in Practice: Encoder and PID Control Explained

Building precise closed-loop robot motion control with ros2_control, encoders, and chained PID controllers.
This article explains how the ros2_control framework implements closed-loop motion control by combining encoder feedback with chained PID controllers and a differential drive controller. It covers motor driving via PWM, encoder data reading, PID tuning, and how these components form a complete control chain from navigation commands to precise motor execution, enabling reliable autonomous robot navigation.
Introduction: The Core Challenge of Robot Motion Control
In autonomous mobile robot development, how to make motors execute commands precisely while sensing actual motion states in real time is the key to reliable robot navigation. While open-loop control is simple to implement, it lacks feedback on actual execution results, often leading to accumulated errors and unpredictable behavior. Closed-loop feedback control fundamentally solves this problem by reading real motion data through encoders and dynamically adjusting control outputs.
The core idea of closed-loop control originates from classical control theory: through negative feedback mechanisms, a system can automatically sense and correct deviations between output and desired values. In robotics, this means the control system no longer "blindly" sends fixed signals to motors but continuously observes actual motor performance and adjusts commands accordingly — this seemingly simple closed-loop structure is the common foundation of all precision motion control, from industrial servo systems to self-driving cars.
Recently, a robotics-focused blogger named Mike (mikelikesrobots) provided an in-depth tutorial in his "Autonomous Exploration Viam Rover" series on how to build a robot motion control system with closed-loop feedback using the ros2_control framework. This article, based on his blog and video content, outlines the complete approach to achieving precise motion control with ros2_control combined with encoder feedback.

ros2_control Framework Overview
What is ros2_control
ros2_control is the standardized framework for robot control in the ROS 2 ecosystem. It establishes a clear abstraction layer between Hardware Interfaces and upper-level Controllers, allowing developers to flexibly switch underlying hardware drivers without modifying upper-level control logic.
ros2_control was born from the experience and redesign of the ros_control framework from the ROS 1 era. In ROS 1, ros_control had already proven the value of a hardware abstraction layer, but its design was limited by ROS 1's single-process architecture and insufficient real-time capabilities. ros2_control builds on this foundation by fully leveraging ROS 2's DDS (Data Distribution Service) communication mechanism, lifecycle node management, and real-time scheduling capabilities, enabling control loops to complete within deterministic time constraints. The framework's core components include the Controller Manager (responsible for loading, configuring, and managing controller lifecycles), the Resource Manager (managing hardware resource state and command interfaces), and various standard controller plugins. This plugin-based architecture means the community can contribute new controller types without modifying the framework itself.
The greatest advantage of this layered design lies in reusability and portability: whether the underlying layer is a simulation environment or a real Viam Rover, as long as a compliant hardware interface is implemented, the same controller configuration works directly. This is a key reason why ros2_control has become the de facto standard for ROS 2 motion control.
Division of Labor Between Hardware Interfaces and Controllers
In the entire control chain, the hardware interface is responsible for communicating with physical devices — writing commands to motors and reading states from encoders; while controllers are responsible for computing appropriate control outputs based on target commands and feedback data. The two are connected through the Resource Manager, forming a complete control loop.
Specifically, hardware interfaces expose standardized State Interfaces (such as position and velocity) and Command Interfaces (such as velocity commands and torque commands) by implementing the hardware_interface::SystemInterface (or ActuatorInterface, SensorInterface) base class. Controllers declare which interfaces they need to access through the Resource Manager, and the framework automatically handles resource allocation and conflict detection at runtime. This decoupled design allows the same hardware to be simultaneously accessed by multiple read-only controllers (such as state broadcasters), while controllers that write commands are guaranteed exclusive access.
Motor Driving and Encoder Feedback
Motor Driving Methods Explained
In Mike's tutorial, he first explains how motors are driven on the Viam Rover. Motors receive velocity or position commands through the hardware interface, converting them into actual voltage/PWM signals to drive wheel rotation. This step belongs to the "command output" phase of the control chain.
PWM (Pulse Width Modulation) is the most common method for controlling DC motor speed. Its principle involves rapidly switching power on and off at a fixed frequency, effectively changing the average voltage applied to the motor by varying the ratio of "on time" to the total cycle (i.e., duty cycle). For example, a 50% duty cycle is equivalent to half the supply voltage. Motor driver chips (such as L298N, TB6612, and other H-bridge drivers) receive PWM signals and direction signals from the microcontroller and convert them into power signals suitable for motor operation. In the ros2_control hardware interface implementation, developers need to convert upper-level velocity commands (typically in rad/s) to corresponding PWM duty cycle values through appropriate mapping relationships.
However, command output alone is insufficient. During actual operation, motors are affected by load, friction, battery voltage fluctuations, and other factors, causing actual speed to often deviate from the target value. Without a feedback mechanism, the system cannot sense or correct this deviation.
How Encoders Work and Data Reading
This is exactly where encoders come into play. Encoders are mounted on the motor or wheel shaft and can measure rotation angle or speed in real time. The tutorial demonstrates in detail the encoder's working mechanism and how to read encoder data into the ros2_control system through the hardware interface.
Encoders are mainly divided into two categories: incremental encoders and absolute encoders. Incremental encoders represent relative displacement by outputting pulse signals, producing a fixed number of pulses per revolution (i.e., resolution, typically expressed as CPR — Counts Per Revolution), requiring a zeroing operation at startup. Absolute encoders have a unique encoded value at each angular position and don't lose position information after power-off. In the mobile robotics field, incremental optical encoders or magnetic encoders are widely used due to their low cost and fast response. Educational platforms like the Viam Rover typically use Hall effect encoders that count by detecting magnetic pole changes. Encoder resolution directly affects velocity estimation accuracy — higher resolution yields smoother velocity feedback even during low-speed motion, providing a better foundation for PID controller regulation.
The actual motion data provided by encoders is an indispensable "feedback signal" for building closed-loop control. With it, the system can compare "desired motion" with "actual motion," then calculate errors and apply corrections. In ros2_control, encoder data is typically exposed to upper-level controllers through State Interfaces in the form of position (radians) and velocity (radians/second), with velocity values generally calculated by differencing adjacent position readings and dividing by the time interval.
Chained PID and Differential Drive Controller Configuration
How Chained PID Controllers Work
The most core part of this tutorial explains how to use ros2_control to chain motor commands with encoder feedback. Mike adopts a Chained PID Controllers approach.
PID controllers continuously correct motor control output through proportional (P), integral (I), and derivative (D) adjustments based on the error between the target value and the actual value from encoder feedback. When actual speed is below the target, the PID increases output; otherwise it decreases output, causing wheel speed to converge stably to the desired value.
PID parameter tuning is the core challenge in engineering practice. The three parameters each serve different roles: the proportional term P determines the system's immediate response strength to error — too large causes oscillation, too small results in sluggish response; the integral term I eliminates steady-state error (the persistent small deviation as the system approaches the target value), but excessive I easily causes integral windup; the derivative term D suppresses overshoot by predicting error change trends but is sensitive to measurement noise. Common tuning methods include the Ziegler-Nichols method (calculating parameters by finding the critical gain and critical period), manual trial-and-error (first adjusting P to critical oscillation, then gradually adding D and I), and model-based automatic tuning tools. In ros2_control, PID parameters are typically set through YAML configuration files and support dynamic adjustment at runtime through ROS 2 parameter services, greatly facilitating on-site debugging.
The term "chained" refers to multiple controllers working in series: the output commands of upper-level controllers become the target inputs for lower-level controllers, passing layer by layer, ultimately achieving precise mapping from high-level motion intent to low-level motor control.
The chained controllers mechanism in ros2_control is an important feature introduced in ROS 2 Humble and later versions. Its core concept allows one controller's output port (Command Interface) to serve as another controller's input port (State/Reference Interface), completing data transfer within the framework without going through external topic communication, ensuring low latency and determinism in the control loop. In this tutorial's scenario, diff_drive_controller outputs left and right wheel velocity reference values, which directly serve as setpoint inputs for their respective PID controllers; the PID controllers then calculate errors based on encoder feedback and output final motor commands. The entire chain executes sequentially within a single update cycle of the Controller Manager, avoiding time synchronization issues caused by multiple topic subscriptions. This architecture is particularly important in scenarios requiring multi-level control (such as force/position hybrid control, impedance control).
The Role of the Differential Drive Controller
Building on PID control, the tutorial introduces the Differential Drive Controller. For robots with a two-wheel differential chassis, the differential drive controller is responsible for decomposing the robot's overall motion commands (such as linear velocity and angular velocity) into target speeds for each wheel.
Differential drive is one of the most classic chassis configurations for mobile robots. Its kinematic model relates the robot's linear velocity v and angular velocity ω to left and right wheel velocities v_L and v_R: v = (v_R + v_L) / 2, ω = (v_R - v_L) / L, where L is the wheel track (distance between the two wheels). Conversely, given desired v and ω, we can solve for v_R = v + ωL/2, v_L = v - ωL/2. The diff_drive_controller in ros2_control implements exactly this forward and inverse kinematic transformation: receiving geometry_msgs/Twist type velocity commands (typically published on the /cmd_vel topic), solving for left and right wheel target speeds and sending them down; simultaneously computing odometry information from encoder-fed wheel speeds and publishing nav_msgs/Odometry messages and TF transforms. This allows the upper navigation stack (such as Nav2) to interface directly with standard interfaces without worrying about underlying hardware details.
This controller works in collaboration with the underlying PID controllers: the differential controller calculates the speed each wheel should achieve, while the PID controllers ensure the wheels actually reach and maintain those speeds. Together, they form a complete closed-loop control system from "I want the robot to move forward and turn" to "each motor executes precisely."
Practical Value and Application Scenarios of Closed-Loop Feedback
The Leap from Open-Loop to Closed-Loop
Compared to open-loop control, closed-loop feedback systems can continuously self-correct during operation, significantly improving motion accuracy and robustness. Whether dealing with changes in ground friction, load differences, or battery voltage drops, closed-loop systems automatically compensate to ensure consistent robot behavior.
For autonomous navigation tasks, precise motion control is the foundation of odometry estimation and path tracking. Odometry is the most fundamental information source for mobile robot localization — by integrating encoder data, a robot can estimate its displacement and heading relative to the starting point. However, pure wheel odometry has inherent cumulative drift problems — wheel slippage, uneven terrain, and other factors cause estimation errors to grow over time. Therefore, in practical autonomous navigation systems, odometry is typically fused with IMU (Inertial Measurement Unit), LiDAR, or visual SLAM sensors through algorithms like Extended Kalman Filtering (EKF) or particle filtering to correct accumulated errors. But regardless of the fusion approach used, high-quality wheel odometry remains indispensable as a foundation — and the prerequisite for high-quality odometry is precisely the accurate closed-loop speed control discussed in this article. Only when motors truly operate at desired speeds can odometry integration based on kinematic models be trustworthy.
Encoder feedback is not only used for PID control but also provides accurate displacement information back to the localization system, forming the underlying support for the robot's autonomous exploration capabilities.
Learning Insights for Developers
The value of Mike's tutorial series lies in grounding the relatively abstract ros2_control framework through a real Viam Rover project — from motor driving and encoder reading to PID and differential drive controller configuration — forming a complete learning loop. For developers looking to master ROS 2 motion control, this is an excellent reference that tightly integrates theory with practice.
It's worth noting that ros2_control has a relatively steep learning curve in the ROS 2 ecosystem, mainly because it involves the intersection of multiple knowledge areas: URDF/XACRO hardware descriptions, YAML parameter configuration, controller plugin loading, and real-time loops. Mike's tutorial, by focusing on the specific Viam Rover platform, connects these scattered knowledge points into a clear practical path, lowering the barrier to entry. Additionally, since ros2_control supports hardware interface plugins for simulators like Gazebo/Isaac Sim, developers can first verify controller configurations in simulation without physical hardware, then seamlessly migrate to real robots — this "simulation-first" development paradigm is also one of the best practices in modern robotics software engineering.
Conclusion
ros2_control provides a standardized, reusable solution for robot motion control through its layered design of hardware interfaces and controllers. Combined with encoder feedback, chained PID controllers, and the differential drive controller, it enables the construction of precise closed-loop control systems.
The entire control chain can be summarized as: upper navigation stack publishes /cmd_vel velocity commands → differential drive controller decomposes them into left and right wheel target speeds → chained PID controllers compare encoder feedback to calculate errors and output correction commands → hardware interface converts commands to PWM signals to drive motors → encoders collect rotation data in real time and feed it back to the system. This closed loop completes one iteration per control cycle (typically 10ms-50ms), ensuring the robot consistently follows the desired trajectory.
For teams and enthusiasts developing autonomous mobile robots, understanding and mastering this control chain is an important step toward reliable autonomous navigation. Interested readers can further consult Mike's original blog (mikelikesrobots.github.io) and accompanying videos for more detailed code configuration and operational demonstrations.
Related articles

multicalc: A no_std Rust Scientific Computing Library for Embedded Systems
multicalc is a Rust scientific computing library for real-time embedded systems, supporting no_std/no-alloc/no-panic with Kalman filtering, LQR control, trajectory planning, and MuJoCo integration for ARM Cortex-M and RISC-V bare-metal platforms.

Reproducing the Deep Network Degradation Problem: Why Residual Connections Save Deep Networks
Reproducing the degradation problem on CIFAR-10: a 56-layer plain network achieves only 84% training accuracy vs 95.1% for 20 layers. ResNet adds just 0.3% parameters but boosts accuracy to 99%.

From Understanding Papers to Independent Innovation: A Guide to Advancing Mathematical Skills for ML Researchers
How ML researchers can bridge the gap from understanding papers to producing original results through active reconstruction, mathematical foundations, deliberate practice, and collaborative environments.