1.创建工作空间和功能包
1.1 工作空间
工作空间是一个存在工程开发相关文件的文件夹,典型的工作空间中一般包括以下目录:
- src: 代码空间,存储所有ROS功能包的源码
- build: 编译空间,存储工作空间编译过程中产生的缓存信息和中间文件
- devel: 开发空间,放置放置编译生成的可执行文件
- install: 安装空间,非必须,编译成功后,可以使用make install命令将可执行文件安装到该空间中,运行该空间中的环境变量脚本。
1.2 创建工作空间
- 创建工作空间目录并初始化
$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/src
$ catkin_init_workspace
- 编译工作空间
$ cd ~/catkin_ws/
$ catkin_make
- 编译过程中,在工作空间的根目录里会自动产生build和devel两个文件夹及其中的文件。编译完成后,devel文件中已经产生了一个setup.*sh形式的环境变量设置脚本。使用source使其生效:
$ source devel/setup.bash
- 可以使用echo命令检查环境变量是否生效
$ echo $ROS_PACKAGE_PATH
注,在终端中使用source命令设置的环境变量只能在当前终端中生效,如果希望环境变量在所有终端中有效,需要在终端配置文件中加入环境变量的设置:
$ echo "source/WORLSPACE/devel/setup.bash">>~/.bashrc
WORKSPACE为自己的工作空间路径
1.3创建功能包
ROS创建功能呢瑰宝的命令为catkin_create_pkg,使用方法如下:
$ catkin_create_pkg <package_name> [depend1] [depend2] [depend3]
depend1、depend2、depend3表示所以来的其他功能包的名称。
1.4 覆盖工作空间
ROS允许多个工作空间共存,每个工作空间的创建、编译和运行方法均相同。存在一种情况,即不同工作空间中存在相同命名的功能包 ,这时涉及到ROS中工作空间的覆盖问题。ROS中所有工作空间的路径会在ROS_PACKAGE_PATH环境变量中记录,当设置多个工作空间的环境变量后,新设置的路径在环境变量中会自动放置在最前端,运行时,ROS会优先查找最前端的工作空间中是否存在指定的功能包,若不存在就会按顺序向后查找。
2.话题中的Publisher和Subscriber
以"Hello World"为例,创建Publisher和Subscriber,具体步骤可以参考Writing a Simple Publisher and Subscriber (C++),该链接下的创建步骤比较老旧,根据其中内容进行适当调整
首先根据步骤1建立工作空间,然后再工作空间catkin_ws/src目录下创建包,本例创建包的命令如下:
catkin_create_pkg wk_tutorials std_msgs roscpp rospy
此时catkin_ws/src目录下多了wk_tutorials的文件夹,打开catkin_ws/src/wk_tutorials/src,在该文件夹下创建Publisher和Subscriber
2.1创建Publisher
流程如下:
- 初始化ROS节点
- 向ROS Master注册节点信息,包括发布的话题名和话题中的消息类型
- 按照一定的频率循环发送消息
在catkin_ws/src/wk_tutorials/src路径下创建talker.cpp,写入内容如下:
//头文件
#include <sstream>
#include "ros/ros.h"
#include "std_msgs/String.h"
int main(int argc, argv, char **argv)
{
//初始化ROS节点,第三个参数为ROS节点的名称
ros::init(argc,arcgv,"tarker");
//创建节点句柄,方便对节点资源的使用和管理;
ros::NodeHandle n;
//在ROS Master注册Publisher,告诉系统该节点会发布以chatter为话题的sting信息
ros::Publisher chatter_pub=n.advertise<std_msgs::String>("chatter",1000);
//设置循环频率。大小为10Hz
ros::Rate loop_rate(10);
int count=0;
while (ros::ok())
{
std_msgs::String msg;
srd::stringstream ss;
ss<<"Hello World"<<count;
msg.data=ss.str();
//ROS_INFO类似于print
ROS_INFO("%s",msg.data.c_str());
//发布封装完毕的消息msg
chatter_pub_publish(msg);
//处理节点订阅话题的所有回调函数
ros::spinOnce();
++count;
}
return 0;
}
2.2创建Subscriber
流程如下:
- 初始化ROS节点
- 订阅需要的话题
- 循环等待话题消息,接受到消息后进入回调函数
- 在回调函数中完成消息处理
在catkin_ws/src/wk_tutorials/src路径下创建listener.cpp,写入内容如下:
//头文件
#include <sstream>
#include "ros/ros.h"
//接受到订阅的消息,进入消息回调函数
void chatterCallback(const std_msgs::String::Const Ptr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, argv, char **argv)
{
//初始化ROS节点,第三个参数为ROS节点的名称
ros::init(argc,arcgv,"tarker");
//创建节点句柄,方便对节点资源的使用和管理;
ros::NodeHandle n;
//创建一个Subscriber,订阅名为chatter的话题,注册回调函数chatterCallback
ros::Subscriber chatter_pub=n.subscribe("chatter",1000,chatterCallback);
//循环等待回调函数
ros:;spin();
return 0;
}
2.3编译功能包
打开catkin_ws/src/wk_tutorials/src路径下的CMakeLists.txt文件,修改文件build对应部分的内容,如图:
//设置头文件的相对路径,全局路径默认是功能包所在目录
include_directories(include ${catkin_INCLUDE_DIRS})
//设置需要编译的得吗和生成的可执行文件,第一个参数为文件名称,后边的参数为源码文件
//需要多个代码文件,可以在后面一次列出,中间使用空格进行分隔
add_executable(talker src/talker.cpp)
//设置链接库,第一个参数为文件名称,后边的参数为链接库
target_link_libraries(talker ${catkin_LIBRARIES})
//设置依赖,一些应用中需要定义与语言无关的消息类型,这些消息类型在编译过程中产生相应的语言的代码
//若编译的可执行文件依赖这些动态生成的代码,需要添加如下配置
add_dependencies(talker beginner_tutorials_generate_messages_cpp)
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)
2.4 运行
- 首先启动roscore
- 然后启动Publisher,即talker文件,运行结果如下:
cd catkin_ws
rosrun wk_tutorials talker
- 新打开终端,运行Subscriber,即listener,运行结果如下:
cd catkin_ws
rosrun wk_tutorials listener
3.自定义话题消息
3.1创建消息文件夹并自定义消息
ROS中提供了一系列不同消息类型的功能包,如std_msgs、geometry_msgs等,很多情况下需,要针对自己的机器人应用设计特定的消息类型,以下列msg文件为例,定义了描述一个个人信息的消息类型
string name
uint8 sex
uint8 age
uint8 unknown=0
uint8 male=1
unit8 female=2
承接第2部分,在catkin_ws/src/wk_tutorials路径下新建msg文件夹,在msg文件夹下新建Person.msg文件写入上述内容。
3.2 修改package.xml
添加如下依赖:
<build_depend>message_generation</build_depend>
<build_export_depend>message_generation</build_export_depend>
<exec_depend>message_runtime</exec_depend>
3.3 修改CMakeLists.txt
- 在find_package添加message_generation
find_package(catkin REQUIRED COMPONENTS
message_generation
roscpp)
- 在add_message_files中添加Person.msg
add_message_files(FILES
Person.msg)
- 取消generate_messages部分的注释
generate_messages(DEPENDENCIES
std_msgs)
- 在catkin_package内添加依赖message_runtime
catkin_package(
CATKIN_DEPENDS
roscpp
rospy
std_msgs
message_runtime)
上述配置完成后,回到根目录即catkin_ws下使用catkin_make命令编译文件,然后输入如下命令可查看Person消息类型
rosmsg show Person
4.服务中的Server和Client
服务(Service)是节点之间同步通信的一种方式,允许客户端(Client)节点发布请求,由服务端(Server)节点处理后反馈应答。以一个简单的加法为例,研究ROS中的服务应用。
4.1 自定义服务数据
ROS服务数据通过通过srv文件进行语言无关的接口定义。文件内包含请求与应答两个数据域,请求与应答的描述之间需要使用’—'进行分割。在catkin_ws/src/wk_tutorials下建立srv文件夹,在srv文件夹下建立AddTwoInts.srv文件:
int64 a
int 64 b
---
int64 sum
打开package.xml文件,添加一下依赖配置(3.2中已经添加)
<build_depend>message_generation</build_depend>
<build_export_depend>message_generation</build_export_depend>
<exec_depend>message_runtime</exec_depend>
打开CMakeLists.txt问价,添加如下(3.3中已经添加了message_generation):
find_package(catkin REQUIRED COMPONENTS
message_generation
roscpp)
add_service_files(
FILES
AddTwoInts.srv
)
其中,message_generation包不仅可以根据话题消息产生相应代码,也可以根据服务消息产生相应代码
4.2 创建server
在catkin_ws/src/wk_tutorials/src下建立server.cpp文件,Server实现流程和代码如下:
- 初始化ROS节点
- 创建Server实例
- 循环等待服务请求,进入回调函数
- 在回调函数中完成服务功能的处理并反馈应答数据
//头文件
#include "ros/ros.h"
#include "wk_tutorials/AddTwoInts.h"
//回调函数。输入参数req,输入参数res
bool add(wk_tutorials::AddTwoInts::Request &req,
wk_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节点初始化
ros::init(argc, argv, "server");
//创建节点句柄
ros::NodeHandle n;
//创建server,注册回调函数add()
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
//循环等待回调函数
ROS_INFO("Ready to add two ints.");
ros::spin();
return 0;
}
4.3 创建Client
创建client.cpp文件,流程和代码如下:
- 初始化ROS节点
- 创建Server实例
- 循环等待服务请求,进入回调函数
- 在回调函数中完成服务功能的处理并反馈应答数据
#include "ros/ros.h"
#include "wk_tutorials/AddTwoInts.h"
#include <cstdlib>
int main(int argc, char **argv)
{
//ROS节点初始化
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;
//创建client,请求add_two_int的service消息
ros::ServiceClient client = n.serviceClient<wk_tutorials::AddTwoInts>("add_two_ints");
//创建wk_tutorials类型的sevice消息
wk_tutorials::AddTwoInts srv;
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
//发布service请求,等待加法运算答疑结果
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;
}
4.4 编译功能包
类似于2.3,编译功能包
add_executable(server src/server.cpp)
target_link_libraries(server ${catkin_LIBRARIES})
add_dependencies(server wk_tutorials_gencpp)
add_executable(client src/client.cpp)
target_link_libraries(client ${catkin_LIBRARIES})
add_dependencies(client wk_tutorials_gencpp)
然后再catkin_ws目录下使用catkin_make编译功能包
4.5运行
- 运行roscore
- 运行server节点
rosrun wk_tutorials server
- 运行client节点
rosrun wk_tutorials client 3 5
此时服务端显示为:
参考:
[1] https://blog.csdn.net/weixin_42075471/article/details/85988995