ROS
通信编程-流程备忘
ROS
项目开发流程
备忘 ROS 的基础开发流程。时间:2020/08/17
一、工作空间和功能包
-
创建工作空间流程
- 新建文件夹
$ mkdir ros_ws && cd ros_ws
- 新建
ros_ws/src
空间$ mkdir src && cd src
- 初始化
ROS
工作空间,注意在ros_ws/src
下$$ catkin_init_workspace
- 编译工作空间,在根目录
/ros_ws
下执行$ catkin_make
,自动搜索src
下的所有功能包并编译。自动产生build
和devel
文件夹,build存放二进制文件,devel
存放可执行文件位于devel/lib
中。 - 编译成功后,进行环境变量添加(临时)
$ source devel/setup.zsh
,我用的zsh
,bash
就替换为bash
。查看当前ROS
包环境变量,$ echo $ROS_PACKAGE_PATH
,不用每次都source
的方式是直接编辑~/.zshrc
文件,在最后加入source /home/username/catkin_ws/devel/setup.zsh
注意这里的路径用pwd
命令查看一下,要加入具体的username
。 - 产生
install
文件夹,$ catkin_make install
,内容几乎与devel
相同。
- 新建文件夹
-
创建功能包流程
- 在
src
下创建功能包,命令格式:$ catkin_create_pkg <pkg_name> [depend1][depend2][depend3]
- 返回工作空间下,进行编译
$ catkin_make
,注意功能包不能重名。 - 功能包与文件夹的区别,功能包必须含有
CMakelist.txt
和package.xml
。
- 在
二、Topic
话题的发布和订阅实现
-
ROS Topic
话题通信,发布者实现流程-
初始化节点
ros::
init
(argc, argv, " [node_name
] "); -
创建节句柄
ros::
NodeHandle
node; -
Master
注册节点信息,创建publisher,发布话题topic,定义话题的消息类型ros::
Publisher
pub = node.advertise<[msg_type
]>(" [topic_name
] ", 1000); -
设置发布频率
ros::
Rate
loop_rate (单位Hz
); -
进入循环发布,以ROS状态为循环条件:
while (ros::ok())
(1) 定义话题中信息数据,并赋值 ros::msg_type
msg;
(2) 节点发布消息 pub.publish
(msg);
(3) 基于发布频率延迟发布 loop_rate.sleep
();
-
-
ROS Topic
话题通信,订阅者实现流程-
初始化节点
ros::
init
(argc, argv, " [node_name
] "); -
创建节句柄
ros::
NodeHandle
node; -
Master
注册节点信息,创建subscriber
,注册回调函数callback_func
。ros::Subscriber sub = node.subscribe([
topic_name
], 1000, callback_func); -
循环等待回调函数 ros::
spin
(); 死循环,不断查找队列数据。 -
关键是回调函数的处理,基本定义可以是
void chatterCallback(
const [msg_type_pkg]::msg_type::ConstPtr& msg
)
{
//do something
}
-
-
配置编译规则并编译
- 打开编写的
pkg
目录下的CMakelist.txt
,加入两句话,一句是声明可执行文件add_executable(<node_name> src/<node.cpp>)
另一个是链接声明target_link_libraries(node_name ${catkin_LIBRARIES})
添加在##build
后面。 - 回到根目录,编译
catkin_make
,100% 出现即为成功。
- 打开编写的
-
自定义消息类型实现
-
在功能包下面创建 msg 文件夹,创建一个xx.msg,配置编译设置
-
先改 package.xml, 加入
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend> -
再改
CMakeLists.txt
,在find_package
中加入message_generation
。在## Declare ROS messages
中加入两行语句add_message_files(FILES xxx.msg)
和
generate_messages(DEPENDENCIES std_msgs)
-
在
## catkin specific configuration ##
中打开注释加入 message_runtime 改成CATKIN_DEPENDS roscpp rospy std_msgs std_srvs message_runtime
-
回到根目录编译工作空间,自动在devel文件夹下动态产生的跟````.msg
文件同名的
.h```头文件,里面有一些类的定义。
-
-
在程序中调用 自定义消息 【发布】节点, 主要注意与
【二】
中【1】
的区别,主流程完全一样,区别在于:- 头文件包含自动生成的
.h
文件 - 注册
master
时需要使用自定义消息类型 - 循环发布中对自定义消息类型进行赋值
- 别忘了
cmakelist.txt
里面添加可执行和依赖库 - 额外的添加动态依赖
add_dependencies(node_name ${${PROJECT_NAME}_gencpp)
- 头文件包含自动生成的
-
在程序中调用 自定义消息 【订阅】节点, 主要注意与
【二】
中【2】
的区别,主流程完全一样,区别在于:- 节点名字肯定要改
- 主要是回调函数,接口改成自定义类型,然后里面也要操作自定义类型的数据。
- 额外的添加动态依赖
-
-
话题通信到此结束。
三、Service
服务的发布和订阅实现
-
在话题中是一直发一直收,在服务中是请求后才发送。服务的服务端实现流程:
-
节点初始化
-
节点句柄定义
ros::NodeHandle n;
-
matster注册一个服务节点
ros::ServiceServer service = n.advertiseService(“node_name”, callback_func);
-
定义服务端的回调函数
- 针对需求req和回复res的具体实现,是否成功服务了。
bool print(std_srvs::SetBool::Request &req, std_srvs::SetBool::Response &res){ }
-
直接进入循环,没有缓冲区设置。
-
-
客户端实现流程
-
节点初始化
-
节点句柄定义
ros::NodeHandle n;
-
matster注册一个服务节点,定义向哪一个服务发送请求,利用标准服务来个例子。
ros::ServiceClient client = n.serviceClient<std_srvs::SetBool>(“node_name”);
-
创建
service
消息,赋值服务的变量。 -
发布
service
请求,等待应答结果。 -
配置
cmakelist.txt
中可执行和链接库,编译工作空间即可。
-
-
自定义服务信息的实现流程
-
在功能包中建立
srv
子文件夹,然后在其中建立自定义的.srv
文件,格式为上下两部分,用 【---
】 分割,上面的部分是服务中的req
请求的内容,下面是res
回复的内容。 -
server
到client
中的数据流向,其实是这个意思,服务端可以理解是一个处理中心,客户端是向服务端发送数据和请求,服务端接受数据并进行处理然后返回客户端,这就是req和res定义的变量,和服务端的回调函数所进行的功能,这个过程就像网络游戏,玩家在自己电脑上按鼠标键盘,服务端处理后再发回玩家这里进行更新。
-