[ROS2] --- action

1 action介绍

ROS通信机制也会被常常用到——那就是动作。从这个名字上就可以很好理解这个概念的含义,这种通信机制的目的就是便于对机器人某一完整行为的流程进行管理。

1.1 客户端/服务器模型

动作和服务类似,使用的也是客户端和服务器模型,客户端发送动作的目标,想让机器人干什么,服务器端执行动作过程, 控制机器人达到运动的目标,同时周期反馈动作执行过程中的状态。
在这里插入图片描述
客户端发送一个运动的目标,想让机器人动起来,服务器端收到之后,就开始控制机器人运动,一边运动,一边反馈当前的状态,如果是一个导航动作,这个反馈可能是当前所处的坐标,如果是机械臂抓取,这个反馈可能又是机械臂的实时姿态。当运动执行结束后,服务器再反馈一个动作结束的信息。整个通信过程就此结束。

1.2 action通信特点

  • 一对多通信
    和服务一样,动作通信中的客户端可以有多个,大家都可以发送运动命令,但是服务器端只能有一个,毕竟只有一个机器人,先执行完成一个动作,才能执行下一个动作。

  • 同步通信
    既然有反馈,那动作也是一种同步通信机制,之前我们也介绍过,动作过程中的数据通信接口,使用.action文件进行定义。

  • 由服务和话题合成
    大家再仔细看下上边的动图,是不是还会发现一个隐藏的秘密。
    动作的三个通信模块,竟然有两个是服务,一个是话题,当客户端发送运动目标时,使用的是服务的请求调用,服务器端也会反馈一个应带,表示收到命令。动作的反馈过程,其实就是一个话题的周期发布,服务器端是发布者,客户端是订阅者。
    没错,动作是一种应用层的通信机制,其底层就是基于话题和服务来实现的。

2 action自定义通信接口

延续上一讲[ROS2] — action,中创建的自定义接口功能包,在src目录下创建action/Concatenate.action文件
Concatenate.action

int16 num_concatenations
---
string final_concatenation
---
string partial_concatenation

3 action编码示例

这里创建功能包名为,learning04_action

3.1 class_action_client.cpp

/**
 * @file class_action_client.cpp
 *
 * @brief A class defined ROS2 action client node that sends as goal the number of string
 *        concatenation the action server should perform. 
 *        The server will send back feedbacks and the final result
 *
 * @author Antonio Mauro Galiano
 * Contact: https://www.linkedin.com/in/antoniomaurogaliano/
 *
 */


#include "custom_interface/action/concatenate.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"


class ConcatenateActionClient : public rclcpp::Node
{
public:
  using Concatenate = custom_interface::action::Concatenate;
  using GoalHandleConcatenate = rclcpp_action::ClientGoalHandle<Concatenate>;

  explicit ConcatenateActionClient(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())
    : Node("class_action_client", node_options), goalDone_(false)
  {
    this->clientPtr_ = rclcpp_action::create_client<Concatenate>(
      this->get_node_base_interface(),
      this->get_node_graph_interface(),
      this->get_node_logging_interface(),
      this->get_node_waitables_interface(),
      "concatenation");

    this->timer_ = this->create_wall_timer(
      std::chrono::milliseconds(500),
      std::bind(&ConcatenateActionClient::SendGoal, this));
  }

  bool GoalDone() const;
  void SendGoal();

private:
  rclcpp_action::Client<Concatenate>::SharedPtr clientPtr_;
  rclcpp::TimerBase::SharedPtr timer_;
  bool goalDone_;

  void FeedbackCallback(
    GoalHandleConcatenate::SharedPtr,
    const std::shared_ptr<const Concatenate::Feedback> feedback);
  void ResultCallback(const GoalHandleConcatenate::WrappedResult & result);
  // be sure to define the parameter as it's here
  // more info at the declaration
  void GoalResponseCallback(const GoalHandleConcatenate::SharedPtr &goalHandle);
};


bool ConcatenateActionClient::GoalDone() const
{
  return this->goalDone_;
}


