昌江黎族自治县网站建设_网站建设公司_页面加载速度_seo优化
2025/12/21 10:15:33 网站建设 项目流程

坐标系是我们非常熟悉的一个概念,也是机器人学中的重要基础,在一个完整的机器人系统中,会存在很多坐标系,这些坐标系之间的位置关系该如何管理?

ROS给我们提供了一个坐标系的管理神器——TF

一、机器人中的坐标系

你有没有玩过迷宫游戏?地图上会有一个“你在这儿”的小红点。如果没有这个红点,你就不知道自己在哪。

坐标系就像那个“你在这儿”的标记,它告诉你:

  • 原点在哪儿:坐标为(0,0,0);
  • 前方是哪边(x轴),右边是哪边(y轴),上方是哪边(z轴);

有了坐标系,我们就可以给任何物体标记一个具体的位置,比如:

“小球在我前方 2 米,右边 1 米的位置”

这就可以写成坐标:(x=2,y=1, z=0)。

在机器人操作系统中,坐标系是描述空间位置和方向的基础语言,那么在机器人身上到底有多少个坐标系?

1.1 机械臂

在机械臂形态的机器人中,主要涉及四大坐标系;

  • 机器人安装的位置叫做基坐标系Base Frame
  • 机器人安装位置在外部环境下的参考系叫做世界坐标系World Frame
  • 机器人末端夹爪的位置叫做工具坐标系;
  • 外部被操作物体的位置叫做工件坐标系。

在机械臂抓取外部物体的过程中,这些坐标系之间的关系也在跟随变化,如图所示;

1.1.1 世界坐标系( World Frame)

World Frame是世界坐标系,这是我们人类设计的外部参考系。通常选择:

  • 机器人工作台的某个固定角落作为原点(0,0,0);
  • 沿着工作台长边方向为X轴正方向;
  • 沿着工作台宽边方向为Y轴正方向;
  • 垂直于工作台向上为Z轴正方向。

这是我们设计和规划机器人任务时使用的全局坐标系,所有其他坐标系最终都要映射到这个坐标系中来统一描述。

1.1.2 基坐标系(Base Frame)

Base Frame是机械臂的基准坐标系。可以这样理解:

  • 机械臂安装到工作台上,我们就把机械臂底座的中心点定义为Base Frame的原点;
  • 这个坐标系是相对于World Frame固定的,一旦安装完成就不再变化;
  • 所有机械臂关节的运动都是相对于这个Base Frame来描述的。

例如,一个六轴机械臂的第一个关节(底座旋转关节)的旋转就是绕着Base FrameZ轴进行的。

1.1.3 工具坐标系(Tool Frame)

Tool Frame是机械臂末端执行器的坐标系。对于不同的任务,工具坐标系有不同的定义:

  • 对于夹爪:通常把原点定义在两个夹爪的中间点;
  • 对于焊枪:把原点定义在焊枪的尖端;
  • 对于吸盘:把原点定义在吸盘的吸附面中心。

这个坐标系是随着机械臂末端一起运动的,它会根据末端的姿态和位置不断变化。工具坐标系的主要作用是:

  • 精确控制工具的操作位置;
  • 进行工具中心点(TCP)的校准;
  • 实现工具与工件的精确对齐。
1.1.4 工件坐标系( Work Frame)

Work Frame是被操作物体所在的坐标系。例如:

  • 要抓取的零件在传送带上的位置;
  • 要焊接的工件在夹具中的位置;
  • 要装配的部件在工作台上的位置。

这个坐标系的特点是:

  • 它可能相对于World Frame是固定的(如固定在夹具中的工件);
  • 也可能相对于World Frame是运动的(如传送带上的零件);
  • 还可能相对于World Frame是未知的(需要视觉系统来识别)。

在实际应用中,我们通常先通过视觉系统或其他传感器识别出Work Frame相对于World Frame的位姿,然后将Tool Frame变换到这个Work Frame中,从而精确地操作工件。

1.2 移动机器人

