ROS学习笔记2

上篇:ROS学习笔记-优快云博客

总之,不知道出于什么原因,上一篇每次重新编辑的时候都只会显示1.27w字时的版本,且历史版本里找不到已发布版本,索性再开一篇新的,学完之前不要随便发布(未发布文章每100字或30s自动保存)



2.5 实操

2.5.1 控制乌龟运动

1.话题与消息获取

列出话题,打开计算图确定是哪一个

获取话题下的消息类型

 获取消息的格式

分别为线速度和角速度

线速度中的xyz对于三维坐标系中的坐标表示的含义

角速度中的xyz可以参考文档:http://www.autolabor.com.cn/book/ROSTutorials/di-2-zhang-ros-jia-gou-she-ji/25-tong-xin-ji-zhi-shi-cao/251-shi-cao-01-hua-ti.html

视频:https://www.bilibili.com/video/BV1Ci4y1L7ZZ?spm_id_from=333.788.videopod.episodes&vd_source=6c634a4d66d78703c01a88d66e89649b&p=100

2.验证速度消息

挂起

控制乌龟运动获得消息

3.通过命令直接实现乌龟圆周运动

4.话题发布

新建资源包

导入4个依赖包

编码如下

#include "ros/ros.h"
#include "geometry_msgs/Twist.h"

int main(int argc, char *argv[])
{
    ros::init(argc,argv,"my_control");
    ros::NodeHandle nh;
    ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel",10);
    ros::Rate rate=1;
    geometry_msgs::Twist twist;
    twist.linear.x = 1.0;
    twist.linear.y = 0.0;
    twist.linear.z = 0.0;
    twist.angular.x = 0.0;
    twist.angular.y = 0.0;
    twist.angular.z = 0.5;
    while(ros::ok())
    {
        pub.publish(twist);
        rate.sleep();
        ros::spinOnce();
    }
    return 0;
}

配置

2.5.2输出乌龟位资

1.话题与消息获取

新建launch文件

<!-- 启动乌龟GUI与键盘控制节点 -->
<launch>
    <!-- 乌龟GUI -->
    <node pkg="turtlesim" type="turtlesim_node" name="turtle1" output="screen" />
    <!-- 键盘控制 -->
    <node pkg="turtlesim" type="turtle_teleop_key" name="key" output="screen" />
</launch>

启动节点(可能要先编译一下)

2.使用指令控制

rostopic echo /turtle1/pose

控制乌龟运动,打印位姿

3.编码实现

配置文件

编码如下

#include "ros/ros.h"
#include "turtlesim/Pose.h"

void doPose(const turtlesim::Pose::ConstPtr &pose)
{
    ROS_INFO("乌龟的位姿信息:坐标(%.2f,%.2f),朝向(%.2f),线速度:%.2f,角速度:%.2f",
            pose->x,pose->y,pose->theta,pose->linear_velocity,pose->angular_velocity);
}

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"apose");
    ros::NodeHandle nh;
    ros::Subscriber sub = nh.subscribe("/turtle1/pose",100,doPose);
    ros::spin();
    return 0;
}

启动乌龟

运行

2.5.3 服务调用生成乌龟

1.话题与消息获取

启动乌龟节点

获取

2.指令控制

效果如图

3.编码实现

创建功能包(roscpp rospy std_msgs turtlesim)

编码

#include "ros/ros.h"
#include "turtlesim/Spawn.h"

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"turtle_3");
    ros::NodeHandle ss;
    ros::ServiceClient client = ss.serviceClient<turtlesim::Spawn>("/spawn");
    turtlesim::Spawn spawn;
    spawn.request.x = 1.2;
    spawn.request.y = 4.0;
    spawn.request.theta = 1.57;
    spawn.request.name = "turtle2";
    client.call(spawn);
    //ros::service::waitForService("/spawn");
    bool flag = client.waitForExistence();和上面注释是一样的
    if(flag)
    {
        ROS_INFO("启动成功");
    }
    else
    {
        ROS_INFO("启动失败");
    }
    return 0;
}