void ConcatenateActionClient::SendGoal()
{
  using namespace std::placeholders;

  this->timer_->cancel();

  this->goalDone_ = false;

  if (!this->clientPtr_)
  {
    RCLCPP_ERROR(this->get_logger(), "Action client not initialized");
  }

  if (!this->clientPtr_->wait_for_action_server(std::chrono::seconds(10)))
  {
    RCLCPP_ERROR(this->get_logger(), "!!ATTENTION!! Action server not available");
    this->goalDone_ = true;
    return;
  }

  auto goalMsg = Concatenate::Goal();
  goalMsg.num_concatenations = 9;

  RCLCPP_INFO(this->get_logger(), "Sending goal");

  auto send_goal_options = rclcpp_action::Client<Concatenate>::SendGoalOptions();
  send_goal_options.feedback_callback =
    std::bind(&ConcatenateActionClient::FeedbackCallback, this, _1, _2);
  send_goal_options.result_callback =
    std::bind(&ConcatenateActionClient::ResultCallback, this, _1);
  // send_goal_options.goal_response_callback =
  //   std::bind(&ConcatenateActionClient::GoalResponseCallback, this, _1);
  auto goal_handle_future = this->clientPtr_->async_send_goal(goalMsg, send_goal_options);
}


void ConcatenateActionClient::FeedbackCallback(
  rclcpp_action::ClientGoalHandle<Concatenate>::SharedPtr,
  const std::shared_ptr<const Concatenate::Feedback> feedback)
{
  RCLCPP_INFO(this->get_logger(),
    "Feedback received: %s",
    feedback->partial_concatenation.c_str());
}


void ConcatenateActionClient::ResultCallback(const GoalHandleConcatenate::WrappedResult & result)
{
  this->goalDone_ = true;
  switch (result.code) {
    case rclcpp_action::ResultCode::SUCCEEDED:
      break;
    case rclcpp_action::ResultCode::ABORTED:
      RCLCPP_ERROR(this->get_logger(), "Goal was aborted");
      return;
    case rclcpp_action::ResultCode::CANCELED:
      RCLCPP_ERROR(this->get_logger(), "Goal was canceled");
      return;
    default:
      RCLCPP_ERROR(this->get_logger(), "Unknown result code");
      return;
  }

  RCLCPP_INFO(this->get_logger(), "Result received");
  for (auto number : result.result->final_concatenation)
  {
    RCLCPP_INFO(this->get_logger(), "%d", number);
  }
}

// defining the parameter directly as a GoalHandleConcatenate::SharedPtr goalHandle
// it's wrong for the send_goal_options.goal_response_callback
// so it doesnt compile
void ConcatenateActionClient::GoalResponseCallback(
  const GoalHandleConcatenate::SharedPtr &goalHandle)
  {
    if (!goalHandle)
    {
      RCLCPP_ERROR(this->get_logger(), "Goal was rejected by server");
    } else
    {
      RCLCPP_INFO(this->get_logger(), "Goal accepted by server, waiting for result");
    }
  }


int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);
  auto action_client = std::make_shared<ConcatenateActionClient>();

  while (!action_client->GoalDone())
  {
    rclcpp::spin_some(action_client);
  }

  rclcpp::shutdown();
  return 0;
}

3.2 class_action_server.cpp

/**
 * @file class_action_server.cpp
 *
 * @brief A class defined ROS2 action server node that concatenates a string 
 *         based on the number of string concatenation sent by a client within a goal request
 *
 * @author Antonio Mauro Galiano
 * Contact: https://www.linkedin.com/in/antoniomaurogaliano/
 *
 */


#include "custom_interface/action/concatenate.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"


class ConcatenateActionServer : public rclcpp::Node
{
public:
  using Concatenate = custom_interface::action::Concatenate;
  using GoalHandleConcatenate = rclcpp_action::ServerGoalHandle<Concatenate>;

  explicit ConcatenateActionServer(const rclcpp::NodeOptions & options = rclcpp::NodeOptions())
    : Node("class_action_server", options)
  {
    using namespace std::placeholders;

    this->actionServer_ = rclcpp_action::create_server<Concatenate>(
      this->get_node_base_interface(),
      this->get_node_clock_interface(),
      this->get_node_logging_interface(),
      this->get_node_waitables_interface(),
      "concatenation",
      std::bind(&ConcatenateActionServer::HandleGoal, this, _1, _2),
      std::bind(&ConcatenateActionServer::HandleCancel, this, _1),
      std::bind(&ConcatenateActionServer::HandleAccepted, this, _1));
  }

private:
  rclcpp_action::Server<Concatenate>::SharedPtr actionServer_;
  rclcpp_action::GoalResponse HandleGoal(
    const rclcpp_action::GoalUUID & uuid,
    std::shared_ptr<const Concatenate::Goal> goal);
  rclcpp_action::CancelResponse HandleCancel(
    const std::shared_ptr<GoalHandleConcatenate> goalHandle);
  void execute(const std::shared_ptr<GoalHandleConcatenate> goalHandle);
  void HandleAccepted(const std::shared_ptr<ConcatenateActionServer::GoalHandleConcatenate> goalHandle);
};


