2 ROS通讯编程基础(2)
迪丽瓦拉
2025-06-01 02:48:48
0

2 ROS1通讯编程基础

  • 2.3 配置文件的解读
    • 2.3.1 CMakeList.txt解读
      • 2.3.1.1 find_package的配置
      • 2.3.1.2 messages, services and actions的配置
      • 2.3.1.3 动态重配参数
      • 2.3.1.4 catkin_package的配置
      • 2.3.1.5 include_package配置
      • 2.3.1.6 C++编译配置
      • 2.3.1.7 Python配置
      • 2.3.1.8 参考文献
    • 2.3.2 package.xml文件解读
  • 2.4 服务通讯编程
    • 2.4.1 服务通讯理论模型
    • 2.4.2 服务通讯的基本编程
      • 2.4.2.1 编写srv服务文件
      • 2.4.2.2 配置自定义服务文件
      • 2.4.2.3 编译自定义服务文件并查看中间文件
      • 2.4.2.4 编译C++代码调用自定义服务
      • 2.4.2.5 编译Python代码调用自定义服务
    • 2.4.3 服务通讯编程总结
      • 2.4.3.1 服务通讯流程
      • 2.4.3.2 服务编程代码逻辑
      • 2.4.3.3 参考文献

  • 其他ROS1学习笔记: ROS1学习笔记
  • 代码仓库:Github连接地址
  • 欢迎各位互相学习交流

2.3 配置文件的解读

2.3.1 CMakeList.txt解读

CMakeList.txt作为ROS编程里,最需要配置的内容,对其默认文档进行解读,主要包括以下几个部分:其中重点在于find_package、一些自定义内容的配置、catkin_package和C++的配置,而且根据之前配置C++时候发现,配置的顺序对编译是有影响的,一般按照下图从左到右的顺序进行配置声明。

图片.png

2.3.1.1 find_package的配置

  1. 新建功能包就包括的依赖项;可以说是功能包编译时候需要的依赖项;
  2. 例如find_package(catkin REQUIRED COMPONENTS roscpp ...其他依赖项名)等

图片.png

2.3.1.2 messages, services and actions的配置

  1. 在msg文件夹下创建自定义消息,然后配置add_message_files(FILES 自定义消息.msg ...其他自定义消息.msg)
  2. 在srv文件夹下创建自定义服务,然后配置add_service_files(FILES 自定义.srv ...其他自定义服务)
  3. 在action文件夹下创建动作内容,然后配置add_action_files(FILES 自定义.action ...其他自定义服务)
  4. 构建自定义messages和services需要的依赖项,generate_messages(DEPENDENCIES std_msgs ...其他依赖项名字)

图片.png

2.3.1.3 动态重配参数

官方声明为Declare ROS dynamic reconfigure parameters:其中内容包括generate_dynamic_reconfigure_options(cfg/DynReconf1.cfg ...其他cfg)

图片.png

2.3.1.4 catkin_package的配置

  1. 官方声明为catkin specific configuration;即可以理解为find_package在执行时候需要的依赖项文件;
  2. 其中内容按照关键字包括①INCLUDE_DIRS include 其他文件夹位置②LIBRARIES 功能包名③ CATKIN_DEPENDS 执行依赖项例如message_runtime④DEPENDS system_lib 其他系统库文件夹

图片.png

2.3.1.5 include_package配置

  1. 官方声明为指定头文件的附加位置,功能包的位置应该在其他位置之前就应该列出;
  2. 例如include_directories(${catkin_INCLUDE_DIRS})其实也等价于当前功能包下的include文件夹即include_directories(include)

图片.png

2.3.1.6 C++编译配置

  1. 声明一个 C++ 库:add_library(${PROJECT_NAME} src/${PROJECT_NAME}/功能包名.cpp)
  2. 添加库的cmake目标依赖,主要正对于自定义消息或者是ROS动态重配置参数,解决本功能包编译时候,如果需要其他依赖的功能包但是没有编译的一个顺序依赖问题:add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
  3. 声明一个 C++ 可执行文件: add_executable(${PROJECT_NAME}_node src/目标代码文件.cpp)
  4. 指定库以链接库或可执行目标:target_link_libraries(${PROJECT_NAME}_node ${catkin_LIBRARIES})

图片.png

2.3.1.7 Python配置

例如:catkin_install_python(PROGRAMS scripts/目标代码.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})