在移动机器人系统中,坐标系一样至关重要,比如:

  • 一个移动机器人的中心点是基坐标系Base Link
  • 雷达所在的位置叫做雷达坐标系Laser Link
  • 机器人要移动,里程计会累积位置,这个位置的参考系叫做里程计坐标系odom
  • 里程计又会有累积误差和漂移,绝对位置的参考系叫做地图坐标系map

如图所示:

1.2.1 地图坐标系(map)

map是地图坐标系,假设我们给房间画一张地图,左下角是 (0,0),正右是x轴,正上是y轴。这是我们人类眼中的世界坐标系。

1.2.2 里程计坐标系(odom)

odom是机器人自己的一套参考系统。可以这样理解:

  • 机器人一启动,就把它站着的那个点当成了(0,0),然后它往前走1米,odom坐标就变成了 (1,0)。

但是,这个位置可能不是房间地图上的 (1,0),因为机器人只靠轮子估算自己走了多远,而轮子会打滑、误差累积。

所以,odom是一种估算出来的位置,但会随着时间逐渐偏离真实位置。

你可以想象odom是机器人的小脑袋,它说:“我大概走了 3 米向前”,但不一定百分百准确。

base_link是移动机器人自己身体的坐标系,一般我们把这个坐标系的原点放在机器人身体中间,比如底盘中心,或两个轮子之间。

这个点不是真的设在哪里,而是我们人为地定义一个“我是谁”的起点。

从此以后:

  • 摄像头在base_link前面0.3米,高度0.5米;
  • 激光雷达在base_link前面0.2米;
  • 机械臂根部就在base_link的正中间;

所有设备的位置都可以用base_link作为基准来描述。

1.3 坐标系变换

既然我们有那么做坐标系,那么一个点的位置,到底是谁说了算?比如:

  • 摄像头说这个红球在x=1米、y=0米的位置,但机器人能直接走过去吗?

不能。因为这个坐标是摄像头视角的。机器人自己并不认识这个坐标。那怎么办?

答案就是:我们要把这个坐标,从摄像头坐标系转换成机器人自己坐标系下的坐标,这样它才能理解红球在哪儿。

这种从一个坐标系转到另一个坐标系的过程,就叫做坐标变换。

关于坐标变换关系的基本理论,在每一本机器人学的教材中都会有讲解,可以分解为平移和旋转两个部分,通过一个四乘四的矩阵进行描述,在空间中画出坐标系,那两者之间的变换关系,其实就是向量的数学描述。

ROSTF功能的底层原理,就是对这些数学变换进行了封装,详细的理论知识可以参考机器人学的教材,也可以看我之前写的文章《第六节、双目视觉之相机标定》。

在本篇博客我们主要讲解TF坐标管理系统的使用方法。

1.3.1 什么是TF

TFROS中的一个系统,全称叫transform(变换),它有两个重要功能:

  • 记录各个坐标系之间的关系;
  • 帮你把一个坐标从A转换到B

比如:

map → odom → base_link → camera_link

每个箭头之间代表坐标变换。比如:

  • mapodom:表示机器人在地图中的真实位置;
  • odombase_link:表示机器人认为自己在哪
  • base_linkcamera_link:表示摄像头安装在哪;

这个结构像什么?像一棵树,每个坐标系都是一个节点,每条边是一种连接关系,所以我们叫它TF树。

1.3.2 案例

如果不用TF,意味着要自己手动计算所有坐标变换!

来看个例子:

  • 摄像头看到红球在它正前方1米处 (1.0, 0.0);
  • 摄像头安装在机器人前方0.3米、高0.5
  • 机器人自己认为它在odom中是 (2.0, 1.0),朝向是东;

如果想知道:这个红球在map中的真实位置是多少?如果没有TF,我们需要:

  • 自己写数学公式:先从camera_link转成base_link,再从base_link转成odom,再从odom转到map
  • 考虑机器人是否旋转过;
  • 考虑每个坐标的时间戳是否一致;
  • 一堆复杂的矩阵变换、旋转计算……

有了TF呢,我们就可以使用TF帮我把camera_link中的这个点转换到map中,它会自动完成所有事情,并且考虑时间、方向、位置等。

二、TF案例

使用TF我们首先需要安装一些依赖库:

