更新命令行,ctrl+c
1.自定义srv
1.1定义srv文件
服务通信中,数据分成两部分,请求与响应,在 srv 文件中请求和响应使用---
分割,具体实现如下:
功能包下新建 srv 目录,添加 xxx.srv 文件,内容:
CMakeLists.txt编辑 srv 相关配置
1.2 编辑配置文件package.xml
需求:
编写服务通信,客户端提交两个整数至服务端,服务端求和并响应结果到客户端。
分析:
在模型实现中,ROS master 不需要实现,而连接的建立也已经被封装了,需要关注的关键点有三个:
- 服务端
- 客户端
- 数据
流程:
- 编写服务端实现;
- 编写客户端实现;
- 编辑配置文件;
- 编译并执行。
0.VScode的配置
需要像之前自定义 msg 实现一样配置c_cpp_properies.json 文件
{
"configurations": [
{
"browse": {
"databaseFilename": "${workspaceFolder}/.vscode/browse.vc.db",
"limitSymbolsToIncludedHeaders": false
},
"includePath": [
"/opt/ros/melodic/include/**",
"/home/su/catkin_ws/src/learning_topic/include/**",
"/usr/include/**",
"/home/su/demo05_ws/devel/include/**"//添加此处
],
"name": "ROS",
"intelliSenseMode": "gcc-x64",
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu11",
"cppStandard": "c++14"
}
],
"version": 4
}
路径的获取:在集成终端中打开
编辑配置文件package.xml
server.cpp
#include"ros/ros.h"
#include"plumbing_server_client/AddInts.h"
/*
需求:
编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器
服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,
客户端再解析
服务器实现:
1.包含头文件
2.初始化 ROS 节点
3.创建 ROS 句柄
4.创建 服务 对象
5.回调函数处理请求并产生响应
6.由于请求有多个,需要调用 ros::spin()
*/
// bool 返回值由于标志是否处理成功
bool doReq(plumbing_server_client::AddInts::Request& req,
plumbing_server_client::AddInts::Response& resp){
int num1 = req.num1;
int num2 = req.num2;
ROS_INFO("服务器接收到的请求数据为:num1 = %d, num2 = %d",num1, num2);
//逻辑处理
if (num1 < 0 || num2 < 0)
{
ROS_ERROR("提交的数据异常:数据不可以为负数");
return false;
}
//如果没有异常,那么相加并将结果赋值给 resp
resp.sum = num1 + num2;
return true;
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
// 2.初始化 ROS 节点
ros::init(argc,argv,"AddInts_Server");
// 3.创建 ROS 句柄
ros::NodeHandle nh;
// 4.创建 服务 对象
ros::ServiceServer server = nh.advertiseService("AddInts",doReq);
ROS_INFO("服务已经启动....");
// 5.回调函数处理请求并产生响应
// 6.由于请求有多个,需要调用 ros::spin()
ros::spin();
return 0;
}
client.cpp
#include"ros/ros.h"
#include"plumbing_server_client/AddInts.h""
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
// 调用时动态传值,如果通过 launch 的 args 传参,需要传递的参数个数 +3
if (argc != 3)
// if (argc != 5)//launch 传参(0-文件路径 1传入的参数 2传入的参数 3节点名称 4日志路径)
{
ROS_ERROR("请提交两个整数");
return 1;
}
// 2.初始化 ROS 节点
ros::init(argc,argv,"AddInts_Client");
// 3.创建 ROS 句柄
ros::NodeHandle nh;
// 4.创建 客户端 对象
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::AddInts>("AddInts");
//ROS中内置了相关函数,这些函数可以让客户端启动后挂起,等待服务器启动
//等待服务启动成功
//方式1
ros::service::waitForService("AddInts");
//方式2
// client.waitForExistence();
// 5.组织请求数据
plumbing_server_client::AddInts ai;
ai.request.num1 = atoi(argv[1]);
ai.request.num2 = atoi(argv[2]);
// 6.发送请求,返回 bool 值,标记是否成功
bool flag = client.call(ai);
// 7.处理响应
if (flag)
{
ROS_INFO("请求正常处理,响应结果:%d",ai.response.sum);
}
else
{
ROS_ERROR("请求处理失败....");
return 1;
}
return 0;
}
2.参数服务器
设置参数
param_set.cpp
#include"ros/ros.h"
int main(int argc,char*argv[])
{
ros::init(argc,argv,"set_param_c");
ros::NodeHandle nh;
//方案1增
nh.setParam("type","xiaobai");
nh.setParam("radius",0.15);
//方案二ros::param
ros::param::set("type_param","xiaohuang");
ros::param::set("radius_param",0.15);
//改,方案1
nh.setParam("radius",0.2);
//方案二
ros::param::set("radius",0.25);
return 0;
}
获取参数
param_get.cpp
#include "ros/ros.h"
int main(int argc,char*argv[])
{
setlocale(LC_ALL,"");
ros::init(argc,argv,"get_param");
ros::NodeHandle nh;
//1.param
double radius=nh.param("radius",0.5);//0.5为赋予的默认值
ROS_INFO("radius=%.2f",radius);
//2.getParam
double radius1=0.0;
// bool result =nh.getParam("radius1",radius1);
//3.getParamCached与getParam类似,只是性能上有提升,一般测试下,看不出来
bool result =nh.getParamCached("radius",radius1);
if(result)
{
ROS_INFO("获取的半径是:%2.f",radius1);
}
else{
ROS_INFO("获取的数据错误");
}
//getParamNames()
std::vector<std::string> names;
nh.getParamNames(names);
//右值引用
for(auto &&name:names)
{
ROS_INFO("遍历的元素:%s",name.c_str());
}
//5.hasParam
bool flag1 =nh.hasParam("radius");
bool flag2=nh.hasParam("radius1");
ROS_INFO("radius存在吗?%d",flag1);
ROS_INFO("radius1存在吗 %d",flag2);
//6.searchParam
std::string key;
nh.searchParam("radius",key);
ROS_INFO("搜索结果:%s",key.c_str());
return 0;
}