在ROS中内置一些比较实用的工具,通过这些工具可以方便快捷的实现某个功能或调试程序,从而提高开发效率,本章主要介绍ROS中内置的如下组件:
本章预期达成的学习目标:
机器人系统上,有多个传感器,如激光雷达、摄像头等,有的传感器是可以感知机器人周边的物体方位(或者称之为:坐标,横向、纵向、高度的距离信息)的,以协助机器人定位障碍物,可以直接将物体相对该传感器的方位信息,等价于物体相对于机器人系统或机器人其它组件的方位信息吗?显示是不行的,这中间需要一个转换过程。更具体描述如下:
场景1:雷达与小车
现有一移动式机器人底盘,在底盘上安装了一雷达,雷达相对于底盘的偏移量已知,现雷达检测到一障碍物信息,获取到坐标分别为(x,y,z),该坐标是以雷达为参考系的,如何将这个坐标转换成以小车为参考系的坐标呢?
场景2:现有一带机械臂的机器人(比如:PR2)需要夹取目标物,当前机器人头部摄像头可以探测到目标物的坐标(x,y,z),不过该坐标是以摄像头为参考系的,而实际操作目标物的是机械臂的夹具,当前我们需要将该坐标转换成相对于机械臂夹具的坐标,这个过程如何实现?
roslaunch turtle_tf2 turtle_tf2_demo_cpp.launch(C++实现)
roslaunch turtle_tf2 turtle_tf2_demo.launch(python实现)
概念
说明
在ROS中坐标变换最初对应的是tf,不过在 hydro 版本开始, tf 被弃用,迁移到 tf2,后者更为简洁高效,tf2对应的常用功能包有:
订阅发布模型中数据载体 msg 是一个重要实现,首先需要了解一下,在坐标转换实现中常用的 msg:
在坐标变换中,频繁的需要使用到坐标系的相对关系以及坐标点信息。
名词解释
std_msgs/Header header # 头信息
uint32 seq #|-- 序列号
time stamp #|-- 时间戳
string frame_id #|-- 坐标 ID
string child_frame_id # 子坐标系的 id
geometry_msgs/Transform transform # 坐标信息
geometry_msgs/Vector3 translation # 偏移(平移)
float64 x #|-- X 方向的偏移量
float64 y #|-- Y 方向的偏移量
float64 z #|-- Z 方向上的偏移量
geometry_msgs/Quaternion rotation # 四元数,旋转
float64 x # 偏航,俯仰,翻滚角度上的变化
float64 y #
float64 z
float64 w
std_msgs/Header header # 头
uint32 seq #|-- 序号
time stamp #|-- 时间戳
string frame_id #|-- 所属坐标系的 id
geometry_msgs/Point point # 点坐标
float64 x #|-- x y z 坐标
float64 y
float64 z
需求
现有一机器人模型,核心构成包含主体与雷达,各对应一坐标系,坐标系的原点分别位于主体与雷达的物理中心,已知雷达原点相对于主体原点位移关系如下:x 0.2 y0.0 z0.5。当前雷达检测到一障碍物,在雷达坐标系中障碍物的坐标为 (2.0 3.0 5.0),请问,该障碍物相对于主体的坐标是多少?
分析
流程:C++ 与 Python 实现流程一致
tf01_static
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"
/*
需求: 发布两个坐标系的相对关系
流程:
1. 包含头文件
2. 设置编码 节点初始化 NodeHandle
3. 创建发布对象
4. 组织被发布的消息
5. 发布数据
6. spin()
*/
int main(int argc, char *argv[])
{
// 2. 设置编码 节点初始化 NodeHandle
setlocale(LC_ALL,"");
ros::init(argc,argv,"static_pub");
ros::NodeHandle nh;(不是必须的)
// 3. 创建发布对象 对应头文件2 —— tf2_ros/static_transform_broadcaster.h 静态坐标转换广播器
tf2_ros::StaticTransformBroadcaster pub;
// 4. 组织被发布的消息 对应头文件3 —— geometry_msgs/TransformStamped.h
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;//雷达x偏移值
tfs.transform.translation.y = 0.0;//雷达y偏移值
tfs.transform.translation.z = 0.5;//雷达z偏移值
// 4.1 需要根据欧拉角转换 对应头文件4 —— tf2/LinearMath/Quaternion.h
tf2::Quaternion qtn; // 创建四元数对象
// 4.2 向该对象设置欧拉角,这个对象可以将欧拉角转换成四元数
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();
// 5. 发布数据
pub.sendTransform(tfs);
// 6. spin()
ros::spin();
return 0;
}
add_executable(demo01_static_pub src/demo01_static_pub.cpp)
add_dependencies(demo01_static_pub ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(demo01_static_pub
${catkin_LIBRARIES}
)
Ctrl + Shift + B
检验方式1
roscore
cd demo02_ws/
source ./devel/setup.bash
rosrun tf01_static demo01_static_pub
rostopic list
rostopic echo /tf_static
rviz
// 核心代码 ---- 将 ps 转换成相对于 base_link的坐标点 头文件5 —— tf2_geometry_msgs/tf2_geometry_msgs.h
geometry_msgs::PointStamped ps_out;
/*
调用buffer(数据存在缓存buffer里面)的转换函数 transform
参数1: 被转换的坐标点
参数2: 目标坐标系
返回值: 输出的坐标点
注意1: 调用时必须包含头文件 tf2_geometry_msgs/tf2_geometry_msgs.h
注意2: 运行时存在的问题,抛出一个异常 base_link不存在
原因:
订阅数据是一个耗时操作,可能调用transform转换函数时,
坐标系相对关系还没有订阅到,因此出现异常
解决:
方案1: 在调用转换函数时,执行休眠
方案2: 进行异常处理
*/
ps_out = buffer.transform(ps,"base_link"); // 目标点信息,基参考系,输出转换后的坐标
#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"
/*
订阅方: 订阅发布的坐标系相对关系,传入一个坐标系,调用tf实现转换
流程:
1. 包含头文件
2. 编码 初始化 NodeHandle(必须)
3. 创建订阅对象: ---> 订阅坐标系相对关系
4. 组织一个坐标点数据
5. 转换算法,需要调用tf内置实现
6. 最后输出
*/
int main(int argc, char *argv[])
{
// 2. 编码 初始化 NodeHandle(必须)
setlocale(LC_ALL,"");
ros::init(argc,argv,"static_sub");
ros::NodeHandle nh;
// 3. 创建订阅对象: ---> 订阅坐标系相对关系 头文件2\3
// 3.1 创建一个buffer缓存
tf2_ros::Buffer buffer;
// 3.2 再创建监听对象(监听对象可以将订阅的数据存入buffer)
tf2_ros::TransformListener listener(buffer);
// 4. 组织一个坐标点数据 头文件4
geometry_msgs::PointStamped ps;
ps.header.stamp = ros::Time::now();
ps.header.frame_id = "laser";// 坐标系 障碍物位置相对于雷达坐标系而言
ps.point.x = 2.0; // 障碍物相对于雷达坐标系的位置
ps.point.y = 3.0;
ps.point.z = 5.0;
// 添加休眠 防止订阅方没有订阅到发布方消息
ros::Duration(2).sleep();
// 5. 转换算法,需要调用tf内置实现
ros::Rate rate(1);
while(ros::ok())
{
// 核心代码 ---- 将 ps 转换成相对于 base_link的坐标点 头文件5
geometry_msgs::PointStamped ps_out;
/*
调用buffer(数据存在缓存buffer里面)的转换函数 transform
参数1: 被转换的坐标点
参数2: 目标坐标系
返回值: 输出的坐标点
注意1: 调用时必须包含头文件 tf2_geometry_msgs/tf2_geometry_msgs.h
注意2: 运行时存在的问题,抛出一个异常 base_link不存在
原因:
订阅数据是一个耗时操作,可能调用transform转换函数时,
坐标系相对关系还没有订阅到,因此出现异常
解决:
方案1: 在调用转换函数时,执行休眠
方案2: 进行异常处理
*/
ps_out = buffer.transform(ps,"base_link"); // 目标点信息,基参考系,输出转换后的坐标
// 6. 最后输出
ROS_INFO("转换后的坐标值:(%.2f,%.2f,%.2f),参考的坐标系:%s",
ps_out.point.x,
ps_out.point.y,
ps_out.point.z,
ps.header.frame_id.c_str()
);
rate.sleep();
ros::spinOnce();
}
return 0;
}
add_executable(demo02_static_sub src/demo02_static_sub.cpp)
add_dependencies(demo02_static_sub ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(demo02_static_sub
${catkin_LIBRARIES}
)
Ctrl + Shift + B
roscore
cd demo02_ws/
source ./devel/setup.bash
rosrun tf01_static demo01_static_pub
cd demo02_ws/
source ./devel/setup.bash
rosrun tf01_static demo02_static_sub
#! /usr/bin/env python
"""
发布方: 发布两个坐标系的相对关系(车辆底盘 --- base_link 和 雷达 --- laser)
流程:
1. 导包
2. 初始化节点
3. 创建发布对象
4. 组织被发布的数据
5. 发布数据
6. spin()
"""
import rospy
import tf2_ros
import tf
from geometry_msgs.msg import TransformStamped
if __name__ == "__main__":
# 2. 初始化节点
rospy.init_node("start_pub_p")
# 3. 创建发布对象 头文件2
pub = tf2_ros.StaticTransformBroadcaster()
# 4. 组织被发布的数据 头文件4
tfs = TransformStamped()
# 4.1 header
tfs.header.stamp = rospy.Time.now()
tfs.header.frame_id = "base_link"
# 4.2 child frame
tfs.child_frame_id = "laser"
# 4.3 相对关系(偏移 与 四元数)
tfs.transform.translation.x = 0.2
tfs.transform.translation.y = 0.0
tfs.transform.translation.z = 0.5
# 4.4 先从欧拉角转换成四元数 头文件3
qtn = tf.transformations.quaternion_from_euler(0,0,0)
# 4.5 再设置四元数
tfs.transform.rotation.x = qtn[0]
tfs.transform.rotation.y = qtn[1]
tfs.transform.rotation.z = qtn[2]
tfs.transform.rotation.w = qtn[3]
# 5. 发布数据
pub.sendTransform(tfs)
# 6. spin()
rospy.spin()
#! /usr/bin/env python
"""
订阅方: 订阅坐标变换消息,传入被转换的坐标,调用转换算法
流程:
1. 导包
2. 初始化
3. 创建订阅对象
4. 组织被转换的坐标点
5. 转换逻辑实现,调用tf封装的算法
6. 输出结果
"""
import rospy
import tf2_ros
from tf2_geometry_msgs import tf2_geometry_msgs
if __name__ == "__main__":
# 2. 初始化
rospy.init_node("static_sub_p")
# 3. 创建订阅对象
# 3.1 创建一个缓存对象 头文件2
buffer = tf2_ros.Buffer()
# 3.2 创建订阅对象(将缓存传入)
sub = tf2_ros.TransformListener(buffer)
# 4. 组织被转换的坐标点 头文件3
ps = tf2_geometry_msgs.PointStamped()
ps.header.stamp = rospy.Time.now()
ps.header.frame_id = "laser"
ps.point.x = 2.0
ps.point.y = 3.0
ps.point.z = 5.0
# 5. 转换逻辑实现,调用tf封装的算法
rate = rospy.Rate(1)
while not rospy.is_shutdown():
# 使用try防止
try:
# 转换实现
"""
参数1: 被转换的坐标点
参数2: 目标坐标系
返回值: 转换后的坐标点
PS:
问题: 抛出异常 base_link 不存在
原因: 转换函数调用时,可能还没有订阅到坐标系的相对关系
解决: try 捕获异常,并处理
"""
ps_out = buffer.transform(ps,"base_link")
# 6. 输出结果
rospy.loginfo("转换后的坐标:(%.2f,%.2f,%.2f),参考的坐标系: %s",
ps_out.point.x,
ps_out.point.y,
ps_out.point.z,
ps_out.header.frame_id)
except Exception as e:
rospy.logwarn("错误提示:%s",e)
rate.sleep()
当坐标系之间的相对位置固定时,那么所需参数也是固定的: 父系坐标名称、子级坐标系名称、x偏移量、y偏移量、z偏移量、x翻滚角度、y俯仰角度、z偏航角度,实现逻辑相同,参数不同,那么 ROS 系统就已经封装好了专门的节点,使用方式如下:
也建议使用该种方式直接实现静态坐标系相对信息发布。
可以借助于rviz显示坐标系关系,具体操作:
所谓动态坐标变换,是指两个坐标系之间的相对位置是变化的。
需求描述
实现分析
实现流程:C++ 与 Python 实现流程一致
tf02_dynamic
roscpp rospy std_msgs tf2 tf2_ros tf2_geometry_msgs geometry_msgs turtlesim
roscore
rosrun turtlesim turtlesim_node
rostopic list
启动窗口4 —— 获取消息格式 turtlesim/Pose
rostopic info /turtle1/pose
rosmsg info turtlesim/Pose
float32 x
float32 y
float32 theta
float32 linear_velocity
float32 angular_velocity
#include"ros/ros.h"
#include"turtlesim/Pose.h"
// 创建发布对象
#include"tf2_ros/transform_broadcaster.h"
// 组织发布数据
#include"geometry_msgs/TransformStamped.h"
// 坐标系四元数
#include"tf2/LinearMath/Quaternion.h"
/*
发布方: 需要订阅乌龟的位姿信息, 转换成相对于窗体的坐标关系,并发布
准 备:
话题: /turtle1/pose
消息: turtlesim/Pose
流程:
1. 包含头文件
2. 设置编码,初始化, NodeHandle
3. 创建订阅对象,订阅/turtle1/pose
4. 回调函数处理订阅信息: 将位姿信息转换成坐标相对关系并发布(关注)
5. spin()
*/
// 给传进回调函数的数据定义范型 头文件2
void doPose(const turtlesim::Pose::ConstPtr& pose){
// 获取位姿信息,转换成坐标系相对关系(核心),并发布
// a. 创建发布对象 头文件3
static tf2_ros::TransformBroadcaster pub;
// b. 组织被发布的数据 头文件4
geometry_msgs::TransformStamped ts;
ts.header.frame_id = "world"; // 相对于世界坐标系
ts.header.stamp = ros::Time::now();
ts.child_frame_id = "turtle1";
// 坐标系偏移量
ts.transform.translation.x = pose->x;
ts.transform.translation.y = pose->y;
ts.transform.translation.z = 0; // z一直是零(2D)
// 坐标系四元数 头文件5
/*
位姿信息中没有四元数,但是有偏航角度,又已知乌龟时2D,没有翻滚和俯仰角,所以得出乌龟的
欧拉角: 0 0 theta
*/
tf2::Quaternion qtn;
qtn.setRPY(0,0,pose->theta);
ts.transform.rotation.x = qtn.getX();
ts.transform.rotation.y = qtn.getY();
ts.transform.rotation.z = qtn.getZ();
ts.transform.rotation.w = qtn.getW();
// c. 发布
pub.sendTransform(ts);
}
int main(int argc, char *argv[])
{
// 2. 设置编码,初始化, NodeHandle
setlocale(LC_ALL,"");
ros::init(argc,argv,"dynamic_pub");
ros::NodeHandle nh;
// 3. 创建订阅对象,订阅/turtle1/pose
ros::Subscriber sub = nh.subscribe("/turtle1/pose",100,doPose);
// 4. 回调函数处理订阅信息: 将位姿信息转换成坐标相对关系并发布(关注)
// 5. spin()
ros::spin();
return 0;
}
add_executable(demo01_dynamic_pub src/demo01_dynamic_pub.cpp)
add_dependencies(demo01_dynamic_pub ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(demo01_dynamic_pub
${catkin_LIBRARIES}
)
#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"
/*
订阅方: 订阅发布的坐标系相对关系,传入一个坐标系,调用tf实现转换
流程:
1. 包含头文件
2. 编码 初始化 NodeHandle(必须)
3. 创建订阅对象: ---> 订阅坐标系相对关系
4. 组织一个坐标点数据
5. 转换算法,需要调用tf内置实现
6. 最后输出
*/
int main(int argc, char *argv[])
{
// 2. 编码 初始化 NodeHandle(必须)
setlocale(LC_ALL,"");
ros::init(argc,argv,"dynamic_sub");
ros::NodeHandle nh;
// 3. 创建订阅对象: ---> 订阅坐标系相对关系 头文件2\3
// 3.1 创建一个buffer缓存
tf2_ros::Buffer buffer;
// 3.2 再创建监听对象(监听对象可以将订阅的数据存入buffer)
tf2_ros::TransformListener listener(buffer);
// 4. 组织一个坐标点数据 头文件4
geometry_msgs::PointStamped ps;
// 参考的坐标系
ps.header.frame_id = "turtle1";// 坐标系 物体位置相对于乌龟坐标系而言
// 时间戳
ps.header.stamp = ros::Time(0.0);
ps.point.x = 2.0; // 物体相对于乌龟坐标系的位置
ps.point.y = 3.0;
ps.point.z = 5.0;
// 添加休眠 防止订阅方没有订阅到发布方消息(这个方法不建议用,最好使用try方法
// ros::Duration(2).sleep();
// 5. 转换算法,需要调用tf内置实现
ros::Rate rate(1);
while(ros::ok())
{
// 核心代码 ---- 将 ps 转换成相对于 base_link的坐标点 头文件5
geometry_msgs::PointStamped ps_out;
/*
调用buffer(数据存在缓存buffer里面)的转换函数 transform
参数1: 被转换的坐标点
参数2: 目标坐标系
返回值: 输出的坐标点
注意1: 调用时必须包含头文件 tf2_geometry_msgs/tf2_geometry_msgs.h
注意2: 运行时存在的问题,抛出一个异常 base_link不存在
原因:
订阅数据是一个耗时操作,可能调用transform转换函数时,
坐标系相对关系还没有订阅到,因此出现异常
解决:
方案1: 在调用转换函数时,执行休眠
方案2: 进行异常处理
*/
try
{
ps_out = buffer.transform(ps,"world"); // 目标点信息,基参考系,输出转换后的坐标 头文件5r
// 6. 最后输出
ROS_INFO("转换后的坐标值:(%.2f,%.2f,%.2f),参考的坐标系:%s",
ps_out.point.x,
ps_out.point.y,
ps_out.point.z,
ps.header.frame_id.c_str()
);
}
catch(const std::exception& e)
{
ROS_INFO("异常消息:%s",e.what());
}
rate.sleep();
ros::spinOnce();
}
return 0;
}
add_executable(demo02_dynamic_sub src/demo02_dynamic_sub.cpp)
add_dependencies(demo02_dynamic_sub ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(demo02_dynamic_sub
${catkin_LIBRARIES}
)
Ctrl + Shift + B
roscore
rosrun turtlesim turtlesim_node
cd demo02_ws/
source ./devel/setup.bash
rosrun tf02_dynamic demo01_dynamic_pub
cd demo02_ws/
source ./devel/setup.bash
rosrun tf02_dynamic demo02_dynamic_sub
cd demo02_ws/
source ./devel/setup.bash
rosrun turtlesim turtle_teleop_key
roscore
rosrun turtlesim turtlesim_node
cd demo02_ws/
source ./devel/setup.bash
rosrun tf02_dynamic demo01_dynamic_pub_p.py
/rosout
/rosout_agg
/tf
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose
rostopic echo /tf 查看具体信息
ransforms:
header:
seq: 0
stamp:
secs: 1658287751
nsecs: 917598485
frame_id: “world”
child_frame_id: “turtle1”
transform:
translation:
x: 5.544444561004639
y: 5.544444561004639
z: 0.0
rotation:
x: 0.0
y: 0.0
z: 0.0
w: 1.0
#! /usr/bin/env python
"""
发布方: 需要订阅乌龟的位姿信息, 转换成相对于窗体的坐标关系,并发布
准 备:
话题: /turtle1/pose
消息: turtlesim/Pose
流程:
1. 包含头文件
2. 初始化ROS节点
3. 创建订阅对象,订阅/turtle1/pose
4. 回调函数处理订阅信息: 将位姿信息转换成坐标相对关系并发布(关注)
5. spin()
"""
import rospy
from turtlesim.msg import Pose
import tf2_ros
from geometry_msgs.msg import TransformStamped
import tf
def doPose(pose):
# 1. 创建发布坐标系相对关系的对象 头文件3
pub = tf2_ros.TransformBroadcaster()
# 2. 将 pose 转换成 坐标系相对关系消息 头文件4
ts = TransformStamped()
ts.header.frame_id = "world"
ts.header.stamp = rospy.Time.now()
ts.child_frame_id = "turtle1"
# 2.1 子级坐标系相对于父级坐标系的偏移量
ts.transform.translation.x = pose.x
ts.transform.translation.y = pose.y
ts.transform.translation.z = 0
# 2.2 四元数 头文件5
# 从欧拉角转换四元数
"""
乌龟是2D,不存在 x 上的翻滚和 y 上的俯仰,只有 z 上的偏航
0 0 pose.theta
"""
qtn = tf.transformations.quaternion_from_euler(0,0,pose.theta)
ts.transform.rotation.x = qtn[0]
ts.transform.rotation.y = qtn[1]
ts.transform.rotation.z = qtn[2]
ts.transform.rotation.w = qtn[3]
# 3. 发布
pub.sendTransform(ts)
if __name__ == "__main__":
# 2. 初始化ROS节点
rospy.init_node("dynamic_pub_p")
# 3. 创建订阅对象 头文件2
sub = rospy.Subscriber("/turtle1/pose",Pose,doPose,queue_size = 100)# /tur是话题名称
# 4. 回调函数处理订阅信息
# 5. spin()
rospy.spin()
roscore
cd demo02_ws/
rosrun turtlesim turtlesim_node
source ./devel/setup.bash
rosrun tf02_dynamic demo01_dynamic_pub_p.py
rosrun tf02_dynamic demo02_dynamic_sub_p.py
控制乌龟运动
rosrun turtlesim turtle_teleop_key
#! /usr/bin/env python
"""
订阅方: 订阅坐标变换消息,传入被转换的坐标,调用转换算法
流程:
1. 导包
2. 初始化
3. 创建订阅对象
4. 组织被转换的坐标点
5. 转换逻辑实现,调用tf封装的算法
6. 输出结果
"""
import rospy
import tf2_ros
from tf2_geometry_msgs import tf2_geometry_msgs
if __name__ == "__main__":
# 2. 初始化
rospy.init_node("dynamic_sub_p")
# 3. 创建订阅对象
# 3.1 创建一个缓存对象 头文件2
buffer = tf2_ros.Buffer()
# 3.2 创建订阅对象(将缓存传入)
sub = tf2_ros.TransformListener(buffer)
# 4. 组织被转换的坐标点 头文件3
ps = tf2_geometry_msgs.PointStamped()
# 时间戳--0
ps.header.stamp = rospy.Time()
# 坐标系
ps.header.frame_id = "turtle1"
ps.point.x = 2.0
ps.point.y = 3.0
ps.point.z = 5.0
# 5. 转换逻辑实现,调用tf封装的算法
rate = rospy.Rate(1)
while not rospy.is_shutdown():
# 使用try防止
try:
# 转换实现
"""
参数1: 被转换的坐标点
参数2: 目标坐标系
返回值: 转换后的坐标点
PS:
问题: 抛出异常 base_link 不存在
原因: 转换函数调用时,可能还没有订阅到坐标系的相对关系
解决: try 捕获异常,并处理
"""
ps_out = buffer.transform(ps,"world")
# 6. 输出结果
rospy.loginfo("转换后的坐标:(%.2f,%.2f,%.2f),参考的坐标系: %s",
ps_out.point.x,
ps_out.point.y,
ps_out.point.z,
ps_out.header.frame_id)
except Exception as e:
rospy.logwarn("错误提示:%s",e)
rate.sleep()