目录
本节将继续介绍三种通信方式,话题通信前一部分见上一节
一、话题通信
1.1 话题通信案例
1.1.1 话题通信自定义msg
1.单一数据类型的局限性
ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty、数组.... 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如: 激光雷达的信息(扫描角度,障碍距离,强度信息)... std_msgs 由于描述性较差而显得力不从心,这种场景下可以使用自定义的消息类型。
2.本节学习目的
创建自定义(人)消息,该消息包含人的信息:姓名、身高、年龄等。
3.自定义msg的具体实现
Ⅰ.定义msg文件
在 plumbing_pub_sub 的功能包下右击新建文件夹msg,msg下新建文件person.msg
并设置人的三个字段
string name int32 age float32 height
Ⅱ.编辑配置文件
①配置package.xml,加入两条记录
<build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend>
②配置Cmakelist.txt
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation )
add_message_files( FILES Person.msg )
generate_messages( DEPENDENCIES std_msgs )
catkin_package( # INCLUDE_DIRS include # LIBRARIES plumbing_pub_sub CATKIN_DEPENDS roscpp rospy std_msgs message_runtime # DEPENDS system_lib )
Ⅲ.编译
编译结果生成许多中间文件:
devel目录下的Person.h、devel/lib/python3/msg目录下的_Person.py文件。
1.1.2 话题通信自定义msg调用(C++实现)
1.需求:
编写发布订阅实现,发布方以10HZ发布自定义信息,接收方订阅自定义信息并输出
2.实现--发布方
Ⅰ.vscode配置:为了方便代码提示以及避免误抛异常,需要先配置vscode,将前面生成的head文件路径配置进 c_cpp_properties.json的includepath属性
**路径名简单获取方式为 pwd 命令行
{ "configurations": [ { "browse": { "databaseFilename": "${workspaceFolder}/.vscode/browse.vc.db", "limitSymbolsToIncludedHeaders": false }, "includePath": [ "/opt/ros/melodic/include/**", "/home/liuhongwei/demo01_ws/src/helloworld1/include/**", "/usr/include/**", "/home/liuhongwei/demo03_ws/devel/include/**" ], "name": "ROS", "intelliSenseMode": "gcc-x64", "compilerPath": "/usr/bin/gcc", "cStandard": "gnu11", "cppStandard": "c++14" } ], "version": 4 }
Ⅱ.发布方实现:
①在src目录下新建文件 demo03_pub_person.cpp
#include "ros/ros.h" #include"plumbing_pub_sub/Person.h" int main(int argc ,char * argv[]) { setlocale(LC_ALL,""); ros::init(argc,argv,"banZhuren"); ros::NodeHandle nh; ros::Publisher pub = nh.advertise<plumbing_pub_sub::Person>("liaoTian",10); //创建发布数据 plumbing_pub_sub::Person person; person.name = "张三"; person.age = 1; person.height = 180.78; //设置发布频率 ros::Rate rate(1); //循环发布数据 while(ros::ok()) { person.age++; //数据发布 pub.publish(person); rate.sleep(); //建议 ros::spinOnce(); } return 0; }
②更改配置文件cmakelist.txt,增加两条信息,修改一条信息
add_executable(demo03_pub src/demo03_pub.cpp) target_link_libraries(demo03_pub ${catkin_LIBRARIES} ) add_dependencies(demo03_pub ${PROJECT_NAME}_generate_messages_cpp)
③编译
④执行
3.实现--订阅方
Ⅰ.新建订阅方文件 demo04_sub.cpp
Ⅱ.实现代码
#include "ros/ros.h" #include "plumbing_pub_sub/Person.h" void doPerson(const plumbing_pub_sub::Person::ConstPtr & person) { ROS_INFO("订阅人的信息:%s,%d,%.2f",person->name.c_str(),person->age,person->height); } int main(int argc,char * argv[]) { setlocale(LC_ALL,""); ROS_INFO("订阅方实现"); ros::init(argc,argv,"jiaZhang"); ros::NodeHandle nh; ros::Subscriber sub = nh.subscribe("liaoTian",10,doPerson); ros::spin(); return 0; }
Ⅲ.编辑配置文件
add_executable(demo04_sub src/demo04_sub.cpp) add_dependencies(demo04_sub ${PROJECT_NAME}_generate_messages_cpp) target_link_libraries(demo04_sub ${catkin_LIBRARIES} )
Ⅳ.编译
Ⅴ.测试
1.1.3话题通信自定义msg调用(python实现)
1.需求
编写发布订阅实现,发布方以10HZ发布自定义信息,接收方订阅自定义信息并输出
2.实现---发布方
Ⅰ. 将前面生成的python文件路径配置进 setting.json
终端中打开,用pwd命令获取路径
/home/liuhongwei/demo03_ws/devel/lib/python2.7/dist-packages
配置文件配置结果如下
{ "python.autoComplete.extraPaths": [ "/opt/ros/melodic/lib/python2.7/dist-packages" ], "python.analysis.extraPaths": [ "/opt/ros/melodic/lib/python2.7/dist-packages", "/home/liuhongwei/demo03_ws/devel/lib/python2.7/dist-packages" ] }
Ⅱ.发布方实现
①scripts目录下添加 demo03_pub.py 文件
#! usr/bin/env python import rospy from plumbing_pub_sub.msg import Person if __name__ =="__main__": rospy.init_node("daMa") pub = rospy.Publisher("jiaoshetou",Person,queue_size=10) #创建person数据 p = Person() p.name = "aoteman" p.age = 8 p.height = 1.85 #创建Rate对象 rate = rospy.Rate(1) #循环发布数据 while not rospy.is_shutdown(): pub.publish(p) rospy.loginfo("发布的消息是:%s,%d,%.2f",p.name,p.age,p.height) rate.sleep()
②添加执行权限
chmod +x *.py
③配置配置文件
catkin_install_python(PROGRAMS scripts/demo01_pub_p.py scripts/demo02_sub_p.py scripts/demo03_pub.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} )
④编译
⑤测试
#! /usr/bin/env python import rospy from plumbing_pub_sub.msg import Person if __name__ == "__main__": rospy.init_node('daMa') pub = rospy.Publisher("jiaoshetou",Person,queue_size=10) p = Person() p.name = "huluwa" p.age = 18 p.height = 0.75 rate = rospy.Rate(1) while not rospy.is_shutdown(): pub.publish(p) rate.sleep() rospy.loginfo("name:%s, age:%d, height:%.2f",p.name, p.age, p.height)
3.实现---订阅方
①添加文件demo04_sub.py并编写
#coding=utf-8 #! /usr/bin/env python import rospy from plumbing_pub_sub.msg import Person def doPer(p): rospy.loginfo("小伙子的数据:%s,%d,%.2f",p.name,p.age,p.height) if __name__ == "__main__": rospy.init_node("daYe") sub = rospy.Subscriber("jiaoshetou",Person,doPer) rospy.spin()
②添加可执行权限
③更改配置文件
④执行
1.2 话题通信总结
1.本章学了什么
话题通信的概念、作用、案例、理论模型、基本操作、C++调用接口、python调用接口、自定义msg 、配置文件更改、linux基础命令、赋予文件执行权限
二、服务通信
2.1 本节学什么
1.服务通信的地位
服务通信也是ROS中一种极其常用的通信模式,服务通信是基于请求响应模式的,是一种应答机制。也即: 一个节点A向另一个节点B发送请求,B接收处理请求并产生响应结果返回给A。比如如下场景:
机器人巡逻过程中,控制系统分析传感器数据发现可疑物体或人... 此时需要拍摄照片并留存。
在上述场景中,就使用到了服务通信。类似于计算机网络的C/S模型
- 一个节点需要向相机节点发送拍照请求,相机节点处理请求,并返回处理结果
2.服务通信的应用场景
服务通信更适用于对时时性有要求、具有一定逻辑处理的应用场景。用于偶然的、对时时性有要求、有一定逻辑处理需求的数据传输场景。
3.能实现的经典案例
实现两个数字的求和,客户端节点,运行会向服务器发送两个数字,服务器端节点接收两个数字求和并将结果响应回客户端。
2.2 服务通信理论模型
2.2.1 服务通信理论图示
2.2.2 服务通信理论模型解释
1.服务通信理论模型的三个角色(为方便理解,举个例子)
Ⅰ.管理者:roscore核心(中介平台)
Ⅱ.服务端:(马桶修理者)
Ⅲ.客户端 :(我)
2.服务通信具体流程
master根据话题实现client和server之间的连接的过程,master起到媒介的应用
Ⅰ.马桶修理平台(server)向中介平台(master)注册自己的信息(话题--疏通下水道+公司联系方式--RPC地址)
Ⅱ.我(client)向中介平台(master)注册自己所需要的服务(话题--疏通下水道)
Ⅲ.中介公司(master)进行话题匹配,并将服务端的联系方式(RPC地址)相应给我(客户端)
Ⅳ.我打电话给保洁公司
Ⅴ.保洁公司到我家修马桶(TCP)
3.注意事项
Ⅰ.和话题通信相比较,应该保证服务端是先启动的,客户端是后请求的
Ⅱ.服务端和客户端都可以有多个
Ⅲ.流程被封装,只需要调用即可
Ⅳ.也用到了话题,要保证话题相同
2.3 服务通信自定义服务消息(srv)
2.3.1 需求流程分析
Ⅰ.需求:服务通信中,客户端提交两个整数至服务端,服务端求和并响应结果到客户端,请创建服务器与客户端通信的数据载体。
Ⅱ.流程:
srv 文件内的可用数据类型与 msg 文件一致,且定义 srv 实现流程与自定义 msg 实现流程类似:
按照固定格式创建srv文件
编辑配置文件
编译生成中间文件
2.3.2 自定义srv文件实现
1.步骤
Ⅰ.在demo03_ws下新建一个功能包,包名为plumbing_server_client,包的依赖为roscpp、rospy、std_msgs
Ⅱ.创建自定义的服务消息
在功能包下新建一个文件夹srv,在里面新建一个文件addints.srv,这个文件就是自定义的服务消息载体,前面说到服务消息分为两部分:请求部分(2个整形数字)和相应部分(1个整形数字)。三个杠是请求与响应的交界线
int32 num1 int32 num2 --- int32 sum
Ⅲ.编辑配置文件
①package.xml加入一条
<build_depend>message_runtime</build_depend> <exec_depend>message_runtime</exec_depend>
②cMakeList.txt修改四处信息
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation )
## Generate services in the 'srv' folder add_service_files( FILES addints.srv )
## Generate added messages and services with any dependencies listed here generate_messages( DEPENDENCIES std_msgs )
catkin_package( # INCLUDE_DIRS include # LIBRARIES plumbing_server_client CATKIN_DEPENDS roscpp rospy std_msgs message_runtime # DEPENDS system_lib )
③编译
④观察生成文件
2.4 使用C++实现服务通信
2.4.1 分析
在模型实现中,ROS master 不需要实现,而连接的建立也已经被封装了,需要关注的关键点有三个:
- 服务端
- 客户端
- 数据
流程:
- 编写服务端实现;
- 编写客户端实现;
- 编辑配置文件;
- 编译并执行。
2.4.2 vscode配置
Ⅰ.获取位置:/home/liuhongwei/demo03_ws/devel/include
Ⅱ.添加到此
2.4.3 服务端实现
1.具体步骤
Ⅰ.在plumbing_server_client 功能包下的 src 目录下建立源文件 demo01_server.cpp
Ⅱ.搭建框架
服务端实现:解析客户端提交的数据,并运算再产生响应
1.包含相关头文件
2.初始化ros节点
3.创建节点句柄
4.创建服务对象
5.处理请求并产生响应
6.spin()Ⅲ.编程实现
#include "ros/ros.h" #include"plumbing_server_client/addints.h" /* 服务端实现:解析客户端提交的数据,并运算再产生响应 1.包含相关头文件 2.初始化ros节点 3.创建节点句柄 4.创建服务对象 5.处理请求并产生响应 6.spin() */ bool doNums(plumbing_server_client::addints::Request & request,plumbing_server_client::addints::Response &response) //返回bool值,表明成功或者失败 { //1.处理请求 int num1 = request.num1; int num2 = request.num2; ROS_INFO("收到的请求数据为:num1=%d,num2=%d",num1,num2); //2.组织响应 int sum = num1+num2; response.sum = sum; ROS_INFO("求和结果为%d",sum); return true; } int main(int argc, char *argv[]) { setlocale(LC_ALL,""); ros::init(argc,argv,"heishui"); //节点名称唯一 ros::NodeHandle nh; ros::ServiceServer server = nh.advertiseService("addInts",doNums); //话题名称、回调函数 ros::spin(); return 0; }
Ⅳ.配置CMakeLists.txt
# Declare a C++ executable ## With catkin_make all packages are built within a single CMake context ## The recommended prefix ensures that target names across packages don't collide add_executable(demo01_server src/demo01_server.cpp)
## Add cmake target dependencies of the executable ## same as for the library above add_dependencies(demo01_server ${PROJECT_NAME}_gencpp) ## Specify libraries to link a library or executable target against target_link_libraries(demo01_server ${catkin_LIBRARIES} )
Ⅴ.编译
Ⅵ.执行
Ⅶ.关于测试
①rosservice的格式:
rosservice + call + 话题名称 + 空格 + table补齐
2.总结
与话题通信相比,服务通信创建服务对象调用的api不同,回调函数参数不同,需要注意下!
2.4.4 客户端实现
1.具体步骤
Ⅰ.当前工能包下(plumbing_server_client)下新建文件 demo02_client.cpp
Ⅱ.搭建框架
客户端:提交两个数据,处理响应结果
1.包含相关头文件
2.初始化ros节点
3.创建节点句柄
4.创建客户端对象
5.提交请求并产生响应Ⅲ.编程实现
#include "ros/ros.h" #include "plumbing_server_client/addints.h" /* 客户端:提交两个数据,处理响应结果 1.包含相关头文件 2.初始化ros节点 3.创建节点句柄 4.创建客户端对象 5.提交请求并产生响应 */ int main(int argc, char *argv[]) { setlocale(LC_ALL,""); ros::init(argc,argv,"daBao"); ros::NodeHandle nh; ros::ServiceClient client = nh.serviceClient<plumbing_server_client::addints>("addInts"); //话题 //5-1组织请求 plumbing_server_client::addints ai; ai.request.num1 = 100; ai.request.num2 = 200; //5-2处理响应 bool flag = client.call(ai); //客户端带着ai对象访问服务器 if(flag) { ROS_INFO("响应成功"); //获取sum ROS_INFO("响应结果 = %d",ai.response.sum); //传进去是引用变量 } else { ROS_INFO("处理失败........."); } return 0; }
Ⅳ.编写配置文件
add_executable(demo02_client src/demo02_client.cpp)
add_dependencies(demo02_client ${PROJECT_NAME}_gencpp)
target_link_libraries(demo02_client ${catkin_LIBRARIES} )
Ⅴ.编译
Ⅵ.测试
2.优化
Ⅰ.为什么优化:用户输入两个数实现交互
Ⅱ.实现参数的动态提交:
①格式:rosrun 包名 节点名称 12 34(两个参数)
②节点执行时需要获取命令中的参数并组织进request,怎么获取命令中的参数
Ⅲ.代码实现
3.思考
Ⅰ.---在上述执行时是先启动服务器端后启动客户端---
如果相反呢?会抛出异常
Ⅱ.如果先启动客户端,会抛出异常,若有需求先启动客户端也不抛出异常,而是挂起等服务器启动后再正常请求。
解决方案:ROS内置函数,可以让客户端启动后挂起等待服务器启动
在client处理相应之前加入这样一行代码
client.waitForExistence(); //如果服务器没启动,服务器就会挂起
服务器已启动客户端就启动了
Ⅲ.最终code
#include "ros/ros.h" #include "plumbing_server_client/addints.h" /* 客户端:提交两个数据,处理响应结果 1.包含相关头文件 2.初始化ros节点 3.创建节点句柄 4.创建客户端对象 5.提交请求并产生响应 */ int main(int argc, char *argv[]) { setlocale(LC_ALL,""); //优化实现,获取命令中的参数 if(argc!=3) { ROS_INFO("提交的参数个数不对"); return 1 ; } ros::init(argc,argv,"daBao"); ros::NodeHandle nh; ros::ServiceClient client = nh.serviceClient<plumbing_server_client::addints>("addInts"); //话题 //5-1组织请求 plumbing_server_client::addints ai; ai.request.num1 = atoi(argv[1]); ai.request.num2 = atoi(argv[2]); //调用判断服务器状态的函数 client.waitForExistence(); //如果服务器没启动,服务器就会挂起 //5-2处理响应 bool flag = client.call(ai); //客户端带着ai对象访问服务器 if(flag) { ROS_INFO("响应成功"); //获取sum ROS_INFO("响应结果 = %d",ai.response.sum); //传进去是引用变量 } else { ROS_INFO("处理失败........."); } return 0; }
2.5 用python实现服务通信
2.5.1 VSCODE配置
1.文件目录
/home/liuhongwei/demo03_ws/devel/lib/python2.7/dist-packages/plumbing_server_client
2.添加至配置文件 setting.json
2.5.2 服务端实现
1.具体步骤
Ⅰ.在功能包plumbing_server_client下,创建scripts文件夹,创建demo01_server.py文件
Ⅱ.框架搭建
Ⅲ.代码实现
#! /usr/bin/env python # encoding:utf-8 import rospy from plumbing_server_client.srv import addints,addintsRequest,addintsResponse """ 服务端:解析客户请求,产生响应 1.导包 2.初始化ros节点 3.创建服务端ros对象 4.处理逻辑(回调函数) 5.spin() """ #参数:封装了请求数据;返回值封装了响应数据 def doNums(request): #1.解析提交的两个证书 num1 = request.num1 num2 = request.num2 #2.求和 sum = num1+ num2 #3.将结果封装进响应对象 response = addintsResponse() response.sum = sum rospy.loginfo("服务器解析的数据 num1 = %d,num2 = %d ,相应的结果 sum = %d",num1,num2,sum) return response if __name__ == "__main__": rospy.init_node("heishui") #第二个参数是参数类型 即srv下面的 addints.srv server = rospy.Service("addintss",addints,doNums) rospy.loginfo("服务器已启动") rospy.spin()
Ⅳ.赋予执行权限
Ⅴ.配置文件
①CMakeLists.txt
## Mark executable scripts (Python etc.) for installation ## in contrast to setup.py, you can choose the destination catkin_install_python(PROGRAMS scripts/demo01_server.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} )
Ⅵ.编译
Ⅶ.执行
2.5.3 客户端实现
1.实现
Ⅰ.建立文件 demo02_client.py
Ⅱ.编程
#! /usr/bin/env python # encoding:utf-8 from http import client from urllib import response import rospy from plumbing_server_client.srv import addints,addintsRequest,addintsResponse """ 客户端:组织并提交请求,处理服务端响应 1.导包 2.初始化ros节点 3.创建客户端ros对象 4.组织请求数据并发送请求 5.处理响应 """ if __name__ == "__main__": rospy.init_node("erHei") #ServiceProxy负责产生一个客户端对象 client = rospy.ServiceProxy("addintss",addints) response = client.call(12,34) rospy.loginfo("响应的数据是%d",response.sum) pass
Ⅲ.赋予权限
Ⅳ.更改配置文件
## Mark executable scripts (Python etc.) for installation ## in contrast to setup.py, you can choose the destination catkin_install_python(PROGRAMS scripts/demo01_server.py scripts/demo02_client.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} )
Ⅴ.编译
Ⅵ.测试
2.针对客户端的优化
Ⅰ.怎么实现参数的动态提交
①需要导入 import sys 模块,此模块下有一个argv数组可以包含传入参数
②代码实现
#! /usr/bin/env python # encoding:utf-8 import sys import rospy from plumbing_server_client.srv import addints,addintsRequest,addintsResponse """ 客户端:组织并提交请求,处理服务端响应 1.导包 2.初始化ros节点 3.创建客户端ros对象 4.组织请求数据并发送请求 5.处理响应 """ if __name__ == "__main__": #判断参数长度 if len(sys.argv)!=3: rospy.logerr("传入的参数个数错误") sys.exit(1) rospy.init_node("erHei") #ServiceProxy负责产生一个客户端对象 client = rospy.ServiceProxy("addintss",addints) #解析传入的参数 num1 = int(sys.argv[1]) num2 = int(sys.argv[2]) response = client.call(num1,num2) rospy.loginfo("响应的数据是%d",response.sum) pass
③测试
2.5.4 服务通信的注意事项
1.先启动客户端再启动服务端会抛出异常,如何让客户端"等一等"服务端呢(挂起)
Ⅰ.需求:需要客户端先于服务端启动,不要抛出异常,挂起,等待服务启动后再次发动请求
Ⅱ.方法:利用ROS内置函数,可以用来判断服务器状态,如果服务没启动,就让客户端挂起
Ⅲ.实现:在调用服务器前加入此行代码
#等待服务启动,服务器没启动则挂起 client.wait_for_service()
Ⅳ.测试
①先启动客户端而不启动服务器,未报异常
②启动服务器,挂起部分恢复正常
③还有一种方法也可达到上述目的
rospy.wait_for_service("话题名")
三、参数服务器
3.1 本节学什么
1.参数服务器的应用场景
参数服务器在ROS中主要用于实现不同节点之间的数据共享(P2P)。参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点也可以往其中存储数据,关于参数服务器的典型应用场景如下:
导航实现时,会进行路径规划,比如: 全局路径规划,设计一个从出发点到目标点的大致路径。本地路径规划,会根据当前路况生成时时的行进路径
上述场景中,全局路径规划和本地路径规划时,就会使用到参数服务器:
- 路径规划时,需要参考小车的尺寸,我们可以将这些尺寸信息存储到参数服务器,全局路径规划节点与本地路径规划节点都可以从参数服务器中调用这些参数(有些路径限宽限高)
参数服务器,一般适用于存在数据共享的一些应用场景。
我们将小车尺寸放到一个参数服务器中,这样各个节点使用小车尺寸判断是否能通行直接从参数服务器里取就行
2. 本章案例
实现参数的增删改查操作。
3.2 参数服务器理论模型
1.理论模型图示
2.理论模型三个角色
Ⅰ.ROS管理者(其中有一个管理参数的公共容器)
Ⅱ.参数的设置方
Ⅲ. 参数的调用方
3.理论模型的流程
Ⅰ.参数的设置方提交参数到管理者,管理者将参数保存至一个参数列表
Ⅱ.参数的调用者向管理者发送一个参数调用请求
Ⅲ.管理者到参数列表中查找相关参数,将参数值返回给调用者
4.参数服务器的注意事项
参数服务器不是为高性能而设计的,因此最好用于存储静态的非二进制的简单数据
5.参数服务器可以使用的数据类型
32-bit integers
booleans
strings
doubles
iso8601 dates
lists
base64-encoded binary data
字典
3.3 C++实现参数操作
3.3.1 参数新增
Ⅰ.建立功能包 plumbing_param_server,并建立依赖包 roscpp、rospy,std_msgs
Ⅱ.定位到功能包下的src文件,新建文件 demo01_param_set.cpp
Ⅲ.编写代码
#include "ros/ros.h" /* 需要实现参数的新增和修改 1.需求:首先设置机器人的共享参数,类型、半径、再修改半径 2.通过两套api实现 ros::NodeHandle setParam ros::param set */ int main(int argc, char *argv[]) { ros::init(argc,argv,"setpara_c"); ros::NodeHandle nh; //参数增加 //方案1:参数名字,参数值 nh.setParam("type","xiaoHuang"); nh.setParam("radius",0.15); //方案2:参数名字,参数值 ros::param::set("type_param","xiaobai"); ros::param::set("radius_param",0.1); return 0; }
Ⅳ.修改配置文件
## Declare a C++ executable ## With catkin_make all packages are built within a single CMake context ## The recommended prefix ensures that target names across packages don't collide add_executable(demo01_param_set src/demo01_param_set.cpp) ## Rename C++ executable without prefix ## The above recommended prefix causes long target names, the following renames the ## target back to the shorter version for ease of user use ## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node" # set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "") ## Add cmake target dependencies of the executable ## same as for the library above # add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) ## Specify libraries to link a library or executable target against target_link_libraries(demo01_param_set ${catkin_LIBRARIES} )
Ⅴ.编译
Ⅵ.执行:执行完毕之后参数已经到参数服务器了
rosparam list命令:列出所有参数列表
rosparam get /type 命令 :获取type的参数值
3.3.2 参数修改
1.修改代码,采用覆盖思想
//修改 方案一:参数覆盖 nh.setParam("radius",0.2); //修改 方案二 ros::param::set("radius_param",0.25);
3.3.3 参数获取
1.查询步骤
Ⅰ.在功能包下的src目录创建 demo02_param_get.cpp
Ⅱ.了解修改API
/* 参数服务器操作之查询_C++实现: 在 roscpp 中提供了两套 API 实现参数操作 ros::NodeHandle param(键,默认值) 存在,返回对应结果,否则返回默认值 getParam(键,存储结果的变量) 存在,返回 true,且将值赋值给参数2 若果键不存在,那么返回值为 false,且不为参数2赋值 getParamCached键,存储结果的变量)--提高变量获取效率 存在,返回 true,且将值赋值给参数2 若果键不存在,那么返回值为 false,且不为参数2赋值 getParamNames(std::vector<std::string>) 获取所有的键,并存储在参数 vector 中 hasParam(键) 是否包含某个键,存在返回 true,否则返回 false searchParam(参数1,参数2) 搜索键,参数1是被搜索的键,参数2存储搜索结果的变量 ros::param ----- 与 NodeHandle 类似 */
Ⅲ.代码实现
#include "ros/ros.h" /* 演示参数查询 */ int main(int argc, char *argv[]) { setlocale(LC_ALL,""); ros::init(argc,argv,"get_param_c"); ros::NodeHandle nh; //ros::NodeHandle---------------------------------------- //1.param(键,默认值) 存在,返回对应结果,否则返回默认值(0.5) double radius = nh.param("radius",0.5); ROS_INFO("radius = %.2f",radius); //2.getParam(键,存储结果的变量):键存在返回值是true,并且将值赋给参数2。不存在为false,不为参数二赋值 double radius2; bool result = nh.getParam("radius",radius2); if(result) { ROS_INFO("获取的半径是 radius = %.2f",radius2); } else { ROS_INFO("被查询的变量不存在"); } //3.getParamCached(键,存储结果的变量) --从缓存获取,效率更高,结果与3一样的 //4.getParamNames(std::vector<std::string>) 获取所有的键名称,并存储在参数 vector 中 std::vector<std::string> names; nh.getParamNames(names); for(auto && name:names) { ROS_INFO("遍历到的元素:%s",name.c_str()); } //5.hasParam(键) 是否包含某个键 是返回true 否则返回false bool flag1 = nh.hasParam("radius"); bool flag2 = nh.hasParam("sjsjsjs"); ROS_INFO("参数一存在吗?%d",flag1); ROS_INFO("参数二存在吗?%d",flag2); //6.searchParam(参数一,参数二) 参数一是搜索的键,参数二存储键的名称 std::string key ; nh.searchParam("radius",key); ROS_INFO("搜索结果%s",key.c_str()); return 0; }
Ⅳ.修改配置文件
## Declare a C++ executable ## With catkin_make all packages are built within a single CMake context ## The recommended prefix ensures that target names across packages don't collide add_executable(demo01_param_set src/demo01_param_set.cpp) add_executable(demo02_param_get src/demo02_param_get.cpp) ## Rename C++ executable without prefix ## The above recommended prefix causes long target names, the following renames the ## target back to the shorter version for ease of user use ## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node" # set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "") ## Add cmake target dependencies of the executable ## same as for the library above # add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) ## Specify libraries to link a library or executable target against target_link_libraries(demo01_param_set ${catkin_LIBRARIES} ) target_link_libraries(demo02_param_get ${catkin_LIBRARIES} )
3.3.4 参数删除
1.删除步骤
Ⅰ.新建文件 demo04_param_del.cpp,修改配置文件
Ⅱ.编写代码
#include "ros/ros.h" /* 仍然两种方式实现 */ int main(int argc, char *argv[]) { setlocale(LC_ALL,""); ros::init(argc,argv,"param_del"); ros::NodeHandle nh; //删除一 bool flag1 = nh.deleteParam("radius"); if(flag1) { ROS_INFO("success"); } else{ ROS_INFO("Failed"); } //删除二 bool flag2 = ros::param::del("radius_param"); if(flag2) { ROS_INFO("success"); } else{ ROS_INFO("Failed"); } return 0; }
3.4 Python实现参数操作
3.4.1 参数新增与修改
1.步骤
Ⅰ.在功能包下新建scripts文件夹,新建文件demo01_param_set.py
Ⅱ.编写代码逻辑
Ⅲ.添加可执行权限
Ⅳ.修改配置文件
## Mark executable scripts (Python etc.) for installation ## in contrast to setup.py, you can choose the destination catkin_install_python(PROGRAMS scripts/demo01_param_set.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} )
Ⅴ.执行效果
命令:rosparam list 列出所有参数
命令:rosparam get 参数名:获取参数值
修改后效果
3.4.2 参数查询
Ⅰ.新建demo02_param_get.py文件,赋予其可执行权限,修改配置文件
Ⅱ.逻辑:demo01_param_set.py 主要负责设置参数,demo02_param_get.py 负责查询参数
Ⅲ.# get_param : 根据键获取参数的值,第二个参数是默认值
radius = rospy.get_param("radius_p",0.5) rospy.loginfo("radius_p = %.2f",radius) radius2 = rospy.get_param("radius_pxxx",0.5) rospy.loginfo("radius_pxxx = %.2f",radius2)
Ⅳ. #get_param_cached 与上述结果一样,由于从高速缓冲区取数据,效率更高
Ⅴ. #get_param_names 获取所有参数键的集合,无需参数
names = rospy.get_param_names() for name in names: rospy.loginfo("name = %s",name)
Ⅵ. #has_param 判断是否有键
flag1 = rospy.has_param("radius_p") if(flag1): rospy.loginfo("键存在") else: rospy.loginfo("键不存在")
Ⅶ. #search_param 存在返回键名
key = rospy.search_param("radius_p") rospy.loginfo("key = %s",key)
Ⅷ.所有代码
#! /usr/bin/env python #encoding:utf-8 import rospy """ 演示参数的查询 """ if __name__ =="__main__": rospy.init_node("get_param_g") # get_param : 根据键获取参数的值,第二个参数是默认值 radius = rospy.get_param("radius_p",0.5) rospy.loginfo("radius_p = %.2f",radius) radius2 = rospy.get_param("radius_pxxx",0.5) rospy.loginfo("radius_pxxx = %.2f",radius2) #get_param_cached #get_param_names 获取所有参数键的集合,无需参数 names = rospy.get_param_names() for name in names: rospy.loginfo("name = %s",name) #has_param 判断是否有键 flag1 = rospy.has_param("radius_p") if(flag1): rospy.loginfo("键存在") else: rospy.loginfo("键不存在") #search_param 存在返回键名 key = rospy.search_param("radius_p") rospy.loginfo("key = %s",key) pass
3.4.3 参数删除
1.实现
Ⅰ.新建demo03_param_del.py,赋予执行权限,修改配置文件
Ⅱ.实现逻辑及编译
#! /usr/bin/env python #encoding:utf-8 import rospy if __name__ == "__main__": rospy.init_node("del_param") #用delete_param()实现参数删除 rospy.delete_param("radius_p") pass
Ⅲ.实现结果:可以看到,成功删除了参数 radius_p ,另外,重复执行会抛出异常
如果不想抛出异常(即重复删除不想抛出错误,则更改代码逻辑)
#! /usr/bin/env python #encoding:utf-8 import rospy if __name__ == "__main__": rospy.init_node("del_param_s") #用delete_param()实现参数删除 try: rospy.delete_param("radius_p") except Exception as e: rospy.loginfo("被删除的参数不存在") pass
这样就不会重复删除参数不会抛出异常啦!