【古月居《ros入门21讲》学习笔记】16_tf坐标系广播与监听的编程实现

目录

说明:

1. 实现过程(C++)

创建功能包(C++)

创建tf广播器代码(C++)

创建tf监听器代码(C++)

配置tf监听器与广播器代码编译规则

编译并运行

编译

运行

2. 实现过程(Python)

创建tf广播器代码(Python)

创建tf监听器代码(Python)

运行效果


说明:

1. 本系列学习笔记基于B站:古月居《ROS入门21讲》课程,且使用的Ubuntu与ROS系统版本与课程完全一致;

虚拟机版本 Linux系统版本 ROS系统版本
VMware WorkStation Pro 16 Ubuntu18.04 Melodic

2. 课程中的所有示例代码均已跑通,且对Pyhon版本的代码也都做了运行验证,并附带验证过程(错误均已修正);

3. 本节是整个笔记的第16节,对应视频课程的第18节,请自行对应学习;

4. 整个系列笔记基本已经完结,但部分章节仍需润色修改 ,后面会陆续发布,请大家持续关注,      创作不易,感谢支持!


1. 实现过程(C++)

创建功能包(C++)

cd ~/catkin_ws/src
catkin_create_pkg learning_tf roscpp rospy tfturtlesim

image-20230606160521970

创建tf广播器代码(C++)

cd ~/catkin_ws/src/learning_tf/src
touch turtle_tf_broadcaster.cpp

  • 定义tf广播器(TransformBroadcaster);

  • 创建坐标变换值;

  • 发布坐标变换(sendTransform)

/**
 * 该例程产生tf数据,并计算、发布turtle2的速度指令
 */
​
#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <turtlesim/Pose.h>
​
std::string turtle_name;
​
void poseCallback(const turtlesim::PoseConstPtr& msg)
{
    // 创建tf的广播器
    static tf::TransformBroadcaster br;
​
    // 初始化tf数据
    tf::Transform transform;
    transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );
    tf::Quaternion q;
    q.setRPY(0, 0, msg->theta);
    transform.setRotation(q);
​
    // 广播world与海龟坐标系之间的tf数据
    br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name));
}
​
int main(int argc, char** argv)
{
    // 初始化ROS节点
    ros::init(argc, argv, "my_tf_broadcaster");
​
    // 输入参数作为海龟的名字
    if (argc != 2)
    {
        ROS_ERROR("need turtle name as argument"); 
        return -1;
    }
​
    turtle_name = argv[1];
​
    // 订阅海龟的位姿话题
    ros::NodeHandle node;
    ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);
​
    // 循环等待回调函数
    ros::spin();
​
    return 0;
};
​

image-20230606161054441

创建tf监听器代码(C++)

cd ~/catkin_ws/src/learning_tf/src
touch turtle_tf_listener.cpp

  • 定义tf监听器(TransformListener);

  • 查找坐标变换(waitForTransform、lookupTransform)

/**
 * 该例程监听tf数据,并计算、发布turtle2的速度指令
 */
​
#include <ros/ros.h>
#include <tf/transform_listener.h>
#include <geometry_msgs/Twist.h>
#include <turtlesim/Spawn.h>
​
int main(int argc, char** argv)
{
    // 初始化ROS节点
    ros::init(argc, argv, "my_tf_listener");
​
    // 创建节点句柄
    ros::NodeHandle node;
​
    // 请求产生turtle2
    ros::service::waitForService("/spawn");
    ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");
    turtlesim::Spawn srv;
    add_turtle.call(srv);
​
    // 创建发布turtle2速度控制指令的发布者
    ros::Publisher turtle_vel = node.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel", 10);
​
    // 创建tf的监听器
    tf::TransformListener listener;
​
    ros::Rate rate(10.0);
    while (node.ok())
    {
        // 获取turtle1与turtle2坐标系之间的tf数据
        tf::StampedTransform transform;
        try
        {
            listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(3.0));
            listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform);
        }
        catch (tf::TransformException &ex) 
        {
            ROS_ERROR("%s",ex.what());
            ros::Duration(1.0).sleep();
            continue;
        }
​
        // 根据turtle1与turtle2坐标系之间的位置关系,发布turtle2的速度控制指令
        geometry_msgs::Twist vel_msg;
        vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(),
                                        transform.getOrigin().x());
        vel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(), 2) +
                                      pow(transform.getOrigin().y(), 2));
        turtle_vel.publish(vel_msg);
​
        rate.sleep();
    }
    return 0;
};

image-20230606162405983

配置tf监听器与广播器代码编译规则

add_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp)
target_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES})
​
add_executable(turtle_tf_listener src/turtle_tf_listener.cpp)
target_link_libraries(turtle_tf_listener ${catkin_LIBRARIES})

