ROS 2边学边练(33)-- 写一个静态广播(C++)

前言

        通过这一篇我们将了解并学习到如何广播静态坐标变换到tf2(由tf2来转换这些坐标系)。

        发布静态变换对于定义机器人底座与其传感器或非移动部件之间的关系非常有用。例如,在以激光扫描仪中心的坐标系中推理激光扫描测量数据是最简单的。

        这是一个独立的教程,涵盖了静态变换的基础知识,包含两个部分。在第一部分,我们将编写代码以将静态变换发布到tf2。在第二部分,我们将说明如何在tf2_ros中使用命令行工具static_transform_publisher

        后面的接着两篇博文中(下一篇可能会穿插一篇有关ROS世界坐标系的博文),我们将重新学习下上一篇博文(《初识tf2》)的一些知识,在后面的其他博文将会涉及到tf2的更多高级功能。

动动手

创建功能包

        我们在之前创建的某个工作空间(比如ros2_ws)或新建一个工作空间内创建一个learning_tf2_cpp包(工作空间的src路径下):

$ros2 pkg create --build-type ament_cmake --license Apache-2.0 --dependencies geometry_msgs rclcpp tf2 tf2_ros turtlesim -- learning_tf2_cpp

写个静态广播节点

        在learning_tf2_cpp/src下,执行如下命令下载官方为我们准备好的源文件:

$wget https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_cpp/src/static_turtle_tf2_broadcaster.cpp

        下载下来的static_turtle_tf2_broadcaster.cpp内容如下:

#include <memory>

#include "geometry_msgs/msg/transform_stamped.hpp"
#include "rclcpp/rclcpp.hpp"
#include "tf2/LinearMath/Quaternion.h"
#include "tf2_ros/static_transform_broadcaster.h"

class StaticFramePublisher : public rclcpp::Node
{
public:
  explicit StaticFramePublisher(char * transformation[])
  : Node("static_turtle_tf2_broadcaster")
  {
    tf_static_broadcaster_ = std::make_shared<tf2_ros::StaticTransformBroadcaster>(this);

    // Publish static transforms once at startup
    this->make_transforms(transformation);
  }

private:
  void make_transforms(char * transformation[])
  {
    geometry_msgs::msg::TransformStamped t;

    t.header.stamp = this->get_clock()->now();
    t.header.frame_id = "world";
    t.child_frame_id = transformation[1];//命令行传入的第二个参数,第一个参数为可执行文件的名称

    t.transform.translation.x = atof(transformation[2]);
    t.transform.translation.y = atof(transformation[3]);
    t.transform.translation.z = atof(transformation[4]);
    tf2::Quaternion q;
    q.setRPY(
      atof(transformation[5]),
      atof(transformation[6]),
      atof(transformation[7]));
    t.transform.rotation.x = q.x();
    t.transform.rotation.y = q.y();
    t.transform.rotation.z = q.z();
    t.transform.rotation.w = q.w();

    tf_static_broadcaster_->sendTransform(t);
  }

  std::shared_ptr<tf2_ros::StaticTransformBroadcaster> tf_static_broadcaster_;
};

int main(int argc, char * argv[])
{
  auto logger = rclcpp::get_logger("logger");

  // Obtain parameters from command line arguments
  if (argc != 8) {
    RCLCPP_INFO(
      logger, "Invalid number of parameters\nusage: "
      "$ ros2 run learning_tf2_cpp static_turtle_tf2_broadcaster "
      "child_frame_name x y z roll pitch yaw");
    return 1;
  }

  // As the parent frame of the transform is `world`, it is
  // necessary to check that the frame name passed is different
  if (strcmp(argv[1], "world") == 0) {
    RCLCPP_INFO(logger, "Your static turtle name cannot be 'world'");
    return 1;
  }

  // Pass parameters and initialize node
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<StaticFramePublisher>(argv));
  rclcpp::shutdown();
  return 0;
}
代码分析
#include "geometry_msgs/msg/transform_stamped.hpp"

        包含transform_stamped.hpp头文件是为了能访问TransformStamped消息类型,并将其发布到转换树当中去。

#include "rclcpp/rclcpp.hpp"

        包含rclcpp.hpp头文件,是为了使用rcl::Node类。

