总之,不知道出于什么原因,上一篇每次重新编辑的时候都只会显示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
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坐标变换
机器人上经常加装许多模块,这些模块的安装位置和机器人中心必然有一定错位,此时,这些模块的坐标就不能等同于机器人的坐标,而是要利用坐标变换将他们转化成机器人坐标系下的坐标。
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
代码编写如下
编写过程可能需要参考
#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 的 tf2
或 tf
库,用于将欧拉角(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
块来处理不同类型的异常。
工作流程
- 程序执行
try
块中的代码。 - 如果
try
块中的代码抛出异常,程序会停止执行该块的剩余代码,并跳转到匹配的catch
块。 - 如果找到匹配的
catch
块,则执行其中的代码;否则,程序会终止或将异常继续向上传递。
命令行实现
rosrun tf2_ros static_transform_publisher x偏移量 y偏移量 z偏移量 z偏航角度 y俯仰角度 x翻滚角度 父级坐标系 子级坐标系
效果与上方相同