[Documentation] [TitleIndex] [WordIndex


The PID controller package is an implementation of a Proportional-Integral-Derivative controller - it is intended for use where you have a straightforward control problem that you just need to throw a PID loop at. It has one purpose and focuses on doing it well. It has numerous features that ease the task of adding a controller and tuning the control loop. Several examples are provided.

This wiki page uses several terms from control system theory:


Installing the package

You can install the pid package from binaries or build from source. If you install from binaries, the example files discussed below will be in /opt/ros/<release>/share/pid

Binary Installation

Replace "indigo" with your release in the command below.

$ sudo apt-get install ros-indigo-pid

Install and Build from source

$ cd catkin_ws/src
$ git clone https://bitbucket.org/AndyZe/pid.git
$ cd ..
$ catkin_make

Example: Run servo_sim.launch

Several launch files are provided in the pid/launch directory which launch simulations that showcase controller capabilities. Servo_sim.launch is one such in which the pid controller controls a second-order plant that simulates a servo controlling the position of a load. The control loop runs at 100 Hz.

Roslaunch pid servo_sim.launch, and several windows will open.

$ roslaunch pid servo_sim.launch

An rqt_plot window opens displaying controller inputs and outputs. Click the pan/zoom button and right-click and drag left to zoom out until you see the desired degree of detail in the plot.


/setpoint, /state, and /control effort are the default topic names that the PID controller subscribes to, and publishes on. They can be changed so as to support multiple PID controller nodes using standard ROS techniques like remapping, or alternatively with parameters.

Run "rqt_graph". A window showing the nodes in this simulation opens.

alt text

The setpoint_node publishes its time-varying setpoint to the PID controller running in the /left_wheel_pid node, which applies corrections via the /control_effort topic to the servo_sim_node. The servo_sim_node publishes the current value of the simulated servo position to the /state topic, which the PID controller subscribes to and bases its control_effort on.

Dynamically Reconfigure PID Gains

Servo_sim.launch also opens an rqt_reconfigure window:


Click on the "left_wheel_pid" entry to display the controls that let you dynamically reconfigure the Kp, Ki and Kd parameters: the PID proportional, integral and derivative contributions to /control_effort. Each parameter has a dropdown scale that lets you select a power of 10 range of the slider, and a slider that lets you set the parameter between -1.0 and +1.0 times the scale. E.g. if you set Kp_scale drop-down to scale_10, and the Kp slider to 0.5, the Kp parameter used by the controller will be 5.

Try dragging the Kd slider to 0 while watching the rqt_plot display. You will see the /control_effort value saturate at the parameterized limit of 10 as well as some overshoot on /state that represents servo position overshoot.

Adjust other parameters and observe their effect. One technique for tuning a PID loop is to adjust Kp as high as you can without inducing wild oscillation, then increase Kd to remove overshoot, then adjust Ki to remove any residual offset after the loop has settled. You should check the tuning with the expected range of loads on the controlled device - the loop may be stable with some loads and unstable with others. Your goal is to get the loop to settle as quickly as possible with as little overshoot as possible.

The idea of dynamic reconfigure for Kp, Ki and Kd is that you get your robot actuator to repeatedly perform a movement, and use dynamic_reconfigure to experimentally find good values for Kp, Ki and Kd. Once you've found values that work well, you should set those parameters in the launch-file node entry that launches that controller.


While servo_sim.launch is running, you can launch a Ziegler-Nichols autotuner. It calculates decent values of Kp, Ki, and Kd by cycling through a range of Kp values and recording oscillations. I wouldn't use this on hardware! The ZN method aims for a fast response time and usually results in significant overshoot. To run it:

$ rosrun pid autotune

If you want to customize the autotuner for your application, these are the values in autotune.cpp that need changing:

Controller Node

The controller node is the main node in the package. It implements the PID algorithm and applies control effort to try and make plant state equal setpoint.


The PID controller subscribes and publishes to the following topic names. The setpoint, state and control_effort topic names are defaults and can be changed in the standard way with ROS techniques like remapping and namespaces, or in a pid-package-specific way using controller parameters.


The following node-private parameters are read at node startup:

How to use Controller Features

These sections give examples of how to use various pid controller features.

Configuring with node-private parameters

The pid controller makes heavy use of parameters to set its operating characteristics. Use the launch-file node-private parameter syntax documented here to set parameters from a launch file, or the rosrun parameter syntax documented here to set parameters from the command line.

The following snippet from a launch file shows how to launch the node and set its parameters from a launch file.

    <node name="left_wheel_pid" pkg="pid" type="controller" >
      <param name="Kp" value="5.0" />
      <param name="Ki" value="0.0" />
      <param name="Kd" value="0.1" />
      <param name="upper_limit" value="10" />
      <param name="lower_limit" value="-10" />
      <param name="windup_limit" value="10" />
      <param name="max_loop_frequency" value="100.0" />
      <param name="min_loop_frequency" value="100.0" />

Dynamic Reconfiguration

rosrun rqt_reconfigure rqt_reconfigure

The pid nodes will be displayed in the left pane. Select any of the nodes and its Kp, Ki, and Kd reconfiguration controls will be displayed in the main pane. Tune each controller as needed.

Support for multiple controllers (with example)

Any real robot is likely to have multiple PID loops, and likely instantiates multiple controller nodes. The problems to deal with are:

ROS provides two ways of supporting this need to connect controller nodes to different topic names, and the pid controller node provides a third:

  1. Push each controller node into its own namespace (perhaps together with its plant). The controller topic names are then prefixed with the namespace. The book "A Gentle Introduction to ROS" by Jason O'Kane, chapter 6 section 6.3 describes the technique and gives examples.

  2. Remap topic names, such that the topic name each controller publishes or subscribes to is remapped to something appropriate. See "A Gentle Introduction to ROS" chapter 6 section 6.4 for an explanation with examples.

  3. Tell the PID controller which topics to publish/subscribe to using parameters. This technique is pid-controller specific - see the parameters section.


Roslaunch the pid differential_drive_sim.launch example. The launch file starts two controller/plant pairs, simulating the left and right drive motors and controllers of a differential drive robot. As is sometimes the case, one wheel needs a positive voltage to drive the robot forward, and the other needs a negative voltage (to turn the wheel the other way) to drive the robot forward.

The figure below shows the node graph.

alt text

Each controller and its associated plant simulation has been pushed down into a separate namespace, identified as "left_wheel" and "right_wheel". See the "ns=left/right_wheel" property in the node elements. Since the default topic names used by controller and plant are relative names, ROS has prepended their namespaces to the topic name paths, and thus each controller node is connected only to the correct plant node.

Both controllers should listen to the same /setpoint topic in this example. The nodes' setpoint topic names are remapped to the top-level setpoint topic published by the setpoint node.

The launchfile snippet shown below instantiates the two nodes and their plants, connecting the topics properly using the push-down and the remapping techniques. The "ns" property pushes the controller down into a namespace. The remap element remaps the node's /namespace/setpoint topic to the global /setpoint topic.

    <node name="controller" pkg="pid" type="controller" ns="left_wheel" output="screen" >
      <param name="node_name" value="left_wheel_pid" />
      <param name="Kp" value="5.0" />
      <param name="Ki" value="0.0" />
      <param name="Kd" value="0.1" />
      <param name="upper_limit" value="10" />
      <param name="lower_limit" value="-10" />
      <param name="windup_limit" value="10" />
      <param name="max_loop_frequency" value="100.0" />
      <param name="min_loop_frequency" value="100.0" />
      <remap from="setpoint" to="/setpoint" />

    <node name="servo_sim_node" pkg="pid" type="plant_sim" ns="left_wheel" output="screen" >
      <param name="plant_order" value="2" />

    <node name="controller" pkg="pid" type="controller" ns="right_wheel" output="screen" >
      <param name="node_name" value="right_wheel_pid" />
      <param name="Kp" value="-4.0" />
      <param name="Ki" value="-0.0" />
      <param name="Kd" value="-0.3" />
      <param name="upper_limit" value="10" />
      <param name="lower_limit" value="-10" />
      <param name="windup_limit" value="10" />
      <param name="max_loop_frequency" value="100.0" />
      <param name="min_loop_frequency" value="100.0" />
      <remap from="setpoint" to="/setpoint" />

    <node name="servo_sim_node" pkg="pid" type="plant_sim" ns="right_wheel" output="screen" >
      <param name="plant_order" value="2" />
      <param name="reverse_acting" value="true" />

A plot of the resulting two-controller simulation is shown below. Note: the Kp, Ki, and Kd parameters are intentionally a little different for each simulated wheel to distinguish the plots.

alt text

Reverse acting support

The launch file snippet above has Kp, Ki, Kd all positive in the left_wheel controller, and all negative in the right_wheel controller. This causes the right wheel controller to emit control_effort that's opposite polarity to the error.


Manual control means the pid controller stops publishing control_effort messages, and the error integral is zero. Automatic control means the controller applies control_effort to reduce the difference between setpoint and state. The controller starts in Auto mode. Send the controller an std_msgs/Bool message with data set false to put it into Manual mode. A message with data set true puts it back into Auto mode. The controller listens on the pid_enable topic. Remap it as needed to put individual controllers or groups of them into Manual or Auto mode.

This feature is useful if you want to suspend a PID controller, maybe swapping in a different controller or taking over manual control.

Faster-than-wallclock simulation

The controller node (and other nodes in the pid package) are designed to support running simulations faster than wallclock using the standard ROS support for this. By setting the "use_sim_time" parameter true, roscpp listens for time to advance as indicated by new time values published on the /clock topic.

The controller waits for time to become non-zero before beginning operation. After that, its calls to time primitives like ros::Rate::sleep() will act based on published time, not system time.


Roslaunch pid sim_time.launch - it runs a control loop simulation faster than wallclock. An rqt_plot window opens showing the simulated temperature control loop in operation. Look at the time scale - it is running at 4X wallclock time.

This is accomplished by the following launchfile statements:

    <param name="use_sim_time" value="true" />

        <node name="sim_time" pkg="pid" type="sim_time" output="screen" >
      <param name="sim_speedup" value="4" />

Setting the global parameter "use_sim_time" true causes roscpp and rospy (which all the nodes use for ros time services like Duration, ros::Time::now(), and ros::Rate::sleep()) to behave as if time advances according to time markers published on the /clock topic, instead of on wallclock time.

The sim_time node publishes time markers on the /clock topic in 10ms increments, at a rate set by the sim_speedup parameter. This launchfile asks for a sim_speedup of 4, so it will publish a 10ms time increment every 2.5ms of wallclock time. Note: it could also run simulation time slower than wallclock.

Other nodes

The pid package contains several other nodes which support the demo launch files:


Filtering is important to eliminate noise in digital signals, especially when differentiating. The following screenshot (Nicholas Paine, 2012) shows a noisy second derivative when the signal is unfiltered. The default cutoff frequency (1/4 of the sampling rate) should be fine for most applications. If you prefer not to filter the signal anyway, just set a very high cutoff frequency (like 10,000).

alt text

Known Issues

If you're seeing high CPU usage, it's probably due to rqt_plot. You can comment it in the launch files. The PID controller itself (no graphics) typically runs at <10% CPU usage.

Submit Issues Here

Submit issues here: bitbucket issues

2024-07-20 13:24