配置

2.5.4 参数服务器设置背景颜色

1.参数获取

2.指令控制

修改参数值

重启

3.编码实现

以下三种的任意一种皆可(修改参数可以不创建句柄)

#include "ros/ros.h"

int main(int argc, char *argv[])
{
    ros::init(argc,argv,"NewColor");
    ros::NodeHandle nh;
    nh.setParam("/turtlesim/background_b",100);
    nh.setParam("/turtlesim/background_g",100);
    nh.setParam("/turtlesim/background_c",100);


    // ros::NodeHandle nh("turtlesim");
    // nh.setParam("background_b",0);
    // nh.setParam("background_g",0);
    // nh.setParam("background_c",0);


    // ros::param::set("/turtlesim/background_b",77);
    // ros::param::set("/turtlesim/background_g",77);
    // ros::param::set("/turtlesim/background_c",77);
    return 0;
}

3 ROS通信机制进阶

3.1 常用API(应用程序编程接口)

3.1.1 初始化函数(init)

参数:

        1. argc 封装实参的个数(n+1)

        2. argv 封装实参的数组

        3. name 为节点命名(唯一性)

        4. options 节点启动选项(可不管,这也就是为什么节点初始化的时候3个参数就够)

        返回值:void

使用:

        1.  argc&argv

                如果按照ROS的特定格式传入实参,那么ROS就可以加以利用,如设置全局参数,给节点重命名.....

        设置全局参数: _参数名:=数值

        2. options

                节点名称需要保持一直,这导致了节点无法重复启动(事实上是重复启动会将之前的节点关闭)

                代码:               

    ros::init(argc,argv,"pub1",ros::init_options::AnonymousName);

                实现原理:在节点启动时,为节点名添加随机数(数值好像与时间辍有关),避免节点名的重复

3.1.2 话题服务对象(advertise)

参数:

        1.话题名称

        2.队列长度

        3.latch(可选)  如果设置为true,会保存发布方的最后一条消息,并且新的订阅对象连接到发布方时,发布方会将这条消息发给订阅者

发布方及其发布逻辑设置为下

    ros::Publisher pub = nn.advertise<demo::person>("list",10,true);
    while(ros::ok())
{
    ee.num++;
    if(ee.num<=10)
    {
        pub.publish(ee);
        rate.sleep();
    }
    ros::spinOnce();
}

发布结果:

3.1.3 回旋函数(spin/spinOnce)

spin()

spin回调后会堵塞进程

spinOnce

spinOnce回调后会继续执行后面的代码

3.1.4 获取时刻的函数(ros::Time::now)

    ros::Time right_now = ros::Time::now();
    ROS_INFO("当前的时间:%.2f",right_now.toSec());
    ROS_INFO("当前的时间:%d",right_now.sec);

now函数可以获取当前时刻并返回一个Time类型的数

注:这个时刻从1970年1月1日00:00:00开始计数(中国为东八区,所以从08:00:00开始)

Time类型在ros命名空间下

toSec可以将时间转化为浮点数

sec返回整数(注:sec是一个字段,而非函数)

3.1.5 设置指定时刻

    Time t1(20,399993652);
    Time t2(100.25);
    ROS_INFO("t1:%.2f",t1.toSec());
    ROS_INFO("t2:%.2f",t2.toSec());

两种设置方式皆可,第一中后面要9位数,表示纳秒

3.1.6 设置持续时刻

程序休眠3秒

    ros::Duration du(3.0);
    du.sleep();

Duration类型变量可以接收浮点数

3.1.7 时间变量加减

    ros::Duration du(3.0);
    ros::Time begin = ros::Time::now();
    du.sleep();
    ros::Time end = ros::Time::now();
    ros::Duration du1 = end - begin;
    ros::Duration du2 = end + du;

除了Time变量之间相加,这些运算都是合法的

