- 其他ROS1学习笔记: ROS1学习笔记
- 代码仓库:Github连接地址
- 欢迎各位互相学习交流
CMakeList.txt
作为ROS编程里,最需要配置的内容,对其默认文档进行解读,主要包括以下几个部分:其中重点在于find_package
、一些自定义内容的配置、catkin_package
和C++的配置,而且根据之前配置C++时候发现,配置的顺序对编译是有影响的,一般按照下图从左到右的顺序进行配置声明。
find_package(catkin REQUIRED COMPONENTS roscpp ...
其他依赖项名)等add_message_files(FILES 自定义消息.msg ...其他自定义消息.msg)
;add_service_files(FILES 自定义.srv ...其他自定义服务)
;add_action_files(FILES 自定义.action ...其他自定义服务)
;generate_messages(DEPENDENCIES std_msgs ...其他依赖项名字)
官方声明为Declare ROS dynamic reconfigure parameters:其中内容包括generate_dynamic_reconfigure_options(cfg/DynReconf1.cfg ...其他cfg)
①INCLUDE_DIRS include 其他文件夹位置
;②LIBRARIES 功能包名
;③ CATKIN_DEPENDS 执行依赖项例如message_runtime
;④DEPENDS system_lib 其他系统库文件夹
。include_directories(${catkin_INCLUDE_DIRS})
其实也等价于当前功能包下的include文件夹即include_directories(include)
。add_library(${PROJECT_NAME} src/${PROJECT_NAME}/功能包名.cpp)
;add_dependencies(${PROJECT_NAME}
${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
;add_executable(${PROJECT_NAME}_node src/目标代码文件.cpp)
;target_link_libraries(${PROJECT_NAME}_node ${catkin_LIBRARIES})
。例如:catkin_install_python(PROGRAMS scripts/目标代码.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
ROS-WIKI:catkinCMakeLists.txt
解读package.xml主要对其构建、运行和测试依赖项(Build, Run, and Test Dependencies)进行分析,这些依赖项即最小的、不依赖其他功能包的包,例如std_msgs、roscpp和rospy这样的,这些包可以有以下几种类型:
(CATKIN_)DEPENDS
时。参考文献:ROSWIKI-package.xml
服务通讯的理论模型如图所示,主要由以下五步骤组成。
①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前面启动!
服务通讯编程,其实是为自定义服务消息进行编程使用,其步骤和自定义消息类型的话题编程类似。
服务编程的基本内容:编写两个节点,一个作为客户端,一个作为服务端,服务端在命令行发布两个数字给服务端,服务端实现求和之后返回给客户端。
下面的代码Github连接:topic_communication
ubuntu@ubuntu:~/catkin_ws/src/service_communication$ tree
.
├── CMakeLists.txt
├── include
│ └── service_communication
├── package.xml
├── scripts
├── src
└── srv5 directories, 2 files
AddTwoInt.srv
用于实现服务程序两数求和,前俩行的a和b表示服务端的请求数据,---
号表示分割,c表示服务端的应答数据,此时的a,b和c只是指代请求端(request)和响应端的数据类型(respond),没有任何操作功能。catkin_ws/src/service_communication/srv/AddTwoInt.srv
# 客户端请求时发送的两个数字
int32 a
int32 b
---
# 服务器响应发送的数据
int32 c
同理,配置srv文件包括在package.xml
和CMakeList.txt
配置两部分。
package.xml
里添加话题的依赖,这一步操作和自定义消息配置是一样的。即在service_communication
文件夹的package.xml
里添加:message_runtime
message_generation
find_package(catkin REQUIRED COMPONENTS ..+ 依赖项功能包)
,大约在第10行,此时依赖项功能包名字为message_generation(build_depend的内容)
,而且必须有std_msgs
作为基础在里面,配置的内容如下所示:find_package(catkin REQUIRED COMPONENTSroscpprospystd_msgsmessage_generation
)
add_service_files(FILES 文件名)
,大约在第58行,如此时的文件名为AddTwoInt.srv:add_service_files(FILESAddTwoInt.srv
)
generate_messages(DEPENDENCIES std_msgs)
,大约在第71行,即表示在编译msg时候得依赖于std_msgs:generate_messages(DEPENDENCIESstd_msgs
)
CATKIN_DEPENDS后+包
,大约在106行,在官网上,并没有要求执行这一步,但是为了规范加上这一步,即:catkin_package(
# INCLUDE_DIRS include
# LIBRARIES service_communicationCATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
/home/ubuntu/catkin_ws/devel/include/功能包名/文件夹下
,Python的中间文件在/home/ubuntu/catkin_ws/devel/lib/python2.7/dist-packages/功能包名/srv文件夹
下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;
}
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;
}
add_executable
、add_dependencies
和target_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
rosrun service_communication server
运行服务程序。最后新建终端输入rosrun service_communication client 1 2
即运行客户端并执行1和2的加法操作。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)
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()
sudo chmod +x *.py
即可赋予所有Python可执行权限。入rosrun service_communication server_py.py
即可运行py文件,同理运行client_py.py文件,其中未按照要求输入会提示No handlers could be found for logger "rosout"
服务通讯的关键点主要在于理解其作用:请求响应模式实现不同节点之间数据交互,用于偶然的、对时时性有要求、有一定逻辑处理需求的数据传输场景。
同时理解服务通讯理论模型的五个步骤,即服务端的启动是需要在客户端之前的,以及在编写代码时候对启动先后的处理。
服务通讯的操作流程和自定义消息话题通讯是类似的,主要包括:
①exec_depend>message_runtime
;②message_generation
;from 功能包名.srv import *
;②正常代码编写;②CMakeList.txt进行配置(目前配置不配置影响不大);③赋予py文件可执行权限;④编译运行。B站视频:【奥特学园】ROS机器人入门课程《ROS理论与实践》零基础教程
ROS-WIKI:编写简单的服务和客户端(C++)
ROS-WIKI:编写简单的服务和客户端(Python)
ROS-WIKI:检验简单的服务和客户端