In this post, we’ll be learning how to use nav2 SImple Command API to write a program that makes your program follow waypoints.
What we are going to create is something like a patroling system, in which the robot patrols a given area.
ROS Inside!
ROS Inside
Before anything else, if you want to use the logo above on your own robot or computer, feel free to download it and attach it to your robot. It is really free. Find it in the link below:
In order to follow this tutorial, we need to have ROS2 installed in our system, and ideally a ros2_ws (ROS2 Workspace). To make your life easier, we have already prepared a rosject with a simulation for that: https://app.theconstructsim.com/l/4da61f89/.
You can download the rosject on your own computer if you want to work locally, but just by copying the rosject (clicking the link), you will have a setup already prepared for you.
After the rosject has been successfully copied to your own area, you should see a Run button. Just click that button to launch the rosject (below you have a rosject example).
How to follow waypoints using nav2 – Run rosject (example of the RUN button)
After pressing the Run button, you should have the rosject loaded. Now, let’s head to the next section to get some real practice.
Launching the simulation
As you may imagine, instead of using a real robot, we are going to use a simulation. The simulation package we are using, neo_simulation2 (by Neobotix), comes along with all the new ROS 2 features.
Like its predecessor, the neo_simulation2 package is fully equipped with all the Neobotix robots that are available in the market.
By the way, Neobotix is a state-of-the manufacturer of mobile robots and robot systems. We offer robots and manipulators for all applications with full ROS support. Neobotix products range from small mobile robots to mobile robot arms and several omnidirectional robots. They are specialized in designing customized mobile robots to meet your unique requirements.
Combining the novelty of ROS 2 and the state-of-the-art Neobotix platforms would allow users to learn and develop various reliable and robust application that caters to their needs in both research and as well as in industries.
Alright, having opened the rosject and explained a little bit about Neobotix, let’s start running some commands in the terminal. For that, let’s open a terminal by clicking the Open a new terminal button.
Open a new Terminal
Once inside the first terminal, let’s run the commands below, to launch a simulation
cd ros2_ws
source install/setup.bash
ros2 launch neo_simulation2 simulation.launch.py
There will be countless red error messages on this simulation terminal. Let’s just ignore those messages for now.
If you want to know a bit more about Neobotix robots, they offer:
In order to move the robot to a desired goal location, pre-defined controllers and planners are available to be readily used. Thanks to Navigation 2, the all-new ROS-2 navigation stack, provides us with localization, global planning, and local planning algorithms, that allow us to jump-start by testing our intended application on the robot real-quick.
Almost all the algorithms found in move_base (ROS-1 Navigation stack) are available in Navigation2. All the Neobotix robots in the simulation for ROS-2 are primed and ready with Navigation2.
Once the simulation is started (seen in the previous tutorial), ROS-2 navigation stack can be launched using the following command
Now, in a second terminal, we can launch the Localization Server using the following command:
The commands above should have launched the simulation, Localization Server, and Path Planner server.
After some seconds, we should have Gazebo (simulation), RViz (Robot Visualization), and Teleop running now. The simulation should be similar to the following:
Simulation – How to follow waypoints using nav2
If the Gazebo simulation doesn’t pop up:
Please open the Gazebo from the below menu bar
RViz would have been loaded as well and can be found in the Graphical tools
Also, another terminal would have popped out in the Graphical tools for the teleoperation. Please follow the instruction given in that terminal for moving the robot.
To make sure everything is working so far, you can send a 2D NavGoal in RViz to make sure the navigation system is working.
The files used to launch the Localization Server and Path Planner are found on the following paths:
ls ~/ros2_ws/src/neobotix_mp_400_navigation/localization_server/launch/localization.launch.py
ls ~/ros2_ws/src/neobotix_mp_400_navigation/path_planner_server/launch/pathplanner.launch.py
These files can also be seen in the Code Editor:
Localization Server and Path Planner – How to follow waypoints using nav2
Feel free to localize and send goals to the robot as shown in this video about ROS2 Navigation for Omnidirectional Robots:
Global Costmap and Local Costmap in RViz
Assuming you have RViz running, you can add Global and Local costmaps to it. For that, click the Add button on the bottom left side of RViz, then Add by Topic, then select Global Costmap:
Add by topic – Global Costmap – How to follow waypoints using nav2
To add Local Costmap, click the Add button on the bottom left side of RViz, then Add by Topic, then select the Map under Local Costmap:
Add by topic – Local Costmap – How to follow waypoints using nav2
Assuming everything went well so far, now we are going to test the waypoint follower.
Waypoint follower
If you forked the rosject (clicking on the link we provided to you earlier), you should have a package named follow_waypoints on your ros2_ws/src folder already, but for documentation purposes, and in case you want to know the baby steps, here is how we created that package.
First, in a fourth terminal we created that package:
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_python follow_waypoints
By listing the content of that ~/ros2_ws/src folder, we see that the package has been created:
ls
# follow_waypoints neo_local_planner2 neo_simulation2 neobotix_mp_400_navigation
That follow_waypoints package has a folder with the same name on it. On that folder, we created a file named follow_waypoints.py
cd ~/ros2_ws/src/follow_waypoints/follow_waypoints
touch follow_waypoints.py
chmod +x follow_waypoints.py
The touch command was used to create the file, and the chmod +x command was used to give execution permissions to that file (make it executable, basically)
We then pasted some content on the follow_waypoints.py file. You can see the content by opening that file using the Code Editor.
Inspection Route – To navigation to – How to follow waypoints using nav2
On lines 33 to 36 we define an inspection_route variable, which essentially is an array that defines the waypoints (positions in the map) that the robot has to go when patrolling.
#! /usr/bin/env python3
# Copyright 2021 Samsung Research America
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import time
from copy import deepcopy
from geometry_msgs.msg import PoseStamped
from rclpy.duration import Duration
import rclpy
from nav2_simple_commander.robot_navigator import BasicNavigator, NavigationResult
def main():
rclpy.init()
navigator = BasicNavigator()
# Inspection route, probably read in from a file for a real application
# from either a map or drive and repeat.
inspection_route = [ # simulation points
[5.0, 0.0],
[-5.0, -5.0],
[-5.0, 5.0]]
# Set our demo's initial pose
# initial_pose = PoseStamped()
# initial_pose.header.frame_id = 'map'
# initial_pose.header.stamp = navigator.get_clock().now().to_msg()
# initial_pose.pose.position.x = 3.45
# initial_pose.pose.position.y = 2.15
# initial_pose.pose.orientation.z = 1.0
# initial_pose.pose.orientation.w = 0.0
# navigator.setInitialPose(initial_pose)
# Wait for navigation to fully activate
navigator.waitUntilNav2Active()
while rclpy.ok():
# Send our route
inspection_points = []
inspection_pose = PoseStamped()
inspection_pose.header.frame_id = 'map'
inspection_pose.header.stamp = navigator.get_clock().now().to_msg()
inspection_pose.pose.orientation.z = 1.0
inspection_pose.pose.orientation.w = 0.0
for pt in inspection_route:
inspection_pose.pose.position.x = pt[0]
inspection_pose.pose.position.y = pt[1]
inspection_points.append(deepcopy(inspection_pose))
nav_start = navigator.get_clock().now()
navigator.followWaypoints(inspection_points)
# Do something during our route (e.x. AI to analyze stock information or upload to the cloud)
# Simply print the current waypoint ID for the demonstation
i = 0
while not navigator.isNavComplete():
i = i + 1
feedback = navigator.getFeedback()
if feedback and i % 5 == 0:
print('Executing current waypoint: ' +
str(feedback.current_waypoint + 1) + '/' + str(len(inspection_points)))
result = navigator.getResult()
if result == NavigationResult.SUCCEEDED:
print('Inspection of shelves complete! Returning to start...')
elif result == NavigationResult.CANCELED:
print('Inspection of shelving was canceled. Returning to start...')
exit(1)
elif result == NavigationResult.FAILED:
print('Inspection of shelving failed! Returning to start...')
# go back to start
# initial_pose.header.stamp = navigator.get_clock().now().to_msg()
# navigator.goToPose(initial_pose)
while not navigator.isNavComplete:
pass
if __name__ == '__main__':
main()
In addition to that follow_waypoints.py file, we also had to create the ~/ros2_ws/src/follow_waypoints/config/follow_waypoints.yaml and ~/ros2_ws/src/follow_waypoints/setup.py files.
Please check that files. If you want a deeper explanation about those files, please check the video available at the end of this post.
Alright, after having created that package and the required configuration files, the next was was compiling the package:
cd ~/ros2_ws
colcon build; source install/setup.bash
Then, to see the robot following the waypoints, we can run:
cd ~/ros2_ws
source install/setup.bash
ros2 run follow_waypoints follow_waypoints_exe
Looking at the simulation and at RViz, you should be able to see the robot moving.
Congratulations. You just learned how to make a robot follow waypoints using nav2 (the official ROS 2 Navigation package)
We hope this post was really helpful to you. If you want a live version of this post with more details, please check the video in the next section.
Youtube video
So this is the post for today. Remember that we have the live version of this post on YouTube. If you liked the content, please consider subscribing to our youtube channel. We are publishing new content ~every day.
Keep pushing your ROS Learning.
Related Courses & Training
If you want to learn more about ROS and ROS2, we recommend the following courses:
ROS2 is an open-source software framework for robotics that provides libraries and tools to help developers create robot applications.
Documentation is an essential part of any software project, and ROS2 welcomes contributions from anyone who wants to improve it.
In this video, we will show you how to find issues to work on, clone tutorial packages into a rosject, and create pull requests. By the end of this video, you will be able to make your first contribution to ROS2 documentation and become part of the ROS2 community.
ROS Inside!
ROS Inside
Before anything else, if you want to use the logo above on your own robot or computer, feel free to download it and attach it to your robot. It is really free. Find it in the link below:
In order to follow this tutorial, we need to have ROS2 installed in our system, and ideally a ros2_ws (ROS2 Workspace). To make your life easier, we have already prepared a rosject with a simulation for that: https://app.theconstructsim.com/l/56492ac1/.
You can download the rosject on your own computer if you want to work locally, but just by copying the rosject (clicking the link), you will have a setup already prepared for you.
After the rosject has been successfully copied to your own area, you should see a Run button. Just click that button to launch the rosject (below you have a rosject example).
How to release a ROS 2 binary package – Part 3 – Run rosject (example of the RUN button)
After pressing the Run button, you should have the rosject loaded. Now, let’s head to the next section to get some real practice.
Starting the simulation
After having opened the rosject, let’s start a simulation following the ROS Nav2 documentation to move the robot around. For that, let’s open a terminal by clicking the Open a new terminal button.
Open a new Terminal
Let’s run the following commands in the first terminal in order to launch the simulation:
If everything went ok, you should have something like we have in the image below, where we have a simulation (on the left side), and the RViz (Robot Visualization) window (on the right side)
Neo robot simulation – setting a pose goal on Nav2
If we just try to set a 2D Pose Goal for the robot to move around, by clicking on 2D Pose Goal on RViz, and then set the goal as we can see in the blue arrow in the image above, the robot should move without any problems (because the rosject is fixed).
Reproducing a problem found in the nav2 documentation
In order to move a robot around in ROS 2, we use the Nav2 package. There is a nav2 documentation in the following link, that we are interested in, for the purpose of this video:
At the end of the page on the link aforementioned, you will find a code that contains the “planner_server” term.
When this post was created, the code was indented more or less like what we see below (planner_server and ros__parameters have the same indentation):
How to easily contribute to ROS2 documentation – Indentation
The instructions in the linked aforementioned say that we could paste that content in the nav2_params.yaml file. .
If you want to find that file on the rosject, you first have to open the Code Editor:
Open the IDE – Code Editor
The nav2 tutorial mentions nav2_params.yaml, but in the specific rosject shared at the beginning of this post, the file used is planner_server.yaml
Once the Code Editor is open, you can find the nav2_params.yaml file at the following path: ~/ros2_ws/src/neobotix_ros2/path_planner_server/config/planner_server.yaml.
nav2_params.yaml – How to easily contribute to ROS2 documentation
You don’t have to, but if you replace the first 10 lines of planner_server.yaml with the non-indented code that was provided on the nav2 tutorial when this post was created, you would replace the first 10 lines with the following content:
If you had pasted the content above (as I said earlier, you don’t have to), you would have to kill the programs launched in the 3 terminals by pressing CTRL+C,
After that, you would have to recompile the ros2_ws using the following commands on the first terminal:
cd ~/ros2_ws
colcon build
After that, you could try to launch the simulation just like we did earlier:
In the first terminal, the simulation would be launched:
But, when launching the Path Planner, you would have the following error:
[planner_server-3] [ERROR] [1678443929.379055702] [rcl]: Failed to parse global arguments
[planner_server-3] terminate called after throwing an instance of 'rclcpp::exceptions::RCLInvalidROSArgsError
[planner_server-3] what(): failed to initialize rcl: Couldn't parse params file:
'--params-file /home/user/ros2_ws/install/path_planner_server/share/path_planner_server/config/planner_server.yaml'.
Error: Cannot have a value before ros__parameters at line 12,
at /tmp/binarydeb/ros-galactic-rcl-yaml-param-parser-3.1.3/src/parse.c:793,
at /tmp/binarydeb/ros-galactic-rcl-3.1.3/src/rcl/arguments.c:406
The error message shows that there is a problem when trying to parse the file. The problem is mainly an indentation problem.
Now, since the problem was found in the nav2 tutorial itself, how to contribute to this fix get resolved?
Contributing to the documentation – Creating a Pull Request
By opening this last link, you will see at the top right corner a button that allows you to edit that file. You just have to click that link.
You can then make your changes, and create a commit. After that, you can just create a Pull Request:
Pull Request – fix nav2 documentation
And that is it.
The repository maintainers will review your changes, and if everything is ok, they will approve it. If not ok, they will suggest some changes. If they suggest changes, you just make the changes as requested.
Congratulations. You just learned how to contribute to the ROS Documentation.
We hope this post was really helpful to you. If you want a live version of this post with more details, please check the video in the next section.
Youtube video
So this is the post for today. Remember that we have the live version of this post on YouTube. If you liked the content, please consider subscribing to our youtube channel. We are publishing new content ~every day.
Keep pushing your ROS Learning.
Related Courses & Training
If you want to learn more about ROS and ROS2, we recommend the following courses:
In this post, you will learn how to publish and subscribe to a topic from a launch file. This post is a response to this question posted on Gazebo Answers.
Step 1: Fire up a system with ROS installation
“Hey, do you mean I have to install ROS first?” Absolutely not! Just log in to The Construct to get access to virtual machines pre-installed with ROS.
Once logged in, click on My Rosjects, then Create a New Rosject, supply the information as shown in the image below, and click Create. Then RUN the rosject.
You might also want to try this on a local PC if you have ROS installed. However, please note that we don’t support local PCs and you will have to fix any errors you run into on your own. The rest of the instruction assumes that you are working on The Construct; please adapt them to your local PC and ROS installation.
PS: we are using ROS Noetic. You should be able to replicate the same on any ROS 1 distro.
Step 2: Create a package to demonstrate how to publish and subscribe to a topic from a launch file
Open a web shell and run the following commands to create the package.
cd catkin_ws/src
source /opt/ros/noetic/setup.bash
catkin_create_pkg test_rostopic
Next, we create the launch directory and the launch files.
cd test_rostopic
mkdir launch
cd launch
touch test_pub.launch
touch test_echo.launch
Nice done! Now head over to the Code Editor to make changes to the launch files. Check the image below for how to open the Code Editor.
Locate the launch folder in the code editor: catkin_ws > src > test_rostopic > launch and open the two launch files.
Paste the following lines into the test_pub.launch file. This is equivalent to rostopic pub /test_topic std_msgs/String "data: 'Hello there!'"
cd ~/catkin_ws
catkin_make
source devel/setup.bash
Success! We’ll test it in the next step.
PS: if your code did not compile correctly, please go over the instructions and ensure you have created the files in the exact locations specified.
Step 3: Test the package to demonstrate how to publish and subscribe to a topic from a launch file
Start the test_pub.launch file in the current terminal.
roslaunch test_rostopic test_pub.launch
When done, open another terminal and run the test_echo.launch file.
roslaunch test_rostopic test_echo.launch
Now you should see something similar to the following on the terminal where test_echo.launch is running:
process[rostopic_echo_node-1]: started with pid [xxxx]
data: "Hello there!"
---
Sweet! So we are able to publish with one launch file and echo what we publish in another launch file. And…we are done here, congrats!
Take home: try combining both into a single launch file. After trying, check the solution in this gist.
Step 4: Check your learning
Do you understand how to publish and subscribe to a topic from a launch file? If you don’t know it yet, please go over the post again, more carefully this time.
(Extra) Step 5: Watch the video to understand how to publish and subscribe to a topic from a launch file
Here you go:
Feedback
Did you like this post? Do you have any questions about how to publish and subscribe to a topic from a launch file? Please leave a comment in the comments section below, so we can interact and learn from each other.
If you want to learn about other ROS topics, please let us know in the comments area and we will do a video or post about it.
“Hey, do you mean I have to install ROS first?” Absolutely not! Just log in to The Construct to get access to virtual machines pre-installed with ROS.
Once logged in, click on My Rosjects, then Create a New Rosject, supply the information as shown in the image below, and click Create. Then RUN the rosject.
You might also want to try this on a local PC if you have ROS installed. However, please note that we don’t support local PCs and you will have to fix any errors you run into on your own. The rest of the instruction assumes that you are working on The Construct; please adapt them to your local PC and ROS installation.
PS:
We are using ROS Kinetic here because that’s what the user on ROS Answers used.
We could put the publisher and the subscriber in a single package, but we’ll create two packages as the user did.
We could put the custom message in any of the two packages, but we’ll put it in the subscriber, just like the user.
Step 2: Create the first package that contains the subscriber and the custom message
Create the package and custom message
Open a web shell and run the following commands to create the package. When creating a custom message, you need extra dependencies message_generation and message_runtime, in addition to the main ROS API, rospy (for Python) in this case, and the types needed for the message, in this case std_msgs.
cd catkin_ws/src
source /opt/ros/kinetic/setup.bash
catkin_create_pkg quadrotor_receive rospy std_msgs message_generation message_runtime
Next, we create the message file, Person.msg. Run the following command in the same terminal you just used.
cd quadrotor_receive
mkdir msg
cd msg
touch Person.msg
Nice done! Now head over to the Code Editor to make changes to the Person.msg file. Check the image below for how to open the Code Editor.
Locate the file in the code editor: catkin_ws > src > quadrotor_receive > msg > Person.msg and paste in the following code.
string name
int32 age
Edit package files
Replace CMakeLists.txt with the one below (where the following changes have been made).
Removed message_runtime from the find_package function.
Uncommented the add_message_files function, and added Person.msg below FILES.
Uncommented the generate_messages function, and left it intact.
Uncommented the line CATKIN_DEPENDS in the catkin_package function.
Removed all commented lines to keep the file short and sweet.
cmake_minimum_required(VERSION 3.0.2)
project(quadrotor_receive)
## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
message_runtime
rospy
std_msgs
)
## Generate messages in the 'msg' folder
add_message_files(
FILES
Person.msg
# Message2.msg
)
## Generate added messages and services with any dependencies listed here
generate_messages(
DEPENDENCIES
std_msgs
)
###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES quadrotor_receive
CATKIN_DEPENDS message_generation message_runtime rospy std_msgs
# DEPENDS system_lib
)
###########
## Build ##
###########
## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
${catkin_INCLUDE_DIRS}
)
Next, add a single line to package.xml so that it looks like the one below. Can you identify the line that was added?
<?xml version="1.0"?>
<package format="2">
<name>quadrotor_receive</name>
<version>0.0.0</version>
<description>The quadrotor_receive package</description>
<!-- One maintainer tag required, multiple allowed, one person per tag -->
<!-- Example: -->
<!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
<maintainer email="user@todo.todo">user</maintainer>
<!-- One license tag required, multiple allowed, one license per tag -->
<!-- Commonly used license strings: -->
<!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
<license>TODO</license>
<!-- <doc_depend>doxygen</doc_depend> -->
<buildtool_depend>catkin</buildtool_depend>
<build_depend>message_generation</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
<build_export_depend>rospy</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>
<build_export_depend>message_generation</build_export_depend>
<exec_depend>message_runtime</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>
<!-- Other tools can request additional information be placed here -->
</export>
</package>
Finally, let’s create the receiver Python node, just as the ROS Answers user created it. Run the following in the same terminal as before. Notice that we’re making the Python script executable.
cd ~/catkin_ws/src/quadrotor_receive/src
touch custom_listener.py
chmod +x custom_listener.py
Open custom_listener.py in the IDE and paste in the following code.
#!/usr/bin/env python
import rospy
from quadrotor_receive.msg import Person
def callback(data):
rospy.loginfo("%s is age: %d" % (data.name, data.age))
def listener():
rospy.init_node('custom_listener', anonymous=True)
rospy.Subscriber("custom_chatter", Person , callback)
# spin() simply keeps python from exiting until this node is stopped
rospy.spin()
if __name__=='__main__':
listener()
So much for all the hard work – let’s see if it works. Time to compile the code. In the same web shell, run the following commands:
Compile and test
cd ~/catkin_ws
catkin_make
source devel/setup.bash
Success! We have successfully created a package with a custom message. Let’s see if we can find the message. You should get an output similar to the one below.
user:~/catkin_ws$ rosmsg show quadrotor_receive/Person
string name
int32 age
Let’s also test receiver node at this point. Though there’s nothing to receive yet, it should not throw any error. First, we need to start roscore.
roscore
Then try to run your subscriber in another terminal. Leave it running!
rosrun quadrotor_receive custom_listener.py
PS: if your code did not compile correctly, please go over the instructions and ensure you have created the files in the exact locations specified.
Step 3: Create the second package that contains the publisher
Create a new folder package. This should be easy-peasy, as most of the work has been done in the previous step. Do the following in a new terminal.
cd ~/catkin_ws/src/
catkin_create_pkg transmit_thrust rospy
Next, create the custom_talker.py node.
cd transmit_thrust/src
touch custom_talker.py
chmod +x custom_talker.py
Open up custom_talker.py in the IDE and paste in the following.
#!/usr/bin/env python
import rospy
from quadrotor_receive.msg import Person
def talker():
pub = rospy.Publisher('custom_chatter', Person)
rospy.init_node('custom_talker', anonymous=True)
r = rospy.Rate(10) #10hz
msg = Person()
msg.name = "ROS User"
msg.age = 4
while not rospy.is_shutdown():
rospy.loginfo(msg)
pub.publish(msg)
r.sleep()
if __name__ == '__main__':
try:
talker()
except rospy.ROSInterruptException: pass
Please note that:
We are importing the custom message from the quadrotor_receive package.
We are creating a publisher for the same topic we created a subscriber for in the quadrotor_receive package.
Nothing more is needed. Let’s compile our new package.
cd ~/catkin_ws
catkin_make
source devel/setup.bash
That done, let’s start the custom talker node.
rosrun transmit_thrust custom_talker.py
You should see some output similar to these on the terminal.
And…on the terminal where you have the listener tunning, you should see something similar to:
[INFO] [1680728823.956347]: ROS User is age: 4
[INFO] [1680728824.056833]: ROS User is age: 4
And we are done here!
Step 4: Check your learning
Do you understand how to write a ROS publisher and subscriber with a custom message? If you don’t know it yet, please go over the post again, more carefully this time.
(Extra) Step 5: Watch the video to understand how to write a ROS publisher and subscriber with a custom message
Here you go:
Feedback
Did you like this post? Do you have any questions about how to write a ROS publisher and subscriber with a custom message? Please leave a comment in the comments section below, so we can interact and learn from each other.
If you want to learn about other ROS topics, please let us know in the comments area and we will do a video or post about it.
ROS2 parameters are great for configurable nodes that you can adapt to your robot configuration simply by changing a configuration file or a launch file. However, if we just implemented the basics you will have to re-run your node each time you change a parameter. You can’t change the parameter on-the-fly and have it updated in the robot. But by adding a callback function that updates the variables in our code, it is possible to do a live parameter update while a program is running, removing the need for a tedious node restart. Learn how to do it in this post.
ROS Inside!
ROS Inside
Before anything else, if you want to use the logo above on your own robot or computer, feel free to download it and attach it to your robot. It is really free. Find it in the link below:
In order to follow this tutorial, we need to have ROS2 installed in our system, and ideally a ros2_ws (ROS2 Workspace). To make your life easier, we have already prepared a rosject with a simulation for that: https://app.theconstructsim.com/l/53e75e28/.
You can download the rosject on your own computer if you want to work locally, but just by copying the rosject (clicking the link), you will have a setup already prepared for you.
After the rosject has been successfully copied to your own area, you should see a Run button. Just click that button to launch the rosject (below you have a rosject example).
Learn how to enable live parameter updates (C++) – Run rosject (example of the RUN button)
After pressing the Run button, you should have the rosject loaded. Now, let’s head to the next section to get some real practice.
Starting the simulation
After having opened the rosject, let’s start a simulation. For that, let’s open a terminal by clicking the Open a new terminal button.
Open a new Terminal
Once inside the terminal, let’s run the commands below:
If everything went well, you should have a simulation loaded and opened automatically in a few seconds. The simulation will open from a top view, but you can use the mouse to move the simulation to a different perspective.
How to enable live parameter updates (C++)
Understanding the problem
Below is an example node in which we have only implemented the barebone basics of parameters in ROS2.
Let’s see it in action and see how it behaves, especially when we update its parameter values.
In a second terminal, let’s run the node for Obstacle Avoidance.
source ~/ros2_ws/install/setup.bash
ros2 run rule_based_obstacle_avoidance obstacle_avoidance
If you watch the simulation for a while, you will see that when the robot detects the wall, it rotates and moves forward until it detects another wall, and repeats the process.
The node name is obstacle_avoidance_node (you can check it in a third terminal by running: ros2 node list)
Now, let’s list the parameters of the node in a third terminal:
Now, still in the third terminal, let’s check the value of the safety_distance parameter:
ros2 param get /obstacle_avoidance_node safety_distance
The output we should have got should be the following:
Double value is: 1.5
Now, let’s set the parameter to a new value:
ros2 param set /obstacle_avoidance_node safety_distance 1.0
The expected output is:
Set parameter successful
Ok, so far so good. But with the new value, we expect the robot to get closer to the wall before turning around because now the safe distance was set from 1.5 meters to 1.0. The problem is that the robot is not considering the new value that we just set.
We can follow the same idea to try to make the robot move faster. Let’s check the current velocity of the robot:
ros2 param get /obstacle_avoidance_node linear_x_velocity
The output we should have got should be the following:
Double value is: 0.2
If we increase the speed:
ros2 param set /obstacle_avoidance_node linear_x_velocity 0.5
The expected output is:
Set parameter successful
The parameter was reported as successfully set, yet the robot does not move faster, because it still uses the value loaded when the node started.
In the current code, parameter values are fixed. As such, every time a parameter value changes, the parameter value in the code stays the same even though you may have expected it to update based on the latest value set.
In order to solve this, we must add a parameter callback function to your code so that the variable in the code gets the freshest data.
Before moving to the next section, please kill the simulation and all nodes running by pressing Ctrl+C on all terminals.
Solution: Add a parameter callback method
Alright, have you closed all programs by pressing CTRL+C on all terminals?
If you check around line 45 on that file, you will find the “private:” section, where we define the private variables of our class, something like the following:
Now, above that “private:” section, around line 38, let’s add the following code to instantiate a ParameterEventHandler, providing the current ROS node to use to create the required subscriptions:
Below the param_subscriber_ we have to set a callback method, in this case, a lambda function:
// Set a callback for this node's parameter, "linear_x_velocity"
auto callback_linear_x = [this](const rclcpp::Parameter &p) {
RCLCPP_INFO(this->get_logger(),
"callback_linear_x: Received an update to parameter \"%s\" "
"of type %s: \"%f\"",
p.get_name().c_str(), p.get_type_name().c_str(),
p.as_double());
linear_x_velocity_ = p.as_double();
};
Then we set “callback_linear_x” as the callback to invoke whenever linear_x_velocity is updated. We store the handle that is returned by “add_parameter_callback“; otherwise, the callback will not be properly registered.
Now that our package has been rebuilt and sourced, let’s launch the simulation again:
ros2 launch neo_simulation2 simulation.launch.py
The simulation should have been opened just like before, but now we will see the parameters affecting the simulation in “real-time”.
Before changing the parameters, let’s also launch the Obstacle Avoidance node, just like before, using the second terminal:
ros2 run rule_based_obstacle_avoidance obstacle_avoidance
You should see the robot approaching the wall, and turning around when getting close to it.
Changing the x velocity using ROS 2 Parameters
Ok, now that the robot is moving, let’s retrieve again the current value of the linear x velocity using the third terminal:
ros2 param get /obstacle_avoidance_node linear_x_velocity
Just like before, the expected output is:
Double value is: 0.2
Now let’s change that value:
ros2 param set /obstacle_avoidance_node linear_x_velocity 1.0
We expect a successful output:
Set parameter successful
If you look at the simulation now, you should see that when the robot is moving forward (not turning around), it moves really faster. So, as we can see, we are now able to make ROS2 Parameters be reflected “instantly”.
This opens up really many different possibilities.
We hope this post was really helpful to you. If you want a live version of this post with more details, please check the video in the next section.
Youtube video
So this is the post for today. Remember that we have the live version of this post on YouTube. If you liked the content, please consider subscribing to our youtube channel. We are publishing new content ~every day.
Keep pushing your ROS Learning.
Related Courses & Training
If you want to learn more about ROS and ROS2, we recommend the following courses:
I would like to dedicate this episode to the people that use other robotics frameworks different from ROS… and succeed. You know, ROS already provides you with a fully working robotics framework as well as many software for main robotics tasks. If you go outside ROS, the robotics world is cold, difficult, and hostile. If a company is able to succeed in such conditions, then that indicates strength and mastery. Good for you! If your company is in such a situation, then this episode is dedicated to you.
Today we are going to learn about such an amazing company that has succeeded worldwide without using ROS. We will learn how they did it, why they don’t use ROS and what do they use instead. We will also learn about their process to get robotics engineers in their team, so this episode can be very interesting for you if you are looking for a job!
But before going into the meat, let me tell you that in case you still believe ROS is a good path for you, you can learn ROS2 with The Construct. We provide self-led training based on robot simulations and remote real robots. You follow our lessons using your web browser and practice from minute one with the simulated robots. Also, as you advance the lesson, you will practice with real robots located in Barcelona. They are available 24/7 for you to remotely connect to them and do your practice.
Have a look at our webpage about the different subjects you can learn on ROS1 and ROS2 and start practicing with robots from minute one with The Construct.
Now let’s go to the meat of the episode. Let me introduce you Denys Kanunikov and Sam Thornton. Denys and Sam work at Mujin, a robotics company that…. well you are going to learn in a minute. For the moment, let me tell you that Denys is the lead robotics system engineer of Mujin and Sam is the Manager of Talent acquisition.