3.1.8 定时器(CreateTimer)

CreateTimer函数共有四个参数,分别代表间隔时间,回调函数,是否只启动一次(bool),是否自动启动(bool),后两个参数默认为false,true。

如果选择不自动启动,可以手动启动

    ros::Timer timer = nh.createTimer(ros::Duration(1),cb,false,false);
    timer.start();
    ros::spin();

回调函数:

其中event.current_real代表当前时刻。

void cb(const ros::TimerEvent& event)
{
   ROS_INFO("------------");
   ROS_INFO("输出的时刻:%.2f",event.current_real.toSec());
}

3.1.9 杀死节点(shutdown)

如下,会在输出10后杀死节点。

    while(ok())
    {
        if(n>=10) ros::shutdown();
        msg.data = "hello";
        pub.publish(msg);
        n++;
        ROS_INFO("%d",n);
        rate.sleep();
    }

3.1.10 日志信息

日志根据严重程度一般分为以下级别debug一般不显示在控制台上

    ROS_DEBUG("调试");
    ROS_INFO("消息");
    ROS_WARN("警告");
    ROS_ERROR("错误");
    ROS_FATAL("严重错误");

3.2 头文件与源文件

3.2.1 自定义头文件

文件位置如下

头文件放在include目录下

包含include的路径

头文件内容:

#ifndef _HELLO_H_
#define _HELLO_H_

namespace hello_ns
{
    class Myhello
    {
        public:
            void run();
    };
    
}

#endif

源文件内容:

#include "ros/ros.h"
#include "head/hello.h"

namespace hello_ns
{
    void Myhello::run()
    {
        ROS_INFO("函数执行");
    }
}

using namespace ros;

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    init(argc,argv,"hello");
    hello_ns::Myhello myhello;
    myhello.run();
    return 0;
}

至于这个namespace为什么这么写,要等c++学完补充(挖坑

Cmake配置

这里include放开

正常配置

3.2.2 自定义源文件

需要编写文件如图

头文件编写和上面一样

源文件和执行文件编写上更像将文件进行了拆分

源文件配置:

#include "ros/ros.h"
#include "head/hello.h"

namespace hello_ns
{
    void Myhello::run()
    {
        ROS_INFO("函数执行");
    }
}

执行文件:

#include "ros/ros.h"
#include "head/hello.h"

using namespace ros;

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    init(argc,argv,"hello");
    hello_ns::Myhello myhello;
    myhello.run();
    return 0;
}

Cmake配置:

声明c++库的配置:

从上到下,分别代表:

        库名

        头文件

        源文件

其余配置如下:

其中可执行文件和库链接

4 ROS运行管理

4.1 ROS元功能包

在ros中,总会出现多个功能包协同起作用的情况。出于便利性考虑,我们不会让使用者一个一个下载功能包,而是打包为一个元功能包统一进行下载。

在src目录下create catkin package一个新文件夹,不用添加依赖

修改package.xml

  <buildtool_depend>catkin</buildtool_depend>
  <exec_depend>api</exec_depend>//依赖(包含)的功能包
  <exec_depend>head</exec_depend>//依赖(包含)的功能包
  <exec_depend>test1</exec_depend>//依赖(包含)的功能包


  <!-- The export tag contains other, unspecified, tags -->
  <export>
    <metapackage />//暂且理解为固定句式
  </export>
</package>

Cmake配置(没有的全删掉了)

不要留空行,会报错!!

 4.2 ROS节点管理launch文件

4.2.1 launch标签(launch)

launch中可以通过deprecated添加警告信息

<launch deprecated="The file is out of time!Please use new one.">
    <node pkg="turtlesim" type="turtlesim_node" name="my_turtle" output="screen" />
    <node pkg="turtlesim" type="turtle_teleop_key" name="my_key" output="screen" />

</launch>

pkg是功能包

type是可执行文件