pi@NanoPC-T6:~/dev_ws$ sudo apt update
pi@NanoPC-T6:~/dev_ws$ sudo apt install ros-humble-tf2-tools
pi@NanoPC-T6:~/dev_ws$ ls $(ros2 pkg prefix tf2_tools)/lib/tf2_tools/
view_frames
pi@NanoPC-T6:~/dev_ws$ ls $(ros2 pkg prefix tf2_tools)/lib/tf2_ros/
buffer_server  static_transform_publisher  tf2_echo  tf2_monitor
pi@NanoPC-T6:~/dev_ws$ sudo pip3 install transforms3d

其中:

  • ros-humble-tf2-tools:安装TF2相关的工具集,提供了一系列调试和可视化TF变换的工具;包含的主要工具有:
    • view_frames:生成TF帧关系的PDF图;
    • tf2_monitor:监控TF帧之间的变换关系;
    • tf2_echo:查看两个特定帧之间的变换数据;
  • transforms3d:一个Python库,用于处理3D空间中的几何变换,提供的主要功能:
    • 旋转矩阵、欧拉角、四元数、轴角之间的转换;
    • 3D变换矩阵的操作;
    • 坐标系变换计算。

接着创建my_learning_tfPython版本的功能包;

pi@NanoPC-T6:~/dev_ws$ cd src
pi@NanoPC-T6:~/dev_ws/src$ ros2 pkg create --build-type ament_python my_learning_tf

2.1 TF命令行操作

ROS中的TF该如何使用呢?我们先通过两只小海龟的示例,了解下基于坐标系的一种机器人跟随算法。

我们首先需要安装ROS 2 Humble版本的Turtle TF2示例程序;

pi@NanoPC-T6:~/dev_ws$ sudo apt install ros-humble-turtle-tf2-py

这个包包含一个经典的"小海龟"示例,会创建两个海龟:

  • 一只可以通过键盘控制移动的海龟;

  • 另一只会自动跟随第一只的海龟;

  • 主要用于学习TF2(变换库)的基本概念,包括:

    • 坐标变换;
    • 帧(frames)的发布和订阅;
    • 位姿变换计算。
2.1.1 运行launch

启动turtle-tf2示例:

[INFO] [launch]: All log files can be found below /home/pi/.ros/log/2025-12-21-00-35-22-173055-NanoPC-T6-7502
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [turtlesim_node-1]: process started with pid [7503]
[INFO] [turtle_tf2_broadcaster-2]: process started with pid [7505]
[INFO] [turtle_tf2_broadcaster-3]: process started with pid [7507]
[INFO] [turtle_tf2_listener-4]: process started with pid [7509]
[turtlesim_node-1] Warning: Ignoring XDG_SESSION_TYPE=wayland on Gnome. Use QT_QPA_PLATFORM=wayland to run on Wayland anyway.
[turtlesim_node-1] [INFO] [1766277322.890167955] [sim]: Starting turtlesim with node name /sim
[turtlesim_node-1] [INFO] [1766277322.899916544] [sim]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]
[turtlesim_node-1] [INFO] [1766277323.918337095] [sim]: Spawning turtle [turtle2] at x=[4.000000], y=[2.000000], theta=[0.000000]
[turtle_tf2_listener-4] [INFO] [1766277324.967066696] [listener]: Successfully spawned turtle2xxxxxxxxxx [INFO] [launch]: All log files can be found below /home/pi/.ros/log/2025-12-21-00-35-22-173055-NanoPC-T6-7502[INFO] [launch]: Default logging verbosity is set to INFO[INFO] [turtlesim_node-1]: process started with pid [7503][INFO] [turtle_tf2_broadcaster-2]: process started with pid [7505][INFO] [turtle_tf2_broadcaster-3]: process started with pid [7507][INFO] [turtle_tf2_listener-4]: process started with pid [7509][turtlesim_node-1] Warning: Ignoring XDG_SESSION_TYPE=wayland on Gnome. Use QT_QPA_PLATFORM=wayland to run on Wayland anyway.[turtlesim_node-1] [INFO] [1766277322.890167955] [sim]: Starting turtlesim with node name /sim[turtlesim_node-1] [INFO] [1766277322.899916544] [sim]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000][turtlesim_node-1] [INFO] [1766277323.918337095] [sim]: Spawning turtle [turtle2] at x=[4.000000], y=[2.000000], theta=[0.000000][turtle_tf2_listener-4] [INFO] [1766277324.967066696] [listener]: Successfully spawned turtle2pi@NanoPC-T6:~/dev_ws$ ros2 launch turtle_tf2_py turtle_tf2_demo.launch.py

