Robot Control
Objective: This tutorial session is devoted to understanding the ros_control framework used to implement and manage robot controllers for simulation within the gazebo.
Tutorial install package:
Package
rrbot_description, rrbot_gazebo, rrbot_control
cd ~/catkin_ws/src
git clone https://github.com/ros-simulation/gazebo_ros_demos.git
Package
mastering_ros_robot_description_pkg
Package
seven_dof_arm_gazebo_simple
Package
gazebo_control_tutorial
Package
actions_tutorial
Package
universal_robot
Package
UR3-IK
1. Ros control overview
The ros_control framework provides the capability to implement and manage robot controllers, which that mainly consists of a feedback mechanism, most probably a PID loop, which can receive a setpoint, and control the output, typically effort, using the feedback from the actuators. The primary motivation of ros_control is the lack of realtime-safe communication layer in ROS.
The framework implements abstractions on hardware interfaces with minimal assumptions on hardware or operating system. The output is applied to the real robot or to its simulation in Gazebo by using a simple Gazebo plugin adapter.
The following figure shows the interconnection of the ROS controller, robot hardware interface, and simulator/real hardware:


More details on all the components of ros_control are shown in the next figure. They can be grouped into two main blocks:
The controllers and the control manager (the blue and yellow blocks; they will be detailed in Section Controllers):
Several controllers may be used (some are already available) and are managed by the Control Manager that is responsible of loading, unloading and switching between them.
The hardware abstraction layer (the gray and orange blocks; they will be detailed in Section Hardware Abstraction Layer).
The ROS controllers do not directly communicate with the hardware, but do it through the Hardware Abstraction Layer, enabling controllers to be hardware-agnostic. It is composed of:
the ros_control interfaces found in the figure within the Hardware Resource Interface Layer
the hardware interfaces that encapsulate the hardware drivers. They are modeled with classes that inherit from the hardware_interface::RobotHW class (this class also acts as resource manager by performing resource conflict checking for a given set of controllers, maintaining a map of hardware interfaces). In the case of simulation, the Gazebo plugin version hardware_interface::RobotHWSim is used.

2. Controllers Block
Available controllers
The main ROS controllers are grouped according to the commands get passed to your hardware/simulator:
effort_controller: efforts commands are used to control joint positions, velocities or efforts.
joint_effort_controller
joint_position_controller
joint_velocity_controller
position_controllers: position commands are used to control joint positions.
joint_position_controller
joint_group_position_controller
velocity_controllers: velocity commands are used to control joint velocities.
joint_velocity_controller
joint_group_velocity_controller
To set an entire trajectory, the following controllers are defined:
joint_trajectory_controllers:
position_controller
velocity_controller
effort_controller
position_velocity_controller
position_velocity_acceleration_controller
Also, a controller is defined to publish joint states:
joint_state_controller
All available controllers can be found here.
3. Controller manager
The controller_manager provides the infrastructure to interact with controllers, i.e. load, unload, start and stop controllers.

This interaction can be done:
Using the command line tools:
$ rosrun controller_manager controller_manager <command> <controller_name>
where the available commands are load, unload, start, stop, spawn (load+start), kill (stop+unload).
Also, several controllers can be loaded and started at once with the spawner tool:
$ rosrun controller_manager spawner [--stopped] name1 name2 name3
Using launch files (which is the most practical and common way):
<launch>
<node pkg="controller_manager"
type="spawner"
args="controller_name1 controller_name2" />
<launch>
Using rqt:
$ rosrun rqt_controller_manager rqt_controller_manager
Using service calls to interact with controllers from another ROS node, e.g.:
controller_manager/load_controller has as request the name of the controller to be loaded and returns success or failure.
controller_manager/switch_controller has as request a list (which can be empty) of controller names to start, a list (which can be empty) of controller names to stop, and the switching policy.
More information can be found in the controller manager wiki page.
4. Configuring and launching controllers
Controllers are usually defined with yaml files (stored in a config folder), like those shown here.
Another example is shown in the following figure, that corresponds to the rrbot_control.yaml configuration file of the rrbot_control package where the controllers for the rrbot robot are defined. In this case there is:
the joint state controller to publish the joint states of the arm (with a publishing rate set to 50 Hz),
two joint position controllers to move each of the two joints upon receiving a goal position.
rrbot_control.yaml
rrbot:
# Publish all joint states -----------------------------------
joint_state_controller:
type: joint_state_controller/JointStateController
publish_rate: 50
# Position Controllers ---------------------------------------
joint1_position_controller:
type: effort_controllers/JointPositionController
joint: joint1
pid: {p: 100.0, i: 0.01, d: 10.0}
joint2_position_controller:
type: effort_controllers/JointPositionController
joint: joint2
pid: {p: 100.0, i: 0.01, d: 10.0}
We can see that all the controllers are inside the namespace rrbot. This configuration file can be loaded in a launch file, prior to the spawning of the controller:
rrbot_control.launch
<launch>
<!-- Load joint controller configurations from YAML file to parameter server -->
<rosparam file="$(find rrbot_control)/config/rrbot_control.yaml" command="load"/>
<!-- load the controllers -->
<node name="controller_spawner" pkg="controller_manager" type="spawner" respawn="false"
output="screen" ns="/rrbot" args="joint_state_controller
joint1_position_controller
joint2_position_controller"/>
<!-- convert joint states to TF transforms for rviz, etc -->
<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher"
respawn="false" output="screen">
<remap from="/joint_states" to="/rrbot/joint_states" />
</node>
</launch>
If order to simulate the robot in Gazebo with those controllers, the corresponding plugin has to be added to the URDF model, as detailed in Section Preparing Gazebo for ros_control.
Important: Note that the controllers have to be spawned within the same namespace where the controls have been defined, rrbot in this case, using the ns=
argument in the controller_spawner node tag. Also, the gazebo plugin will need to be within this namespace.
Working with rrbot using ros_control
Advertising joint values: The joint position controllers of the rrbot robot of Section Configuring and launching controllers can be tested by advertising the desired joint values:
$ roslaunch rrbot_gazebo rrbot_world.launch
$ roslaunch rrbot_control rrbot_control.launch
$ rostopic pub -1 /rrbot/joint1_position_controller/command std_msgs/Float64 "data: 1.5"
$ rostopic pub -1 /rrbot/joint2_position_controller/command std_msgs/Float64 "data: 1.0"
5. Hardware Abstraction Layer
ros_control intefaces
The connection of the controllers with the hardware interface (that encapsulates the hardware) is done through the ros_control interfaces. Some of the commonly used are:
EffortJointInterface: to send the effort command.
VelocityJointInterface: to send the velocity command.
PositionJointInterface: to send the position command.
Joint State Interfaces: to retrieve the joint states from the actuators encoder.
Hardware interface
To control a given robot using ros_control, a class derived from the hardware_interface::RobotHW class should be implemented. This class should support one or more of the standard interfaces like e.g. the EffortJointInterface or VelocityJointInterface:

