2 ROS1通讯编程基础(2)

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

5 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里添加:
<exec_depend>message_runtime</exec_depend>
<build_depend>message_generation</build_depend>
    1. 在功能包的CmakeLists.txt添加编译选项,此时和自定义消息也是一样的,包括四部分
    • 5.1. 添加编译依赖项功能包find_package(catkin REQUIRED COMPONENTS ..+ 依赖项功能包),大约在第10行,此时依赖项功能包名字为message_generation(build_depend的内容),而且必须有std_msgs作为基础在里面,配置的内容如下所示:
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
    • 5.2 . 添加自定义srv文件add_service_files(FILES 文件名),大约在第58行,如此时的文件名为AddTwoInt.srv:
add_service_files(
   FILES
   AddTwoInt.srv
)
    • 5.3. 添加srv的编译依赖项generate_messages(DEPENDENCIES std_msgs),大约在第71行,即表示在编译msg时候得依赖于std_msgs
generate_messages(
  DEPENDENCIES
  std_msgs
)
    • 5.4. 添加执行时的依赖:catkin_package的关键字CATKIN_DEPENDS后+包,大约在106行,在官网上,并没有要求执行这一步,但是为了规范加上这一步,即:
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES service_communication
  CATKIN_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 <cstdlib>
#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::AddTwoInt
  ros::ServiceClient client = n.serviceClient<service_communication::AddTwoInt>("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 sys

if __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.b
    rospy.loginfo("提交的数据:a = %d, b = %d, c = %d",req.a, req.b, c)

    # 创建响应对象,赋值并返回
    # resp = AddTwoIntResponse()
    # resp.c = c
    resp = AddTwoIntResponse(c)
    return resp


if __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_runtime</exec_depend>②<build_depend>message_generation</build_depend>
  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:检验简单的服务和客户端

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ROS2编程基础课程文档 ROS 2(机器人操作系统2)是用于机器人应用的开源开发套件。ROS 2之目的是为各行各业的开发人员提供标准的软件平台,从研究和原型设计再到部署和生产。 ROS 2建立在ROS 1的成功基础之上,ROS 1目前已在世界各地的无数机器人应用中得到应用。 特色 缩短上市时间 ROS 2提供了开发应用程序所需的机器人工具,库和功能,可以将时间花在对业务非常重要的工作上。因为它 是开源的,所以可以灵活地决定在何处以及如何使用ROS 2,以及根据实际的需求自由定制,使用ROS 2 可以大幅度提升产品和算法研发速度! 专为生产而设计 凭借在建立ROS 1作为机器人研发的事实上的全球标准方面的十年经验,ROS 2从一开始就被建立在工业级 基础上并可用于生产,包括高可靠性和安全关键系统。 ROS 2的设计选择、开发实践和项目管理基于行业利 益相关者的要求。 多平台支持 ROS 2在Linux,Windows和macOS上得到支持和测试,允许无缝开发和部署机器人自动化,后端管理和 用户界面。分层支持模型允许端口到新平台,例如实时和嵌入式操作系统,以便在获得兴趣和投资时引入和推 广。 丰富的应用领域 与之前的ROS 1一样,ROS 2可用于各种机器人应用,从室内到室外、从家庭到汽车、水下到太空、从消费 到工业。 没有供应商锁定 ROS 2建立在一个抽象层上,使机器人库和应用程序与通信技术隔离开来。抽象底层是通信代码的多种实现, 包括开源和专有解决方案。在抽象顶层,核心库和用户应用程序是可移植的。 建立在开放标准之上 ROS 2中的默认通信方法使用IDL、DDS和DDS-I RTPS等行业标准,这些标准已广泛应用于从工厂到航空 航天的各种工业应用中。 开源许可证 ROS 2代码在Apache 2.0许可下获得许可,在3条款(或“新”)BSD许可下使用移植的ROS 1代码。这两个 许可证允许允许使用软件,而不会影响用户的知识产权。 全球社区 超过10年的ROS项目通过发展一个由数十万开发人员和用户组成的全球社区,为机器人技术创建了一个庞大 的生态系统,他们为这些软件做出贡献并进行了改进。 ROS 2由该社区开发并为该社区开发,他们将成为未 来的管理者。 行业支持 正如ROS 2技术指导委员会成员所证明的那样,对ROS 2的行业支持很强。除了开发顶级产品外,来自世界 各地的大大小小公司都在投入资源为ROS 2做出开源贡献。 与ROS1的互操作性 ROS 2包括到ROS 1的桥接器,处理两个系统之间的双向通信。如果有一个现有的ROS 1应用程序, 可 以通过桥接器开始尝试使用ROS 2,并根据要求和可用资源逐步移植应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值