ROS简介
参考教程:cn - ROS Wiki

ros下编译一个hello world程序
创建工作空间,在终端上创建一个工程文件夹名为hello_ws,并在src目录下创建一个ros包
1 2 3 4 5
| mkdir -p hello_ws/src cd hello_ws catkin_make cd /src catkin_create_pkg hello_world roscpp std_msgs
|
roscpp和std_msgs是hello_world的依赖项。
进入hello_world包的目录,并创建一个名为
hello.cpp`的C++源文件:
1 2
| cd ~/hello_ws/src/hello_world/src vi hello.cpp
|
编辑hello.cpp
文件,并添加以下内容:
1 2 3 4 5 6 7 8 9
| #include "ros/ros.h" #include "std_msgs/String.h" #include <stdio.h>
int main(int argc, char **argv) { printf("hello world\n"); return 0; }
|
现在,将这包中的源文件添加到hello_world包中的CMakeLists.txt
中:
1 2 3
| add_executable(hello_node src/hello.cpp)
target_link_libraries(hello_node ${catkin_LIBRARIES})
|
最后,编译ROS包:
1 2 3
| cd ~/hello_ws catkin_make source devel/setup.bash
|
在另一个终端中启动ROS核心:
在之前的终端中运行发布者节点:
1
| rosrun hello_world hello_node
|
现在,就可以在ros环境下打印出hello world。
以下是一个工作空间的正常目录:

话题和服务的区别

服务
在ROS中,服务(Service)用于实现节点之间的请求-响应通信模式。服务允许一个节点向另一个节点发送请求,并等待该节点的响应。服务通常用于执行一些计算密集型或耗时的任务,或者获取节点的状态信息。
在ROS中创建并使用服务(Service)
创建服务文件:
在ROS软件包中创建srv
目录,并在其中创建服务文件。服务文件使用.srv
扩展名。例如,创建一个名为MyService.srv
的服务文件。
1 2
| mkdir srv vi MyService.srv
|
定义服务消息结构:
- 打开您服务文件
MyService.srv
,并在其中定义服务消息结构。服务消息结构由请求和响应组成。请求和响应。这两部分用一条---
线隔开,例如:
1 2 3 4
| int64 a int64 b --- int64 sum
|
编辑**CMakeLists.txt**
文件:
- 在您的ROS软件包目录中,打开
CMakeLists.txt
文件,并确保它包含了正确的服务生成器指令。将以下行添加到CMakeLists.txt
文件中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| find_package(catkin REQUIRED COMPONENTS rospy std_msgs message_generation )
add_service_files( FILES MyService.srv )
generate_messages( DEPENDENCIES std_msgs )
|
构建您的软件包:
- 返回到您的工作空间目录,并运行
catkin_make
来构建您的软件包。这将编译并生成您的服务代码。
1 2
| cd ~/catkin_ws catkin_make
|
使用服务:
- 在您的ROS节点中,您可以使用
rospy
或roscpp
等库来编写服务的客户端和服务器端。首先,确保您的节点依赖于生成的消息包。然后,在您的节点代码中导入所需的服务消息,并编写客户端和服务器端的逻辑。
服务器端:
创建一个ROS节点来处理服务请求,
在另一个节点中,创建一个服务客户端来发送请求,并处理响应。
ros下创建服务端和客户端
以下为写两个数相加的客服端和服务端代码
创建一个软件包
1
| catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
|
同理在软件包下面创建srv文件夹,里面写srv的文件,命名AddTwoInts.srv
1 2 3 4
| int64 a int64 b --- int64 sum
|
在src文件夹下编写服务器端文件
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 26
|
#include "ros/ros.h" #include "std_msgs/String.h" #include "beginner_tutorials/AddTwoInts.h"
bool add(beginner_tutorials::AddTwoInts::Request &req, beginner_tutorials::AddTwoInts::Response &res) { res.sum = req.a + req.b; ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b); ROS_INFO("sending back response: [%ld]", (long int)res.sum); return true; }
int main(int argc, char **argv) { ros::init(argc, argv, "add_two_ints_server"); ros::NodeHandle n;
ros::ServiceServer service = n.advertiseService("add_two_ints", add); ROS_INFO("Ready to add two ints."); ros::spin();
return 0; }
|
客服端文件
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 26 27 28 29 30 31
|
#include "ros/ros.h" #include "beginner_tutorials/AddTwoInts.h"
int main(int argc, char **argv) { ros::init(argc, argv, "add_two_ints_client"); if (argc != 3) { ROS_INFO("usage: add_two_ints_client X Y"); return 1; }
ros::NodeHandle n; ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints"); beginner_tutorials::AddTwoInts srv; srv.request.a = atoll(argv[1]); srv.request.b = atoll(argv[2]); if (client.call(srv)) { ROS_INFO("Sum: %ld", (long int)srv.response.sum); } else { ROS_ERROR("Failed to call service add_two_ints"); return 1; }
return 0; }
|
之后修改CMakeLists.txt文件
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 26 27 28 29 30 31 32 33 34 35
| cmake_minimum_required(VERSION 2.8.3) project(beginner_tutorials)
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation )
add_service_files( FILES AddTwoInts.srv )
generate_messages( DEPENDENCIES std_msgs )
catkin_package( CATKIN_DEPENDS roscpp rospy std_msgs message_runtime )
include_directories( ${catkin_INCLUDE_DIRS} )
add_executable(add_two_ints_server src/server.cpp) target_link_libraries(add_two_ints_server ${catkin_LIBRARIES}) add_dependencies(add_two_ints_server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_executable(add_two_ints_client src/client.cpp) target_link_libraries(add_two_ints_client ${catkin_LIBRARIES}) add_dependencies(add_two_ints_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
|
话题
ros下如何创建消息
在刚在hello_world的软件包下,创建一个名为msg
的文件夹,用于存放自定义消息文件。
创建一个名为MyMessage.msg
的文件,并在其中定义消息结构。例如:
在软件包目录中,打开CMakeLists.txt
文件将以下行添加到CMakeLists.txt
文件中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| find_package(catkin REQUIRED COMPONENTS roscpp std_msgs message_generation )
add_message_files( FILES MyMessage.msg )
generate_messages( DEPENDENCIES std_msgs )
|
之后退出编译后,生成的.h文件会出现在devel/include目录下。
ROS中订阅节点和发布节点
ros节点间进行通信的主要机制是发送和接收消息,不同的消息分门别类的组织起来,组织结构被成为话题,节点可以将消息发布到特定的话题,也可以订阅话题从而获取接收消息。
节点是执行某些动作的进程,节点发布消息,并由其他节点接收消息,这些发布的消息,在ros中称为话题。
以下是如何使用C++编写一个ROS程序,其中包括一个发布者节点和一个订阅者节点,用于发送和接收”Hello, World!”消息。
首先,确保您已经创建了一个ROS工作空间(如果没有,请先创建)。
创建一个名为catkin_ws
的工程文件夹以及一个hello_world
ROS包:
1 2 3
| mkdir -p catkin_ws/src cd ~/catkin_ws/src catkin_create_pkg hello_world roscpp std_msgs
|
进入hello_world
包下的src目录,并创建一个名为hello_publisher.cpp
的C++源文件:
1 2
| cd ~/catkin_ws/src/hello_world/src vi hello_publisher.cpp
|
编辑hello_publisher.cpp
文件,并添加以下内容:
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 26 27 28 29 30 31 32 33 34 35
| #include "ros/ros.h" #include "std_msgs/String.h"
int main(int argc, char **argv) { ros::init(argc, argv, "hello_publisher");
ros::NodeHandle nh;
ros::Publisher pub = nh.advertise<std_msgs::String>("hello_topic", 10);
ros::Rate rate(1);
while (ros::ok()) { std_msgs::String msg; msg.data = "Hello, World!";
pub.publish(msg);
ROS_INFO("Publishing: %s", msg.data.c_str());
rate.sleep(); }
return 0; }
|
创建一个名为hello_subscriber.cpp
的C++源文件:
编辑hello_subscriber.cpp
文件,并添加以下内容:
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"
void chatterCallback(const std_msgs::String::ConstPtr& msg) { ROS_INFO("Received: [%s]", msg->data.c_str()); }
int main(int argc, char **argv) { ros::init(argc, argv, "hello_subscriber");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe("hello_topic", 10, chatterCallback);
ros::spin();
return 0; }
|
将这两个源文件添加包中的CMakeLists.txt
中:
1 2 3 4 5
| add_executable(hello_publisher src/hello_publisher.cpp) add_executable(hello_subscriber src/hello_subscriber.cpp)
target_link_libraries(hello_publisher ${catkin_LIBRARIES}) target_link_libraries(hello_subscriber ${catkin_LIBRARIES})
|
最后,编译ROS包:
1 2
| cd ~/catkin_ws catkin_make
|
在一个终端中启动ROS核心:
在另一个终端中运行发布者节点:
1 2
| source devel/setup.bash rosrun hello_world_cpp hello_publisher
|
在第三个终端中运行订阅者节点:
1 2
| source devel/setup.bash rosrun hello_world_cpp hello_subscriber
|
现在,您应该能够在订阅者节点的终端中看到打印出的消息:”Received: Hello, World!”。这样就完成了一个简单的ROS节点的创建和通信。

ROS下多个节点执行
假如我的文件构造结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| workspace/ |-- src/ | |-- package1/ | | |-- CMakeLists.txt | | |-- package.xml | | |-- src/ | | |-- node1.cpp | | |-- node2.cpp | |-- package2/ | |-- CMakeLists.txt | |-- package.xml | |-- src/ | |-- node3.cpp | |-- node4.cpp |-- launch/ | |-- all_nodes.launch
|
将多个包的多个节点放入到all_nodes.launch文件中。
1 2 3 4 5 6 7 8 9
| <launch> <!-- 启动 package1 中的节点 --> <node pkg="package1" type="node1" name="node1" output="screen"/> <node pkg="package1" type="node2" name="node2" output="screen"/>
<!-- 启动 package2 中的节点 --> <node pkg="package2" type="node3" name="node3" output="screen"/> <node pkg="package2" type="node4" name="node4" output="screen"/> </launch>
|
之后在工作空间的目录下进行编译后执行。
1 2 3
| source devel/setup.bash catkin_make roslaunch launch/my_launch_file.launch
|