一个基于 ROS 2 + turtlesim + C++ 的小海龟巡逻练习项目。
项目通过 自定义 Service 接口 + 发布/订阅控制 的方式,让客户端周期性发送随机目标点,服务端接收目标点后控制 turtlesim 中的小海龟移动到指定位置。
这个项目适合用来练习:
- ROS 2 C++ 节点开发
- 自定义
.srv服务接口 - ROS 2 Service 客户端 / 服务端通信
publisher / subscriber的基本使用turtlesim的运动控制- 基于目标点的简单比例控制
- ROS 2 launch 文件编写与参数传递
本项目实现了一个完整的目标点控制流程:
- 客户端每隔一段时间生成一个随机目标点
- 客户端调用
patrol服务,把目标点发送给服务端 - 服务端判断目标点是否合法
- 若合法,则更新当前目标点
- 服务端持续订阅小海龟当前位置
- 根据当前位置和目标点计算控制量
- 发布速度指令到
/turtle1/cmd_vel - 小海龟朝新的目标点移动
从功能上说,它更准确地是一个“随机目标点追踪”项目,而不是固定路径巡逻项目。
ros2-turtle-patrol/
├── .gitignore
├── README.md
└── src/
├── chapt4_turtle_interfaces/
│ ├── CMakeLists.txt
│ ├── package.xml
│ └── srv/
│ └── Patrol.srv
└── demo_cpp_service/
├── CMakeLists.txt
├── package.xml
├── launch/
│ └── demo.launch.py
└── src/
├── patrol_client.cpp
└── turtle_control.cpp
这是接口包,用于定义自定义服务:
float32 target_x
float32 target_y
---
int8 SUCESS = 1
int8 FAIL = 0
int8 result说明:
target_x、target_y:客户端发送的目标点坐标result:服务端返回的处理结果SUCESS/FAIL:结果状态常量
注意:源码中常量名写的是 SUCESS,这是代码中的实际拼写。
这是功能包,包含两个 C++ 节点和一个 launch 文件:
patrol_client.cpp:服务客户端,周期性发送随机目标点turtle_control.cpp:服务端 + 控制节点,接收目标点并驱动小海龟移动launch/demo.launch.py:一键启动整个示例系统
节点名:
turtle_patrol_client
主要功能:
- 创建
patrol服务客户端 - 使用 10 秒定时器周期性触发请求
- 在发送请求前等待服务可用
- 随机生成目标点
target_x和target_y - 异步发送服务请求
- 根据服务端响应输出成功或失败日志
客户端的随机目标点生成逻辑是:
request->target_x = static_cast<float>(rand() % 15);
request->target_y = static_cast<float>(rand() % 15);这意味着客户端会生成 0 ~ 14 之间的整数坐标。
节点名:
turtle_control
主要功能:
- 创建
patrol服务 - 接收客户端发送的目标点
- 判断目标点是否合法
- 若合法则更新目标点
- 订阅
/turtle1/pose获取当前海龟位姿 - 发布
/turtle1/cmd_vel控制海龟运动
/patrol
- 订阅:
/turtle1/pose - 发布:
/turtle1/cmd_vel
数据流关系如下:
patrol_client
└── 调用服务 /patrol
↓
turtle_control
├── 订阅 /turtle1/pose
└── 发布 /turtle1/cmd_vel
↓
turtlesim 中的 turtle1
项目现在已经加入了 demo.launch.py,可以一次性启动整个示例系统。
这个 launch 文件会:
- 声明一个 launch 参数
launch_arg_bg - 启动
turtlesim_node - 将
launch_arg_bg作为background_g参数传给turtlesim_node - 启动
patrol_client - 启动
turtle_control
默认参数:
launch_arg_bg = 150
也就是说,默认会将 turtlesim 背景的绿色通道设置为 150。
整个项目的运行流程如下:
- 启动
turtlesim_node - 启动
turtle_control节点 - 服务端创建
patrol服务,并持续监听/turtle1/pose - 启动
patrol_client节点 - 客户端每隔 10 秒生成一个随机目标点
- 客户端向
patrol服务发送请求 - 服务端判断坐标是否合法
- 若合法,则更新目标坐标
- 控制节点根据当前位姿与目标点计算速度
- 小海龟朝目标点移动
服务端采用了一个很基础的目标点控制方法。
通过订阅 /turtle1/pose 获取当前:
xytheta
distance = sqrt((target_x - current_x)^2 + (target_y - current_y)^2)
angle = atan2(target_y - current_y, target_x - current_x) - theta
- 当距离大于
0.1时,才开始运动 - 当角度误差大于
0.2时,优先旋转 - 当角度误差较小时,开始前进
线速度使用:
linear.x = k * distance
并设置了最大线速度限制:
max_speed = 3.0
服务端只接受满足条件的目标点。根据当前源码,判断逻辑等价于:
target_x != 0target_y != 0target_x < 12.0target_y < 12.0
因此:
- 坐标为
0的点不会通过 - 大于等于
12.0的点不会通过
而客户端随机生成的是 0 ~ 14,所以有些请求会被服务端拒绝,这是代码设计上的正常现象。
建议环境:
- Ubuntu 22.04
- ROS 2 Humble
rclcppgeometry_msgsturtlesimcolcon
在工作空间根目录执行:
colcon build --packages-select chapt4_turtle_interfaces demo_cpp_service编译完成后加载环境:
source install/setup.bashsource install/setup.bash
ros2 launch demo_cpp_service demo.launch.py指定背景绿色通道:
ros2 launch demo_cpp_service demo.launch.py launch_arg_bg:=0或者:
ros2 launch demo_cpp_service demo.launch.py launch_arg_bg:=255ros2 run turtlesim turtlesim_nodesource install/setup.bash
ros2 run demo_cpp_service turtle_controlsource install/setup.bash
ros2 run demo_cpp_service patrol_client运行后,客户端会每隔 10 秒发送一个随机目标点,小海龟会尝试朝该目标点移动。
demo_cpp_service 当前会编译出两个可执行程序:
turtle_controlpatrol_client
这个项目有几个比较明显的特点:
它不是单纯地“收到消息就动”,而是通过 Service 先更新目标点,再通过订阅/发布实现持续控制。
接口包与功能包是分开的:
chapt4_turtle_interfaces只负责接口demo_cpp_service只负责功能实现
这比较符合 ROS 2 工程化的组织方式。
除了原来的手动多终端运行,现在也可以通过 launch 文件一键启动整个系统,并通过参数修改 turtlesim 的背景绿色通道。
代码重点是练习通信与控制流程,因此控制器设计得比较基础,便于理解。
根据当前源码,这个项目还有一些比较明显的限制:
当前直接计算:
atan2(...) - theta
没有把角度归一化到 [-pi, pi],因此在某些角度情况下可能不是最优转向。
当角度误差较大时,代码里使用的是:
msg.angular.z = fabs(angle);这会丢失正负号,意味着转向方向逻辑并不完整,严格来说还有改进空间。
客户端生成 0 ~ 14,服务端只接受小于 12.0 且非 0 的点,因此会出现部分请求天然失败。
它没有预设多个巡逻点,也没有闭环路线,只是不断接收新的随机目标点。
后续可以继续扩展:
- 对角度误差做归一化处理
- 保留角速度符号,让海龟能正确选择左转或右转
- 优化随机坐标生成范围,使其与合法范围一致
- 支持多个巡逻点
- 实现固定路线巡逻
- 增加目标到达提示
- 增加更清晰的状态反馈
- 使用更多 launch 参数,例如背景 RGB、定时周期、速度参数等
通过这个项目,可以练习:
- ROS 2 workspace 与 package 的组织方式
- 自定义
.srv接口的创建与使用 - ROS 2 C++ Service 客户端与服务端开发
publisher / subscriber的配合使用- turtlesim 的基础控制
- launch 文件编写与参数传递
- 从“请求目标”到“闭环移动”的基本思路
Apache-2.0