image-20230606165523395

编译并运行

编译
cd ~/catkin_ws
catkin_make
source devel/setup.bash(如已配置 **.bashrc**文件,则此步不需要,配置方法在publisher的章节里)

image-20230606173417459

运行
roscore
rosrun turtlesim turtlesim_node

下面我们传入参数 第1个参数:因为在turtle_tf_broadcaster.cpp定义节点时使用了"my_tf_broadcaster"的名字,所以后面使用__name:=传入新的名字代 替"my_tf_broadcaster",以此来避免名字重复(ROS中的节点名字不能重复),如此就能重复跑这个程序了。

第2个参数:turtle名称 turtle1 和 turtle2。

rosrun learning_tf turtle_tf_broadcaster __name:=turtle1_tf_broadcaster /turtle1
rosrun learning_tf turtle_tf_broadcaster __name:=turtle2_tf_broadcaster /turtle2
rosrun learning_tf turtle_tf_listener

上面完成后,会有一只海龟在中心点,另一只海龟在左下方,并且左下方的海龟会跑向中间的那只海龟

image-20230607105934024

打开键盘控制节点,通过键盘控制中心点那只海龟,另只海龟也会自动的跟随前面的那只海龟同步去运动

rosrun turtlesim turtle_teleop_key

image-20230607110507433

2. 实现过程(Python)

创建tf广播器代码(Python)

cd ~/catkin_ws/src/learning_tf
mkdir scripts
cd scripts
touch turtle_tf_broadcaster.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 该例程将请求/show_person服务,服务数据类型learning_service::Person
​
import roslib
roslib.load_manifest('learning_tf')
import rospy
​
import tf
import turtlesim.msg
​
def handle_turtle_pose(msg, turtlename):
    br = tf.TransformBroadcaster()
    br.sendTransform((msg.x, msg.y, 0),
                     tf.transformations.quaternion_from_euler(0, 0, msg.theta),
                     rospy.Time.now(),
                     turtlename,
                     "world")
​
if __name__ == '__main__':
    rospy.init_node('turtle_tf_broadcaster')
    turtlename = rospy.get_param('~turtle')
    rospy.Subscriber('/%s/pose' % turtlename,
                     turtlesim.msg.Pose,
                     handle_turtle_pose,
                     turtlename)
    rospy.spin()
​
​

image-20230607111815061

创建tf监听器代码(Python)

cd ~/catkin_ws/src/learning_tf/scripts
touch turtle_tf_listener.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 该例程将请求/show_person服务,服务数据类型learning_service::Person
​
import roslib
roslib.load_manifest('learning_tf')
import rospy
import math
import tf
import geometry_msgs.msg
import turtlesim.srv
​
if __name__ == '__main__':
    rospy.init_node('turtle_tf_listener')
​
    listener = tf.TransformListener()
​
    rospy.wait_for_service('spawn')
    spawner = rospy.ServiceProxy('spawn', turtlesim.srv.Spawn)
    spawner(4, 2, 0, 'turtle2')
​
    turtle_vel = rospy.Publisher('turtle2/cmd_vel', geometry_msgs.msg.Twist,queue_size=1)
​
    rate = rospy.Rate(10.0)
    while not rospy.is_shutdown():
        try:
            (trans,rot) = listener.lookupTransform('/turtle2', '/turtle1', rospy.Time(0))
        except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):
            continue
​
        angular = 4 * math.atan2(trans[1], trans[0])
        linear = 0.5 * math.sqrt(trans[0] ** 2 + trans[1] ** 2)
        cmd = geometry_msgs.msg.Twist()
        cmd.linear.x = linear
        cmd.angular.z = angular
        turtle_vel.publish(cmd)
​
        rate.sleep()
​
​

image-20230607112048252

注意:给turtle_tf_broadcaster.py 和 turtle_tf_listener.py文件赋予作为程序文件执行的权限,

点击turtle_tf_broadcaster.py文件,右键,属性,权限,勾选(允许作为程序文件执行),

turtle_tf_listener.py文件操作同上,在ROS-Melodic版本中,python文件不需要编译,直接运行即可。

image-20230607112635115

运行效果

roscore
rosrun turtlesim turtlesim_node
​
rosrun learning_tf turtle_tf_broadcaster.py __name:=turtle1_tf_broadcaster _turtle:=turtle1
rosrun learning_tf turtle_tf_broadcaster.py __name:=turtle2_tf_broadcaster _turtle:=turtle2
rosrun learning_tf turtle_tf_listener.py
​
rosrun turtlesim turtle_teleop_key

image-20230607113817681

image-20230607113919029


猜你喜欢

转载自blog.csdn.net/2201_75902080/article/details/134681572
今日推荐