#include "tf2/LinearMath/Quaternion.h"
#include "tf2_ros/static_transform_broadcaster.h"

        包含Quaternion.h是为了利用四元数类中提供的方便转换欧拉角为四元数(或四元数转换为欧拉角)的功能,包含static_transform_broadcaster.h是为了使用TransformBroadcaster从而使得发布静态转换变得简单。

tf_static_broadcaster_ = std::make_shared<tf2_ros::StaticTransformBroadcaster>(this);

this->make_transforms(transformation);

        StaticFramePublisher类的构造函数中初始化名称为static_turtle_tf2_broadcaster的节点。然后,创建StaticTransformBroadcaster,它将在启动时发送一个静态转换。 

geometry_msgs::msg::TransformStamped t;

t.header.stamp = this->get_clock()->now();
t.header.frame_id = "world";
t.child_frame_id = transformation[1];

        在这里,我们创建一个TransformStamped对象,它将是我们在填充后发送的消息载体。在传递实际的转换值之前,我们需要为其提供适当的元数据初始化一下。 

  1.         t.header.stamp,这个好理解,将当前时间作为时间戳填充上;
  2.         t.hear.frame_id,我们创建连接(link)的父坐标系名称,我们取名“world”;
  3.         t.child_frame_id, 我们创建连接(link)的子坐标系名称;
t.transform.translation.x = atof(transformation[2]);
t.transform.translation.y = atof(transformation[3]);
t.transform.translation.z = atof(transformation[4]);
tf2::Quaternion q;
q.setRPY(
  atof(transformation[5]),
  atof(transformation[6]),
  atof(transformation[7]));
t.transform.rotation.x = q.x();
t.transform.rotation.y = q.y();
t.transform.rotation.z = q.z();
t.transform.rotation.w = q.w();

        这里是填充关于小海龟的6D(六个自由度)位姿数据(偏移及旋转)。

tf_static_broadcaster_->sendTransform(t);

         数据都准备好了之后,我们调用sendTransform函数将消息t(静态转换)发布广播出去。

更新package.xml和CMakeLists.txt

package.xml

<description>Learning tf2 with rclcpp</description>
<maintainer email="mike@qq.com">mike</maintainer>
<license>Apache License 2.0</license>

        根据实际情况手动修改。

CMakeLists.txt

add_executable(static_turtle_tf2_broadcaster src/static_turtle_tf2_broadcaster.cpp)
ament_target_dependencies(
   static_turtle_tf2_broadcaster
   geometry_msgs
   rclcpp
   tf2
   tf2_ros
)

        添加可执行程序,名字为static_turtle_tf2_broadcaster,我们在使用ros2 run命令时会使用到此生成的可执行程序。

install(TARGETS
   static_turtle_tf2_broadcaster
   DESTINATION lib/${PROJECT_NAME})

        指定构建安装的路径(可执行程序生成的路径),方便调用程序按图索骥。

构建

        都搞定之后,我们就可以构建这个包了,但,养成好习惯,先检查依赖再构建。

$rosdep install -i --from-path src --rosdistro iron -y
$colcon build --packages-select learning_tf2_cpp

运行

        我们新开一个终端,先进入工作空间根路径然后source下环境:

$source install/setup.bash

        现在我们终于可以运行了,但我们需要在调用可执行文件时还需将位姿数据传进去,方便该节点填充消息(x,y,z,r,p,y->0,0,1,0,0,0):

$ros2 run learning_tf2_cpp static_turtle_tf2_broadcaster mystaticturtle 0 0 1 0 0 0

上面的参数z为1,代表给mystaticturtle坐标系广播了一个离地1米高度的海龟位姿信息(让mystaticturtle坐标系朝着这个小目标前进)。

补充一点机器人学背景专业知识。

在ROS(Robot Operating System)中,6自由度(6DOF,即6 Degrees of Freedom)通常指的是一个刚体在空间中的完全定位所需的参数。这些参数描述了刚体在三维空间中的位置和姿态。具体来说,6自由度包括:

    三个位置参数(Position):
        X轴位移(X-axis translation)
        Y轴位移(Y-axis translation)
        Z轴位移(Z-axis translation)

这三个参数描述了刚体在三维空间中的位置。

    三个姿态参数(Attitude)或方向参数(Orientation):
        横滚角(Roll)
        俯仰角(Pitch)
        偏航角(Yaw)

这三个参数描述了刚体在三维空间中的方向或姿态。这些角度通常使用欧拉角、四元数或其他方式来表示。