输出时会多一行

 4.2.2 node标签

  • pkg="包名"

    节点所属的包

  • type="nodeType"

    节点类型(与之相同名称的可执行文件)

  • name="nodeName"

    节点名称(在 ROS 网络拓扑中节点的名称)

  • args="xxx xxx xxx" (可选)

    将参数传递给节点

  • machine="机器名"

    在指定机器上启动节点

  • respawn="true | false" (可选)

    如果节点退出,是否自动重启

  • respawn_delay=" N" (可选)

    如果 respawn 为 true, 那么延迟 N 秒后启动节点

  • required="true | false" (可选)

    该节点是否必须,如果为 true,那么如果该节点退出,将杀死整个 roslaunch

  • ns="xxx" (可选)

    在指定命名空间 xxx 中启动节点

  • clear_params="true | false" (可选)

    在启动前,删除节点的私有空间的所有参数

  • output="log | screen" (可选)

    日志发送目标,可以设置为 log 日志文件,或 screen 屏幕,默认是 log

respawn和required不能同时为true

args的参数传递给argv

ns可以用来防止命名重复

4.2.3 include标签

include可以包含其他launch文件

路径格式:"$(find 功能包名)/xxx/xxx.launch"

<launch>
    <include file="$(find launch1_basic)/launch/start_turtle.launch" />
</launch>

4.2.4 remap标签

remap可以把节点的话题重命名

注意一个node里面只有一个/

<launch deprecated="The file is out of time!Please use new one.">
    <node pkg="turtlesim" type="turtlesim_node" name="my_turtle" output="screen" >
        <remap from="/turtle1/cmd_vel" to="/cmd_vel" />
    </node>
    <node pkg="turtlesim" type="turtle_teleop_key" name="my_key" output="screen" />
</launch>

这样启动节点后控制运动的话题变成了"/cmd_vel"

在启动乌龟节点后,不能和往常一样控制乌龟运动,而是要启动"/cmd_vel"控制乌龟运动

注:关于 teleop_twist_keyboard.py的使用

teleop_twist_keyboard.py要在系统的python环境下运行,这意味着要退出anaconda的虚拟环境

conda deactivate

4.2.5 param标签

name:名称

type:数据类型

指定参数类型,如果未指定,roslaunch 会尝试确定参数类型,规则如下:

  • 如果包含 '.' 的数字解析未浮点型,否则为整型

  • "true" 和 "false" 是 bool 值(不区分大小写)

  • 其他是字符串

value:数值

    <param name="param_A" type="int" value="100" />
    <node pkg="turtlesim" type="turtlesim_node" name="my_turtle" output="screen" >
        <remap from="/turtle1/cmd_vel" to="/cmd_vel" />
        <param name="param_B" type="double" value="3.14" />
    </node>

param在node下时会被包含在node的命名空间里

4.2.6 group标签

<group>标签可以对节点分组,具有 ns 属性,可以让节点归属某个命名空间

属性

  • ns="名称空间" (可选)

  • clear_params="true | false" (可选)

    启动前,是否删除组名称空间的所有参数(慎用....此功能危险)

<launch>
    <group ns="first">
        <node pkg="turtlesim" type="turtlesim_node" name="my_turtle" output="screen" />
        <node pkg="turtlesim" type="turtle_teleop_key" name="my_key" output="screen" />
    </group>
    <group ns="second">
        <node pkg="turtlesim" type="turtlesim_node" name="my_turtle" output="screen" />
        <node pkg="turtlesim" type="turtle_teleop_key" name="my_key" output="screen" />
    </group>
</launch>

 酱紫,就可以同时启动、两个相同的节点,因为它们处于不同的命名空间下

4.2.7 arg标签

<arg>标签是用于动态传参,类似于函数的参数,可以增强launch文件的灵活性

属性

  • name="参数名称"

  • default="默认值" (可选)

  • value="数值" (可选)

    不可以与 default 并存

  • doc="描述"

    参数说明

<launch>

    <arg name="length" default="0.5" />

    <param name="A" value="${length}" />

