ROS 2 control configuration
The next configuration step is to create the configuration for ros2_control and ros2_controllers. So that we can control the robot with ROS 2 tools.
System interface (ros2_control)
First we will create the configuration for the ros2_control interface.
This needs to be done with another xacro file. Create a new file called
robby.ros2_control.xacro
in the urdf
folder.
touch urdf/robby.ros2_control.xacro
Paste the following content into the file:
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="robby_control" params="
name
prefix
use_mock_hardware
bus_config
master_config
can_interface_name
master_bin
">
<ros2_control name="${name}" type="system">
<xacro:unless value="${use_mock_hardware}">
<hardware>
<plugin>canopen_ros2_control/RobotSystem</plugin>
<param name="bus_config">${bus_config}</param>
<param name="master_config">${master_config}</param>
<param name="can_interface_name">${can_interface_name}</param>
<param name="master_bin">${master_bin}</param>
</hardware>
</xacro:unless> -->
<xacro:if value="${use_mock_hardware}">
<hardware>
<plugin>mock_components/GenericSystem</plugin>
<param name="calculate_dynamics">true</param>
</hardware>
</xacro:if>
<joint name="${prefix}left_wheel_joint">
<command_interface name="velocity"/>
<param name="device_name">left_drive</param>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
<joint name="${prefix}right_wheel_joint">
<command_interface name="velocity"/>
<param name="device_name">right_drive</param>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
</ros2_control>
</xacro:macro>
</robot>
Let’s check the contents:
<ros2_control name="${name}" type="system">
</ros2_control>
This creates a ros2_control system interface with the name ${name}
.
<xacro:unless value="${use_mock_hardware}">
<hardware>
<plugin>canopen_ros2_control/RobotSystem</plugin>
<param name="bus_config">${bus_config}</param>
<param name="master_config">${master_config}</param>
<param name="can_interface_name">${can_interface_name}</param>
<param name="master_bin">${master_bin}</param>
</hardware>
</xacro:unless> -->
<xacro:if value="${use_mock_hardware}">
<hardware>
<plugin>mock_components/GenericSystem</plugin>
<param name="calculate_dynamics">true</param>
</hardware>
</xacro:if>
This creates the system interface using the plugin canopen_ros2_control/RobotSystem
if use_mock_hardware
is false. Otherwise it uses the plugin mock_components/GenericSystem
.
is used.
The plugin canopen_ros2_control/RobotSystem
is a plugin that is provided by the
canopen_ros2_control
package. It needs the following parameters:
bus_config: Path to bus.yml
master_config: Path to master.dcf (generated)
can_interface_name: Name of the CAN interface
master_bin: Path to the master binary
The plugin mock_components/GenericSystem
is a plugin that is provided by the
mock_components
package. It enables us to create a mock system on ros2_control
level.
<joint name="${prefix}left_wheel_joint">
<command_interface name="velocity"/>
<param name="device_name">left_drive</param>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
<joint name="${prefix}right_wheel_joint">
<command_interface name="velocity"/>
<param name="device_name">right_drive</param>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
Here we define the interfaces the joints have and which CANopen device
is used for the joint. The device_name
parameter is used to find the
device in the bus configuration.
Now we need to include this file in the robby.system.xacro
file.
<?xml version="1.0"?>
<robot xmlns:xacro="http://wiki.ros.org/xacro" name="test_robot">
<xacro:arg name="use_mock_hardware" default="false" />
<xacro:arg name="can_interface_name" default="vcan0" />
<xacro:include filename="$(find robby_description)/urdf/robby.robot.xacro"/>
<xacro:include filename="$(find robby_description)/urdf/robby.ros2_control.xacro"/>
<link name="world" />
<xacro:robby
name="robby"
prefix=""
parent="world"
>
<origin xyz="0 0 0.014" rpy="0 0 -1.57" />
</xacro:robby>
<xacro:robby_control
name="robby"
prefix=""
use_mock_hardware="$(arg use_mock_hardware)"
bus_config="$(find robby_description)/config/robby/bus.yml"
master_config="$(find robby_description)/config/robby/master.dcf"
can_interface_name="$(arg can_interface_name)"
master_bin=""
/>
</robot>
Controllers
Now we need to create the configuration for the controllers. Create a new
file called ros2_controllers.yaml
in the config/robby
folder.
touch config/robby/ros2_controllers.yaml
For our robot we need a joint_state_broadcaster which publishes the state
of the wheels to the joint_states
topic. We will also use a forward_command_controller
and a diff_drive_controller
to control the robot. Therefore, we need
to tell the controller_manager
about these controllers. Add the following lines
to the ros2_controllers.yaml
file:
controller_manager:
ros__parameters:
update_rate: 50 # Hz
joint_state_broadcaster:
type: joint_state_broadcaster/JointStateBroadcaster
robby_base_controller:
type: diff_drive_controller/DiffDriveController
robby_forward_controller:
type: forward_command_controller/ForwardCommandController
We also need to configure the controllers. For the forward_command_controller
this is straight forward. Add the following lines to the ros2_controllers.yaml
file:
robby_forward_controller:
ros__parameters:
left_wheel: left_wheel_joint
right_wheel: right_wheel_joint
interface_name: velocity
The diff_drive_controller is more complex. We need to configure many things.
Add the following lines to the ros2_controllers.yaml
file:
robby_base_controller:
ros__parameters:
left_wheel_names: ["left_wheel_joint"]
right_wheel_names: ["right_wheel_joint"]
wheel_separation: 0.24
#wheels_per_side: 1 # actually 2, but both are controlled by 1 signal
wheel_radius: 0.035
wheel_separation_multiplier: 1.0
left_wheel_radius_multiplier: 1.0
right_wheel_radius_multiplier: 1.0
publish_rate: 50.0
odom_frame_id: world
base_frame_id: base_link
pose_covariance_diagonal : [0.001, 0.001, 0.001, 0.001, 0.001, 0.01]
twist_covariance_diagonal: [0.001, 0.001, 0.001, 0.001, 0.001, 0.01]
open_loop: true
enable_odom_tf: true
cmd_vel_timeout: 0.5
#publish_limited_velocity: true
use_stamped_vel: false
#velocity_rolling_window_size: 10
# Velocity and acceleration limits
# Whenever a min_* is unspecified, default to -max_*
linear.x.has_velocity_limits: true
linear.x.has_acceleration_limits: true
linear.x.has_jerk_limits: false
linear.x.max_velocity: 1.0
linear.x.min_velocity: -1.0
linear.x.max_acceleration: 1.0
linear.x.max_jerk: 0.0
linear.x.min_jerk: 0.0
angular.z.has_velocity_limits: true
angular.z.has_acceleration_limits: true
angular.z.has_jerk_limits: false
angular.z.max_velocity: 1.0
angular.z.min_velocity: -1.0
angular.z.max_acceleration: 1.0
angular.z.min_acceleration: -1.0
angular.z.max_jerk: 0.0
angular.z.min_jerk: 0.0
This is it, we are done with the configurations. Now we only need to write launch files to use the robot.