rclcpp_action::GoalResponse ConcatenateActionServer::HandleGoal(
    const rclcpp_action::GoalUUID & uuid,
    std::shared_ptr<const Concatenate::Goal> goal)
{
  RCLCPP_INFO(rclcpp::get_logger("server"),
              "Got goal request with %d string concatenations",
              goal->num_concatenations);
  (void)uuid;
  // conditional to reject numbers of concatenations
  if ((goal->num_concatenations > 10) && (goal->num_concatenations < 2)) 
  {
    return rclcpp_action::GoalResponse::REJECT;
  }
  return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
}


rclcpp_action::CancelResponse ConcatenateActionServer::HandleCancel(
    const std::shared_ptr<GoalHandleConcatenate> goalHandle)
{
  RCLCPP_INFO(rclcpp::get_logger("server"), "Got request to cancel goal");
  (void)goalHandle;
  return rclcpp_action::CancelResponse::ACCEPT;
}


void ConcatenateActionServer::execute(
  const std::shared_ptr<GoalHandleConcatenate> goalHandle)
{
  RCLCPP_INFO(rclcpp::get_logger("server"), "Executing the concatenation");
  rclcpp::Rate loop_rate(1);
  const auto goal = goalHandle->get_goal();
  auto feedback = std::make_shared<Concatenate::Feedback>();
  std::string myString = "HELLOWORLD";
  auto &concatenation = feedback->partial_concatenation;
  concatenation = myString;
  concatenation = concatenation + " " + myString;
  auto result = std::make_shared<Concatenate::Result>();

  for (int i = 1; (i < goal->num_concatenations) && rclcpp::ok(); ++i)
  {
    // check if there is a cancel request
    if (goalHandle->is_canceling())
    {
      result->final_concatenation = concatenation;
      goalHandle->canceled(result);
      RCLCPP_INFO(rclcpp::get_logger("server"), "Goal Canceled");
      return;
    }
    // update the final concatenation
    concatenation = concatenation + " " + myString;
    // update and publish feedback of the partial concatenation
    goalHandle->publish_feedback(feedback);
    RCLCPP_INFO(rclcpp::get_logger("server"), "Publish Feedback");

    loop_rate.sleep();
  }

  // check if goal is done
  if (rclcpp::ok())
  {
    result->final_concatenation = concatenation;
    goalHandle->succeed(result);
    RCLCPP_INFO(rclcpp::get_logger("server"), "Goal Succeeded");
  }
}


void ConcatenateActionServer::HandleAccepted(
  const std::shared_ptr<GoalHandleConcatenate> goal_handle)
{
  using namespace std::placeholders;
  // this needs to return quickly to avoid blocking the executor, so spin up a new thread
  std::thread{std::bind(&ConcatenateActionServer::execute, this, _1), goal_handle}.detach();
}


int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);

  auto action_server = std::make_shared<ConcatenateActionServer>();

  rclcpp::spin(action_server);
  rclcpp::shutdown();
  return 0;
}

3.3 CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(learning04_action)

# Default to C99
if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(custom_interface REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_action REQUIRED)
find_package(rclcpp_components REQUIRED)

# add_executable(simple_action_server src/simple_action_server.cpp)
# ament_target_dependencies(simple_action_server
#   "rclcpp"
#   "rclcpp_action"
#   "custom_interface")

add_executable(class_action_server src/class_action_server.cpp)
ament_target_dependencies(class_action_server
  "rclcpp"
  "rclcpp_action"
  "custom_interface")

# add_executable(simple_action_client src/simple_action_client.cpp)
# ament_target_dependencies(simple_action_client
#   "rclcpp"
#   "rclcpp_action"
#   "custom_interface")

add_executable(class_action_client src/class_action_client.cpp)
ament_target_dependencies(class_action_client
  "rclcpp"
  "rclcpp_action"
  "custom_interface")

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  ament_lint_auto_find_test_dependencies()
endif()

