ROS2从入门到精通1-3:详解ROS2动作通信机制与自定义动作

目录

  • 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服务

  • 客户端

    1. 注册一个客户端
      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"
      );
      
    2. 定义目标响应回调函数
      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");
      }
      
    3. 定义过程反馈回调函数
      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());
      }
      
    4. 定义结果回调函数
      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服务

  • 客户端

    1. 注册一个客户端
      self.client_ = ActionClient(self, RotateAbsolute, '/turtle1/rotate_absolute')
      
    2. 定义目标响应回调函数
      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)
      
    3. 定义过程反馈回调函数
      def OnFeedbackCallback(self, feedback_msg):
          feedback = feedback_msg.feedback
          self.get_logger().info(f'Received feedback: {feedback.remaining:.2f}') 
      
    4. 定义结果回调函数
      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深度学习实战》
  • 《机器学习强基计划》
  • 《运动规划实战精讲》

👇源码获取 · 技术交流 · 抱团学习 · 咨询分享 请联系👇

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/545037.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计模式——观察者模式17

观察者模式指多个对象间存在一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式。 中介者模式是N对N的双向关系。观察者模式是1对N的单向关系。 设计模式&#xff0c;一定要敲代码…

【Linux网络编程】UDP协议

UDP协议 1.再谈端口号端口号划分认识知名端口号(Well-Know Port Number)两个问题netstatpidof 2.UDP协议2.1UDP的特点2.2面向数据报2.3UDP的缓冲区2.4UDP使用注意事项2.5基于UDP的应用层协议 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 1.再谈端口号 端口…

如何选择适用于Mac的文件恢复软件?适用于 Mac 的最佳数据恢复软件清单

有人会说&#xff0c;我们的数字生活正变得几乎和我们的物理生活一样重要。我们在线工作&#xff0c;将记忆保存在数码照片库中&#xff0c;在信使中交流&#xff0c;并保留各种文档的数字扫描。 每个人都知道备份是必不可少的。建议每天至少同步一个数字备份&#xff08;例如…

Spring Boot 学习(3)——Spring Initializr 创建项目问题解决

产生问题的原因&#xff0c;各种的版本都较老&#xff0c;所以导致出现问题。目前暂未打到合适的教程&#xff0c;按老教程学起来先。 小白瞎学&#xff0c;大神勿喷&#xff01; 再次强调环境&#xff1a;maven 3.3.9、jdk 1.8、idea 2017、Spring 4.3.13、Spring Boot 1.5.…

C/C++基础----指针

指针的定义 在c/c中&#xff0c;有一个特殊的变量指向我们电脑中某个内存地址&#xff0c;进而可以让我们操作这段内存&#xff0c;指的就是指针类型 语法&#xff1a; int a 10; int* p &a;&符号是取出某个变量的内存地址 把这个内存地址赋值给一个变量p&#xff…

数据适配器对象(DataAdapter)

一、DataAdapter对象概述 1、 DataAdapter是一个特殊的类&#xff0c;其作用是数据源与DataSet对象之间沟通的桥梁。 2、 DataAdapter提供了双向的数据传输机制 &#xff08;1&#xff09; 在数据源上执行Select语句&#xff0c;把查询结果集传送到DataSet对象的…

基于Spring Boot的入职匹配推荐系统设计与实现

基于Spring Boot的入职匹配推荐系统设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 管理员登录界面&#xff0c;登录成功后进入到系统操…

STC89C52学习笔记(九)

STC89C52学习笔记&#xff08;九&#xff09; 综述&#xff1a;本文主要介绍了蜂鸣器、蜂鸣器如何使用以及如何利用蜂鸣器播放不同频率声音。 一、蜂鸣器 1.定义和作用 电信号→声音信号&#xff0c;常用来产生按键音和报警音。 2.分类 有源&#xff1a;自带振荡器&#…

设计模式面试题

概述 设计模式分类 创建型模式 用于描述“怎样创建对象”&#xff0c;主要特点是“将对象的创建与使用分离”。使用者不需要官族对象创建的细节。结构型模式 用于描述如何将类或对象按照某种布局组成更大的结构。行为型模式 用于描述类或对象之间怎样相互协作共同完成单个对象…

面试经典150题——二叉树的最大深度