For instance, the following code shows the class implemented for a robot with two position-controlled joints. It uses the JointPositionInterface and the JointStateInterface:
#include <hardware_interface/joint_command_interface.h>
#include <hardware_interface/joint_state_interface.h>
#include <hardware_interface/robot_hw.h>
class MyRobot : public hardware_interface::RobotHW
{
public:
MyRobot()
{
// connect and register the joint state interface
hardware_interface::JointStateHandle state_handle_a("A", &pos[0], &vel[0], &eff[0]);
jnt_state_interface.registerHandle(state_handle_a);
hardware_interface::JointStateHandle state_handle_b("B", &pos[1], &vel[1], &eff[1]);
jnt_state_interface.registerHandle(state_handle_b);
registerInterface(&jnt_state_interface);
// connect and register the joint position interface
hardware_interface::JointHandle pos_handle_a(jnt_state_interface.getHandle("A"), &cmd[0]);
jnt_pos_interface.registerHandle(pos_handle_a);
hardware_interface::JointHandle pos_handle_b(jnt_state_interface.getHandle("B"), &cmd[1]);
jnt_pos_interface.registerHandle(pos_handle_b);
registerInterface(&jnt_pos_interface);
}
private:
hardware_interface::JointStateInterface jnt_state_interface;
hardware_interface::PositionJointInterface jnt_pos_interface;
double cmd[2];
double pos[2];
double vel[2];
double eff[2];
};
In this example, the controller manager (and the controllers inside the controller manager) will get read access to the joint state of the robot (pos, vel and eff variables) through the hardware_interface::JointStateInterface
and write access to the command to be sent to the robot (the cmd variable) through the hardware_interface::PositionJointInterface
. Two functions should be added to this class, namely read() and write(), to fill the pos, vel and eff with the latest available values from the robot and to run the command available at cmd on the robot. These functions will then be called in the control loop before and after the computation of the control command as shown next:

More information can be found in the ros_controls git repository.
Joint Limits
The joint_limits_interface package contains data structures for representing joint limits, methods to populate them through URDF or yaml files, and methods to enforce these limits. An example of joint limits specification in URDF and YAML formats is:
<joint name="$foo_joint" type="revolute">
<!-- other joint description elements -->
<!-- Joint limits -->
<limit lower="0.0"
upper="1.0"
effort="10.0"
velocity="5.0" />
<!-- Soft limits -->
<safety_controller k_position="100"
k_velocity="10"
soft_lower_limit="0.1"
soft_upper_limit="0.9" />
</joint>
A joint limits specification in YAML format that can be loaded to the ROS parameter server looks like:
joint_limits:
foo_joint:
has_position_limits: true
min_position: 0.0
max_position: 1.0
has_velocity_limits: true
max_velocity: 2.0
has_acceleration_limits: true
max_acceleration: 5.0
has_jerk_limits: true
max_jerk: 100.0
has_effort_limits: true
max_effort: 5.0
bar_joint:
has_position_limits: false # Continuous joint
has_velocity_limits: true
max_velocity: 4.0
An example of how to read these values from the URDF and populate the joint_limits_interface data structures can be found in the GitHub wiki.
Transmissions
Transmission interfaces implement mechanical transmissions, like a mechanical reducer with a given ratio n, and therefore maps effort/flow variables to output effort/flow variables while preserving power.
Transmissions are defined in the URDF robot file as detailed in the ROS wiki urdf transmission page, e.g.:
<transmission name="simple_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="foo_joint">
<hardwareInterface>EffortJointInterface</hardwareInterface>
</joint>
<actuator name="foo_motor">
<mechanicalReduction>50</mechanicalReduction>
<hardwareInterface>EffortJointInterface</hardwareInterface>
</actuator>
</transmission>
The complete control loop when considering joint limits and the existence of transmissions is shown in the next figure:

6.ROS Control and Gazebo
Data flow of Ros control.
Simulation a ros controllers in gazebo shown in the picture below

Following the example of gazebo_control_tutorial, to moves the seven_dof_arm robot along a sinusoidal trajectory by advertising the desired joint values.
$ roslaunch gazebo_control_tutorial aruco_seven_dof_arm_with_rgbd_world_position_controllers.launch
$ rosrun gazebo_control_tutorial pubcontrols
$ roslaunch gazebo_control_tutorial kinect_arm_aruco_cube_rviz_b.launch
Last updated
Was this helpful?