install(TARGETS
  # simple_action_server
	class_action_server
	# simple_action_client
  class_action_client
  DESTINATION lib/${PROJECT_NAME})

ament_package()

3.4 package.xml

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>learning04_action</name>
  <version>0.0.0</version>
  <description>Action based tutorial</description>
  <maintainer email="user@todo.todo">Antonio Mauro Galiano</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <depend>custom_interface</depend>
  <depend>rclcpp</depend>
  <depend>rclcpp_action</depend>
  <depend>rclcpp_components</depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

4 编译运行

# 编译
colcon build

# source环境变量
source install/setup.sh

# 运行publisher
ros2 run learning04_action class_action_client

# 运行subsriber
ros2 run learning04_action class_action_server

5 action常用指令

# 查看action信息
ros2 action info /turtle/roate_absolute

# 设置action通信结构
ros2 interface show /turtle/roate_absolute

# 发送action请求
ros2 action send_goal <action_name> <action_type> <values>
ros2 action send_goal /turtle/roate_absolute turtlesim/action/RotateAbsolute "{theta: 1.57}"
ros2 action send_goal /turtle/roate_absolute turtlesim/action/RotateAbsolute "{theta: 1.57}" --feedback

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

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

相关文章

zabbix 进阶

zabbix的字段发现机制&#xff1a; zabbix客户端主动和服务端联系&#xff0c;将自己的地址和端口发送服务端实现字段添加监控主机。 客户端是主动一方。 缺点&#xff1a;自定义网段中主机数量太多&#xff0c;登记耗时会很久&#xff0c;而且这个自动发现机制不是很稳定。…

c-语言->数据在内存的存储

系列文章目录 文章目录 系列文章目录前言 前言 目的&#xff1a;学习整数在内存的储存&#xff0c;什么是大小端&#xff0c;浮点数的储存。 1. 整数在内存中的存储 在讲解操作符的时候&#xff0c;我们就讲过了下⾯的内容&#xff1a; 整数的2进制表⽰⽅法有三种&#xff0…

Minio保姆级教程

转载自&#xff1a;www.javaman.cn Minio服务器搭建和整合 1、centos安装minio 1.1、创建安装目录 mkdir -p /home/minio1.2、在线下载minio #进入目录 cd /home/minio #下载 wget https://dl.minio.io/server/minio/release/linux-amd64/minio1.3、minio配置 1.3.1、添加…

基于Springboot的校园失物招领系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的校园失物招领系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

三、jvm中的对象及引用

一、对象在jvm的创建过程 检查加载-->分配内存-->内存空间初始化-->设置-->对象初始化 1) 检查加载 首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用&#xff0c;并且检查类是否已经被加载、解析和初始化过。 虚拟机遇到一条 new 指令时&#xf…

【Deeplearning4j】小小的了解下深度学习

文章目录 1. 起因2. Deeplearning4j是什么3. 相关基本概念4. Maven依赖5. 跑起来了&#xff0c;小例子&#xff01;6. 鸢尾花分类代码 7. 波士顿房价 回归预测代码 8. 参考资料 1. 起因 其实一直对这些什么深度学习&#xff0c;神经网络很感兴趣&#xff0c;之前也尝试过可能因…

Python实现GUI图片浏览程序

Python实现GUI图片浏览程序 下面程序需要pillow库。pillow是 Python 的第三方图像处理库&#xff0c;需要安装才能实用。pillow是PIL&#xff08; Python Imaging Library&#xff09;基础上发展起来的&#xff0c;需要注意的是pillow库安装用pip install pillow&#xff0c;导…

javaEE -14(10000字 JavaScript入门 - 1)

一&#xff1a;初始 JavaScript JavaScript (简称 JS)是世界上最流行的编程语言之一&#xff0c;它是一个脚本语言, 通过解释器运&#xff0c;主要在客户端(浏览器)上运行, 现在也可以基于 node.js 在服务器端运行. JavaScript 和 HTML 和 CSS 之间的关系&#xff1a; HTML…

Rellax.js,一款超酷的 JavaScript 滚动效果库

嗨&#xff0c;大家好&#xff0c;欢迎来到猿镇&#xff0c;我是镇长&#xff0c;lee。 又到了和大家见面的时间&#xff0c;今天和大家分享一款轻松实现视差滚动效果的 JavaScript 库——Rellax.js。无需大量的配置&#xff0c;即可为你的网站增色不少。 什么是Rellax.js&am…