从输出消息中可知:运行该演示会启动4个节点:

  • 1turtlesim_node节点:/sim
  • 2个turtle_tf2_broadcaster节点:/broadcaster1/broadcaster2,这两个广播者节点会发布这两只小海龟的坐标系;
  • 1turtle_tf2_listener/listener,这个侦听者节点会计算两只小海龟坐标系之间的差异,并让一只小海龟(turtle2)跟随另一只小海龟(turtle1)移动;

然后会利用turtlesim_node节点生成2只小海龟(turtle1turtle2)。然后turtle2会沿着一条弧线移动靠近turtle1

然后我们可以控制其中的一只小海龟,另外一只小海龟会自动跟随运动;

pi@NanoPC-T6:~/dev_ws$  ros2 run turtlesim turtle_teleop_key

然后就可以利用键盘来控制turtle1的旋转和移动,而turtle2也会跟随turtle1进行旋转和移动。

2.1.2 查看TF

软件包tf2_tools中有一个view_frames工具(可执行文件),可以用来创建通过ROS2系统的tf2正在广播的坐标变换树的图形(diagram)。在一个新的终端中运行以下命令:

pi@NanoPC-T6:~/dev_ws$  ros2 run tf2_tools view_frames 
[INFO] [1766277680.566351638] [view_frames]: Listening to tf data for 5.0 seconds...
[INFO] [1766277685.582050455] [view_frames]: Generating graph in frames.pdf file...
[INFO] [1766277685.593336251] [view_frames]: Result:tf2_msgs.srv.FrameGraph_Response(frame_yaml="turtle2: \n  parent: 'world'\n  broadcaster: 'default_authority'\n  rate: 62.697\n  most_recent_transform: 1766277685.575447\n  oldest_transform: 1766277680.535321\n  buffer_length: 5.040\nturtle1: \n  parent: 'world'\n  broadcaster: 'default_authority'\n  rate: 62.701\n  most_recent_transform: 1766277685.575661\n  oldest_transform: 1766277680.535844\n  buffer_length: 5.040\n")pi@NanoPC-T6:~/dev_ws$ ls
build                           install  turtlesim.yaml
frames_2025-12-21_00.41.25.gv   log
frames_2025-12-21_00.41.25.pdf  src

view_frames会订阅5s内的数据,tf2侦听者节点正在侦听通过ROS2系统正在广播的坐标系,并会绘制将这些坐标系相互连接起来的坐标变换树。

我们打开frames_2025-12-21_00.41.25.pdf文件,就可以看到如下图所示的坐标变换树图形:

从图中可以看到当前系统一共有三个坐标系:

  • world:世界坐标系,它的原点(0,0)在小海龟运行的地图的左下角,向右为x轴,向前为y轴;
  • turtle1:小海龟1自身坐标系;
  • turtle2:小海龟2自身坐标系;

其中world坐标系是turtle1坐标系和turtle2坐标系的父级坐标系。view_frames还报告了一些有关何时接收到最早和最近的坐标系变换以及为调试目的将tf2坐标系发布到tf2的频率等信息。

2.1.3 查询坐标变换信息

只看到坐标系的结构还不行,如果我们想要知道某两个坐标系之间的具体关系,可以通过tf2_echo这个工具查看。运行该工具的命令语法如下:

ros2 run tf2_tools tf2_ros <source_frame> <target_frame>

例如,来看一下turtle2坐标系相对于turtle1坐标系的坐标变换,请运行以下命令:

