文章目录
- 坐标msg消息
- 静态坐标变换
- 1. C++实现
- 发布方 demo01_static_pub.cpp
- 订阅方 demo02_static_sub.cpp
- 2. Python实现
- 发布方 demo01_static_pub_p.py
- 订阅方 demo02_static_sub_p.py
- 动态坐标变换
- 获取话题: rostopic list
- 获取消息类型:rostopic info /turtle1/pose
- 获取消息格式: rosmsg info turtlesim/Pose
- 1. C++实现
- 发布方 demo01_dynamic_pub.cpp
- 订阅方 demo02_dynamic_sub.cpp
- 2. Python实现
- 发布方 demo01_dynamic_sub_p.py
- 订阅方 demo02_dynamic_sub_p.py
坐标msg消息
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 坐标A相对于坐标B的欧拉角(三个角)
float64 x 四元数,旋转
float64 y
float64 z
float64 w
geometry_msgs/PointStamped
传输某个坐标系内坐标点的信息
std_msgs/Header header
uint32 seq
time stamp
string frame_id
geometry_msgs/Point point
float64 x
float64 y
float64 z
静态坐标变换
静态坐标变换,是指两个坐标系之间的相对位置是固定的。
问题背景:
现有一机器人模型,核心构成包含主体与雷达,各对应一坐标系,坐标系的原点分别位于主体与雷达的物理中心,已知雷达原点相对于主体原点位移关系如下: x 0.2 y0.0 z0.5。当前雷达检测到一障碍物,在雷达坐标系中障碍物的坐标为 (2.0 3.0 5.0),请问,该障碍物相对于主体的坐标是多少?
建文件
tf01_static
roscpp rospy std_msgs tf2 tf2_ros tf2_geometry_msgs geometry_msgs
查看
rostopic list
rostopic echo /tf_static
图形化设置
rviz
transforms:
header:
seq: 0
stamp:
secs: 1658132852
nsecs: 825164513
frame_id: “base_link”
child_frame_id: “laser”
transform:
translation:
x: 0.2
y: 0.0
z: 0.5
rotation:
x: 0.0
y: 0.0
z: 0.0
w: 1.0
1. C++实现
发布方 demo01_static_pub.cpp
#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;
}
订阅方 demo02_static_sub.cpp
#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;
tf2_ros::Buffer buffer;
tf2_ros::TransformListener listener(buffer);
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();
ros::Rate rate(1);
while(ros::ok())
{
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.header.frame_id.c_str()
);
rate.sleep();
ros::spinOnce();
}
return 0;
}
2. Python实现
发布方 demo01_static_pub_p.py
"""
发布方: 发布两个坐标系的相对关系(车辆底盘 --- 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__":
rospy.init_node("start_pub_p")
pub = tf2_ros.StaticTransformBroadcaster()
tfs = TransformStamped()
tfs.header.stamp = rospy.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
qtn = tf.transformations.quaternion_from_euler(0,0,0)
tfs.transform.rotation.x = qtn[0]
tfs.transform.rotation.y = qtn[1]
tfs.transform.rotation.z = qtn[2]
tfs.transform.rotation.w = qtn[3]
pub.sendTransform(tfs)
rospy.spin()
订阅方 demo02_static_sub_p.py
"""
订阅方: 订阅坐标变换消息,传入被转换的坐标,调用转换算法
流程:
1. 导包
2. 初始化
3. 创建订阅对象
4. 组织被转换的坐标点
5. 转换逻辑实现,调用tf封装的算法
6. 输出结果
"""
import rospy
import tf2_ros
from tf2_geometry_msgs import tf2_geometry_msgs
if __name__ == "__main__":
rospy.init_node("static_sub_p")
buffer = tf2_ros.Buffer()
sub = tf2_ros.TransformListener(buffer)
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
rate = rospy.Rate(1)
while not rospy.is_shutdown():
try:
"""
参数1: 被转换的坐标点
参数2: 目标坐标系
返回值: 转换后的坐标点
PS:
问题: 抛出异常 base_link 不存在
原因: 转换函数调用时,可能还没有订阅到坐标系的相对关系
解决: try 捕获异常,并处理
"""
ps_out = buffer.transform(ps,"base_link")
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 系统就已经封装好了专门的节点,摄像头相对于基坐标系位置
rosrun tf2_ros static_transform_publisher 0.1 0.0 0.3 0 0 0 /base_link /camera
rviz
依次是偏航角,俯仰角,翻滚角
![在这里插入图片描述](https://img-blog.csdnimg.cn/04180e71a01244d9b68744fb451d2862.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/af8c4dd406a947598f97e575b1e7470d.png)
偏航:Yaw
俯仰:Pitch
翻滚:Roll
X轴:红色(翻滚角)正值 顺时针转动;负值 逆时针转动
Y轴:绿色(俯仰角)
Z轴:蓝色(偏航角)
![在这里插入图片描述](https://img-blog.csdnimg.cn/267fc4228d964b9dbc6bb61080eb5322.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/2661ee2dd6244117aefa2671085cb45a.png)
动态坐标变换
话题:/turtle1/pose
消息类型:turtlesim/Pose
消息格式:
float32 x
float32 y
float32 theta
float32 linear_velocity
float32 angular_velocity
获取话题: rostopic list
rosrun turtlesim turtlesim_node
rostopic list
/rosout
/rosout_agg
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose 位姿发布使用的话题
获取消息类型:rostopic info /turtle1/pose
turtle1/pose
Type: turtlesim/Pose 消息类型
Publishers: * /turtlesim (http://rosnoetic-VirtualBox:37261/)
Subscribers: None
获取消息格式: rosmsg info turtlesim/Pose
float32 x
float32 y
float32 theta
float32 linear_velocity
float32 angular_velocity
1. C++实现
发布方 demo01_dynamic_pub.cpp
#include"ros/ros.h"
#include"turtlesim/Pose.h"
#include"tf2_ros/transform_broadcaster.h"
#include"geometry_msgs/TransformStamped.h"
#include"tf2/LinearMath/Quaternion.h"
void doPose(const turtlesim::Pose::ConstPtr& pose){
static tf2_ros::TransformBroadcaster pub;
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;
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();
pub.sendTransform(ts);
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
ros::init(argc,argv,"dynamic_pub");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe("/turtle1/pose",100,doPose);
ros::spin();
return 0;
}
订阅方 demo02_dynamic_sub.cpp
roscore
rosrun turtlesim turtlesim_node
rosrun tf02_dynamic demo01_dynamic_pub
rosrun tf02_dynamic demo02_dynamic_sub
#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,"dynamic_sub");
ros::NodeHandle nh;
tf2_ros::Buffer buffer;
tf2_ros::TransformListener listener(buffer);
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;
ros::Rate rate(1);
while(ros::ok())
{
geometry_msgs::PointStamped ps_out;
try
{
ps_out = buffer.transform(ps,"world");
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;
}
2. Python实现
发布方 demo01_dynamic_sub_p.py
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
"""
发布方: 需要订阅乌龟的位姿信息, 转换成相对于窗体的坐标关系,并发布
准 备:
话题: /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):
pub = tf2_ros.TransformBroadcaster()
ts = TransformStamped()
ts.header.frame_id = "world"
ts.header.stamp = rospy.Time.now()
ts.child_frame_id = "turtle1"
ts.transform.translation.x = pose.x
ts.transform.translation.y = pose.y
ts.transform.translation.z = 0
"""
乌龟是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]
pub.sendTransform(ts)
if __name__ == "__main__":
rospy.init_node("dynamic_pub_p")
sub = rospy.Subscriber("/turtle1/pose",Pose,doPose,queue_size = 100)
rospy.spin()
订阅方 demo02_dynamic_sub_p.py
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
"""
订阅方: 订阅坐标变换消息,传入被转换的坐标,调用转换算法
流程:
1. 导包
2. 初始化
3. 创建订阅对象
4. 组织被转换的坐标点
5. 转换逻辑实现,调用tf封装的算法
6. 输出结果
"""
import rospy
import tf2_ros
from tf2_geometry_msgs import tf2_geometry_msgs
if __name__ == "__main__":
rospy.init_node("dynamic_sub_p")
buffer = tf2_ros.Buffer()
sub = tf2_ros.TransformListener(buffer)
ps = tf2_geometry_msgs.PointStamped()
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
rate = rospy.Rate(1)
while not rospy.is_shutdown():
try:
"""
参数1: 被转换的坐标点
参数2: 目标坐标系
返回值: 转换后的坐标点
PS:
问题: 抛出异常 base_link 不存在
原因: 转换函数调用时,可能还没有订阅到坐标系的相对关系
解决: try 捕获异常,并处理
"""
ps_out = buffer.transform(ps,"world")
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()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)