simulinkveristandlabview联合仿真环境搭建

目录 开篇废话 软件版本 明确需求 软件安装 matlab2020a veristand2020 R4 VS2017 VS2010 软件安装验证 软件资源分享 开篇废话 推免之后接到的第一个让人难绷的活&#xff0c;网上开源的软件资料和成功的案例很少&#xff0c;查来查去就那么几篇&#xff0c;而且版本…

以pycharm为例,生成Python项目所需要的依赖库/包文档:requirements.txt

平时我们在编写或者使用别人的Python项目时&#xff0c;往往会看到一个文档requirements.txt&#xff0c;该文档是描述一个Python项目中的第三方库的名称以及版本。本文介绍导出python当前项目依赖包requirements.txt的操作步骤。 方法一&#xff1a;如果每个项目有对应的虚拟…

Java零基础——Elasticsearch篇

1.Elasticsearch简介 Elasticsearch是一个基于Lucene的一个开源的分布式、RESTful 风格的搜索和数据分析引擎。Elasticsearch是用Java语言开发的&#xff0c;并作为Apache许可条款下的开放源码发布&#xff0c;是一种流行的企业级搜索引擎。Elasticsearch用于云计算中&#xf…

13. MySQL 日志

目录 错误日志 binlog日志 概述 日志格式 查询日志 慢查询日志 错误日志 错误日志是MySQL中最重要的日志之一&#xff0c;它记录了当mysqld启动和停止时&#xff0c;以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时&#…

用Rust刷LeetCode之26 删除有序数组中的重复项

26. 删除排序数组中的重复项[1] 难度: 简单 老的描述: 新的描述: 注意是 排序数组,非严格递增排列,即已经是排好序的,只不过有重复元素 func removeDuplicates(nums []int) int { if len(nums) 0 { return 0 } i : 0 for j : 1; j < len(nums); j { …

Kubernetes架构及核心部件

文章目录 1、Kubernetes集群概述1.1、概述1.2、通过声明式API即可 2、Kubernetes 集群架构2.1、Master 组件2.1.1、API Server2.1.2、集群状态存储2.1.3、控制器管理器2.1.4、调度器 2.2、Worker Node 组件2.2.1、kubelet2.2.2、容器运行时环境2.2.3、kube-proxy 2.3、图解架构…

AI隆重软件,AI原创文章隆重软件

随着信息量的急剧增加&#xff0c;许多写作者、网站管理员和内容创作者们纷纷感受到了文章降重的压力。原始文本的降重&#xff0c;需要保留关键信息的同时避免重复&#xff0c;这是一项既繁琐又耗时的任务。 改写软件的批量降重功能 147SEO改写软件在降重领域的卓越表现主要体…

PHP基础 - 注释变量

一. 语言开始标识 在PHP中,文件的开头需要使用语言开始标识来指定该文件是PHP代码。标识通常为"<?php",也可以是"<?",但建议使用"<?php"以确保代码的兼容性和可读性。 <?php // PHP代码从这里开始写 二. PHP注释 注释是用…

svn使用步骤

服务器端主要用来创建仓库&#xff0c;然后供客户端去访问与下载。 客户端&#xff1a; 图形化界面的使用&#xff1a;这里使用的是tortoise工具 1.创建一个文件夹作为自己的本地仓库目录 2.鼠标右键文件夹&#xff0c;在菜单中点击SVN checkout 3.找个图 这一步骤相当于git中…

nodejs+vue+微信小程序+python+PHP的基于大数据的家电销售分析系统设计与实现-计算机毕业设计推荐django

系统按照用户的实际需求开发而来&#xff0c;贴近生活。从管理员通过正确的账号的密码进入系统&#xff0c;可以使用相关的系统应用。管理员总体负责整体系统的运行维护&#xff0c;统筹协调。 系统整体模块设计&#xff1a;系统分为管理员和用户两大角色&#xff0c;系统管理员…

Linux 系统上配置 SSH 密钥

1. 生成 SSH 密钥 打开终端&#xff0c;运行以下命令来生成 SSH 密钥&#xff1a; ssh-keygen -t rsa -b 4096 -C "wqzbxhexample.com" 替换 "wqzbxhexample.com" 为你在 GitHub 注册时使用的邮箱地址。 2. 添加 SSH 密钥到 SSH 代理 运行以下命令来启…