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翻滚角度 父级坐标系 子级坐标系

 效果与上方相同

<think>好的,我现在需要帮助用户寻找关于ROS学习资料或笔记。用户提到了ROS学习教程、笔记以及开源机器人操作系统相关的资料。我需要结合提供的引用内容来回答,并且按照系统级指令的要求来组织内容。 首先,我需要回顾用户提供的四个引用内容: 引用1和2介绍了ROS的基本概念和用途,提到它是一个开源的软件框架,用于机器人软件开发,适用于多节点多任务的场景,应用广泛。 引用3提到仿真机器人gazebo和开源功能包,这可能涉及到ROS的仿真工具和学习资源。 引用4提到了在Ubuntu系统上运行程序时需要注意的权限问题,比如使用chmod命令,这可能与ROS环境配置相关。 接下来,用户需要学习资料和笔记。我需要根据这些引用内容,推荐相关的资源,并确保回答结构清晰,分步骤说明。 可能需要包括以下几个方面: 1. 官方文档和教程:ROS官网的教程是基础,适合入门。 2. 书籍推荐:结合引用中的内容,可能有相关书籍。 3. 开源项目和实践平台:如Gazebo仿真环境,结合引用3提到的内容。 4. 社区和论坛:如ROS Answers、GitHub等,用户可能在这里找到笔记和解决方案。 5. 注意事项:比如系统权限问题,引用4提到的chmod命令,需要在学习过程中注意。 需要确保回答中引用的每个部分都正确标注引用标识,例如[^1]、[^2]等。同时,根据用户要求生成相关问题,这些问题应该与ROS学习资料相关,帮助用户进一步探索。 现在需要组织这些信息,用清晰的结构分点说明,并且使用正确的LaTeX格式,虽然用户的问题中没有数学公式,但如果有的话需要按照规则处理。但在这个回答中可能不需要数学公式,所以主要注意中文回答和相关问题生成。 最后,检查是否符合所有系统级指令,特别是引用标注的位置是否正确,是否有生成相关问题,以及回答结构是否合理。</think>以下是关于ROS学习资料及笔记的整理和推荐: ### 1. **ROS官方文档与教程** - **核心内容**:ROS官方提供了完整的入门指南和进阶教程,涵盖节点通信、消息格式、工具链使用等基础内容[^2]。 - **学习路径**: 1. 安装ROS(推荐Ubuntu系统,注意权限设置[^4])。 2. 学习`roscore`、`rosnode`、`rostopic`等核心命令。 3. 实践消息发布/订阅、服务调用等基础通信模型。 --- ### 2. **推荐书籍与开源项目** - **书籍**: - *《ROS机器人编程实践》*:结合仿真工具Gazebo[^3],讲解机器人建模与算法实现。 - *《A Gentle Introduction to ROS》*:适合初学者的开源电子书。 - **开源项目**: - **TurtleBot3**:入门级移动机器人平台,配套教程详细。 - **MoveIt!**:机械臂运动规划框架,提供丰富的案例[^3]。 --- ### 3. **仿真工具与实战平台** - **Gazebo**:ROS官方推荐的仿真环境,支持多种机器人模型(如无人机、无人车)[^3]。 - **实践建议**: ```bash # 安装Gazebo与ROS插件 sudo apt-get install ros-<distro>-gazebo-ros-pkgs ``` - 通过仿真实验验证SLAM、路径规划等算法。 --- ### 4. **社区资源与学习笔记** - **ROS Answers**:官方问答社区,可搜索常见问题(如权限错误)。 - **GitHub仓库**:搜索`ROS学习笔记`或`ROS Tutorial`,许多开发者分享代码与配置示例。 - **博客与视频**: - 知乎专栏“ROS从入门到放弃”提供避坑指南。 - YouTube频道“The Construct”提供免费ROS课程。 --- ### 5. **关键注意事项** - **系统权限**:在Ubuntu中运行ROS节点时,需使用`chmod +x`赋予可执行权限[^4]。 - **版本兼容性**:ROS 1(Noetic)与ROS 2(Foxy)差异较大,需明确学习目标版本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值