在ROS中,这些参数经常用于描述传感器数据(如IMU、激光雷达等)、机器人末端执行器的位置、机器人的自身姿态等。例如,tf2库就用于处理这些变换,允许用户在不同坐标系之间转换点、向量和姿态。

当处理机器人学中的运动时,了解这些参数和它们如何影响机器人在空间中的位置和姿态是非常重要的。

位置参数(Position)

    X轴位移(X-axis translation):
        这个参数描述了刚体在X轴方向上的位置。正值通常表示向右移动,负值表示向左移动。它决定了刚体在三维空间中的水平位置。
    Y轴位移(Y-axis translation):
        这个参数描述了刚体在Y轴方向上的位置。正值通常表示向前移动,负值表示向后移动。它决定了刚体在三维空间中的前后位置。
    Z轴位移(Z-axis translation):
        这个参数描述了刚体在Z轴方向上的位置。正值通常表示向上移动,负值表示向下移动。它决定了刚体在三维空间中的垂直位置。

姿态参数(Attitude)或方向参数(Orientation)

姿态参数通常用来描述刚体在三维空间中的方向或朝向。有多种方式可以表示姿态,但最常见的包括欧拉角和四元数。这里,我们简单解释欧拉角:

    横滚角(Roll):
        这个参数描述了刚体绕其X轴的旋转。正值通常表示顺时针旋转,负值表示逆时针旋转。它描述了刚体在水平面上的旋转。
    俯仰角(Pitch):
        这个参数描述了刚体绕其Y轴的旋转。正值通常表示向上抬头,负值表示向下低头。它描述了刚体在垂直平面内的旋转。
    偏航角(Yaw):
        这个参数描述了刚体绕其Z轴的旋转。正值通常表示向右转动,负值表示向左转动。它描述了刚体在水平面上的方向改变。

需要注意的是,欧拉角存在万向锁问题,即在某些特定姿态下,横滚角和俯仰角会变得不确定,影响姿态描述的准确性。因此,在实际应用中,有时会更倾向于使用四元数或其他方法来表示姿态。

         我们再来检查一下主题/tf_static发布的静态转换消息(不知道是哪个主题可以ros2 topic list确认一下)。

$ros2 topic echo /tf_static

 如果一切顺利,我们将会看到下面的内容。

发布静态转换的正确方式

        除了我们上面编写构建的StaticTransformBroadcaster可以发布静态转换外,我们还可以利用tf2_ros工具(名字为static_transform_publisher)来实现同样的目的(实际情况就是直接使用现成的工具而不是自己去实现,上面的代码实现是为了让我们了解到其内部的实现,知己知彼嘛),我们可以直接在命令行调用此工具或者在launch文件里添加使用。

        使用以米为单位的x/y/z偏移和以弧度为单位的滚转/俯仰/偏航,将静态坐标变换发布到tf2。我们来看看怎么调用:

$ros2 run tf2_ros static_transform_publisher --x x --y y --z z --yaw yaw --pitch pitch --roll roll --frame-id frame_id --child-frame-id child_frame_id

        可以看出,除了6D参数外还有我们在自己编写的代码里面的父子坐标系名称。我们执行一下,效果与上面的是一样的。

 

 

        使用以米和四元数为单位的x/y/z偏移将静态坐标变换发布到tf2。

$ros2 run tf2_ros static_transform_publisher --x x --y y --z z --qx qx --qy qy --qz qz --qw qw --frame-id frame_id --child-frame-id child_frame_id

补充四元数知识点:

四元素(Quaternion)是一种用于表示旋转的数学工具,在机器人学和计算机图形学等领域有广泛应用。它由一个实部和三个虚部组成,可以表示为(w, x, y, z)或(w, V),其中V是虚部,表示为向量(x, y, z)。四元素满足特定的数学规则,包括虚部的乘法规则,如i^2 = j^2 = k^2 = ijk = −1等。

在ROS(Robot Operating System)中,四元素常用于表示机器人的姿态。姿态描述了机器人在三维空间中的位置和朝向,而四元素是描述这种姿态的一种常用方式。ROS提供了处理四元素的库和工具,使得机器人软件能够方便地处理与姿态相关的任务。

在使用四元素时,需要注意其标准化问题。标准化四元素意味着将其长度(模长)调整为1,以确保其正确表示旋转。未标准化的四元素可能会导致错误的结果。