</launch>

 注:赵虚左视频里$后用的是小括号,但是实测小括号会报错,要用大括号

4.3 工作空间覆盖

打开.bashrc文件(点开头的文件是隐藏文件,要按Ctrl+H显示)

121行左右,刷新的环境变量

 package的环境变量文件(setup.bash)在devel目录下

将路径添加进去

这样在刷新环境变量时会同时刷新该环境,并引发工作空间覆盖,即直接调用时,会优先调用后刷新的目录的功能包

注:当一个环境依赖于另一个环境时,进行工作空间覆盖可能会导致依赖错误引发问题

BUG 说明:

当在 .bashrc 文件中 source 多个工作空间后,可能出现的情况,在 ROS PACKAGE PATH 中只包含两个工作空间,可以删除自定义工作空间的 build 与 devel 目录,重新 catkin_make,然后重新载入 .bashrc 文件,问题解决。

在ros中应尽量避免功能包重名,该问题目前还没有解决方案,所以在功能包重名时,可以为功能包起别名或加前缀

4.4 ROS节点名称重名

4.4.1 rosrun设置命名空间

如图,在启动节点后面加上__ns:=xx,节点在命名空间下

4.4.2 rosrun设置别名

在启动节点】后面加上__name:=xx,节点的名称被设置为xx

4.4.3 通过launch实现

格式如下

<launch>
    <node pkg="turtlesim" type="turtlesim_node" name="t1" ns="tt" />
</launch>

 4.4.4 编码实现

重映射:参考3.1.1

命名空间:

        核心代码:

  std::map<std::string, std::string> map;
  map["__ns"] = "xxxx";
  ros::init(map,"wangqiang");

        实现如下:

#include "ros/ros.h"

void cb(const ros::TimerEvent& event)
{
   ROS_INFO("------------");
   ROS_INFO("输出的时刻:%.2f",event.current_real.toSec());
}

 int main(int argc, char *argv[])
 {
    setlocale(LC_ALL,"");
    std::map<std::string , std::string> map;
    map["__ns"] = "names";
    ros::init(map,"Time");
    ros::NodeHandle nh;
    ros::Timer timer = nh.createTimer(ros::Duration(1),cb,false,false);
    timer.start();
    ros::spin();
    return 0;
 }
 

4.5 话题重名

4.5.1 通过终端重映射

rosrun teleop_twist_keyboard teleop_twist_keyboard.py /cmd_vel:=/turtle1/cmd_vel

最后话题名:=重映射的话题名

这样可以顶掉原本的话题,让话题建立连接

4.5.2 launch实现

和4.2.4一致

4.5.3 编码实现

1. 全局话题
    Publisher pub = nh.advertise<std_msgs::String>("/yy",10);

话题以/开头,其余位置不变

    Publisher pub = nh.advertise<std_msgs::String>("/yy/zz",10);

酱紫另起一个命名空间也是可以的

2. 局部话题
    Publisher pub = nh.advertise<std_msgs::String>("yy",10);

和之前正常写是一样的

3. 私有话题
    NodeHandle nh("~");
    Publisher pub = nh.advertise<std_msgs::String>("yy",10);

私有话题的实现需要借助节点

修改如上,则该句柄创建的话题为私有话题

4.6 参数重名

4.6.1 rosrun设置参数

4.6.2 launch文件设置

<launch>
    <param name="radius" type="double" value="0.3" />
    <node pkg="turtlesim" type="turtlesim_node" name="t1" ns="xxx">
        <param name="radius" type="double" value="0.5" />
    </node>
</launch>

4.6.3 编码设置

    NodeHandle nh;
    nh.setParam("/radius_nh",100);
    nh.setParam("radius_nh",100);
    NodeHandle pr("~");
    pr.setParam("radius_pr",100);
    param::set("/radiusA",100);
    param::set("radiusA",100);
    param::set("~radiusA",100);

4.7 分布式通信(需要多个计算机,记得补!)

