3 ROS1通讯编程提高
- 其他ROS1学习笔记: ROS1学习笔记
- 代码仓库:Github连接地址
- 欢迎各位互相学习交流
3.1 使用VS Code编译ROS1
3.1.1 VS Code的安装和配置
3.1.1.1 VS Code以及插件安装
- VS Code的下载安装:
VS Code的下载链接:VS Code 官网连接;历史版本下载链接:VS Code历史版本,选择合适的版本,对于Linux系统下载deb文件双击运行,或者输入命令sudo dpkg -i vscode安装包名.deb即可。
- VS Code的插件配置:
-
- VS Code编译ROS需要的插件主要包括:ROS、C/C++、Python、CMake、CMake Tools以及中文汉化插件Chinese(simplified) Language Pack for Visual Studio Code,如图所示:
-
- 其中Python可能需要设置解析器的位置即usr/bin/python2.7,重启VS Code,配置好的插件环境如图所示:
3.1.1.2 VS Code配置ROS1工作空间
- VS Code打开工作空间:
-
- 在终端进入到工作空间即
cd catkin_ws
- 在终端进入到工作空间即
-
- 输入命令code .直接在VS Code打开工作空间,如图所示
- 配置工作空间下的json文件:在VS Code打开的工作空间,会出现一个.vscode文件夹,里面存放了三个json文件,用于配置C++和Python编译ROS,下面依次介绍:
-
task.json
文件:用于配置C++和Python编译ROS任务,配置好之后,可基本上后续不需要进行改动。
// /home/ubuntu/catkin_ws/.vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"args": [
"--directory",
"/home/ubuntu/catkin_ws"
],
"problemMatcher": ["$catkin-gcc"],
"command":"catkin_make",
"group": "build",
"label": "catkin_make: build_by_vscode",
"presentation": {"reveal": "always"}
}
]
}
-
c_cpp_properties.json
:用于配置C++编译ROS时的一些C++特有的路径和代码规范,配置本次配置好,后续可能出现修改地方主要在于这些路径位置。
// /home/ubuntu/catkin_ws/.vscode/c_cpp_properties.json
{
"configurations": [
{
"browse": {
"databaseFilename": "",
"limitSymbolsToIncludedHeaders": true
},
"includePath": [
"/opt/ros/melodic/include/**",
"${workspaceFolder}/devel/include/**",//配置成自身工作空间的devel文件夹的路径;
//目的是指向自定义msg/action/srv等路径.
"/usr/include/**"
],
"name": "ROS",
"cppStandard":"c++17",
"intelliSenseMode": "gcc-x64",
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11"
}
],
"version": 4
}
-
settings.json
:用于配置Python编译ROS时的一些Python包路径和规范,配置好之后,可能出现变换的地方也在这些路径位置。
// /home/ubuntu/catkin_ws/.vscode/settings.json
{
"python.autoComplete.extraPaths": [
"/opt/ros/melodic/lib/python2.7/dist-packages",
"${workspaceFolder}/devel/lib/python2.7/dist-packages"
// 如果是高版本ROS可能是Python3,按照自定义消息一样查看中间文件位置确定
],
"cmake.configureOnOpen": true,
"cmake.sourceDirectory": "${workspaceFolder}/src"
}
3.1.1.3 VS Code运行ROS1的方法
- 编译运行:
- 方法一:使用快捷键或者在VS Code的编译器【终端】–【运行生成任务】即
Crtl+Shift+B
,点击之后选择catkin_make: build_by_vscode
(在task.json的label名字)即可编译整个工作空间。 - 方法二:在VS Code下面的终端(或者在【终端】–【新终端】即Crtl+Shift+`)cd 到catkin_ws下进行catkin_make编译
3.1.1.3 参考资料
3.1.2 使用VScode编译C++文件
-
在
catkin_ws/src/
目录下右键create catkin package
创建包,然后按照提示命名如test
,然后输入依赖项roscpp rospy std_msgs
等 -
在
catkin_ws/src/test/src
下右键新建test_c.cpp
文件,编辑test_c.cpp
文件。
// test_c.cpp文件用于测试ROS1环境
#include "ros/ros.h"
int main(int argc, char* argv[])
{
ros::init(argc,argv,"i_am_nodename");
ROS_INFO("HELLO");
return 0;
}
- 配置catkin_ws/src/test/src/CMakeLists.txt文件,然后编译运行。
-
- 在136行,取消
add_executable(${PROJECT_NAME}_node src/test_node.cpp)
的注释,修改为add_executable(i_am_nodename src/test_c.cpp)
- 在136行,取消
-
- 其次在149-151行的
target_link_libraries(${PROJECT_NAME}_node…)
修改为target_link_libraries(i_am_nodename…)
即可
- 其次在149-151行的
-
- 编译运行(直接在VScode的终端下的+号新建终端,输入
roscore
,新建终端输入rosrun test i_am_nodename
即可查看INFO的消息)。
- 编译运行(直接在VScode的终端下的+号新建终端,输入
3.1.3使用VScode编译Python文件
- 之前同编译C++是Step1-Step3,此时在
catkin_ws/src/test/
文件夹下创建Python文件夹scripts
,在scripts
文件夹里新建Python文件test_p.py
- 编写Python程序,赋予可执行权限,然后运行客户端roscore,最后
rosrun test test_p.py
即可。
#! /usr/bin/env python
import rospy
if __name__ =='__main__':
rospy.init_node("test_p")
rospy.loginfo("ok")
Tips:
在Ubuntu20.04版本的ROS需要配置CMakeLists文件,大约在161行处,类似C++一样添加Python文件的位置。同时对于高版本的Python编译ROS在Python文件头部是没有#!/usr/bin/env
python,采用命令:sudo ln -s /usr/bin/python3 /usr/bin/python
创建Python的连接。
3.2 ROS1动作编程
3.2.1 动作编程模型
在任何ROS的大型系统中,如果向节点发送请求以执行某些任务,最后收到对请求的回复,这一操作主要是通过ROS的服务编程实现,但是如果服务需要很长时间来执行,用户可能希望能够在执行期间取消请求或获得有关请求进展情况的定期反馈,基本的服务编程的应答模式是满足不了的,因此ROS提供actionlib
即动作编程完成这些操作。
-
动作编程的作用:针对服务编程的应答模式的提升,在服务端执行过程中能够取消或者获得服务器状态,动作编程一般适用于耗时的请求响应场景,用以获取连续的状态反馈。
-
动作编程的例子:如巡检机器人导航过程中,从A到B的导航过程,应该是一个动作编程,即目标是B,在A到B时,提供导航状态。
在动作编程中,客户端发送目标(goal)或者取消指令(cancel)给服务端,服务端提供状态(status)、实时反馈(feedback)和结果(result)。
官方以移动机器人为例解释goal、feedback和result,即
- 1)目标(goal):为了使用动作完成任务,引入了可以由 ActionClient 发送到 ActionServer 的目标的概念。在移动基地的情况下,目标是 PoseStamped 消息,其中包含有关机器人应移动到世界何处的信息,在官方洗碗例子中对目标的描述也是需要选择的那个洗碗工是谁。
- 2)回馈(feedback):服务器持续反馈 ActionClient目标的增量进度。对于移动机器人,这可能是机器人沿路径的当前位置。
- 3)结果(result):目标完成后,结果从 ActionServer 发送到 ActionClient,这与反馈不同,因为它只发送一次。当客户端请求的目的(goal)是提供某种信息时,这就非常有用。对于移动机器人,结果不是很重要,但它可以是机器人的最终位置(不代表就是目的地)。
3.2.2 动作编程基础示例
动作编程的基本思想和自定义的消息话题编程是一致的。
动作编程内容:洗盘子动作编程,两个节点,一个为客户端一个为服务端,客户端发送数字1(代码实现)给给服务端,作为选择哪个洗碗工,服务端接收到之后持续反馈进度给客户端,最后发送数字100(代码实现)作为结果返回。
3.2.2.1 使用VS Code创建动作编程项目
(一)使用VS Code新建功能包:
- 打开VS Code和工作空间,新建动作编程功能包并创建ROS文件夹结构。
-
- 打开VS Code和工作空间,在工作空间
catkin_ws
下输入命令code .
打开VS Code,打开后如果出现提示Select a kit for catkin_ws
,则选择第一项Scan for kit
即可。
- 打开VS Code和工作空间,在工作空间
-
- 新建动作编程功能包,右键工作空间catkin_ws的src目录,选择
create catkin package
然后在VS Code的提示下输入包名action_communication
,回车之后输入依赖项roscpp rospy std_msgs
即可
- 新建动作编程功能包,右键工作空间catkin_ws的src目录,选择
-
- 新建ROS结构的文件夹,即在功能包下新建
action
文件夹和scripts
文件夹。
- 新建ROS结构的文件夹,即在功能包下新建
3.2.2.2 配置action动作文件
(二)定义action动作文件:
- 定义动作文件。在action_communication/action下创建自定义动作文件.action,这里为DoDishes.action文件。其中的内容分为三个部分:
- 第一部分goal对应目标数据,即客户端发布数据。
- 第二部分result定义结果,服务端应答给客户端的结果。
- 第三部分是反馈部分,服务端反馈一些信息给客户端。
# catkin_ws/src/action_communication/action/DoDishes.action
# Define the goal
uint32 dishwasher_id # Specify which dishwasher we want to use
---
# Define the result
uint32 total_dishes_cleaned
---
# Define a feedback message
float32 percent_complete
(三)配置action文件
- 配置msg文件包括在package.xml和CMakeList.txt配置两部分。
- 在package.xml添加依赖项actionlib和actionlib_msgs四项,二者都是依赖项和执行依赖项。
// /catkin_ws/src/action_communication/package.xml
<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
- 在功能包的CmakeLists.txt添加编译选项,包括四部分。
-
- 添加编译依赖项功能包:
find_package(catkin REQUIRED COMPONENTS ..+ 依赖项功能包)
,大约在第10行,添加actionlib
和actionlib_msgs
两项:
- 添加编译依赖项功能包:
# /catkin_ws/src/action_communication/CMakeLists.txt
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
actionlib
actionlib_msgs
)
-
- 添加自定义action文件:
add_action_files(FILES 文件名)
,大约在第66行,如此时的文件名为DoDishes.action
:
- 添加自定义action文件:
# /catkin_ws/src/action_communication/CMakeLists.txt
add_action_files(
FILES
DoDishes.action
)
-
- 添加action的编译依赖项
std_msgs
和actionlib_msgs:generate_messages(DEPENDENCIES 依赖项名)
,大约在第71行,即表示在编译msg时候得依赖于std_msgs
:
- 添加action的编译依赖项
# /catkin_ws/src/action_communication/CMakeLists.txt
generate_messages(
DEPENDENCIES
std_msgs
actionlib_msgs
)
-
- 添加执行时的依赖:
catkin_package
的关键字CATKIN_DEPENDS后+包
,大约在106行,添加actionlib和actionlib_msgs
如:
- 添加执行时的依赖:
# /catkin_ws/src/action_communication/CMakeLists.txt
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES action_communication
CATKIN_DEPENDS roscpp rospy std_msgs actionlib actionlib_msgs
# DEPENDS system_lib
)
(四)编译action文件,查看中间文件:
- 在工作空间下编译,此时在工作空间的devel目录会多出几个文件,如C++的中间文件在
/home/ubuntu/catkin_ws/devel/include/功能包名/文件夹下
,Python的中间文件在/home/ubuntu/catkin_ws/devel/lib/python2.7/dist-packages/功能包名/msg
3.2.2.3 C++实现action基本编程
(五)C++实现action基本编程:
- 实现动作服务器
-
- 首先在工作空间的根目录的src文件夹里创建服务程序
DoDishes_server_cpp.cpp
。
- 首先在工作空间的根目录的src文件夹里创建服务程序
# catkin_ws/src/action_communication/src/DoDishes_server_cpp.cpp
#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include "action_communication/DoDishesAction.h"
typedef actionlib::SimpleActionServer<action_communication::DoDishesAction> Server;
// 收到action的goal后调用该回调函数
void callback(const action_communication::DoDishesGoalConstPtr& goal, Server* server)
{
ros::Rate r(1);
action_communication::DoDishesFeedback feedback;
ROS_INFO("收到客户端的goal,洗碗工 %d 开始洗盘子.", goal->dishwasher_id);
// 假设洗盘子的进度,并且按照1hz的频率发布进度feedback
for(int i=1; i<=10; i++)
{
feedback.percent_complete = i * 10;
server->publishFeedback(feedback);
ROS_INFO("feedback ing ...");
r.sleep();
}
// 当action完成后,向客户端返回结果
ROS_INFO("洗碗工完成了工作,发送result(100个)给客户端.");
action_communication::DoDishesResult res;
res.total_dishes_cleaned = 100;
server->setSucceeded(res);
}
int main(int argc, char** argv)
{
setlocale(LC_ALL,"");
ros::init(argc, argv, "do_dishes_server");
ros::NodeHandle nh;
// 定义一个服务器
Server server(nh, "do_dishes", boost::bind(&callback, _1, &server), false);
// 服务器开始运行
ROS_INFO("action服务端启动,等待指令");
server.start();
ros::spin();
return 0;
}
-
- 创建客户端文件
DoDishes_client_cpp.cpp
- 创建客户端文件
# catkin_ws/src/l_t/src/DoDishes_client_cpp.cpp
#include <actionlib/client/simple_action_client.h>
#include "action_communication/DoDishesAction.h"
typedef actionlib::SimpleActionClient<action_communication::DoDishesAction> Client;
// 当action完成后会调用该回调函数一次
void doneCb(const actionlib::SimpleClientGoalState& state,
const action_communication::DoDishesResultConstPtr& result){
ROS_INFO("result:洗碗工把盘子已经洗完啦!洗了%d个",result->total_dishes_cleaned);
ros::shutdown();
}
// 当action激活后会调用该回调函数一次
void activeCb(){
ROS_INFO("服务器已经打开,可以执行任务");
}
// 收到feedback后调用该回调函数
void feedbackCb(const action_communication::DoDishesFeedbackConstPtr& feedback)
{
ROS_INFO("feedback:洗盘子的进度:%f", feedback->percent_complete);
}
int main(int argc, char** argv)
{
setlocale(LC_ALL,"");
ros::init(argc, argv, "do_dishes_client");
// 定义一个客户端
Client client("do_dishes", true);
// 等待服务器端
client.waitForServer();
ROS_INFO("goal:选择员工1洗盘子:");
// 创建一个action的goal
action_communication::DoDishesGoal goal;
goal.dishwasher_id = 1;
// 发送action的goal给服务器端,并且设置回调函数
client.sendGoal(goal, &doneCb, &activeCb, &feedbackCb);
ros::spin();
return 0;
}
-
- 配置动作的服务端和客户端代码的编译选项。在功能包的目录下,打开其
CMakeLists.txt
文件。
- 配置动作的服务端和客户端代码的编译选项。在功能包的目录下,打开其
# catkin_ws/src/action_communication/CMakeLists.txt
add_executable(dodisher_server_cpp src/DoDishes_server_cpp.cpp)
add_executable(dodisher_client_cpp src/DoDishes_client_cpp.cpp)
add_dependencies(dodisher_server_cpp ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(dodisher_client_cpp ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(dodisher_server_cpp ${catkin_LIBRARIES})
target_link_libraries(dodisher_client_cpp ${catkin_LIBRARIES})
-
- 在工作空间的根目录下进行编译操作。
-
- 运行服务编程。首先打开一个终端输入roscore,打开ROS,再新建一个终端输入
rosrun action_communication DoDishes_server
运行服务程序。最后新建终端输入rosrun action_communication DoDishes_client
即运行客户端并执行动作操作。
- 运行服务编程。首先打开一个终端输入roscore,打开ROS,再新建一个终端输入
3.2.2.4 Python实现action基本编程\
(六)Python实现action基本编程:
- 在scripts文件夹创建Python服务端文件
-
DoDishes_server_py.py
,代码见注释
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# catkin_ws/src/action_communication/scripts/DoDishes_server_py.py
import rospy
import actionlib
from action_communication.msg import *
class myActionServer:
def __init__(self):
# 1.创建服务器,执行服务器回调函数
self.server = actionlib.SimpleActionServer("dodishes", DoDishesAction, self.serverCb, False)
self.server.start()
rospy.loginfo("服务端启动")
def serverCb(self,goal):
id = goal.dishwasher_id
rospy.loginfo("收到来自客户端的goal,洗碗工 %d 开始工作",id)
rate = rospy.Rate(1)
for i in range(1, 10):
rospy.loginfo("feedback ing...")
feedBack_obj = DoDishesFeedback()
feedBack_obj.percent_complete = i
self.server.publish_feedback(feedBack_obj)
rate.sleep()
res = DoDishesResult()
res.total_dishes_cleaned = 100
rospy.loginfo("洗碗工完成了工作,发送result(100个)给客户端.")
self.server.set_succeeded(res)
if __name__ == "__main__":
rospy.init_node("action_server_py")
server = myActionServer()
rospy.spin()
-
- 编写客户端
DoDishes_client_py.py
文件
- 编写客户端
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# catkin_ws/src/action_communication/scripts/DoDishes_client_py.py
import rospy
import actionlib
from action_communication.msg import *
def result_cb(state,result):
if state == actionlib.GoalStatus.SUCCEEDED:
rospy.loginfo("result:洗碗工把盘子已经洗完啦!洗了%d个",result.total_dishes_cleaned)
def active_cb():
rospy.loginfo("服务被激活....")
def feedback_cb(f):
rospy.loginfo("当前进度:%.2f",f.percent_complete)
if __name__ == "__main__":
# 2.初始化 ROS 节点
rospy.init_node("action_client_py")
# 3.创建 action Client 对象
client = actionlib.SimpleActionClient("dodishes",DoDishesAction)
# 4.等待服务
client.wait_for_server()
# 5.组织目标对象并发送
goal_obj = DoDishesGoal()
goal_obj.dishwasher_id = 1
rospy.loginfo("goal:选择员工1洗盘子:")
client.send_goal(goal_obj,result_cb,active_cb,feedback_cb)
# 6.编写回调, 激活、连续反馈、最终响应
# 7.spin
rospy.spin()
-
- 给Python文件赋予可执行权限,在script文件夹下输入命令sudo chmod +x *.py即可赋予所有Python可执行权限。
-
- 运行py文件
3.2.3 动作编程总结
3.2.3.1 动作编程的顺序流程
- 动作编程的顺序流程:
-
- 在action文件夹编写自定义action文件,配置package.xml文件包括actionlib和actionlib_msg,分别包括
<build_depend>和<exec_depend>
四项;
- 在action文件夹编写自定义action文件,配置package.xml文件包括actionlib和actionlib_msg,分别包括
-
- 配置CMakeList.txt文件包括
-
- 添加编译依赖项功能包:
find_package
;
- 添加编译依赖项功能包:
-
- 添加自定义action文件:
add_action_files
;
- 添加自定义action文件:
-
- 添加action的编译依赖项:
generate_messages里添加std_msgs和actionlib_msgs
;
- 添加action的编译依赖项:
-
- 添加执行时的依赖:
catkin_package里添加actionlib actionlib_msgs
;
- 添加执行时的依赖:
-
- C++代码:
-
- 添加头文件(头文件为功能包名字/自定义动作.h);
-
- 正常编写代码;
-
- CMakeList.txt进行配置(不仅有
add_executable和target_link_libraries还包括add_dependencies
);
- CMakeList.txt进行配置(不仅有
-
- 编译运行;
-
- Python代码:
-
- 添加包,
from 功能包名.msg import *
;
- 添加包,
-
- 正常代码编写;②CMakeList.txt进行配置(目前配置不配置影响不大);
-
- 赋予py文件可执行权限;
-
- 编译运行。
3.2.3.2 动作编程逻辑
- 编程逻辑顺序:
3.2.4 参考
- ROS-WIKI:actionlib package summary(DoDishes)
- 高级action教程-ROS-WIKI:cn/actionlib_tutorials/Tutorials
- B站视频:【奥特学园】ROS机器人入门课程《ROS理论与实践》零基础教程