i@NanoPC-T6:~/dev_ws$  ros2 run tf2_ros tf2_echo turtle2 turtle1[INFO] [1766280764.210970045] [tf2_echo]: Waiting for transform turtle2 ->  turtle1: Invalid frame ID "turtle2" passed to canTransform argument target_frame - frame does not exist
At time 1766280765.158573876
- Translation: [0.000, 0.000, 0.000]
- Rotation: in Quaternion (xyzw) [0.000, 0.000, 0.291, 0.957]
- Rotation: in RPY (radian) [0.000, -0.000, 0.590]
- Rotation: in RPY (degree) [0.000, -0.000, 33.831]
- Matrix:0.831 -0.557  0.000  0.0000.557  0.831  0.000  0.0000.000  0.000  1.000  0.0000.000  0.000  0.000  1.000
At time 1766280766.150517038
- Translation: [0.000, 0.000, 0.000]
- Rotation: in Quaternion (xyzw) [0.000, 0.000, 0.291, 0.957]
- Rotation: in RPY (radian) [0.000, -0.000, 0.590]
- Rotation: in RPY (degree) [0.000, -0.000, 33.831]
- Matrix:0.831 -0.557  0.000  0.0000.557  0.831  0.000  0.0000.000  0.000  1.000  0.0000.000  0.000  0.000  1.000
At time 1766280767.158621948
......

可以看到当前两个坐标系的变换由两部分组成,平移变换和旋转变换,由于此时两个小海龟已经重叠了,所以平移变换等于0,而旋转变换有个比较小弧度的旋转(围绕z轴的)。

在旋转变换里面三种描述方式,分别是:

  • 四元数;
  • 欧拉角;
  • 轴角之间的转换;

如果我们同时在运行turtle_teleop_key节点的终端中旋转和移动turtle1,这样就会在这个终端中输出如下所示的消息:

At time 1766280771.159109321
- Translation: [1.648, -0.010, 0.000]
- Rotation: in Quaternion (xyzw) [0.000, 0.000, 0.210, 0.978]
- Rotation: in RPY (radian) [0.000, -0.000, 0.423]
- Rotation: in RPY (degree) [0.000, -0.000, 24.227]
- Matrix:0.912 -0.410  0.000  1.6480.410  0.912  0.000 -0.0100.000  0.000  1.000  0.0000.000  0.000  0.000  1.000
At time 1766280772.151677830
- Translation: [0.830, 0.003, 0.000]
- Rotation: in Quaternion (xyzw) [0.000, 0.000, 0.850, 0.526]
- Rotation: in RPY (radian) [0.000, -0.000, 2.033]
- Rotation: in RPY (degree) [0.000, -0.000, 116.475]
- Matrix:-0.446 -0.895  0.000  0.8300.895 -0.446  0.000  0.0030.000  0.000  1.000  0.0000.000  0.000  0.000  1.000

从上述输出消息中可以看出,当通过键盘驱动小海龟turtle1四处移动时,可以看到随着两只小海龟相对移动而发生的坐标变换的变化情况。

2.1.4 坐标系可视化

rviz2是一种可视化工具,可用于检查tf2坐标系,现在可以使用rviz2来看看这两只小海龟的坐标系;

i@NanoPC-T6:~/dev_ws$ ros2 run rviz2 rviz2 -d $(ros2 pkg prefix --share turtle_tf2_py)/rviz/turtle_rviz.rviz

再让小海龟动起来,Rviz中的坐标轴就会开始运动,这样是不是更加直观了呢!

小海龟跟随的案例有点意思,这背后的原理是怎样的呢?大家不要着急,我们先来了解下TF的使用方法,便于大家慢慢理解。

2.2 静态TF广播

我们说TF的主要作用是对坐标系进行管理,那就管理一个试试呗?

坐标变换中最为简单的应该是相对位置不发生变化的情况,比如你家的房子在哪个位置,只要房子不拆,这个坐标应该就不会变化。

在机器人系统中也很常见,比如激光雷达和机器人底盘之间的位置关系,安装好之后基本不会变化。

TF中,这种情况也称之为静态TF变换,我们来看看在程序中该如何实现?

2.2.1 代码实现

打开my_learning_tf功能包,在my_learning_tf目录下创建static_tf_broadcaster.py

