Every ROSject created in ROSDS contains from the beginning 2 workspaces pre-configured in the user home folder. These 2 workspaces inherit another workspace, which contains all the simulations TheConstruct provides, which is called the Public Simulation Workspace (public_sim_ws). Finally, this simulations workspace inherit only the ROS distribution installed. It may vary depending on the distro you have chosen the given ROSject.
That’s the structure TheConstruct engineers defined to provide all the simulations we have pre-installed and compiled, and also gives to the developer the power to overwrite any of those simulations (you can launch any of those simulations on the fly in ROSDS!). Basically, a ROS workspace that overlays another can have packages with the same name having priority to be executed (It can become quite confusing, but you can check all the rules here: http://wiki.ros.org/catkin/Tutorials/workspace_overlaying).
Finally, our team suggests to the community to develop a ROS project separating the simulations from robotic behaviors packages. What is the difference? Basically, we believe a developer should be able to switch between simulations/robots and robotic algorithms. E.g: You could use the same obstacle avoidance algorithm with any mobile robot, since you configure its parameters properly. That’s why a ROSject have from scratch 2 workspaces in it: simulations_ws and catkin_ws. Of course, the developer can change it to the architecture fits better to his project. The original workspaces overlaying in a ROSject is like shown below:
Use the ROS Development Studio (ROSDS), an online platform for developing for ROS within a PC browser. Easy-peasy. I’m using this option for this post.
Once you log in, click on the New ROSject button to create a project that will host your code. Then Click on Open ROSject to launch the development/simulation environment.
To open a “terminal” on ROSDS, pick the Shellapp from the Tools menu.
You can find the IDE app on the Tools menu.
You have ROS installed on a local PC. Okay, skip to Step 2.
Next step!
Step 2: Reproduce the problem – let’s lose some messages from the publisher!
1. If you are using ROSDS, start by creating a and opening a ROSject as described above.
2. Open a terminal and create a package:
user:~$ cd catkin_ws/src
user:~/catkin_ws/src$ catkin_create_pkg talker_listener roscpp
Created file talker_listener/CMakeLists.txt
Created file talker_listener/package.xml
Created folder talker_listener/include/talker_listener
Created folder talker_listener/src
Successfully created files in /home/user/catkin_ws/src/talker_listener. Please adjust the values in package.xml.
user:~/catkin_ws/src$
3. Create the talker and listener source files
user:~/catkin_ws/src$ cd talker_listener/src
user:~/catkin_ws/src/talker_listener/src$ touch talker.cpp listener.cpp
4. Open the talker.cpp and listener.cpp files in the IDE and paste in the following code
talker.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
/**
* This tutorial demonstrates simple sending of messages over the ROS system.
*/
int main(int argc, char **argv)
{
/**
* The ros::init() function needs to see argc and argv so that it can perform
* any ROS arguments and name remapping that were provided at the command line.
* For programmatic remappings you can use a different version of init() which takes
* remappings directly, but for most command-line programs, passing argc and argv is
* the easiest way to do it. The third argument to init() is the name of the node.
*
* You must call one of the versions of ros::init() before using any other
* part of the ROS system.
*/
ros::init(argc, argv, "talker");
/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the last
* NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;
/**
* The advertise() function is how you tell ROS that you want to
* publish on a given topic name. This invokes a call to the ROS
* master node, which keeps a registry of who is publishing and who
* is subscribing. After this advertise() call is made, the master
* node will notify anyone who is trying to subscribe to this topic name,
* and they will in turn negotiate a peer-to-peer connection with this
* node. advertise() returns a Publisher object which allows you to
* publish messages on that topic through a call to publish(). Once
* all copies of the returned Publisher object are destroyed, the topic
* will be automatically unadvertised.
*
* The second parameter to advertise() is the size of the message queue
* used for publishing messages. If messages are published more quickly
* than we can send them, the number here specifies how many messages to
* buffer up before throwing some away.
*/
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
/**
* A count of how many messages we have sent. This is used to create
* a unique string for each message.
*/
int count = 0;
while (ros::ok())
{
/**
* This is a message object. You stuff it with data, and then publish it.
*/
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
/**
* The publish() function is how you send messages. The parameter
* is the message object. The type of this object must agree with the type
* given as a template parameter to the advertise<>() call, as was done
* in the constructor above.
*/
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
listener.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
/**
* This tutorial demonstrates simple receipt of messages over the ROS system.
*/
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
/**
* The ros::init() function needs to see argc and argv so that it can perform
* any ROS arguments and name remapping that were provided at the command line.
* For programmatic remappings you can use a different version of init() which takes
* remappings directly, but for most command-line programs, passing argc and argv is
* the easiest way to do it. The third argument to init() is the name of the node.
*
* You must call one of the versions of ros::init() before using any other
* part of the ROS system.
*/
ros::init(argc, argv, "listener");
/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the last
* NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;
/**
* The subscribe() call is how you tell ROS that you want to receive messages
* on a given topic. This invokes a call to the ROS
* master node, which keeps a registry of who is publishing and who
* is subscribing. Messages are passed to a callback function, here
* called chatterCallback. subscribe() returns a Subscriber object that you
* must hold on to until you want to unsubscribe. When all copies of the Subscriber
* object go out of scope, this callback will automatically be unsubscribed from
* this topic.
*
* The second parameter to the subscribe() function is the size of the message
* queue. If messages are arriving faster than they are being processed, this
* is the number of messages that will be buffered up before beginning to throw
* away the oldest ones.
*/
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
/**
* ros::spin() will enter a loop, pumping callbacks. With this version, all
* callbacks will be called from within this thread (the main one). ros::spin()
* will exit when Ctrl-C is pressed, or the node is shutdown by the master.
*/
ros::spin();
return 0;
}
5. Add the following lines to the build section of the CMakeList.txt file, so the files can build:
7. Start the listener node first in the current terminal and then start the talker node in another terminal.
Start the listener first. You should not see any message yet.
user:~/catkin_ws$ rosrun talker_listener listener
(PS: If you get an error that master cannot be reached, run nohup roscore & in the current terminal to start the ROS master)
Start the talker in a new terminal.
user:~$ cd catkin_ws
user:~/catkin_ws$ source devel/setup.bash
user:~/catkin_ws$ rosrun talker_listener talker
You would notice that even though the talker publishes the messages starting from zero, the listener only gets the messages starting from 3 (or even 4):
# Talker
user:~/catkin_ws$ rosrun talker_listener talker
[ INFO] [1586183868.773366254]: hello world 0
[ INFO] [1586183868.873393538]: hello world 1
[ INFO] [1586183868.973385317]: hello world 2
[ INFO] [1586183869.073401909]: hello world 3
[ INFO] [1586183869.173393278]: hello world 4
[ INFO] [1586183869.273392714]: hello world 5
#### Listener
user:~/catkin_ws$ rosrun talker_listener listener
[ INFO] [1586183869.073691834]: I heard: [hello world 3]
[ INFO] [1586183869.173605854]: I heard: [hello world 4]
[ INFO] [1586183869.273767103]: I heard: [hello world 5]
So the problem is real! Let’s fix it in the next section!
Step 2: Fix the problem: avoid losing messages from the publisher
What’s the point of talking when no one is listening? We need to make the talker wait for the listener before publishing the message by adding the following lines to the talker.cpp file, before the publish command:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
/**
* This tutorial demonstrates simple sending of messages over the ROS system.
*/
int main(int argc, char **argv) {
/**
* The ros::init() function needs to see argc and argv so that it can perform
* any ROS arguments and name remapping that were provided at the command
* line. For programmatic remappings you can use a different version of init()
* which takes remappings directly, but for most command-line programs,
* passing argc and argv is the easiest way to do it. The third argument to
* init() is the name of the node.
*
* You must call one of the versions of ros::init() before using any other
* part of the ROS system.
*/
ros::init(argc, argv, "talker");
/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the
* last NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;
/**
* The advertise() function is how you tell ROS that you want to
* publish on a given topic name. This invokes a call to the ROS
* master node, which keeps a registry of who is publishing and who
* is subscribing. After this advertise() call is made, the master
* node will notify anyone who is trying to subscribe to this topic name,
* and they will in turn negotiate a peer-to-peer connection with this
* node. advertise() returns a Publisher object which allows you to
* publish messages on that topic through a call to publish(). Once
* all copies of the returned Publisher object are destroyed, the topic
* will be automatically unadvertised.
*
* The second parameter to advertise() is the size of the message queue
* used for publishing messages. If messages are published more quickly
* than we can send them, the number here specifies how many messages to
* buffer up before throwing some away.
*/
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
/**
* A count of how many messages we have sent. This is used to create
* a unique string for each message.
*/
int count = 0;
while (ros::ok()) {
/**
* This is a message object. You stuff it with data, and then publish it.
*/
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
/**
* The publish() function is how you send messages. The parameter
* is the message object. The type of this object must agree with the type
* given as a template parameter to the advertise<>() call, as was done
* in the constructor above.
*/
while (chatter_pub.getNumSubscribers() == 0)
loop_rate.sleep();
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
# Talker
user:~/catkin_ws$ rosrun talker_listener talker
[ INFO] [1586183868.773366254]: hello world 0
[ INFO] [1586183868.873393538]: hello world 1
[ INFO] [1586183868.973385317]: hello world 2
#### Listener
user:~/catkin_ws$ rosrun talker_listener listener
[ INFO] [1586183869.073691834]: I heard: [hello world 0]
[ INFO] [1586183869.173605854]: I heard: [hello world 1]
[ INFO] [1586183869.273767103]: I heard: [hello world 2]
Yes, we did it! See you next time.
Extra: Video of the post
Here below you have a “sights and sounds” version of this post, just in case you prefer it that way. Enjoy!
Feedback
Did you like this post? Do you have any questions about the explanations? Whatever the case, please leave a comment on the comments section below, so we can interact and learn from each other.
If you want to learn about other ROS or ROS2 topics, please let us know in the comments area and we will do a video or post about it.
About:
In this video we’re going to show you how to send successive goals to the Navigation Stack using Waypoints.
RELATED ROS RESOURCES&LINKS:
ROS Development Studio (ROSDS) —▸ http://bit.ly/2QTy0wr
Robot Ignite Academy –▸ http://bit.ly/2QRTZE9
—
Feedback
—
Did you like this video? Do you have questions about what is explained? Whatever the case, please leave a comment on 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 on the comments area and we will do a video about it 🙂
—
Feedback
—
Did you like this video? Do you have questions about what is explained? Whatever the case, please leave a comment on 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 on the comments area and we will do a video about it 🙂
—
Feedback
—
Did you like this video? Do you have questions about what is explained? Whatever the case, please leave a comment on 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 on the comments area and we will do a video about it 🙂
This 14th of December of 2018, ROS2 Crystal Clemmyswas released officially. It’s a very important step due to the fact that it adds enough new functionality versus its prior older brother ROS2 Bouncy Bolson to be a real option to consider to make the jump from ROS to ROS2.
So here are some of my findings and facts about ROS2 versus ROS1. Just bear in mind that crystal is evolving really fast and it wouldn’t be a surprise that more improvements appear in the near future.
Use differences between ROS2 and ROS1
We will talk about ROS2 Crystal being the latest release. We will talk about the main basic differences in the way you use ROS2 versus ROS1:
Launch files using .xml format are gone. Forget what you knew about launching nodes and other launch files using the package structure. The system now relies upon python files launching binaries or python scripts. You will use the python module launch and package launch_ros. Here you have a basic example of how the binary tc_node from a package called the_construct_pkg would be launched now.
from launch import LaunchDescription import launch_ros.actions
Catkin_make is gone: Catkin has been replaced by colcon. This new building system is in essence a universal building system for the ROS ecosystem. This means that it can compile not only ROS2 packages, but packages from ROS1 or even packages without any manifest. This is really useful to compile non-ROS-Packages like Gazebo, which traditionally was something to compile separated from ROS. Its grammar is very similar to the good old catkin_make:
ROS Packages support exists but with some manual labor: ROS2 supports perfectly Cpp projects with the rclcpp. Python projects is another story. Although you can create rospackages that use python code with rclpy ros library in their scripts and rosrun those programs, the packages need to be configured manually for python use adding the ament_python export in the package xml. But that is defining the build type of the package and therefore, for the moment you won’t have easy mixing cpp and python code in the same package.
As for the main great features that ROS2 gives among others is the great capability of managing easily multiple robots and preliminary real-time programming support, features that are very welcomed by the community no doubt about that.
Can I use ROS2 completely forgetting ROS1? ROS1Bridge
The quick answer is: Yes but with drawbacks. So ROS2 Crystal has worked with the new GAzebo with ROS2 support, so you have access to creating your own simulations using only ROS2. You have access to the main ROS packages like tf, navigation, image_transport, rqt, and the big RVIZ. So it would seem that 90% of the current ROS users would be satisfied.
But the reality is that a huge amount of packages don’t come out of the bix working for ROS2 or are installed through debians. You will be obliged to compile them and probably make the modifications for ROS2 system. But there is an option which is using the ros1_bridge package.
This package allows you to run for example Gazebo simulations using ROS1, but communicate and control with ROS2 packages. It connects topics and services with the same message type and name. Crystal supports ros1bridge by default but only basic message types. If you want to use nonbasic message types, such as gazebo_ros_msgs you will have to compile ros1-bridge from source and in the same workspace have all the necessary messages you want to use with ROS2. Probably in the near future, it will support more message by default.
ROS2 Recommended?
Absolutely! It feels cleaner and faster for sure. And unless you need to use specific packages that don’t exist yet in ROS2 and are difficult to port, I would use it no doubt. Its obvious that ROS2 will become the main ROS on the long run and now its functional enough to do practically anything that you did with the good old ROS1. And you have the added bonus of all the improvements added to the mix. So my recommendation is to go there and have a try with ROS2.
If you are a previous ROS1 Developer:
You are bound to stumble upon some functionality that you were used to that now has no support or is buggy. But no doubt this is the future and is just like when rosbuild was removed back in the old days of ROS1. Just deal with the changes and you will see its worth the initial headaches. Here you have a migration ROS1 to ROS2 guide.
But I would highly recommend to for the next years until ROS2 gets total support, combine ROS1 and ROS2. Especially in simulations which are not yet ported to ROS2 because they tend to be the less updated and more critical to change. Just until more and more simulations get ROS2 versions.
If you are new in the ROS world:
You won’t notice most of the missing or buggy features that another person used to ROS1 would notice. You will undoubtedly have a headstart and ROS2 is not much more difficult to learn than ROS1, as far as the basics are concerned.