当然可以!以下是完全适配 CSDN 文章排版 的整理版内容,已按照你提供的结构进行优化排版:保留原始逻辑、注释清晰、语法高亮标记明确、标题分级规范、重点突出,并在文末添加了你提到的进阶建议 ------ OMPL 路径规划极简代码片段,方便你后续扩展文章内容。
ROS Action 完整示例:客户端发目标 + 服务器接参数(lambda 替代 boost::bind)
本文提供一个极简可运行的 ROS Action 示例 ,包含「OMPLDWBNavigator 服务器类 + 测试客户端」,清晰演示参数传递全过程。使用 C++11 lambda 表达式替代 boost::bind 实现回调绑定,适配 ROS Noetic / Melodic 等主流版本。
✅ 特点:代码简洁、可直接编译运行、适合新手理解 Action 通信机制
🔁 场景:机器人导航中"客户端发送目标 → 服务器执行路径规划 + 控制"的典型流程
一、完整源代码(可直接编译运行)
保存为 ompl_dwb_navigator_demo.cpp
cpp
#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include <actionlib/client/simple_action_client.h>
#include <move_base_msgs/MoveBaseAction.h> // 标准导航 Action 类型(ROS 自带)
// 核心服务器类:OMPLDWBNavigator(简化版,贴合实际项目结构)
class OMPLDWBNavigator {
private:
ros::NodeHandle nh_; // ROS 节点句柄
// Action 服务器对象(类型:MoveBaseAction)
actionlib::SimpleActionServer<move_base_msgs::MoveBaseAction> as_;
std::string action_name_; // Action 名称(与客户端一致)
/**
* 回调函数:接收客户端发送的目标参数
* @param goal 客户端发送的目标(由 ROS 框架自动传入,对应原 boost::bind 的 _1)
*/
void executeCB(const move_base_msgs::MoveBaseGoalConstPtr& goal) {
ROS_INFO("[服务器] 收到客户端目标!");
ROS_INFO("[服务器] 目标位置:x = %.2f, y = %.2f",
goal->target_pose.pose.position.x,
goal->target_pose.pose.position.y);
// 模拟执行导航任务(实际项目中替换为 OMPL 路径规划 + DWB 速度控制)
ros::Duration(2).sleep(); // 休眠 2 秒,模拟任务执行
// 向客户端反馈任务执行成功
as_.setSucceeded();
ROS_INFO("[服务器] 目标执行完成!");
}
public:
/**
* 构造函数:用 lambda 替代 boost::bind 绑定回调
* 核心:[this] 捕获对象指针,(goal) 接收 ROS 自动传入的参数
*/
OMPLDWBNavigator(const std::string& name)
: as_(nh_, name,
// lambda 替代 boost::bind(&OMPLDWBNavigator::executeCB, this, _1)
[this](const move_base_msgs::MoveBaseGoalConstPtr& goal) {
this->executeCB(goal); // 手动转发目标参数
},
false), // false:不自动启动服务器,手动调用 as_.start()
action_name_(name) {
as_.start(); // 启动 Action 服务器
ROS_INFO("[服务器] 已启动,等待客户端发送目标...");
}
~OMPLDWBNavigator() {} // 析构函数(空实现,实际项目可加资源释放)
};
// ---------------------- 测试客户端(发送目标给服务器)----------------------
void sendGoalToServer() {
// 1. 创建 Action 客户端(参数1:Action 名称,需与服务器一致;参数2:true=自动自旋)
actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> ac("ompl_dwb_navigator", true);
ROS_INFO("[客户端] 等待服务器启动...");
ac.waitForServer(); // 阻塞等待服务器上线
ROS_INFO("[客户端] 服务器已连接,准备发送目标!");
// 2. 构造目标数据(可修改 x、y 坐标测试)
move_base_msgs::MoveBaseGoal goal;
goal.target_pose.header.frame_id = "base_link"; // 坐标系(简化使用 base_link)
goal.target_pose.header.stamp = ros::Time::now(); // 时间戳
goal.target_pose.pose.position.x = 1.0; // 目标 x 坐标
goal.target_pose.pose.position.y = 2.0; // 目标 y 坐标
goal.target_pose.pose.orientation.w = 1.0; // 姿态(无旋转,w=1 表示姿态正常)
// 3. 发送目标给服务器
ac.sendGoal(goal);
ROS_INFO("[客户端] 目标已发送:x = 1.0, y = 2.0");
// 4. 等待服务器执行完成,并判断结果
ac.waitForResult();
if (ac.getState() == actionlib::SimpleClientGoalState::SUCCEEDED) {
ROS_INFO("[客户端] 服务器执行目标成功!");
} else {
ROS_WARN("[客户端] 服务器执行目标失败!");
}
}
// ---------------------- 主函数(启动节点)----------------------
int main(int argc, char** argv) {
// 初始化 ROS 节点(节点名称:ompl_dwb_navigator_demo)
ros::init(argc, argv, "ompl_dwb_navigator_demo");
// 1. 创建服务器对象(启动 Action 服务器)
OMPLDWBNavigator server("ompl_dwb_navigator"); // Action 名称统一
// 2. 启动客户端并发送目标(测试用,实际项目可拆分到单独节点)
sendGoalToServer();
ros::spin(); // 保持节点运行(循环处理回调)
return 0;
}
二、编译配置文件(CMakeLists.txt)
放在工作空间 src 目录下(与 .cpp 文件同目录)
cmake
cmake_minimum_required(VERSION 3.0.2)
project(ompl_dwb_navigator_demo) # 项目名称,需与包名一致
# 查找依赖包(ROS 核心依赖)
find_package(catkin REQUIRED COMPONENTS
roscpp
actionlib
move_base_msgs
)
# 声明包的依赖(供其他包调用)
catkin_package(
CATKIN_DEPENDS roscpp actionlib move_base_msgs
)
# 包含头文件目录
include_directories(
${catkin_INCLUDE_DIRS}
)
# 编译节点(可执行文件名称:ompl_dwb_navigator_demo)
add_executable(ompl_dwb_navigator_demo src/ompl_dwb_navigator_demo.cpp)
# 链接依赖库
target_link_libraries(ompl_dwb_navigator_demo
${catkin_LIBRARIES}
)
三、运行步骤(详细可复制)
bash
# 1. 启动 ROS 核心(新终端)
roscore
# 2. 编译代码(在工作空间根目录,新终端)
catkin_make
source devel/setup.bash
# 3. 运行节点(新终端)
rosrun ompl_dwb_navigator_demo ompl_dwb_navigator_demo
四、预期运行日志(参数传递全过程)
[ INFO] [1712345678.901234]: [服务器] 已启动,等待客户端发送目标...
[ INFO] [1712345678.901567]: [客户端] 等待服务器启动...
[ INFO] [1712345678.901789]: [客户端] 服务器已连接,准备发送目标!
[ INFO] [1712345678.902012]: [客户端] 目标已发送:x = 1.0, y = 2.0
[ INFO] [1712345678.902234]: [服务器] 收到客户端目标!
[ INFO] [1712345678.902456]: [服务器] 目标位置:x = 1.00, y = 2.00
[ INFO] [1712345680.902678]: [服务器] 目标执行完成!
[ INFO] [1712345680.902901]: [客户端] 服务器执行目标成功!
五、核心知识点(适配文章讲解)
1. lambda 替代 boost::bind 的核心逻辑(对应原问题)
| 原写法(boost::bind) | Lambda 等价写法 | 说明 |
|---|---|---|
boost::bind(&OMPLDWBNavigator::executeCB, this, _1) |
[this](const auto& goal) { this->executeCB(goal); } |
完全等价,无功能差异 |
this |
捕获列表 [this] |
获取当前对象指针,用于调用成员函数 |
_1 |
lambda 参数 goal |
接收 ROS 框架自动传入的目标参数 |
💡 使用 lambda 更现代、更直观,且避免了
boost::bind的语法复杂性和编译警告。
2. 参数传递核心疑问解答(读者常见问题)
-
「参数什么时候传?」
当客户端调用
ac.sendGoal(goal)后,ROS ActionLib 框架会自动触发服务器端的executeCB回调函数。 -
「参数从哪来?」
来自客户端构造的
move_base_msgs::MoveBaseGoal对象,如x=1.0, y=2.0。 -
「参数在哪接?」
在服务器的
executeCB函数中通过const move_base_msgs::MoveBaseGoalConstPtr& goal参数接收,可直接访问其字段。
3. 避坑提醒(文章重点标注)
✅ 依赖包无需额外安装 :move_base_msgs 是 ROS 自带导航包,直接编译即可。
⚠️ lambda 不可少 [this] :否则无法调用类成员函数 executeCB,编译报错。
❗ Action 名称必须一致 :服务器与客户端都使用 "ompl_dwb_navigator",否则无法通信。
🔧 进阶建议:加入 OMPL 路径规划极简代码片段(可用于后续文章扩展)
如果你想让这个示例更贴近真实导航场景,可以在 executeCB 中加入一个极简 OMPL 路径规划模拟逻辑,如下所示:
cpp
// 在 executeCB 中添加路径规划模拟(伪 OMPL)
ROS_INFO("[服务器] 开始路径规划(OMPL 模拟)...");
// 简化:假设我们有一个"地图"边界
double start_x = 0.0, start_y = 0.0;
double goal_x = goal->target_pose.pose.position.x;
double goal_y = goal->target_pose.pose.position.y;
std::vector<std::pair<double, double>> path;
// 模拟生成一条直线路径(每 0.5 米一个点)
for (double t = 0; t <= 1.0; t += 0.5) {
double x = start_x + t * (goal_x - start_x);
double y = start_y + t * (goal_y - start_y);
path.push_back({x, y});
ROS_INFO("[OMPL] 路径点: (%.2f, %.2f)", x, y);
}
ROS_INFO("[服务器] 路径规划完成,共 %lu 个路径点。", path.size());
📌 提示:真实 OMPL 集成需结合
ompl库、状态空间定义、碰撞检测(如costmap_2d),但此模拟可用于教学演示路径生成过程。
你可以在后续文章中逐步引入 nav_core 插件接口、global_planner 封装或 ompl_ros 集成,层层递进。
✅ CSDN 排版说明(作者自用参考)
- 所有代码块均标注语言类型(
cpp/cmake/bash/plaintext),CSDN 可自动高亮; - 标题使用
#和##分级,支持右侧文章目录生成; - 关键知识点使用表格、加粗、引用块突出显示,提升可读性;
- 注释详尽,段落清晰,读者可一键复制运行;
- 最后添加"进阶建议",为系列文章留出扩展空间。
需要我帮你把这个示例拆成两个独立节点(服务端 + 客户端分离)、或者封装成 ROS Package 模板(含 package.xml)吗?我可以继续补充,让你的文章更完整~ 😊