5 ROS常用组件

5. 1 TF坐标变换

参考word

机器人上经常加装许多模块,这些模块的安装位置和机器人中心必然有一定错位,此时,这些模块的坐标就不能等同于机器人的坐标,而是要利用坐标变换将他们转化成机器人坐标系下的坐标。

 5.1.1 msg消息

查看坐标点

rosmsg info geometry_msgs/PointStamped

也可以酱紫写

rosmsg show geometry_msgs/PointStamped

std_msgs/Header header
  uint32 seq        //序列换号
  time stamp        //时间辍
  string frame_id   //该坐标点以哪个坐标为参考物
geometry_msgs/Point point
  float64 x
  float64 y
  float64 z

坐标系相对关系

rosmsg info geometry_msgs/TransformStamped

std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id        //被参考的坐标系
string child_frame_id    //另一个坐标系
geometry_msgs/Transform transform    //描述二者具体关系
  geometry_msgs/Vector3 translation  //子坐标系相对于副坐标系的位移量
    float64 x
    float64 y
    float64 z
  geometry_msgs/Quaternion rotation  //子坐标系相对于副坐标系的偏航量
    float64 x
    float64 y
    float64 z
    float64 w

5.1.2 静坐标变换

发布方

新建功能包,依赖包:

roscpp rospy std_msgs tf2 tf2_ros tf2_geometry_msgs geometry_msgs

代码编写如下

编写过程可能需要参考

vscode进行ros编写c++无代码提示或者提示滞后

vscode用c++编写ROS时突然无法补全头文件

#include "ros/ros.h"
#include "tf2_ros/static_transform_broadcaster.h"
#include "geometry_msgs/TransformStamped.h"
#include "tf2/LinearMath/Quaternion.h"

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"static_pub");
    ros::NodeHandle nh;
    tf2_ros::StaticTransformBroadcaster pub;
    geometry_msgs::TransformStamped tfs;
    tfs.header.stamp = ros::Time::now();
    tfs.header.frame_id = "base_link";//相对坐标系中被参考的那个(父坐标系)
    tfs.child_frame_id = "laser";//子坐标系
    //坐标值
    tfs.transform.translation.x = 0.2;
    tfs.transform.translation.y = 0.0;
    tfs.transform.translation.z = 0.5;
    //需要根据欧拉角转换
    tf2::Quaternion qtn; //创建四元数 对象
    //向该对象设置欧拉角,这个对象可以将欧拉角转换成四元数
    qtn.setRPY(0,0,0); //欧拉角的单位是弧度
    //四元数
    tfs.transform.rotation.x = qtn.getX();
    tfs.transform.rotation.y = qtn.getY();
    tfs.transform.rotation.z = qtn.getZ();
    tfs.transform.rotation.w = qtn.getW();
    //发布数据
    pub.sendTransform(tfs);
    ros::spin();
    return 0;
}

配置如下

代码详解:
StaticTransformBroadcaster
tf2_ros::StaticTransformBroadcaster pub;

tf2_ros::StaticTransformBroadcaster 是 ROS (Robot Operating System) 的 tf2 库中的一个类,用于发布静态的坐标变换。静态变换是那些在整个程序运行期间都不会改变的变换,例如机器人传感器相对于机器人基座的固定位置。

发布的代码

sendTransform(const geometry_msgs::msg::TransformStamped &transform)
TransformStamped
    geometry_msgs::TransformStamped tfs;

geometry_msgs::TransformStamped 是 ROS 中的一个消息类型,用于表示坐标变换。它包含了一个时间戳、父坐标系、子坐标系,以及两者之间的变换信息(包括平移和旋转)。

tf2 库中,TransformStamped 常用于发布或接收坐标变换信息。

TransformStamped的结构