图片.png

2.3.1.8 参考文献

ROS-WIKI:catkinCMakeLists.txt

2.3.2 package.xml文件解读

解读package.xml主要对其构建、运行和测试依赖项(Build, Run, and Test Dependencies)进行分析,这些依赖项即最小的、不依赖其他功能包的包,例如std_msgs、roscpp和rospy这样的,这些包可以有以下几种类型:

    1. Build Dependencies() :指定构建这个包需要哪些包,其包括这些包的头文件,链接来自这些包的库或在构建时需要其他资源(尤其是当这些包在CMake中使用find_package()时)。
    1. Build Export Dependencies() :指定需要哪些包来对这个功能包构建库。例如,当将它们的头文件传递包含在公共头文件中时,尤其是当这些包在CMake的catkin_package()中声明为 (CATKIN_)DEPENDS时。
    1. Execution Dependencies() :指定运行此包中的代码需要哪些包。例如,赖此包中的共享库时,尤其当这些包在CMake中的catkin_package()中声明为(CATKIN_)DEPENDS时。
    1. Test Dependencies() :仅指定单元测试的附加依赖项。这一项不应该使用任何已经作为构建或运行依赖项的依赖项。
    1. Build Tool Dependencies() :指定此包需要自行构建的构建系统工具。在ROS中,一般而言catkin是唯一的构建工具。
    1. Documentation Tool Dependencies() :指定此包生成文档所需的文档工具。
    1. Run Dependencies() :指定运行此包中的代码需要哪些包,或针对此包构建库。类似于Execution Dependencies(),当依赖共享库(shared libraries)或将它们的头文件传递地包含在此包的公共头文件中时,尤其是当这些包在CMake中的catkin_package()中声明为 (CATKIN_)DEPEND。

参考文献:ROSWIKI-package.xml

2.4 服务通讯编程

2.4.1 服务通讯理论模型

服务通讯的理论模型如图所示,主要由以下五步骤组成。

图片.png

  • ①Server注册:Server 启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含提供的服务的名称。ROS Master 会将节点的注册信息加入到注册表中。

  • ②Client注册:Client 启动后,也会通过RPC在ROS Master 中注册自身信息,包含需要请求的服务的名称。ROS Master 会将节点的注册信息加入到注册表中。

  • ③ROS Master实现信息匹配:ROS Master会根据注册表中的信息匹配Server和Client,并通过 RPC向Client发送 Server的TCP地址。

  • ④Client发送请求:Client 根据步骤2 响应的信息,使用 TCP 与 Server 建立网络连接,并发送请求数据。

  • ⑤Server发送响应:Server 接收、解析请求的数据,并产生响应结果返回给 Client。

注意:与话题通讯的具有很大的区别,Server必须在Client前面启动

2.4.2 服务通讯的基本编程

服务通讯编程,其实是为自定义服务消息进行编程使用,其步骤和自定义消息类型的话题编程类似。

  • 服务编程的基本内容:编写两个节点,一个作为客户端,一个作为服务端,服务端在命令行发布两个数字给服务端,服务端实现求和之后返回给客户端。

  • 下面的代码Github连接:topic_communication

2.4.2.1 编写srv服务文件

    1. 新建功能包;在工作空间下新建功能包service_communication,依赖项包括std_msgs roscpp rospy等基础项目。

图片.png

    1. 创建文件夹目录,在service_communication功能包下面,创建文件夹srv和文件夹scripts分别用于存放服务文件和Python文件。
ubuntu@ubuntu:~/catkin_ws/src/service_communication$ tree
.
├── CMakeLists.txt
├── include
│   └── service_communication
├── package.xml
├── scripts
├── src
└── srv5 directories, 2 files
    1. ** 编写服务文件**。在srv文件夹下新建自定义服务文件AddTwoInt.srv用于实现服务程序两数求和,前俩行的a和b表示服务端的请求数据,---号表示分割,c表示服务端的应答数据,此时的a,b和c只是指代请求端(request)和响应端的数据类型(respond),没有任何操作功能。
catkin_ws/src/service_communication/srv/AddTwoInt.srv
# 客户端请求时发送的两个数字
int32 a
int32 b
---
# 服务器响应发送的数据
int32 c

2.4.2.2 配置自定义服务文件