此外,ROS中的tf2系统也涉及到四元素的使用。tf2用于进行坐标旋转以及tf、msg两种四元素数据结构的变换,它使得在不同坐标系之间转换姿态变得简单高效。

综上所述,四元素是机器人学和计算机图形学中的重要工具,尤其在ROS中,它扮演着描述机器人姿态的关键角色。

  static_transform_publisher既可以直接在命令行使用又可以在launch文件里面使用,我们来看看它在launch文件里面是怎么用的。

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
             package='tf2_ros',
             executable='static_transform_publisher',
             arguments = ['--x', '0', '--y', '0', '--z', '1', '--yaw', '0', '--pitch', '0', '--roll', '0', '--frame-id', 'world', '--child-frame-id', 'mystaticturtle']
        ),
    ])

        看起来是不是比较容易接受,但需要注意的是,除了参数--frame-id 和参数 --child-frame-id外的其他参数都是可选的,如果未指定某个特定选项,则会假定使用恒等变换(默认值)。

本篇完。

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

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

相关文章

基于人工智能的机动车号牌检测与推理系统v1.0

基于人工智能的机动车号牌检测与推理系统v1.0代码重构与实现。 目前整合3中现有算法&#xff0c;并完成阶段性改造&#xff0c;包括【传统方法检测车牌&#xff0c;SVM推理字符】、【YOLO方法检测车牌&#xff0c;SVM推理字符】、【YOLO方法检测车牌&#xff0c;CNN推理字符】&…

MapReduce案例-电影网站数据统计分析

本文适合大数据初学者学习MapReduce统计分析业务问题的步骤和基础的MapReduce编程方法&#xff0c;初步掌握Hadoop对计算任务的管理。 本文末尾有全部数据集和完整代码连接。 1.准备工作 安装Hadoop:Hadoop 3.3.2 离线安装-CSDN博客 按照好Hadoop之后要检查一下datanode运行情况…

Llama网络结构介绍

LLaMA现在已经是开源社区里炙手可热的模型了&#xff0c;但是原文中仅仅介绍了其和标准Transformer的差别&#xff0c;并没有一个全局的模型介绍。因此打算写篇文章&#xff0c;争取让读者不参考任何其他资料把LLaMA的模型搞懂。 结构 如图所示为LLaMA的示意图&#xff0c;由…

ESP32学习第一天-ESP32点亮LED,按键控制LED状态,LED流水灯

第一天使用到的函数: 函数第一个参数设置哪一个引脚&#xff0c;第二个参数设置引脚模式。 pinMode(led_pin,OUTPUT); //设置引脚模式 函数的第一个参数设置哪一个引脚&#xff0c;第二个参数设置是高电平还是低电平。 digitalWrite(led_pin,HIGH);//将引脚电平拉高 #incl…

电脑怎么拖动文件到想要的位置?电脑上拖拽没了的文件怎么找回

在日常的办公和学习中&#xff0c;电脑文件拖拽操作是每位用户都不可或缺的技能。然而&#xff0c;有时在拖动文件时&#xff0c;可能会因为误操作或其他原因&#xff0c;导致文件消失或移至未知位置。本文将详细解析如何在电脑上轻松拖动文件到指定位置&#xff0c;并为您提供…

大模型中的位置编码ALiBi,RoPE的总结和实现

目录 Alibi与旋转位置编码的比较 1. Alibi和旋转位置编码的外推性能比较 2. Alibi的处理方式 注意力线性偏置&#xff1a;ALiBi位置编码的实现 1. ALiBi的基本概念 2. ALiBi的实现方式 ALiBi位置编码的代码解读 1. 导入必要的库 2. 定义get_slopes函数 3. 定义get_al…

C++ Primer 总结索引 | 第十三章:拷贝控制

1、类可以定义构造函数&#xff0c;用来控制在创建此类型对象时做什么 类如何控制该类型对象拷贝、赋值、移动或销毁时做什么 类通过一些 特殊的成员函数 控制这些操作&#xff0c;包括&#xff1a;拷贝构造函数、移动构造函数、拷贝赋值运算符、移动赋值运算符 以及 析构函数 …

API请求报错 Required request body is missing问题解决

背景 在进行调用的时候&#xff0c;加载方法&#xff0c;提示以下错误 错误信息如下&#xff1a; {"code": 10001,"msg": "Required request body is missing: XXX","data": null,"extra": null }Required request body…

Qt使用miniblink第三方浏览器模块