Header header           // 包括时间戳和父坐标系
string child_frame_id   // 子坐标系的名称
Transform transform     // 包含平移和旋转的变换

  • header:
    • stamp: 时间戳,表示变换的时间点。
    • frame_id: 父坐标系的名称。
  • child_frame_id: 子坐标系的名称。
  • transform:
    • translation: 偏移量,平移部分,类型为 geometry_msgs::Vector3,包含 x, y, z
    • rotation: 旋转部分,类型为 geometry_msgs::Quaternion,包含 x, y, z, w
 qtn.setRPY

qtn.setRPY(0, 0, 0); 是一种用于设置四元数值的简化方法,它来自 ROS 的 tf2tf 库,用于将欧拉角(Roll、Pitch、Yaw)转换为四元数。

setRPY 的作用
  • Roll (R): 绕 X 轴的旋转(单位:弧度)。
  • Pitch (P): 绕 Y 轴的旋转(单位:弧度)。
  • Yaw (Y): 绕 Z 轴的旋转(单位:弧度)。

该函数将给定的欧拉角值转换为四元数表示,并设置四元数对象的值。

 转化并将四元数赋值给子坐标系

    qtn.setRPY(0,0,0); //欧拉角的单位是弧度
    tfs.transform.rotation.x = qtn.getX();
    tfs.transform.rotation.y = qtn.getY();
    tfs.transform.rotation.z = qtn.getZ();
    tfs.transform.rotation.w = qtn.getW();
 话题内容

seq:序列号

 rviz

命令行键入

修改参考的坐标系(父坐标系)

点击ADD,选择TF,OK

就能在屏幕上看见坐标系了

 订阅方

编码如下

#include "ros/ros.h"
#include "tf2_ros/transform_listener.h"
#include "tf2_ros/buffer.h"
#include "geometry_msgs/PointStamped.h"
#include "tf2_geometry_msgs/tf2_geometry_msgs.h"

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"static_sub");
    ros::NodeHandle nh;
    //创建buffer(缓存)
    tf2_ros::Buffer buffer;
    //创建监听对象(将订阅数据存入buffer中)
    tf2_ros::TransformListener listener(buffer);
    geometry_msgs::PointStamped ps;
    //位置基于laser对象
    ps.header.frame_id = "laser";
    ps.header.stamp = ros::Time::now();
    ps.point.x = 2.0;
    ps.point.y = 3.0;
    ps.point.z = 5.0;
    ros::Rate rate(10);
    ros::Duration(2).sleep();
    while(ros::ok())
    {
        //将ps转化为相对于base_link的坐标对象
        geometry_msgs::PointStamped ps_out;
        ps_out = buffer.transform(ps,"base_link");
        ROS_INFO("转换后的坐标值:(%.2f,%.2f,%.2f),参考坐标系:%s",
                    ps_out.point.x,
                    ps_out.point.y,
                    ps_out.point.z,
                    ps_out.header.frame_id.c_str());
        rate.sleep();
        ros::spinOnce();
    }
    return 0;
}

实现效果

 对于节点未启动也可以这么处理

    while(ros::ok())
    {
        try
        {
             //将ps转化为相对于base_link的坐标对象
        geometry_msgs::PointStamped ps_out;
        ps_out = buffer.transform(ps,"base_link");
        ROS_INFO("转换后的坐标值:(%.2f,%.2f,%.2f),参考坐标系:%s",
                    ps_out.point.x,
                    ps_out.point.y,
                    ps_out.point.z,
                    ps_out.header.frame_id.c_str());
        }
        catch(const std::exception& e)
        {
            ROS_INFO("异常消息:%s",e.what());
        }
        
       
        rate.sleep();
        ros::spinOnce();
    }

实现效果

代码详解:
PointStamped

geometry_msgs::PointStamped 是 ROS 的一种消息类型,主要用于表示带有时间戳和坐标系信息的点。它可以用来描述空间中的某个点的位置,并指定该点的位置相对于某个坐标系。

PointStamped 的结构

geometry_msgs::PointStamped 包括以下字段:

