目录
- 0 专栏介绍
- 1 动作通信模型
- 2 动作模型实现(C++)
- 3 动作模型实现(Python)
- 4 自定义动作
0 专栏介绍
本专栏旨在通过对ROS2的系统学习,掌握ROS2底层基本分布式原理,并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。
🚀详情:《ROS2从入门到精通》
1 动作通信模型
在ROS2中,动作通信机制提供了一种用于执行长时间运行任务的方式,由三个部分组成:
- 目标(goal):对客户端的请求进行处理和响应
- 反馈(feedback):对监控任务的动作执行过程提供反馈数据
- 结果(result):对监控任务的执行结果提供反馈
举个机器人导航的任务说明
假设有一个机器人需要从起始点导航到目标点。通过使用动作通信机制,可以定义一个导航动作,其中目标是目标位置。机器人将发送导航目标给动作服务器,服务器将返回连续的反馈,例如机器人的当前位置,障碍物检测等。最终,当机器人到达目标位置时,服务器将返回导航任务的结果。
再比如机械臂控制
通过定义动作目标和提供连续的反馈,可以实现对机械臂的精确控制。例如,可以定义一个动作来控制机械臂移动到特定位置,反馈可以包括当前位置、力传感器的读数等。一旦机械臂到达目标位置,服务器将返回控制任务的结果
动作是应用级通信机制,建立在话题和服务之上,但却与话题和服务通信不同:
- 动作通信更适合描述一项任务的执行过程,而话题通信更适合传输实时数据;
- 动作通信返回一系列的反馈和一个最终结果,而服务通常只返回单个响应;
- 动作通信是可中断的,即在执行过程中可以取消,而服务通信通常是一次性的请求-响应模式;
- …
总之,通过使用动作通信,可以实现更复杂的交互和反馈,并灵活地处理长时间运行的任务
2 动作模型实现(C++)
实验目标:客户端提交请求给
turtlesim
功能包的/rotate_absolute
动作,在界面上使乌龟旋转,并不断监听乌龟的实时旋转数据,以及到达目标角度的反馈。
-
服务器
本实验中无需编程,为
turtlesim::Action
定义的/rotate_absolute
服务 -
客户端
- 注册一个客户端
client_ptr_ = rclcpp_action::create_client<TurtleAction>( get_node_base_interface(), get_node_graph_interface(), get_node_logging_interface(), get_node_waitables_interface(), "/turtle1/rotate_absolute" );
- 定义目标响应回调函数
void OnGoalResponseCallback(GoalHandle::SharedPtr goal_message) { if (!goal_message) { RCLCPP_ERROR(get_logger(), "Client: Goal was rejected by server"); rclcpp::shutdown(); } else RCLCPP_INFO(get_logger(), "Client: Goal accepted by server, waiting for result"); }
- 定义过程反馈回调函数
void OnFeedbackCallback(GoalHandle::SharedPtr, const std::shared_ptr<const TurtleAction::Feedback> feedback_message) { std::stringstream ss; ss << "Client: Received feedback: " << feedback_message->remaining; RCLCPP_INFO(this->get_logger(), "%s", ss.str().c_str()); }
- 定义结果回调函数
void OnResultCallback(const GoalHandle::WrappedResult & result_message) { switch (result_message.code) { case rclcpp_action::ResultCode::SUCCEEDED: break; case rclcpp_action::ResultCode::ABORTED: RCLCPP_ERROR(get_logger(), "Client: Goal was aborted"); rclcpp::shutdown(); return; case rclcpp_action::ResultCode::CANCELED: RCLCPP_ERROR(get_logger(), "Client: Goal was canceled"); rclcpp::shutdown(); return; default: RCLCPP_ERROR(get_logger(), "Client: Unknown result code"); rclcpp::shutdown(); return; } RCLCPP_INFO(get_logger(), "Client: Result received: %.2f", (result_message.result->delta)); rclcpp::shutdown(); }
- 注册一个客户端
实验结果如下所示,在本实验中,theta
表示发送的目标角度;remaining
是过程反馈的目标角度差值信息;delta
是结果响应,表示距离初始位置的角度偏差
3 动作模型实现(Python)
实验目标:客户端提交请求给
turtlesim
功能包的/rotate_absolute
动作,在界面上使乌龟旋转,并不断监听乌龟的实时旋转数据,以及到达目标角度的反馈。
-
服务器
本实验中无需编程,为
turtlesim::Action
定义的/rotate_absolute
服务 -
客户端
- 注册一个客户端
self.client_ = ActionClient(self, RotateAbsolute, '/turtle1/rotate_absolute')
- 定义目标响应回调函数
def OnGoalResponseCallback(self, future): goal_handle = future.result() if not goal_handle.accepted: self.get_logger().info('Client: Goal was rejected by server') return self.get_logger().info('Client: Goal accepted by server, waiting for result') self._get_result_future = goal_handle.get_result_async() self._get_result_future.add_done_callback(self.OnResultCallback)
- 定义过程反馈回调函数
def OnFeedbackCallback(self, feedback_msg): feedback = feedback_msg.feedback self.get_logger().info(f'Received feedback: {feedback.remaining:.2f}')
- 定义结果回调函数
def OnResultCallback(self, future): if future.done(): self.get_logger().info(f'Action done!') result = future.result().result self.get_logger().info(f'Result: {result.delta:.2f}') elif future.cancelled(): self.get_logger().info(f"Client: Goal was canceled") else: raise RuntimeWarning("Client: Unknown result code")
- 注册一个客户端
实验结果如下所示,在本实验中,theta
表示发送的目标角度;remaining
是过程反馈的目标角度差值信息;delta
是结果响应,表示距离初始位置的角度偏差
4 自定义动作
自定义动作的通用流程如下:
- 功能包下新建
action
文件夹,在其中添加自定义服务xxx.action
,注意目标、反馈和结果数据结构使用---
分割- 功能包
package.xml
中添加编译依赖与执行依赖<buildtool_depend>rosidl_default_generators</buildtool_depend> <exec_depend>rosidl_default_runtime</exec_depend> <member_of_group>rosidl_interface_packages</member_of_group>
- 功能包
CMakeLists.txt
中添加编译消息相关依赖find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} "xxx.action" DEPENDENCIES xxx_actions ) ament_export_dependencies(rosidl_default_runtime)
- 编译自定义动作,在
install/<pkg_name>/include
中生成由xxx.action
编译的C++可识别的xxx.hpp
头文件- 引入
xxx.hpp
即可调用自定义动作
下面给出一个实例
实现一个自定义动作,请求机器人开始圆周运动,反馈机器人的实时运动角度,并返回最终结果
添加如下自定义动作,并按上面步骤配置依赖
bool enable # goal
---
bool finish # result
---
int32 state # feedback
定义一个服务器、一个客户端,限于篇幅只贴出部分代码,完整代码见文末。
- 服务器
class OwnActionServerNode : public rclcpp::Node { public: using OwnAction = own_action_lab::action::MoveCircle; using GoalHandle = rclcpp_action::ServerGoalHandle<OwnAction>; explicit OwnActionServerNode(const rclcpp::NodeOptions & action_server_options = rclcpp::NodeOptions()) : Node("own_action_server", action_server_options) { action_server_ = rclcpp_action::create_server<OwnAction>( get_node_base_interface(), get_node_clock_interface(), get_node_logging_interface(), get_node_waitables_interface(), "/move", std::bind(&OwnActionServerNode::OnHandleGoal, this, std::placeholders::_1, std::placeholders::_2), std::bind(&OwnActionServerNode::OnHandleCancel, this, std::placeholders::_1), std::bind(&OwnActionServerNode::OnHandleAccepted, this, std::placeholders::_1) ); } }
- 客户端
class OwnActionClientNode : public rclcpp::Node { public: using OwnAction = own_action_lab::action::MoveCircle; using GoalHandle = rclcpp_action::ClientGoalHandle<OwnAction>; explicit OwnActionClientNode(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions()) : Node("own_action_client", node_options) { client_ptr_ = rclcpp_action::create_client<OwnAction>( get_node_base_interface(), get_node_graph_interface(), get_node_logging_interface(), get_node_waitables_interface(), "/move" ); } }
实测效果如下:
完整代码通过下方博主名片联系获取
🔥 更多精彩专栏:
- 《ROS从入门到精通》
- 《Pytorch深度学习实战》
- 《机器学习强基计划》
- 《运动规划实战精讲》
- …