同理,配置srv文件包括在package.xmlCMakeList.txt配置两部分。

    1. 在功能包的package.xml里添加话题的依赖,这一步操作和自定义消息配置是一样的。即在service_communication文件夹的package.xml里添加:
message_runtime
message_generation
    1. 在功能包的CmakeLists.txt添加编译选项,此时和自定义消息也是一样的,包括四部分
    • 5.1. 添加编译依赖项功能包find_package(catkin REQUIRED COMPONENTS ..+ 依赖项功能包),大约在第10行,此时依赖项功能包名字为message_generation(build_depend的内容),而且必须有std_msgs作为基础在里面,配置的内容如下所示:
find_package(catkin REQUIRED COMPONENTSroscpprospystd_msgsmessage_generation
)
    • 5.2 . 添加自定义srv文件add_service_files(FILES 文件名),大约在第58行,如此时的文件名为AddTwoInt.srv:
add_service_files(FILESAddTwoInt.srv
)
    • 5.3. 添加srv的编译依赖项generate_messages(DEPENDENCIES std_msgs),大约在第71行,即表示在编译msg时候得依赖于std_msgs
generate_messages(DEPENDENCIESstd_msgs
)
    • 5.4. 添加执行时的依赖:catkin_package的关键字CATKIN_DEPENDS后+包,大约在106行,在官网上,并没有要求执行这一步,但是为了规范加上这一步,即:
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES service_communicationCATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)

2.4.2.3 编译自定义服务文件并查看中间文件

    1. 编译功能包,在工作空间catkin_ws下执行catkin_make编译功能包,编译成功后,查看中间文件。同样的C++的中间文件在/home/ubuntu/catkin_ws/devel/include/功能包名/文件夹下Python的中间文件在/home/ubuntu/catkin_ws/devel/lib/python2.7/dist-packages/功能包名/srv文件夹

图片.png

2.4.2.4 编译C++代码调用自定义服务

    1. 创建C++服务端程序,并在CMakeList.txt配置C++文件
    • 7.1. 创建客户端程序。创建客户端程序clientc_cpp.cpp。此时额外注意如果客户端比服务器提前启动的解决办法!!client.waitForExistence()!

catkin_ws/src/service_communication/src/client_cpp.cpp

/*** AddTwoInt Client*/
#include 
#include "ros/ros.h"
#include "service_communication/AddTwoInt.h"int main(int argc, char **argv)
{setlocale(LC_ALL,"");// 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,service消息类型是service_communication::AddTwoIntros::ServiceClient client = n.serviceClient("add_two_ints");// 创建service_communication::AddTwoInt类型的service消息service_communication::AddTwoInt srv;srv.request.a = atoll(argv[1]);srv.request.b = atoll(argv[2]);// 发布service请求,等待加法运算的应答结果// 等待客户端启动client.waitForExistence();// 也可以采用代码ros::service::waitForService("AddTwoInt");if (client.call(srv)){ROS_INFO("c: %ld", (long int)srv.response.c);}else{ROS_ERROR("客户端没有启动");return 1;}return 0;
}
    • 7.2. 在功能包的src目录下,创建服务程序server_cpp.cpp。

catkin_ws/src/service_communication/src/server_cpp.cpp

/*** AddTwoInt Server*/#include "ros/ros.h"
#include "service_communication/AddTwoInt.h"// service回调函数,输入参数req,输出参数res
bool add(service_communication::AddTwoInt::Request  &req,service_communication::AddTwoInt::Response &res)
{// 将输入参数中的请求数据相加,结果放到应答变量中res.c = 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.c);return true;
}int main(int argc, char **argv)
{// ROS节点初始化ros::init(argc, argv, "add_two_ints_server");// 创建节点句柄ros::NodeHandle n;// 创建一个名为add_two_ints的server,注册回调函数add()ros::ServiceServer service = n.advertiseService("add_two_ints", add);// 循环等待回调函数ROS_INFO("Ready to add two ints.");ros::spin();return 0;
}
    • 7.3. 配置功能包下的CMakeLists.txt文件,和自定义消息话题通讯一样,也包括add_executableadd_dependenciestarget_link_libraries三项。注意与自定义消息话题通讯区别在于add_dependencies的参数

catkin_ws/src/service_communication/CMakeLists.txt