"""
ROS2 TF示例-广播静态的坐标变换@author: zy
@since : 2025/12/21
"""import rclpy                                                                 # ROS2 Python接口库
from rclpy.node import Node                                                  # ROS2 节点类
from geometry_msgs.msg import TransformStamped                               # 坐标变换消息
import tf_transformations                                                    # TF坐标变换库
from tf2_ros.static_transform_broadcaster import StaticTransformBroadcaster  # TF静态坐标系广播器类class StaticTFBroadcaster(Node):def __init__(self, name):super().__init__(name)                                                  # ROS2节点父类初始化self.tf_broadcaster = StaticTransformBroadcaster(self)                  # 创建一个TF广播器对象static_transformStamped = TransformStamped()                            # 创建一个坐标变换的消息对象static_transformStamped.header.stamp = self.get_clock().now().to_msg()  # 设置坐标变换消息的时间戳static_transformStamped.header.frame_id = 'world'                       # 设置一个坐标变换的源坐标系static_transformStamped.child_frame_id  = 'house'                       # 设置一个坐标变换的目标坐标系static_transformStamped.transform.translation.x = 10.0                  # 设置坐标变换中的X、Y、Z向的平移static_transformStamped.transform.translation.y = 5.0                    static_transformStamped.transform.translation.z = 0.0quat = tf_transformations.quaternion_from_euler(0.0, 0.0, 0.0)          # 将欧拉角转换为四元数(roll, pitch, yaw)static_transformStamped.transform.rotation.x = quat[0]                  # 设置坐标变换中的X、Y、Z向的旋转(四元数)static_transformStamped.transform.rotation.y = quat[1]static_transformStamped.transform.rotation.z = quat[2]static_transformStamped.transform.rotation.w = quat[3]self.tf_broadcaster.sendTransform(static_transformStamped)              # 广播静态坐标变换,广播后两个坐标系的位置关系保持不变def main(args=None):rclpy.init(args=args)                                # ROS2 Python接口初始化node = StaticTFBroadcaster("static_tf_broadcaster")  # 创建ROS2节点对象并进行初始化rclpy.spin(node)                                     # 循环等待ROS2退出node.destroy_node()                                  # 销毁节点对象rclpy.shutdown()

这段代码创建了一个静态坐标系变换,将world坐标系和house坐标系之间的关系固定下来并广播到ROS2系统中。

完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置:

    entry_points={'console_scripts': ['static_tf_broadcaster = my_learning_tf.static_tf_broadcaster:main',],},
2.2.2 编译运行

编译程序:

pi@NanoPC-T6:~/dev_ws$ colcon build --paths src/my_learning_tf
pi@NanoPC-T6:~/dev_ws$ source install/setup.sh

启动终端,运行如下命令:

pi@NanoPC-T6:~/dev_ws$ ros2 run my_learning_tf static_tf_broadcaster

接着我们使用view_frames 查看TF树;

pi@NanoPC-T6:~/dev_ws$ ros2 run tf2_tools view_frames 

可以看到当前系统中存在两个坐标系,一个是world,一个是house,两者之间的相对位置不会发生改变,通过一个静态的TF对象进行维护。

2.2.3 流程分析

总结一下,想要实现一个静态TF广播,代码的实现流程是这样做;

开始↓
rclpy.init()           # 初始化ROS2↓
创建StaticTFBroadcaster节点↓
节点初始化:- 创建StaticTransformBroadcaster- 设置world到house的坐标变换- 位置: (10, 5, 0)- 姿态: 无旋转- 广播该静态变换↓
rclpy.spin()           # 进入事件循环↓
节点持续运行,静态变换保持在系统中↓
按Ctrl+C触发退出↓
node.destroy_node()    # 清理节点↓
rclpy.shutdown()       # 关闭ROS2

参考文章

[1] 古月居ROS2入门教程学习笔记

[2] 什么是ROS中的tf?让我们从坐标系说起

[3] 第六节、双目视觉之相机标定

[4] 机器人操作系统ROS2

[5] tf2系列教程(二):在ROS 2中进行tf2简介演示

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询