动态坐标变换(待续

创建项目功能包依赖于 tf2、tf2_ros、tf2_geometry_msgs、roscpp rospy std_msgs geometry_msgs、turtlesim

C++:

1.1发布方:

#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
        消息: /turtle1/Pose

    流程:
    1.包含头文件
    2.设置编码、初始化、NodeHandle
    3.创建订阅对象,订阅/turtle1/pose
    4.回调函数处理订阅的消息:将位姿信息转换成坐标相对关系,并发布(关注)
    5.spin()
*/
void doPose(const turtlesim::Pose::ConstPtr& pose){
    //获取位姿信息,转换成坐标系相对关系(核心),并发布
    //a.创建发布对象
    static tf2_ros::TransformBroadcaster pub;
    //b.组织被发布的数据
    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;
    //坐标系四元数
    /*
        位姿信息中没有四元数,但是有个偏航角度,又已知乌龟是 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;
}

在这里插入图片描述调用rviz比较直观
在这里插入图片描述

1.2 订阅方

与静态坐标系一样,修改几个参数就可
1.参考的坐标系
2.坐标点的时间戳
3.目标坐标系

#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.创建一个订阅对象  --->订阅坐标系相对关系 (3-1  3-2 是结合使用的)
    // 3-1. 创建一个 buffer 缓存
    tf2_ros::Buffer buffer;
    // 3-2. 再创建一个监听对象(监听对象可以将订阅的数据存入 buffer )
    tf2_ros::TransformListener listen(buffer);
    // 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;
    //添加休眠  (文章“注”的部分添加休眠)
    // ros::Duration(2).sleep();
    // 5.转换算法,调用 tf 内置实现
    ros::Rate rate(10);
    while (ros::ok())
    {
        // 核心代码 ----  ps 转换为 base_link 的坐标点
        geometry_msgs::PointStamped ps_out;     
        /*
            调用了 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");  
            // 6.最后输出
            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)
       {
        //    std::cerr << e.what() << '\n';
           ROS_INFO("异常消息:%s",e.what());
       }
       
        rate.sleep();
        ros::spinOnce();
    }
    return 0;
}

在这里插入图片描述
当启动键盘控制节点后,输出的坐标值会变

Python:

发布方:

#! usr/bin/env python
import rospy
from turtlesim.msg import Pose
import tf2_ros
from geometry_msgs.msg import TransformStamped
import tf_conversions

"""
    发布方:订阅乌龟的位姿信息,转换成坐标系的相对关系,再发布
      备:
        话题:/turtle1/pose
        类型:/turtlesim/Pose
      程:
        1.导包
        2.初始ROS化节点
        3.创建订阅对象
        4.回调函数处理订阅到的消息(核心)
        5.spin()
"""
def doPose(pose):
    #1. 创建一个发布坐标系相对关系的对象
    pub = tf2_ros.TransformBroadcaster()
    #2.  pose 转换成 坐标系相对关系消息
    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.0
    #四元数
    #从 欧拉角 转换 四元数
    """
        乌龟是 2D ,不存在x上的翻滚,y上的俯仰,只有z上的偏航
        0 0 pose.theta
    """
    qtn = tf_conversions.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.创建订阅对象
    sub = rospy.Subscriber("/turtle1/pose",Pose,doPose,queue_size=100)
    # 4.回调函数处理订阅到的消息(核心)
    # 5.spin()
    rospy.spin()

订阅方:

与静态订阅方一样,修改部分内容(3个)
第四部分:时间戳、frame_id
try内:坐标系world

#! /usr/bin/env python

import rospy
import tf2_ros
from tf2_geometry_msgs import tf2_geometry_msgs

"""
    订阅方:订阅坐标变换消息,传入被转换的坐标点,调用转换算法
    流程:
        1.导包
        2.初始化节点
        3.创建订阅对象
        4.组织被转换的坐标点
        5.转换逻辑实现,调用tf封装的算法
        6.输出结果
"""

if __name__ == "__main__":
    # 2.初始化节点
    rospy.init_node("static_sub_p")
    # 3.创建订阅对象
    # 3-1.创建缓存对象
    buffer = tf2_ros.Buffer()
    # 3-2.创建订阅对象的坐标点
    sub = tf2_ros.TransformListener(buffer)
    # 4.组织被转换的坐标点
    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(10)
    while not rospy.is_shutdown():
        try:
            # 转换实现
            """"
                参数1:被转换的坐标点
                参数2:目标坐标系
                返回值:转换后的坐标点

                注:
                问题:抛出异常 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()
 

备注:python未跑出来,python模块出错

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值