add_executable(server src/server_cpp.cpp)
target_link_libraries(server ${catkin_LIBRARIES})
add_dependencies(server ${PROJECT_NAME}_gencpp)add_executable(client src/client_cpp.cpp)
target_link_libraries(client ${catkin_LIBRARIES})
add_dependencies(client ${PROJECT_NAME}_gencpp
    • 7.4. 在工作空间进行编译;
    • 7.5. 运行服务编程。首先打开一个终端输入roscore,打开ROS,再新建一个终端输入先rosrun service_communication server运行服务程序。最后新建终端输入rosrun service_communication client 1 2即运行客户端并执行1和2的加法操作。

图片.png

2.4.2.5 编译Python代码调用自定义服务

    1. 在scripts文件夹编写Python代码,同时赋予可执行权限。
    • 8.1. 编写Python的客户端代码client_py.py代码,同样注意这个服务器是否没有启动的优化。

catkin_ws/src/service_communication/scripts/client_py.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-#1.导包
import rospy
from service_communication.srv import *
import sysif __name__ == "__main__":#优化实现if len(sys.argv) != 3:rospy.logerr("请正确提交参数")sys.exit(1)# 2.初始化 ROS 节点rospy.init_node("AddTwoInt_Client_p")# 3.创建请求对象client = rospy.ServiceProxy("AddTwoInt",AddTwoInt)# 请求前,等待服务已经就绪client.wait_for_service()# 4.发送请求,接收并处理响应req = AddTwoIntRequest()req.a = int(sys.argv[1])req.b = int(sys.argv[2]) resp = client.call(req)rospy.loginfo("响应结果:%d",resp.c)
    • 8.2. 编写Python的服务器server_py.py代码:

catkin_ws/src/service_communication/scripts/server_py.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-# 1.导包
import rospy
from service_communication.srv import *
# 回调函数的参数是请求对象,返回值是响应对象
def doReq(req):# 解析提交的数据c = req.a + req.brospy.loginfo("提交的数据:a = %d, b = %d, c = %d",req.a, req.b, c)# 创建响应对象,赋值并返回# resp = AddTwoIntResponse()# resp.c = cresp = AddTwoIntResponse(c)return respif __name__ == "__main__":# 2.初始化 ROS 节点rospy.init_node("AddTwoInt_server_p")# 3.创建服务对象server = rospy.Service("AddTwoInt",AddTwoInt,doReq)# 4.回调函数处理请求并产生响应# 5.spin 函数rospy.spin()
    • 8.3. 给Python文件赋予可执行权限,在script文件夹下输入命令sudo chmod +x *.py即可赋予所有Python可执行权限。
    • 8.4. 先roscore打开ROS,再输入rosrun service_communication server_py.py即可运行py文件,同理运行client_py.py文件,其中未按照要求输入会提示No handlers could be found for logger "rosout"

图片.png

2.4.3 服务通讯编程总结

服务通讯的关键点主要在于理解其作用:请求响应模式实现不同节点之间数据交互,用于偶然的、对时时性有要求、有一定逻辑处理需求的数据传输场景

同时理解服务通讯理论模型的五个步骤,即服务端的启动是需要在客户端之前的,以及在编写代码时候对启动先后的处理

2.4.3.1 服务通讯流程

服务通讯的操作流程和自定义消息话题通讯是类似的,主要包括:

  1. 在srv文件夹编写自定义srv文件,配置package.xml文件包括①exec_depend>message_runtimemessage_generation
  2. 配置CMakeList.txt文件包括①添加编译依赖项功能包:find_package;②添加自定义msg文件:add_message_files;③添加msg的编译依赖项:generate_messages;④添加执行时的依赖:catkin_package;
  3. C++代码:①添加头文件(头文件为功能包名字/自定义服务名.h);②正常编写代码;③CMakeList.txt进行配置(不仅有add_executable和target_link_libraries还包括add_dependencies);④编译运行;
  4. Python代码:①添加包,from 功能包名.srv import *;②正常代码编写;②CMakeList.txt进行配置(目前配置不配置影响不大);③赋予py文件可执行权限;④编译运行。

2.4.3.2 服务编程代码逻辑

图片.png

2.4.3.3 参考文献

B站视频:【奥特学园】ROS机器人入门课程《ROS理论与实践》零基础教程

ROS-WIKI:编写简单的服务和客户端(C++)

ROS-WIKI:编写简单的服务和客户端(Python)

ROS-WIKI:检验简单的服务和客户端

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...