文章目录 一、前言二、miniblink简介三、miniblink使用四、运行效果五、工程结构 一、前言 本文取自刘典武大师&#xff1a;Qt编写地图综合应用58-兼容多浏览器内核 用Qt做项目过程中&#xff0c;遇到需要用到浏览器控件的项目&#xff0c;可能都会绕不开一个问题&#xff0c;那…

机器人模型匹配控制(MPC)MATLAB实现

模型匹配控制&#xff08;Model matching control&#xff09;是指设计一个控制器使闭环系统的传递函数tf(s)与td(s)相一致&#xff01; mpcDesigner 可以分为&#xff1a; 2时域精确模型匹配控制3频域精确模型匹配控制 机械臂控制中应用模型匹配控制&#xff08;Model Matc…

手把手教你搭建鲜花团购小程序

随着互联网的快速发展&#xff0c;线上小程序商城已经成为了一种流行的电商模式。对于花店来说&#xff0c;开发线上小程序商城不仅可以扩大销售渠道&#xff0c;提高销售效率&#xff0c;还可以增加客户粘性&#xff0c;提升品牌形象。下面就以花店为例&#xff0c;教你怎么开…

【python】Python成语接龙游戏[1-3难度均有](源码+数据)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

平衡二叉树(AVLTree)

AVLTree 1、树的分类2、平衡二叉树2.1、构建一个平衡二叉树2.2、删除节点2.3、搜索方式2.3.1、广度优先搜索&#xff08;BFS&#xff09;2.3.2、深度优先搜索&#xff08;DFS&#xff09; 1、树的分类 树形结构是编程当中特别常见的一种数据结构。比如电脑中的文件管理系统就大…

模拟BACnet设备(八)

文章目录 前言模拟呼梯设备的功能前期准备——xml文件的编写创建工程&#xff0c;建立BACnet模拟设备如何将设备的对象列表打包发送呢&#xff1f;被订阅的属性值变化时&#xff0c;如何主动通知对方&#xff1f;读写属性值完整代码小结 前言 前面一到七篇&#xff0c;从理论&…

[Collection与数据结构] PriorityQueue与堆

1. 优先级队列 1.1 概念 前面介绍过队列&#xff0c;队列是一种先进先出(FIFO)的数据结构&#xff0c;但有些情况下&#xff0c;操作的数据可能带有优先级&#xff0c;一般出队列时&#xff0c;可能需要优先级高的元素先出队列&#xff0c;该中场景下&#xff0c;使用队列显然…

Rust - 引用和借用

上一篇章末尾提到&#xff0c;如果仅仅支持通过转移所有权的方式获取一个值&#xff0c;那会让程序变得复杂。 Rust 能否像其它编程语言一样&#xff0c;使用某个变量的指针或者引用呢&#xff1f;答案是可以。 Rust 通过 借用(Borrowing) 这个行为来达成上述的目的&#xff0…

深入探索GDB:Linux下强大的调试神器

目录 一、GDB简介&#xff1a;源码级调试的基石 二、GDB基础操作&#xff1a;从入门到熟练 启动与基本命令 三、GDB进阶功能&#xff1a;解锁更深层次的调试能力 1. 回溯追踪&#xff1a;洞察调用栈 2. 动态内存检测&#xff1a;揪出内存问题 3. 条件断点与观察点&#…

JavaSE——程序逻辑控制

1. 顺序结构 顺序结构 比较简单&#xff0c;按照代码书写的顺序一行一行执行。 例如&#xff1a; public static void main(String[] args) {System.out.println(111);System.out.println(222);System.out.println(333);} 运行结果如下&#xff1a; 如果调整代码的书写顺序 , …

C++:继承作业题

1. 关于以下菱形继承说法不正确的是&#xff08; &#xff09; &#xfeff;class B {public: int b;};class C1: public B {public: int c1;};class C2: public B {public: int c2;};class D : public C1, public C2 {public: int d;};A.D总共占了20个字节B.B中的内容总共在D…

PE文件格式

PE文件格式 PE头&#xff1a;DOS头DOS存根NT头NT头&#xff1a;文件头NT头&#xff1a;可选头 节区头.text(代码)(节区头).data(数据)(节区头).rdata.idata&#xff0c;导入表 最后给出一个PE文件的16进制编辑器中的截图&#xff0c;找到其中每一个头的信息&#xff0c;和导入表…