ROS2与tf2入门教程-添加框架 (Python)
说明:
- 介绍如何用Python添加框架
概述:
在之前的教程中,我们通过编写一个 tf2 广播器和一个 tf2 监听器重新创建了海龟演示。
本教程将教您如何向转换树添加额外的固定和动态帧。
实际上,在 tf2 中添加框架与创建 tf2 广播器非常相似,但是这个示例将向您展示 tf2 的一些附加功能。
对于许多与转换相关的任务,在本地框架内思考会更容易。
例如,最容易推断激光扫描仪中心框架中的激光扫描测量值。
tf2 允许您为系统中的每个传感器、链接或关节定义一个本地框架。
当从一帧转换到另一帧时,tf2 将处理所有引入的隐藏中间帧转换。
tf2树:
- tf2 建立了框架的树状结构,因此不允许框架结构中出现闭环。
- 这意味着一个框架只有一个父级,但它可以有多个子级。
- 目前,我们的 tf2 树包含三个框架:world、turtle1和turtle2。
- 两个海龟框架turtle1和turtle2是框架world的子框架。
- 如果我们想在tf2中添加一个新的frame,三个已经存在的frame中的一个需要是父frame,新的frame会成为它的子frame
固定帧广播器:
- carrot1坐标系与turtle1坐标系在y轴间隔2米
- 下载固定帧广播器代码
cd ~/tf2_ws/src/learning_tf2_py/learning_tf2_py
wget https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_py/turtle_tf2_py/fixed_frame_tf2_broadcaster.py
- 打开fixed_frame_tf2_broadcaster.py文件
from geometry_msgs.msg import TransformStamped
import rclpy
from rclpy.node import Node
from tf2_ros import TransformBroadcaster
class FixedFrameBroadcaster(Node):
def __init__(self):
super().__init__('fixed_frame_tf2_broadcaster')
self.br = TransformBroadcaster(self)
self.timer = self.create_timer(0.1, self.broadcast_timer_callback)
def broadcast_timer_callback(self):
t = TransformStamped()
t.header.stamp = self.get_clock().now().to_msg()
t.header.frame_id = 'turtle1'
t.child_frame_id = 'carrot1'
t.transform.translation.x = 0.0
t.transform.translation.y = 2.0
t.transform.translation.z = 0.0
t.transform.rotation.x = 0.0
t.transform.rotation.y = 0.0
t.transform.rotation.z = 0.0
t.transform.rotation.w = 1.0
self.br.sendTransform(t)
def main():
rclpy.init()
node = FixedFrameBroadcaster()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
rclpy.shutdown()
- 赋执行权限
sudo chmod +x fixed_frame_tf2_broadcaster.py
- 添加入口,修改setup.py在'console_scripts': [里添加
'fixed_frame_tf2_broadcaster = learning_tf2_py.fixed_frame_tf2_broadcaster:main',
- 新建launch文件
cd ~/tf2_ws/src/learning_tf2_py/launch
vim turtle_tf2_fixed_frame_demo.launch.py
- 内容如下:
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
def generate_launch_description():
demo_nodes = IncludeLaunchDescription(
PythonLaunchDescriptionSource([os.path.join(
get_package_share_directory('learning_tf2_py'), 'launch'),
'/turtle_tf2_demo.launch.py']),
)
return LaunchDescription([
demo_nodes,
Node(
package='learning_tf2_py',
executable='fixed_frame_tf2_broadcaster',
name='fixed_broadcaster',
),
])
- 构建
colcon build --symlink-install --packages-select learning_tf2_py
- 加载工作空间
. ~/tf2_ws/install/local_setup.bash
测试:
- 启动turtle_tf2_fixed_frame_demo.launch.py文件
. ~/tf2_ws/install/local_setup.bash
ros2 launch learning_tf2_py turtle_tf2_fixed_frame_demo.launch.py
生成TF树
ros2 run tf2_tools view_frames
查看TF树
evince frames.pdf
- 新carrot1框架出现在转换树中,如图
通过参数指定海龟2跟随不同的坐标系
- 可以这样运行
ros2 launch learning_tf2_py turtle_tf2_fixed_frame_demo.launch.py target_frame:=carrot1
- 修改文件turtle_tf2_fixed_frame_demo.launch.py文件,并通过参数添加参数
- 修改后内容如下:
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
from launch.substitutions import LaunchConfiguration
def generate_launch_description():
target_frame = LaunchConfiguration('target_frame')
declare_target_frame_cmd = DeclareLaunchArgument(
'target_frame',
default_value='turtle1',
description='target_frame')
demo_nodes = IncludeLaunchDescription(
PythonLaunchDescriptionSource([os.path.join(
get_package_share_directory('learning_tf2_py'), 'launch'),
'/turtle_tf2_demo.launch.py']),
launch_arguments={'target_frame': target_frame}.items()
)
return LaunchDescription([
declare_target_frame_cmd,
demo_nodes,
Node(
package='learning_tf2_py',
executable='fixed_frame_tf2_broadcaster',
name='fixed_broadcaster',
),
])
通过DeclareLaunchArgument定义参数target_frame
通过LaunchConfiguration获取target_frame的值,定义为变量target_frame
最后launch_arguments里面再赋值给turtle_tf2_demo.launch.py的target_frame参数
重新构建
colcon build --symlink-install --packages-select learning_tf2_py
- 重新启动turtle_tf2_fixed_frame_demo.launch.py
ros2 launch learning_tf2_py turtle_tf2_fixed_frame_demo.launch.py target_frame:=carrot1
- 键盘控制
ros2 run turtlesim turtle_teleop_key
- 指定跟随carrot1坐标系,打开后, 第二只海龟, 会停留在第一只海龟的一侧2米附近.
- 指定跟随turtle1坐标系,打开后, 第二只海龟, 会叠在第一只海龟上
动态帧广播器代码
- carrot1坐标系与turtle1坐标系在在X和Y轴上随机变化
- 下载动态帧广播器代码
cd ~/tf2_ws/src/learning_tf2_py/learning_tf2_py
wget https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_py/turtle_tf2_py/dynamic_frame_tf2_broadcaster.py
- 打开文件dynamic_frame_tf2_broadcaster.py
import math
from geometry_msgs.msg import TransformStamped
import rclpy
from rclpy.node import Node
from tf2_ros import TransformBroadcaster
class DynamicFrameBroadcaster(Node):
def __init__(self):
super().__init__('dynamic_frame_tf2_broadcaster')
self.br = TransformBroadcaster(self)
self.timer = self.create_timer(0.1, self.broadcast_timer_callback)
def broadcast_timer_callback(self):
seconds, _ = self.get_clock().now().seconds_nanoseconds()
x = seconds * math.pi
t = TransformStamped()
t.header.stamp = self.get_clock().now().to_msg()
t.header.frame_id = 'turtle1'
t.child_frame_id = 'carrot1'
t.transform.translation.x = 10 * math.sin(x)
t.transform.translation.y = 10 * math.cos(x)
t.transform.translation.z = 0.0
t.transform.rotation.x = 0.0
t.transform.rotation.y = 0.0
t.transform.rotation.z = 0.0
t.transform.rotation.w = 1.0
self.br.sendTransform(t)
def main():
rclpy.init()
node = DynamicFrameBroadcaster()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
rclpy.shutdown()
- 赋执行权限
sudo chmod +x dynamic_frame_tf2_broadcaster.py
- 添加入口,修改setup.py在'console_scripts': [里添加
'dynamic_frame_tf2_broadcaster = learning_tf2_py.dynamic_frame_tf2_broadcaster:main',
- 创建一个新的启动文件
cd ~/tf2_ws/src/learning_tf2_py/launch
vim turtle_tf2_dynamic_frame_demo.launch.py
- 内容如下
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
from launch.substitutions import LaunchConfiguration
def generate_launch_description():
target_frame = LaunchConfiguration('target_frame')
declare_target_frame_cmd = DeclareLaunchArgument(
'target_frame',
default_value='carrot1',
description='target_frame')
demo_nodes = IncludeLaunchDescription(
PythonLaunchDescriptionSource([os.path.join(
get_package_share_directory('learning_tf2_py'), 'launch'),
'/turtle_tf2_demo.launch.py']),
launch_arguments={'target_frame': target_frame}.items()
)
return LaunchDescription([
declare_target_frame_cmd,
demo_nodes,
Node(
package='learning_tf2_py',
executable='dynamic_frame_tf2_broadcaster',
name='dynamic_broadcaster',
),
])
- 重新构建包
cd ~/tf2_ws/
colcon build --symlink-install --packages-select learning_tf2_py
- 并启动turtle_tf2_dynamic_frame_demo.launch.py启动文件
ros2 launch learning_tf2_py turtle_tf2_dynamic_frame_demo.launch.py
- 默认第二只海龟跟随carrot1坐标系随机位置,您将看到第二只海龟跟随不断变化的位置。
参考:
获取最新文章: 扫一扫右上角的二维码加入“创客智造”公众号