2026.8.02-初入ros+slam+opencv第七天-C++服务端开发

📅 2026/8/3 4:34:34 👁️ 阅读次数 📝 编程学习
2026.8.02-初入ros+slam+opencv第七天-C++服务端开发

一.掌握点

掌握 ROS服务通信(一问一答同步阻塞模式),独立编写服务客户端代码,调用 turtlesim 自带/spawn服务,自动生成第二只乌龟;分清服务端、客户端,区分服务与话题通信差异。

二.核心理论知识点

话题 vs 服务
话题:单向持续数据流,非阻塞,适合传感器实时数据(乌龟位姿、速度指令)
服务:一问一答同步阻塞通信,需要反馈结果,适合触发式动作(生成 / 删除乌龟)
角色区分
服务端:turtlesim_node,提供/spawn服务,接收请求、完成任务并返回结果
客户端:手写程序,主动发起请求**
关键规则**
允许多个客户端同时调用同一个服务;
同一个服务名称只能存在一个服务端,不能重复启动。

三.实操完整流程

1.在功能包src目录新建 turtle_spawn_client.cpp,编写服务客户端代码

2.代码核心组成:

//引入ROS核心头文件,ros初始化、节点句柄、客户端、日志打印都依赖它#include<ros/ros.h>//引入Spawn服务消息头文件,调用/spawn生成乌龟专用#include<turtlesim/Spawn.h>intmain(intargc,char**argv){// 1.初始化ROS节点// argc,argv固定写法,用于支持命令行传参;// "spawn_client_node":ROS系统内部节点名称,rosnode list可以看到ros::init(argc,argv,"spawn_client_node");// 创建节点句柄nh,相当于程序和roscore通信的“通道”,发布、订阅、服务都需要它setlocale(LC_ALL,"zh_CN.UTF-8");ros::NodeHandle nh;// 2.创建服务客户端对象// nh.serviceClient<消息类型>(服务名称)// <turtlesim::Spawn>:使用的服务消息类型// "/spawn":要连接的服务(turtlesim自带生成乌龟服务)ros::ServiceClient client=nh.serviceClient<turtlesim::Spawn>("/spawn");// 3.实例化服务消息对象// 服务消息分为两部分:request 请求数据、response 返回结果turtlesim::Spawn srv;// 设置请求参数:生成乌龟坐标、朝向、名字srv.request.x=4.0;srv.request.y=4.0;srv.request.theta=0;srv.request.name="turtle2";// ROS日志打印(绿色普通信息)ROS_INFO("等待 /spawn 服务上线...");// 阻塞函数:一直等待,直到/spawn服务启动成功,代码才向下运行// 解决问题:先启动客户端、后打开turtlesim造成调用失败client.waitForExistence();ROS_INFO("发起生成乌龟请求...");// 发起同步服务调用!【重点】// 程序会卡在这一行,等待服务端处理并返回结果(阻塞)// 调用成功返回true,失败返回falseboolresult=client.call(srv);if(result){// srv.response 存放服务返回的数据// .c_str():把C++字符串转为C语言字符串,满足ROS_INFO打印要求ROS_INFO("生成成功!乌龟名称:%s",srv.response.name.c_str());}else{// ERROR级别日志,红色警告ROS_ERROR("生成乌龟失败!");}return0;}

nh.serviceClient<消息类型>(服务名) 创建客户端

turtlesim::Spawn srv; 承载请求 request 和响应 response 数据

client.waitForExistence():阻塞等待服务上线,解决启动顺序问题

client.call(srv):发送请求,同步阻塞,等待服务端返回结果

3.修改CMakeLists.txt添加编译规则
add_executable(可执行文件名 cpp源码路径)
target_link_libraries链接库

4.编译工作空间 catkin_make,刷新环境变量
多终端顺序启动测试:roscore → turtlesim_node → 自定义客户端节点

四.易错点

turtlesim::Spawn模板参数不能删除,指定通信的数据格式;

三层名称不要混淆:cpp 文件名、CMake 生成的可执行程序名、ros::init内部节点名,三者互相独立;

waitForExistence()仅仅检测服务是否存在,不会发送请求;真正执行功能依靠client.call();
若删除waitForExistence(),先启动客户端、后启动乌龟仿真,call 直接返回 false,调用失败;

服务客户端代码不需要 ros::spin();

重复运行客户端创建同名乌龟,调用会失败,进入错误分支。