ROS1 (Robot Operating System 1) is an open-source framework designed to simplify the development of complex robotic systems. It provides a rich set of tools, libraries, and conventions that enable developers to create robust and scalable robot applications. This tutorial will guide you through the fundamental concepts of ROS1, its core components, and how to start developing with it.
ROS1 Architecture
ROS1 is built on a graph-based architecture where processing takes place in nodes that may receive, post, and multiplex sensor data, control, state, planning, actuator, and other messages. The key concepts in ROS1 are:
- Nodes: Executable programs that perform computation.
- Topics: Named buses over which nodes exchange messages.
- Messages: ROS data type used when subscribing or publishing to a topic.
- Services: Request/reply interactions between nodes.
- Master: Name service for ROS (i.e., helps nodes find each other).
- Parameter Server: A shared, multi-variate dictionary that nodes can use to store and retrieve parameters at runtime.
Setting Up Your Development Environment
Before we start developing with ROS1, we need to set up our environment. We’ll use ROS Noetic, which is designed for Ubuntu 20.04.
- Install ROS Noetic by following the official installation guide.
- Set up your catkin workspace:
1
2
3
4
| mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
catkin_make
source devel/setup.bash
|
Add the source command to your ~/.bashrc
file to automatically set up your environment for ROS development:
1
2
| echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc
|
Creating a ROS Package
In ROS, software is organized in packages. Let’s create a simple package:
1
2
| cd ~/catkin_ws/src
catkin_create_pkg my_first_package rospy std_msgs
|
This creates a new package named “my_first_package” with dependencies on rospy (Python client library for ROS) and std_msgs (standard message types).
Writing a Simple Publisher Node
ROS supports writing packages in either Python or C++.
Python Example
Create a new file named talker.py
in the scripts
directory of your package:
1
2
3
4
| mkdir ~/catkin_ws/src/my_first_package/scripts
cd ~/catkin_ws/src/my_first_package/scripts
touch talker.py
chmod +x talker.py
|
Add the following code to talker.py
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #!/usr/bin/env python3
import rospy
from std_msgs.msg import String
def talker():
pub = rospy.Publisher('chatter', String, queue_size=10)
rospy.init_node('talker', anonymous=True)
rate = rospy.Rate(1) # 1 Hz
while not rospy.is_shutdown():
hello_str = "Hello, ROS! %s" % rospy.get_time()
rospy.loginfo(hello_str)
pub.publish(hello_str)
rate.sleep()
if __name__ == '__main__':
try:
talker()
except rospy.ROSInterruptException:
pass
|
Explanation of the Code
This line specifies the interpreter to use for the script. It ensures the script is run with Python 3.
1
2
| import rospy
from std_msgs.msg import String
|
rospy
: The Python library for ROS, enabling communication between nodes.std_msgs.msg.String
: The ROS message type used for publishing and subscribing to strings.
1
2
| def talker():
pub = rospy.Publisher('chatter', String, queue_size=10)
|
rospy.Publisher
: Creates a ROS publisher that publishes messages to the chatter
topic.queue_size=10
: Limits the number of messages queued for delivery to subscribers.
1
| rospy.init_node('talker', anonymous=True)
|
- Initializes the ROS node named
talker
. anonymous=True
: Appends a random identifier to the node name to avoid name conflicts.
1
| rate = rospy.Rate(1) # 1 Hz
|
- Creates a
Rate
object to enforce a loop frequency of 1 Hz.
1
2
3
4
5
| while not rospy.is_shutdown():
hello_str = "Hello, ROS! %s" % rospy.get_time()
rospy.loginfo(hello_str)
pub.publish(hello_str)
rate.sleep()
|
- Loops until the node shuts down.
rospy.get_time()
: Retrieves the current ROS time.rospy.loginfo()
: Logs a message to the console and ROS log files.pub.publish(hello_str)
: Publishes the message to the chatter
topic.rate.sleep()
: Sleeps to maintain the desired loop frequency.
1
2
3
4
5
| if __name__ == '__main__':
try:
talker()
except rospy.ROSInterruptException:
pass
|
- Ensures the
talker
function runs when the script is executed. rospy.ROSInterruptException
: Catches exceptions triggered when the node is interrupted.
C++ Example
Create a new file named talker.cpp
in the src
directory of your package:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| #include <ros/ros.h>
#include <std_msgs/String.h>
#include <sstream>
int main(int argc, char **argv) {
ros::init(argc, argv, "talker");
ros::NodeHandle nh;
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter", 10);
ros::Rate loop_rate(1);
while (ros::ok()) {
std_msgs::String msg;
std::stringstream ss;
ss << "Hello, ROS! " << ros::Time::now();
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
}
return 0;
}
|
Explanation of the Code
1
2
3
| #include <ros/ros.h>
#include <std_msgs/String.h>
#include <sstream>
|
- Includes the necessary ROS and message type headers.
<sstream>
: Provides functionality to construct strings.
1
2
| ros::init(argc, argv, "talker");
ros::NodeHandle nh;
|
ros::init
: Initializes the ROS node with the name talker
.ros::NodeHandle
: Provides communication with the ROS system.
1
| ros::Publisher pub = nh.advertise<std_msgs::String>("chatter", 10);
|
- Advertises a topic named
chatter
with message type std_msgs::String
and a queue size of 10.
1
| ros::Rate loop_rate(1);
|
- Sets the loop frequency to 1 Hz.
1
2
3
4
5
6
7
8
9
10
11
| while (ros::ok()) {
std_msgs::String msg;
std::stringstream ss;
ss << "Hello, ROS! " << ros::Time::now();
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
}
|
- Loops until the ROS system is shut down.
ros::Time::now()
: Retrieves the current ROS time.ROS_INFO
: Logs a message to the console and ROS log files.pub.publish(msg)
: Publishes the message to the chatter
topic.ros::spinOnce()
: Processes incoming messages.loop_rate.sleep()
: Sleeps to maintain the desired loop frequency.
- Exits the program when the loop ends.