This tutorial provides references of some commonly used ROS 2 features. Please refer to the official tutorials for a full introduction of the awesome ROS 2
- Install ROS2 dashing
- Workspace and Packages
- Run ROS2 command
- Changes in Build System
- ament vs catkin
- CMakelist and packge.xml changes in ROS2
- New Features in ROS2
- ROS2 Launch
- basic launch file
- launch arguments
- launch parameters
- nested launch files
- ROS 2 Networking
$ source /opt/ros/$ROS_DISTRO/setup.bash
$ mkdir -p ~/ros2_ws/src
$ cd ~/ros2_ws
And then put some packages into the src folder
- Using command
$ ros2 pkg create --<package name> [deps]
- Create Package Manually
- Create C++ package
- The package should contain a file named
package.xml
provides meta information about the package - The package should contain a file named
CMakeLists.txt
, provide information about the package and dependencies Samplepackage.xml
- The package should contain a file named
- C++ demo package demo_nodes_cpp
- Publisher/Subscriber
- Launch
- Service
- Create Python package
- The package should contain a file named
package.xml
provides meta information about the package - The package should contain a file named
setup.py
, provide information about the package
- The package should contain a file named
- Python demo package rclpy
- Publisher/Subscriber
- Launch
- Service
$ colcon build --symlink-install
Reference Page: colcon documentation
- Show all output immediately on the console
$ colcon build --event-handlers console_direct+
- Build only a single package (or selected packages)
$ colcon build --packages-select <name-of-pkg> <name-of-another-pkg>
- Build selected packages including their dependencies
$ colcon build --packages-up-to <name-of-pkg>
-
To ignore the package while building insert AMENT_IGNORE into the package
-
Clean cmake cache
$ colcon build --symlink-install --packages-select turtlebot2_drivers --cmake-clean-cache
-
run ros2 node
ros2 run <package_name> <executable>
-
ros2 launch
- launch file should be saved as
.launch.py
. eg.my_file.launch.py
ros2 launch <package_name> <launch_file>
- launch file should be saved as
-
ros2 pkg
ros2 pkg list ros2 pkg prefix <package_name> ros2 pkg -h # ros2 pkg help
-
ros2 topic
ros2 topic echo <topic_name> ros2 topic list ros2 topic -h # ros2 topic help
-
ros2 parameter
ros2 param list ros2 param set node_name Parameter value ros2 param set catographer_node use_sim_time true # set the use_sim_time param to be true ros2 param -h # ros2 param help
-
Stop/Start daemon
ros2 daemon stop ros2 daemon start
User Code ---> rclcpp/py ---> rcl ---> rmw ---> rmw_
- rclcpp/rclpy: language specific ROS clients libraries
- rcl: c Library
- rmw: ROS middleware interface: hide specific DDS implementation and streamline QoS configuration
- rmw_: DDS adapters
Reference link: ros1_bridge
- Dynamic bridge vs Static bridge
Dynamic bridge can automatically open bridges while listening to topics from both sides. However, inconsistency of received message can close the dynamic bridge and lead to a temporary loss of transfer.
Static bridges can be modified so that they could pass custom messages of a single topic in one direction only. The performance of Static bridges doesn’t depend on the periodical consistency of the messages.
- How to write custom pairs of messages for ros1_bridge
-
Why
/tf_static
Reference Link: TF2 Designtf messages do not deal with low bandwidth networks well. Within a single well connected network tf works fine, but as you transition to wireless and lossy networks with low bandwidth the tf messages start to take up a large fraction of the available bandwidth. This is partially due to the many to many nature and partially due to the fact that there is no way to choose the desired data rate for each consumer.
Solution: Add support for
/tf_static
topic which will only publish latched topics. When the subscriber is not yet established, publisher will store the latest message. Once subscriber being established, the subscriber will not miss the message.
Reference Link: tf2_ros
- Broadcasting Transforms
-
Broadcast Transformation:
Publish to
/tf
topictf2_ros::TransformBroadcaster()
constructortf2_ros::TransformBroadcaster::sendTransform
to send transforms
-
Broadcast Static Transformation :
Publish to
/tf_static
topic.Use for "latching" behavior when transforms that are not expected to change.
tf2_ros::StaticTransformBroadcaster()
, constructor,tf2_ros::StaticTransformBroadcaster::sendTransform
to send static transforms
-
Transform Listener
After the TransformListener object is created, it starts receiving tf2 transform over the wire, and buffers them for up to 10 seconds.The TransformListener object should be scoped to persist otherwise it's cache will be unable to fill and almost every query will fail. The common method is to make the TransformaListener object a member variable of a class.
Reference Link Ament Tutorial
ament
is a meta build system to improve building applications which are split into separate packages. It consists of two major parts:
- a build system (e.g. CMake, Python setup tools) to configure, build, and install a single package
- a tool to invoke the build of individual packages in their topological order
The tool relies on meta information about the packages to determine their dependencies and their build type. This meta information is defined in a manifest file called package.xml
Each package is built separately with its own build system. In order to make the output of one package available to other packages each package can extend the environment in a way that downstream packages can find and use its artifacts and resources. If the resulting artifacts are installed into /usr, for example, it might not be necessary to alter the environment at all since these folders are commonly being searched by various tools.
Reference link: ROS2 QoS design
ROS1 uses TCP as the underlying transport,which is unsuitable for lossy networks such as wireless links. ROS2 uses UDP as its transport, which offers a variety of Quality of Service (QoS) policies. This new feature benefits from the flexibility of controlling the right Quality of Service profile needed for a node to expect and act accordingly(In real-time computing systems where the right Quality of Service profile is needed to meet deadlines).
Reference link: Difference Between TCP and UDP
TCP (Transmission Control Protocol) is connection oriented, whereas UDP (User Datagram Protocol) is connection-less. This means that TCP tracks all data sent, requiring acknowledgment for each octet (generally). UDP does not use acknowledgments at all, and is usually used for protocols where a few lost datagrams do not matter.
Reference Link:Why ROS2 does not need a master
ROS1 uses TCP, so ROS1 has a centralized network configuration which requires a running ROS master to take care of naming and registration services. With the help of the master, ROS nodes could find each other on the network and communicate in a peer-to-peer fashion. In ROS1 setting, all nodes will depend on the central ROS master. When the network becomes lossy and unstable(especially if nodes are distributed on several computers), the communication will not be reliable for real-time application.
ROS2 uses Data Distribution Service (DDS) as the communication middleware. ROS2 provides a Middleware Interface(RMW) that allows users to choose different Quality of Service(QoS). The real-time publish-subscribe (RTPS) protocol allows ROS2 nodes to automatically find each other on the network, thus there is no need for a ROS2 master. This is a important point in terms of fault tolerance.
Reference Link: QoS policies
1. Current available ROS2 QoS policy options:
-
Deadline
- A DataWriter and a DataReader must update data at least once every deadline period.
-
History
- This controls whether the data transport should deliver only the most recent value, all intermediate values, o deliver something in between, which is configurable via the
depth
(size of the queue) option- KEEP_LAST: only store up to N samples, configurable via the queue depth option.
- KEEP_ALL : store all samples, subject to the configured resource limits of the underlying middleware.
- This controls whether the data transport should deliver only the most recent value, all intermediate values, o deliver something in between, which is configurable via the
-
Depth
- Size of the queue: only honored if used together with “keep last”.
-
Reliability
- BEST_EFFERT: data transport is executed ad soon as possible. But may lose them if the network is not robust.
- RELIABLE: missed samples are retransmitted, therefore, sample delivery is guaranteed delivered. may retry multiple times.
-
Durability
- With this policy, the service attempts to keep several samples so that they can be delivered to any potential late-joining DataDreader. The number of saved samples depends on HISTORY. This option has several values, such as the following:
- TRANSIENT_LOCAL: the publisher becomes responsible for persisting samples for “late-joining” subscribers.
- VOLATILE: no attempt is made to persist samples.
- With this policy, the service attempts to keep several samples so that they can be delivered to any potential late-joining DataDreader. The number of saved samples depends on HISTORY. This option has several values, such as the following:
2. The currently-defined QoS profiles for different use case:
Reference Link RMW QoS Profile Header File
- Default QoS settings for publishers and subscribers:
rmw_qos_profile_default
- In order to make the transition from ROS 1 to ROS 2, exercising a similar network behavior is desirable. By default, publishers and subscribers are reliable in ROS 2, have volatile durability, and “keep last” history.
- Services:
rmw_qos_profile_services_default
- In the same vein as publishers and subscribers, services are reliable. It is especially important for services to use volatile durability, as otherwise service servers that re-start may receive outdated requests. While the client is protected from receiving multiple responses, the server is not protected from side-effects of receiving the outdated requests.
- Sensor data:
rmw_qos_profile_sensor_data
- For sensor data, in most cases it’s more important to receive readings in a timely fashion, rather than ensuring that all of them arrive. That is, developers want the latest samples as soon as they are captured, at the expense of maybe losing some. For that reason the sensor data profile uses best effort reliability and a smaller queue depth.
- Parameters:
rmw_qos_profile_parameter_events
- Parameters in ROS 2 are based on services, and as such have a similar profile. The difference is that parameters use a much larger queue depth so that requests do not get lost when, for example, the parameter client is unable to reach the parameter service server.
- System default
rmw_qos_profile_system_default
- This uses the system default for all of the policies.
- rclcpp::SystemDefaultsQoS
- rclcpp::ParametersQoS
- rclcpp::SensorDataQoS
- rclcpp::ServicesQoS
ROS 2 Dashing API changes link
If you have no idea what depth to use and don’t care right now (maybe just prototyping), then we recommend using 10, as that was the default before and should preserve existing behavior.
publishers:
- pub_ = create_publisher<std_msgs::msg::String>("chatter");
# Assume a history setting of KEEP_LAST with depth 10
+ pub_ = create_publisher<std_msgs::msg::String>("chatter", 10);
subscribers
- sub_ = create_subscription<std_msgs::msg::String>("chatter", callback);
# Assume a history setting of KEEP_LAST with depth 10
+ sub_ = create_subscription<std_msgs::msg::String>("chatter", 10, callback);
Few more examples Publisher:
sample_pub_ = create_publisher<geometry_msgs::msg::PoseArray>("particlecloud",
rclcpp::SensorDataQoS());
pose_pub_ = create_publisher<geometry_msgs::msg::PoseWithCovarianceStamped>("amcl_pose",
rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable());
Note that transient_local
is similar to latching in ROS 1.
ROS 2 provides a life cycle management system for nodes, which gives user greater control over the sates of ROS system. A managed node allows the launch system to ensure that all components needed for the application have been instantiated correctly before it allows any component to begin executing its behavior.
One of the example use case would be a system with sensors that have long boosting time. The device driver can be instantiated to configuring state, and start when the sensor is ready to send data. This gives the developer more flexibility of how their packages can be launched, and not worry about stopping the entire system just to change the state of one node.
Note that the life cycle node does not inherit from the regular rclcpp::node::Node
but from rclcpp_lifecycle::LifecycleNode
.
ROS Launch system is a tool for easily launching multiple ROS nodes, setting parameters for those nodes, describing the configuration of their system and then execute it as described. launch system will also monitor the state of the processes launched.
ROS 2 launch system has been significantly changed compare to ROS 1 launch system. The new features are listed as follows:
- No longer uses XML configuration files, launch files are Python scripts
- Support for Managed Nodes (Lifecycle)
- Node composition from shared libraries
Launch files should be saved as .launch.py
for example: my_file.launch.py
.
this example launches the kobuki_node which is an excitable node from turtlebot2_drivers package(provided by turtlebot2_demo
repository)
import launch
import launch_ros
def generate_launch_description():
return launch.LaunchDescription([
launch_ros.actions.Node(
package="turtlebot2_drivers",
node_executable="kobuki_node",
node_name="kobuki_node",
output="screen")
])
This example shows ROS2 launches the static_state_publisher
node from tf2_ros
package with arguments:
launch_ros.actions.Node(
package="tf2_ros",
node_executable="static_transform_publisher",
arguments=['0','0', '0.161', '0','0','0','1', 'base_link', 'laser']
)
ROS2 parameters are stored in a parameter yaml files, the following is a parameter file example from ros 2 tutorial:
parameter_blackboard:
ros__parameters:
some_int: 42
a_string: "Hello world"
some_lists:
some_integers: [1, 2, 3, 4]
some_doubles : [3.14, 2.718]
To include the parameter setting, you can include the launch parameter in a launch argument. For example, to launch the urg_node with urg_node parameters:
launch_ros.actions.Node(
package="urg_node",
node_executable="urg_node",
output="screen",
arguments=["__params:=/PATH/urg_node.yaml"]
)
ROS 2 also allows users to launch another launch file inside one launch file Import libraries:
import launch.actions
import launch.launch_description_sources
The nested launch file can be called as the following example:
launch.actions.IncludeLaunchDescription(
launch.launch_description_sources.PythonLaunchDescriptionSource([PATH, '/my_file.launch.py']),
)
Import libraries:
import os
from ament_index_python.packages import get_package_share_directory
Finding the package directory:
# package directory:
my_pkg_path = ament_index_python.packages.get_package_share_directory(‘PACAKGE_NAME’)
# Combining two path into one:
my_config_file_path = os.path.join(my_pkg_path, 'configuration_files')
Reference Link: Turtlebot3 demo launch file
Remapping node names: Names within a node (e.g. topics/services) can be remapped using the syntax
<old name>:=<new name>
name node itself can be remapped using:
__node:=<new node name>
namespace can be remapped as:
__ns:=<new node namespace>
example for remapping talker
node from ROS2 demo package:
$ ros2 run demo_nodes_cpp talker __ns:=/demo __node:=my_talker chatter:=my_topic
- namespace:
->demo
- node name:
talker->/demo/my_talker
- topic name:
chatter->/demo/my_topic
- ROS 2 UDP uses multicast to allow different node to discover each other and establish communication.
- ROS 2 DDS implementation allows users to specify a domain ID and create a logical barrier to segregate networks.
- If two or more machines are on the same network, and allows multicast, messages can be passed to different machines.
- No longer needed to set ROS_HOSTNAME, ROS_MASTER, ROS_MASTER_URI
- Configure your network to allow multicast
- Make sure to connect your machines under the same subnet(same network mask)
- Ping two machines between each other
- Export the same ROS_DOMAIN_ID into different host
To check if multicast is enabled on both machines, first set the ROS_DOMAIN_ID for both machines. ROS_DOMAIN_ID should be same across different hosts, otherwise, nodes cannot automatically discover each other
Open host 1 terminal
$ export ROS_DOMAIN_ID=<Your Domain ID> # Domain ID between 0-232
$ source /opt/ros/dashing/setup.bash
$ ros2 multicast receive
Open host 2 terminal
$ export ROS_DOMAIN_ID=<Your Domain ID> # Domain ID between 0-232
$ source /opt/ros/dashing/setup.bash
$ ros2 multicast send
If terminal receives message from the second host machine, that means nodes on both machines can communicate with each other.
Also, if you don't want different machines to interfere each other, just simply set ROS_DOMAIN_ID differently.