1. 题目描述 ​ 2. 题目分析与解析 这个题目有过一定基础的都应该知道&#xff0c;采用递归解决问题&#xff0c;因为要求一个二叉树的深度&#xff08;也就是高度&#xff09;&#xff0c;其实上就是根节点的左子树和右子树中高度最高的那个。因此这个问题就可以拆解为&…

微服务之OpenFeign服务接口调用

一、概述 1.1简介 OpenFeign客户端是一个web声明式http远程调用工具&#xff0c;直接可以根据服务名称去注册中心拿到指定的服务IP集合&#xff0c;提供了接口和注解方式进行调用&#xff0c;内嵌集成了Ribbon本地负载均衡器。 Feign是一个声明性web服务客户端。它使编写web服…

BackTrader 中文文档(二十三)

原文&#xff1a;www.backtrader.com/ 基准测试 原文&#xff1a;www.backtrader.com/blog/posts/2016-07-22-benchmarking/benchmarking/ backtrader包括两种不同类型的对象&#xff0c;可以帮助跟踪&#xff1a; 观察者 分析器 问题 #89是关于添加针对资产的基准测试。这是…

[阅读笔记12][LLaVA-1.5]Improved Baselines with Visual Instruction Tuning

1.5版本是llava作者在23年10月提交的。 作者对原始的llava进行了四个很小的改进&#xff0c;之后就刷了11个数据集的sota。而且可以看到llava用于训练的数据量很小&#xff0c;与instructBLIP和通义千问比少多了。 然后这里就是llava1.5进行的四个小改进。 第一点是prompt明确短…

【Excel如何在表格中筛选重复的值之条件格式】

在使用excel进行统计时经常会遇到&#xff0c;数据统计出现重复的现象&#xff0c;为了确保数据的唯一性&#xff0c;可以用到条件格式筛选出重复值&#xff0c;以确保数据的正确性。 筛选重复值&#xff1a; 选中要筛选的范围&#xff0c;行或列或整个表选中【开始】-【条件…

vue快速入门(二十三)侦听器的简单写法与完整写法

注释很详细&#xff0c;直接上代码 上一篇 新增内容 侦听器简单写法侦听对象或属性侦听器完整写法侦听对象&#xff08;可选深度侦听&#xff09; 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name…

Zookeeper(从入门到掌握)看完这一篇就够了

文章目录 一、初识 Zookeeper1.Zookeeper 概念2.Zookeeper 数据模型3.Zookeeper 服务端常用命令4.Zookeeper 客户端常用命令 二、ZooKeeper JavaAPI 操作1.Curator 介绍1.Curator API 常用操作&#xff08;1&#xff09;建立连接&#xff08;2&#xff09;添加节点&#xff08;…

C#版Facefusion ,换脸器和增强器

C#版Facefusion &#xff0c;换脸器和增强器 目录 说明 效果 项目 调用代码 说明 Facefusion是一款最新的开源AI视频/图片换脸项目。是原来ROOP的项目的延续。项目官方介绍只有一句话&#xff0c;下一代换脸器和增强器。 代码实现参考 https://github.com/facefusion/f…

AI天使汇联合150家顶级基金、战投,征集优秀AI创业项目

鉴于AI天使汇主办的2024年3月期优秀项目征集活动效果超出预期&#xff0c;3月活动最后TOP20路演者中已有多家快速拿到了TS。 路演活动质量受到了AI创业公司和基金/战投伙伴的高度评价&#xff0c;现在开始四月期活动报名! 本期征集活动联合的顶级基金和战投数量增加到了150家…

Shell脚本学习(一):Shell内置命令与Shell运算符

Shell内置命令 理解内置命令的含义。 内置命令介绍 Shell内置命令&#xff0c;就是由Bash Shell自身提供的命令&#xff0c;而不是文件系统中的可执行文件。 使用type 可以用来确定一个命令是否是内置命令&#xff1a; type 命令演示&#xff1a; 对于上述演示的两个命令来…

【我的代码生成器】生成React页面类

有了数据表的结构信息&#xff0c;就能生成React 的页面类&#xff0c;快捷方便。 生成界面如下&#xff1a; 生成的React FrmUser.js页面如下&#xff1a; 只需再写里面的操作逻辑代码。