std_msgs/Header header     // 包含时间戳和坐标系信息
geometry_msgs/Point point  // 表示点的三维坐标 (x, y, z)
  • header:
    • stamp: 时间戳,表示该点的位置记录的时间。
    • frame_id: 坐标系的名称,指定点的坐标相对于哪个坐标系。
  • point:
    • x, y, z: 表示点的三维空间坐标。
 try语句

基本语法:

try {
    // 可能会抛出异常的代码
} catch (const ExceptionType& e) {
    // 捕获异常并处理
}

  • try: 包含可能抛出异常的代码。
  • catch: 用于捕获并处理异常。可以有多个 catch 块来处理不同类型的异常。

工作流程

  1. 程序执行 try 块中的代码。
  2. 如果 try 块中的代码抛出异常,程序会停止执行该块的剩余代码,并跳转到匹配的 catch 块。
  3. 如果找到匹配的 catch 块,则执行其中的代码;否则,程序会终止或将异常继续向上传递。
命令行实现


rosrun tf2_ros static_transform_publisher x偏移量 y偏移量 z偏移量 z偏航角度 y俯仰角度 x翻滚角度 父级坐标系 子级坐标系

 效果与上方相同

ROS2学习笔记包含以下内容: ### RCL(ROS Client Library) RCL是ROS客户端库,是ROS的一种API,提供了对ROS话题、服务、参数、Action等接口。不同的语言对应着不同的rcl,但基本功能相同。例如Python语言提供了rclpy来操作ROS2的节点话题服务等,C++则使用rclcpp提供API操作ROS2的节点话题和服务等。ROS2 API的实现层级中,最下面是第三方的DDS,rmw(中间件接口)层是对各家DDS的抽象层,基于rmw实现了rclc,基于rclc,ROS2官方实现了rclpy和rclcpp [^5]。 ### GUI和CLI - **GUI(Graphical User Interface)**:图形用户界面,如Windows系统,可通过鼠标点击按钮等图形化交互完成任务。 - **CLI(Command - Line Interface)**:命令行界面,如终端的黑框框,没有图形化 [^5]。 ### API(Application Programming Interface) 应用程序编程接口。比如写了一个库,里面有很多函数,使用者不知函数内部实现,需看文档或注释了解函数入口参数、返回值及用途。在C和C++中表现为头文件,在Python中表现为Python文件 [^5]。 ### 命令行操作 - `ros2 pkg --h`:获取pkg的帮助命令。 - `ros2 pkg executables 包名`:输出所有功能包下的可执行程序。 - `ros2 pkg list`:列出所有功能包。 - `ros2 pkg prefix 包名`:列出功能包路径。 - `ros2 pkg xml`:输出功能包的package.xml内容 [^2]。 ### 创建节点模板(Python) ```python import rclpy from rclpy.node import Node class Mynode(Node): def __init__(self): super().__init__("hello_node_py") self.get_logger().info("hello world!(py继承)") def main(): rclpy.init() node = Mynode() rclpy.shutdown() if __name__ == '__main__': main() ``` ### 示例代码 以下是一个创建HelloWorld节点的Python代码,初始化时输出“hello world”日志: ```python #!/usr/bin/env python3 # -*- coding: utf-8 -*- import rclpy from rclpy.node import Node import time class HelloWorldNode(Node): def __init__(self, name): super().__init__(name) while rclpy.ok(): self.get_logger().info("Hello World") time.sleep(0.5) def main(args=None): rclpy.init(args=args) node = HelloWorldNode("node_helloworld_class") rclpy.spin(node) node.destroy_node() rclpy.shutdown() ``` ### 编译运行 ```bash cd ~/ros2_ws colcon build --packages-up-to topic_pkg ros2 launch topic_pkg pub_sub.launch.xml ``` ### 小乌龟教程相关 之前的配置ros2环境课程(https://docs.ros.org/en/foxy/Tutorials/Configuring - ROS2 - Environment.html)可教你如何配置环境,后续可安装小乌龟包turtlesim [^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值