在ROS(Robot Operating System)中,Action 是一种高级通信机制,适用于需要长时间运行的任务,并且这些任务可以分解为多个步骤或需要实时反馈的场景。Action 通信结合了 Goal(目标)、Feedback(反馈) 和 Result(结果) 的特性,非常适合处理复杂的任务。
以下是 Action 通常使用的场景及其作用,以及几个经典的应用情景:
Action 的作用
-
支持长时间运行的任务:
- Action 允许任务在后台运行,客户端不需要阻塞等待任务完成。
- 例如,机器人导航到目标点可能需要几分钟,Action 可以持续提供反馈。
-
实时反馈:
- 在任务执行过程中,Action Server 可以不断向客户端发送反馈信息。
- 例如,机器人移动时,可以实时反馈当前位置。
-
任务取消和状态管理:
- 客户端可以随时取消任务,Action Server 会处理取消逻辑。
- 例如,用户在机器人导航过程中取消目标点。
-
任务结果返回:
- 任务完成后,Action Server 会返回最终结果。
- 例如,机器人到达目标点后,返回成功或失败的状态。
Action 的经典应用场景
1. 机器人导航(Navigation)
- 场景描述:
- 机器人需要从当前位置移动到目标位置。
- 导航过程可能需要较长时间,且需要实时反馈机器人的位置和状态。
- Action 的作用:
- Goal:目标位置(如坐标点)。
- Feedback:机器人当前的位置、速度、剩余距离等。
- Result:是否成功到达目标位置。
- 示例:
- ROS 中的
move_base
包就是一个典型的 Action Server,用于实现机器人导航。
- ROS 中的
2. 机械臂运动控制(Arm Motion Planning)
- 场景描述:
- 机械臂需要从当前位置移动到目标姿态(如抓取物体)。
- 运动规划过程可能需要较长时间,且需要实时反馈机械臂的状态。
- Action 的作用:
- Goal:目标姿态(如关节角度或末端执行器位置)。
- Feedback:机械臂的当前姿态、运动进度等。
- Result:是否成功到达目标姿态。
- 示例:
- ROS 中的
MoveIt!
框架使用 Action 来实现机械臂的运动规划和控制。
- ROS 中的
3. 物体识别与抓取(Object Detection and Grasping)
- 场景描述:
- 机器人需要识别桌面上的物体并抓取。
- 识别和抓取过程可能需要多次尝试,且需要实时反馈识别结果和抓取进度。
- Action 的作用:
- Goal:目标物体(如物体的位置或类别)。
- Feedback:识别进度、抓取尝试次数等。
- Result:是否成功抓取物体。
- 示例:
- 使用 Action 实现物体识别和抓取任务,结合视觉和机械臂控制。
4. 路径规划与避障(Path Planning and Obstacle Avoidance)
- 场景描述:
- 机器人在移动过程中需要动态规划路径并避开障碍物。
- 路径规划过程需要实时反馈当前路径和障碍物信息。
- Action 的作用:
- Goal:目标位置。
- Feedback:当前路径、障碍物信息、规划进度等。
- Result:是否成功规划并到达目标位置。
- 示例:
- ROS 中的
global_planner
和local_planner
使用 Action 实现路径规划和避障。
- ROS 中的
5. 长时间任务监控(Long-running Task Monitoring)
- 场景描述:
- 机器人执行一个长时间任务(如清洁、巡检等),需要实时监控任务进度。
- Action 的作用:
- Goal:任务目标(如清洁区域或巡检路线)。
- Feedback:任务进度、当前状态等。
- Result:任务是否成功完成。
- 示例:
- 使用 Action 实现清洁机器人或巡检机器人的任务监控。
6. 多步任务执行(Multi-step Task Execution)
- 场景描述:
- 机器人需要执行一个多步骤的任务(如打开门、进入房间、关闭门)。
- 每个步骤可能需要较长时间,且需要实时反馈当前步骤的状态。
- Action 的作用:
- Goal:任务目标(如进入房间)。
- Feedback:当前步骤、步骤进度等。
- Result:任务是否成功完成。
- 示例:
- 使用 Action 实现多步骤任务的执行和监控。
Action 的优势
- 异步通信:
- 客户端不需要阻塞等待任务完成,可以同时处理其他任务。
- 实时反馈:
- 客户端可以实时了解任务进度,便于监控和调整。
- 任务管理:
- 支持任务取消、暂停和恢复,适合复杂的任务场景。
- 模块化设计:
- 将任务目标、反馈和结果分离,便于扩展和维护。
总结
Action 通信在 ROS 中广泛应用于需要长时间运行、实时反馈和任务管理的场景。经典的应用包括机器人导航、机械臂控制、物体抓取、路径规划等。通过 Action,ROS 提供了一种高效、灵活的通信机制,适用于复杂的机器人任务。
我们结合机器人导航具体看以下action代码
在机器人导航场景中,Action通信 的客户端和服务端分别对应不同的功能模块。以下是详细的解释:
1. 客户端节点(Client)
对应模块
- 在机器人导航中,客户端通常是 任务规划模块 或 用户交互模块。
- 例如:
- 用户通过界面发送导航目标。
- 高级任务规划器(如任务调度系统)发送导航指令。
主要职责
- 发送导航目标:
- 客户端向服务端发送目标位置(如地图坐标或目标点名称)。
- 目标通常包括位置(x, y)和方向(theta)。
- 接收反馈:
- 客户端实时接收服务端发送的反馈信息,如机器人当前位置、速度、剩余距离等。
- 反馈信息可以用于显示导航进度或调整任务。
- 处理结果:
- 客户端接收服务端返回的最终结果,如是否成功到达目标位置。
- 根据结果决定下一步操作(如重新规划路径或结束任务)。
逻辑流程
- 创建 Action Client。
- 等待 Action Server 启动。
- 发送导航目标。
- 监听反馈信息并处理。
- 接收最终结果并处理。
2. 服务端节点(Server)
对应模块
- 在机器人导航中,服务端通常是 导航栈(Navigation Stack) 的核心模块。
- 例如:
move_base
是 ROS 中常用的导航 Action Server。
主要职责
- 接收导航目标:
- 服务端接收客户端发送的目标位置。
- 路径规划:
- 根据目标位置和当前地图,使用全局规划器(如 A* 或 Dijkstra)生成一条全局路径。
- 局部控制:
- 使用局部规划器(如 DWA 或 TEB)控制机器人沿路径移动,同时避开动态障碍物。
- 发布反馈:
- 实时向客户端发送反馈信息,如机器人当前位置、速度、剩余距离等。
- 返回结果:
- 当机器人到达目标位置或任务失败时,返回最终结果。
逻辑流程
- 创建 Action Server。
- 等待客户端发送目标。
- 接收目标后,启动路径规划和局部控制。
- 在导航过程中,实时发布反馈信息。
- 导航完成后,返回结果。
3. 具体逻辑示例
以下是一个典型的机器人导航场景中,客户端和服务端的逻辑流程:
客户端逻辑
- 初始化:
- 创建 Action Client,连接到
move_base
Action Server。
- 创建 Action Client,连接到
- 发送目标:
- 用户输入目标位置(如 x=5.0, y=3.0, theta=0.0)。
- 客户端将目标发送给服务端。
- 监听反馈:
- 实时接收服务端发送的反馈信息,如机器人当前位置。
- 在界面上显示导航进度。
- 处理结果:
- 如果导航成功,显示“到达目标”。
- 如果导航失败,显示“任务失败”并提示用户重新规划。
服务端逻辑
- 初始化:
- 创建 Action Server,等待客户端连接。
- 接收目标:
- 接收客户端发送的目标位置。
- 路径规划:
- 使用全局规划器生成一条从当前位置到目标位置的路径。
- 局部控制:
- 控制机器人沿路径移动,同时避开动态障碍物。
- 发布反馈:
- 实时发布机器人当前位置、速度、剩余距离等信息。
- 返回结果:
- 如果机器人到达目标位置,返回“成功”。
- 如果任务失败(如路径被阻塞),返回“失败”。
4. 代码示例
以下是一个简化的客户端和服务端代码示例:
客户端代码
#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h>
#include <move_base_msgs/MoveBaseAction.h>
// 定义反馈回调函数
void feedbackCB(const move_base_msgs::MoveBaseFeedbackConstPtr &feedback) {
// 打印反馈信息
ROS_INFO("Current Position: x=%f, y=%f",
feedback->base_position.pose.position.x,
feedback->base_position.pose.position.y);
}
int main(int argc, char** argv) {
ros::init(argc, argv, "navigation_client");
ros::NodeHandle nh;
// 创建Action Client,连接到move_base
actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> client("move_base", true);
// 等待Action Server启动
ROS_INFO("Waiting for action server to start.");
client.waitForServer();
ROS_INFO("Action server started, sending goal.");
// 创建目标
move_base_msgs::MoveBaseGoal goal;
goal.target_pose.header.frame_id = "map";
goal.target_pose.header.stamp = ros::Time::now();
goal.target_pose.pose.position.x = 5.0; // 目标位置的x坐标
goal.target_pose.pose.position.y = 3.0; // 目标位置的y坐标
goal.target_pose.pose.orientation.w = 1.0; // 目标方向
// 发送目标,并绑定反馈回调函数
client.sendGoal(goal, &feedbackCB);
// 等待结果
client.waitForResult();
if (client.getState() == actionlib::SimpleClientGoalState::SUCCEEDED) {
ROS_INFO("Goal reached!");
} else {
ROS_INFO("Failed to reach goal.");
}
return 0;
}
服务端代码
#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include <move_base_msgs/MoveBaseAction.h>
class MoveBaseServer {
public:
MoveBaseServer(std::string name) :
as_(nh_, name, boost::bind(&MoveBaseServer::executeCB, this, _1), false),
action_name_(name) {
as_.start();
ROS_INFO("%s: Action Server started.", action_name_.c_str());
}
void executeCB(const move_base_msgs::MoveBaseGoalConstPtr &goal) {
// 模拟导航过程
ros::Rate rate(1);
bool success = true;
for (int i = 0; i < 5; ++i) {
// 检查任务是否被取消
if (as_.isPreemptRequested() || !ros::ok()) {
as_.setPreempted();
success = false;
break;
}
// 发布反馈
move_base_msgs::MoveBaseFeedback feedback;
feedback.base_position.header.stamp = ros::Time::now();
feedback.base_position.pose.position.x = i * 1.0;
feedback.base_position.pose.position.y = i * 0.5;
as_.publishFeedback(feedback);
rate.sleep();
}
// 返回结果
if (success) {
move_base_msgs::MoveBaseResult result;
result.result = "Goal reached!";
as_.setSucceeded(result);
}
}
private:
ros::NodeHandle nh_;
actionlib::SimpleActionServer<move_base_msgs::MoveBaseAction> as_;
std::string action_name_;
};
int main(int argc, char** argv) {
ros::init(argc, argv, "move_base_server");
MoveBaseServer server("move_base");
ros::spin();
return 0;
}
以上代码中有两处地方解释以下
第一点是MoveBaseServer构造函数中使用初始化列表 初始化Action Server,这里使用boost库中的bind函数绑定回调函数 executeCB,用于处理客户端发送的目标。这是ros1的写法,在ros2中Boost 被逐步替换为 C++11/14/17 的标准库功能(如 std::bind),我们可以直接使用 std::bind来绑定回调函数
第二点是参数的类型,接收端接收参数的类型一定是加上ConstPtr,这是一个固定的规则,是 ROS 中 Action 通信机制的一部分。
比如move_base_msgs::MoveBaseGoalConstPtr 是 move_base_msgs::MoveBaseGoal 的智能指针类型。
ConstPtr 表示这是一个常量指针(即指向常量数据的指针),确保回调函数不会修改目标数据。
不管是goal还是feedback模块,都需要遵循,
5. 总结
- 客户端:负责发送导航目标、接收反馈和处理结果。
- 服务端:负责路径规划、局部控制、发布反馈和返回结果。
- 典型应用:机器人导航、机械臂控制、物体抓取等。