ROS2进阶
TF(Transform)是ROS2中用于处理机器人各部件之间坐标系变换的核心功能,是机器人开发中定位、导航、感知的基础。机器人有很多部件,每个部件都有自己的局部坐标系。TF 的作用就是实时记录并计算这些坐标系之间的位置(平移)和姿态(旋转)关系,让你能轻松把一个坐标系下的点,转换成另一个坐标系下的点。坐标系(Frame):每个部件的坐标参考系,比如base_link(底盘坐标系)、laser_l
ROS2常用工具
坐标变换工具
TF(Transform)是ROS2中用于处理机器人各部件之间坐标系变换的核心功能,是机器人开发中定位、导航、感知的基础。
机器人有很多部件,每个部件都有自己的局部坐标系。TF 的作用就是实时记录并计算这些坐标系之间的位置(平移)和姿态(旋转)关系,让你能轻松把一个坐标系下的点,转换成另一个坐标系下的点。
核心术语
坐标系(Frame):每个部件的坐标参考系,比如base_link(底盘坐标系)、laser_link(雷达坐标系)、map(地图坐标系)。
变换(Transform):两个坐标系之间的关系,包含平移(x/y/z)和旋转(四元数 / 欧拉角)。
TF树:所有坐标系通过父子关系组成的树形结构。
发布机械臂底座到摄像头的静态TF
先安装相关库
sudo apt install ros-$ROS_DISTRO-tf-transformations
在新建一个工作空间,并在下面建一个src文件夹
![]()
在src目录下新建一个python_tf功能包
ros2 pkg create --build-type ament_python --dependencies rclpy geometry_msgs tf_ros tf_transformations --license Apache-2.0 python_tf
新建一个py文件编写代码

import rclpy
from rclpy.node import Node
from tf2_ros import StaticTransformBroadcaster # 静态坐标发布器
from geometry_msgs.msg import TransformStamped # 接口消息
from tf_transformations import quaternion_from_euler # 欧拉角转四元素函数
import math
class StaticTFBroadcaster(Node):
def __init__(self):
super().__init__('static_tf_broadcaster')
self.static_broadcaster = StaticTransformBroadcaster(self) # 创建静态坐标发布器
self.publish_static_tf()
# 发布静态TF
def publish_static_tf(self):
transform = TransformStamped() # 消息接口
transform.header.frame_id = 'base_link' # 父坐标系
transform.child_frame_id = 'camera_link' # 子坐标系
transform.header.stamp = self.get_clock().now().to_msg()
transform.transform.translation.x=0.5
transform.transform.translation.y=0.3
transform.transform.translation.x=0.6
q = quaternion_from_euler(math.radians(180),0,0) # 欧拉角转四元素 q=x,y,z,w
transform.transform.rotation.x = q[0]
transform.transform.rotation.y = q[1]
transform.transform.rotation.z = q[2]
transform.transform.rotation.w = q[3]
# 发布静态坐标关系
self.static_broadcaster.sendTransform(transform)
self.get_logger().info(f'发布静态TF:{transform}')
def main():
rclpy.init()
node = StaticTFBroadcaster()
rclpy.spin(node)
rclpy.shutdown()
在setup.py中配置可执行文件

构建、配置,然后运行
ros2 run python_tf static_tf_broadcaster

我们可以再开一个终端,查看一下话题
ros2 topic echo /tf_static
虽然静态tf只发布一次,但该话题会持续保留一条最新的数据

还可以运行查看两个坐标系的位置关系
ros2 run tf2_ros tf2_echo base_link camera_link

发布摄像头到瓶子的动态TF
新建一个dynamic_tf_broadcaster.py文件
import rclpy
from rclpy.node import Node
from tf2_ros import TransformBroadcaster
from geometry_msgs.msg import TransformStamped
from tf_transformations import quaternion_from_euler
import math
class TFBroadcaster(Node):
def __init__(self):
super().__init__('tf_broadcaster')
self.broadcaster = TransformBroadcaster(self)
self.timer=self.create_timer(0.01,self.publish_tf)
def publish_tf(self):
transform = TransformStamped()
transform.header.frame_id = 'camera_link' # 父坐标系
transform.child_frame_id = 'bottle_link' # 子坐标系
transform.header.stamp = self.get_clock().now().to_msg()
transform.transform.translation.x=0.2
transform.transform.translation.y=0.3
transform.transform.translation.x=0.5
q = quaternion_from_euler(0,0,0)
transform.transform.rotation.x = q[0]
transform.transform.rotation.y = q[1]
transform.transform.rotation.z = q[2]
transform.transform.rotation.w = q[3]
self.broadcaster.sendTransform(transform)
self.get_logger().info(f'发布TF:{transform}')
def main():
rclpy.init()
node = TFBroadcaster()
rclpy.spin(node)
rclpy.shutdown()
再配置setup.py

构建、配置环境,再运行
ros2 run python_tf dynamic_tf_broadcaster
可以再开一个终端查看
ros2 run tf2_ros2 tf2_echo camera_link bottle_link

查询TF关系
订阅话题,收集所有坐标系关系
新建一个tf_listener.py
import rclpy
from rclpy.node import Node
from tf2_ros import TransformListener,Buffer # 坐标监听器
from tf_transformations import euler_from_quaternion # 四元素转欧拉角
import math
class TFBroadcaster(Node):
def __init__(self):
super().__init__('tf_broadcaster')
self.buffer = Buffer()
self.listener = TransformListener(self.buffer,self)
self.timer=self.create_timer(1.0,self.get_transform)
def get_transform(self):
try:
result = self.buffer.lookup_transform('base_link','bottle_link',rclpy.time.Time(seconds=0.0),rclpy.time.Time(seconds=1.0))
transform = result.transform
self.get_logger().info(f'平移:{transform.translation}')
self.get_logger().info(f'旋转:{transform.rotation}')
rotation_eular = euler_from_quaternion(transform.rotation.x,transform.rotation.y,transform.rotation.z,transform.rotation.w)
self.get_logger().info(f'旋转RPY:{rotation_eular}')
except Exception as e:
self.get_logger().info(f'报错:{str(e)}')
def main():
rclpy.init()
node = TFBroadcaster()
rclpy.spin(node)
rclpy.shutdown()
配置可执行文件

分别运行第一个(静态发布底座和摄像头)、第二个(动态发布摄像头到瓶子)、和该可执行文件(可获取底座和瓶子的坐标关系)

常用可视化工具
rqt图形化工具
rqt是ROS提供的一套图形化工具框架,本质是基于Qt开发的ROS可视化工具集合,把ROS中分散的功能(如话题查看、参数配置、日志监控等)整合到统一的图形界面中,大幅降低开发和调试成本,由多个 “插件” 组成。
安装TF树插件
打开终端,输入如下命令
sudo apt install ros-humble-rqt-tf-tree -y
更新配置文件
rm -rf ~/.config/ros/org/rqt_gui.ini
打开rqt
rqt
然后就能看到TF Tree插件

右侧就能显示当前节点的TF结构了(当前无运行,你运行之前的可执行文件就有显示了)

rviz可视化工具
rvis可以实现坐标变换的可视化
打开rviz
rviz2
点击左下角的“Add”添加可视化视图

点击“TF”,点击“OK”,就添加了坐标变换视图

ros2 bag数据记录工具
先打开一个海龟模拟器
ros2 run turtlesim turtlesim_node
再打开一个按键控制
ros2 run turtlesim turtle_teleop_key
再打开一个终端,启动记录数据工具
ros2 bag record /turtle1/cmd_vel
# 话题名
将光标放在按键控制终端,使用“上下左右”箭头移动小海龟

接下来将三个终端都关闭,打开一个新的海归模拟器
在第三个终端输入“ls ros*”,查看所有ros文件,可以看到有两个rosbag2文件夹(我好像打开了两次),里面分别有.yaml和.db3文件

可以通过如下命令,详细查看记录里面的内容
cat rosbag2_2026_02_03-18_01_13/metadata.yaml

通过话题数据包发布话题,控制小海龟移动
ros2 bag play rosbag2_2026_02_03-18_01_13/
可以看到,和之前录入的移动数据一致

机器人建模与仿真
使用URDF创建机器人
URDF(统一机器人描述格式)使用XML(可扩展标记语言)来描述机器人的几何结构、传感器和传感器等信息,是ROS实现机器人仿真、运动规划、可视化的基础。
机器人在 URDF 中被描述为树形结构,核心层级:根连竿 (link) → 关节 (joint) → 子连杆 (link) → 关节 (joint) → ...
连杆 (link):机器人的刚体部件(如底座、手臂、夹爪、轮子),描述部件的几何形状、质量、惯性、坐标系、可视化 / 碰撞属性。
关节 (joint):连接两个连杆的运动副,描述关节的类型(转动 / 移动 / 固定)、运动范围、限位、原点位置、父/子连杆关系。
先安装一个URDF插件

创建工作空间,创建src文件夹,创建功能包(src目录下)
ros2 pkg create bot_description --build-type ament_cmake --license Apache-2.0
创建一个urdf文件夹,存放机器人的模型文件
新建一个first_robot.urdf文件

<?xml version="1.0"?>
<robot name="first_robot">
<!-- 机器人的身体部分 -->
<link name="base_link">
<!-- 部件的外观描述 -->
<visual>
<!-- 沿着自己几何中心的偏移和选择量 -->
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<!-- 几何形状 -->
<geometry>
<!-- 圆柱体 -->
<cylinder radius="0.10" length="0.12"/>
</geometry>
<!-- 材质、颜色 -->
<material>
<color rgba="1.0 1.0 1.0 0.5"/>
</material>
</visual>
</link>
<!-- 机器人的IMU部件,惯性测量传感器 -->
<link name="imu_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<!-- 正方体 -->
<box size="0.02 0.02 0.02"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 0.5"/>
</material>
</visual>
</link>
<!-- 机器人的关节 -->
<joint name="imu_joint" type="fixed">
<parent link="base_link"/>
<child link="imu_link"/>
<!-- 固定位置 -->
<origin xyz="0.0 0.0 0.03" rpy="0.0 0.0 0.0"/>
</joint>
</robot>
进入urdf文件夹,使用urdf_to_graphviz first_robot.urdf命令,进行结构可视化
然后就生成了.gv和.pdf两个文件

打开pdf文件就能直观查看结构

在Rviz中显示机器人
打开Rviz
Rviz2
点击左下角的“Add”,选择“RobotModel”,点击“OK”

将“描述来源”改为“File”

点击“...”,选择文件

选择“.urdf”文件,点击“open”

可以看到有个小圆点,双指可放大视图(为什么是正方形?我没找到原因,伙伴们帮忙指点迷津吧)


rviz默认不加载urdf里的joint关系,一般从TF发布获取
先安装joint-state-publisher节点和robot-state-publisher
sudo apt install ros-humble-joint-state-publisher
sudo apt install ros-humble-robot-state-publisher
在功能包下创建一个launch文件夹方便我们同时运行,创建一个.launch.py文件

打开CMakeLists.txt文件,将launch和urdf文件夹拷贝到install的share目录下

回到工作空间,colcon build构建一下

再继续编写launch文件
import launch
import launch_ros
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
# 获取默认的urdf路径
urdf_package_path = get_package_share_directory('bot_description')
default_urdf_path = os.path.join(urdf_package_path, 'urdf', 'first_robot.urdf')
# 声明参数
action_declare_arg_mode_path = launch.actions.DeclareLaunchArgument(
name='model',default_value=default_urdf_path,description='加载的模型文件路径'
)
# 通过文件路径,或区内人,并转换成参数值对象
substitutions_commend_result=launch.substitutions.Command(['cat ',launch.substitutions.LaunchConfiguration('model')])
robot_description_value=launch_ros.parameter_descriptions.ParameterValue(substitutions_commend_result,value_type=str)
action_robot_state_publisher=launch_ros.actions.Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{'robot_description':robot_description_value}]
)
action_joint_state_publisher=launch_ros.actions.Node(
package='joint_state_publisher',
executable='joint_state_publisher',
parameters=[{'joint_description':robot_description_value}]
)
action_rviz_node=launch_ros.actions.Node(
package='rviz2',
executable='rviz2',
)
return launch.LaunchDescription([
action_declare_arg_mode_path,
action_robot_state_publisher,
action_joint_state_publisher,
action_rviz_node,
])
再构建配置一下环境,再启动
ros2 launch bot_description display_robot.launch.py

将Fixed Frame可以改为“base_link”(TF中已经有了说明)

点击“Add”,选择“RobotModel”,点击“OK”

Topic description可以选择刚刚创建的robot description

选完之后放大,可以看到右边有模型了。TF也没有报错了

可以再点击“Add”,添加“TF”模块

将“show names”打开,“Marker Scale”调小,可以发现有圆柱坐标,只是图像没颜色了(关闭时的配置都保留,不然下一次又要Add)

使用Xacro简化URDF
Xacro是基于XML的宏语言,使用它可以将部件等定义为宏,在需要时调用即可。
打开src目录下的urdf文件夹,新建一个first_robot.xacro文件

<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="first_robot">
<!-- 定义底座宏:参数为length和radius] -->
<xacro:macro name="base_link" params="length radius">
<link name="base_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="${radius}" length="${length}"/>
</geometry>
<material>
<color rgba="1.0 1.0 1.0 0.5"/>
</material>
</visual>
</link>
</xacro:macro>
<!-- 定义IMU链接宏:参数为imu_name和xyz -->
<xacro:macro name="imu_link" params="imu_name xyz">
<link name="${imu_name}_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<box size="0.02 0.02 0.02"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 0.5"/>
</material>
</visual>
</link>
<joint name="${imu_name}_joint" type="fixed">
<parent link="base_link"/>
<child link="${imu_name}_link"/>
<origin xyz="${xyz}" rpy="0.0 0.0 0.0"/>
</joint>
</xacro:macro>
<!-- 调用宏 -->
<xacro:base_link length="0.12" radius="0.1"/>
<xacro:imu_link imu_name="imu_up" xyz="0.0 0.0 0.03"/>
<xacro:imu_link imu_name="imu_down" xyz="0.0 0.0 -0.03"/>
</robot>
xacro不能直接用,还需要转换为urdf
先下载工具
sudo apt install ros-humble-xacro
然后再使用“xacro+文件路径”解析文件,转换为urdf格式
xacro ros2/src/bot_description/urdf/first_robot.xacro
生成的就是urdf格式的

再将display_robot.launch.py里的原来的“cat”命令改为“xacro”

再构建配置,运行
ros2 launch bot_description display_robot.launch.py model:=/home/lin07/ros2/src/bot_description/urdf/first_robot.xacro
# xacro文件路径
圆柱体还是显示不了

创建机器人及传感器部件
先创建机器人的身体部分,在urdf文件夹下,新建一个base.urdf.xacro文件
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="base_xacro" params="length radius">
<link name="base_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="${radius}" length="${length}"/>
</geometry>
<material name="white">
<color rgba="1.0 1.0 1.0 0.5"/>
</material>
</visual>
</link>
</xacro:macro>
</robot>
再创建传感器部分,在urdf文件夹下新建一个sensor文件夹,在文件夹下新建一个imu.urdf.xacro文件
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="imu_xacro" params="xyz">
<link name="imu_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<box size="0.02 0.02 0.02"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 0.5"/>
</material>
</visual>
</link>
<joint name="imu_joint" type="fixed">
<parent link="base_link"/>
<child link="imu_link"/>
<origin xyz="${xyz}" rpy="0.0 0.0 0.0"/>
</joint>
</xacro:macro>
</robot>
再创建一个camera.urdf.xacro文件
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="camera_xacro" params="xyz">
<link name="camera_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<box size="0.02 0.02 0.02"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 0.5"/>
</material>
</visual>
</link>
<joint name="camera_joint" type="fixed">
<parent link="base_link"/>
<child link="camera_link"/>
<origin xyz="${xyz}" rpy="0.0 0.0 0.0"/>
</joint>
</xacro:macro>
</robot>
再创建一个laser.urdf.xacro文件
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="laser_xacro" params="xyz">
<!-- 雷达支撑杆 -->
<link name="laser_cylinder_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="0.01" length="0.10"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 1.5"/>
</material>
</visual>
</link>
<!-- 雷达 -->
<link name="laser_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="0.02" length="0.02"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 1.0"/>
</material>
</visual>
</link>
<joint name="laser_joint" type="fixed">
<parent link="laser_cylinder_link"/>
<child link="laser_link"/>
<origin xyz="0.0 0.0 0.05" rpy="0.0 0.0 0.0"/>
</joint>
<joint name="laser_cylinder_joint" type="fixed">
<parent link="base_link"/>
<child link="laser_cylinder_link"/>
<origin xyz="${xyz}" rpy="0.0 0.0 0.0"/>
</joint>
</xacro:macro>
</robot>
在urdf文件夹下创建一个bot.urdf.xacro文件,进行组装
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="bot">
<xacro:include filename="$(find bot_description)/urdf/base.urdf.xacro"/>
<xacro:include filename="$(find bot_description)/urdf/sensor/imu.urdf.xacro"/>
<xacro:include filename="$(find bot_description)/urdf/sensor/camera.urdf.xacro"/>
<xacro:include filename="$(find bot_description)/urdf/sensor/laser.urdf.xacro"/>
<!-- 调用宏 -->
<xacro:base_xacro length="0.12" radius="0.10"/>
<xacro:imu_xacro xyz="0.0 0.0 0.02"/>
<xacro:camera_xacro xyz="0.10 0.0 0.075"/>
<xacro:laser_xacro xyz="0.0 0.0 0.10"/>
</robot>
构建配置,运行
ros2 launch bot_description display_robot.launch.py model:=/home/lin07/ros2/src/bot_description/urdf/bot.urdf.xacro
哇有了有了(家人们,我第一次打开没有形状只有坐标系,重新运行了一次就有了)

完善机器人执行器部件
在urdf下新建一个actuator文件夹,在actuator目录下新建一个wheel.urdf.xacro文件
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="wheel_xacro" params="wheel_name xyz">
<link name="${wheel_name}_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="1.57079 0.0 0.0"/>
<geometry>
<cylinder radius="0.032" length="0.04"/>
</geometry>
<material name="yellow">
<color rgba="1.0 1.0 0.0 0.8"/>
</material>
</visual>
</link>
<joint name="${wheel_name}_joint" type="continuous">
<parent link="base_link"/>
<child link="${wheel_name}_link"/>
<origin xyz="${xyz}" rpy="0.0 0.0 0.0"/>
<axis xyz="0 1 0"/>
</joint>
</xacro:macro>
</robot>
在新建一个万向轮文件caster.urdf.xacro
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="caster_xacro" params="caster_name xyz">
<link name="${caster_name}_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<sphere radius="0.016"/>
</geometry>
<material name="yellow">
<color rgba="1.0 1.0 0.0 0.8"/>
</material>
</visual>
</link>
<joint name="${caster_name}_joint" type="fixed">
<parent link="base_link"/>
<child link="${caster_name}_link"/>
<origin xyz="${xyz}" rpy="0.0 0.0 0.0"/>
</joint>
</xacro:macro>
</robot>
再更新bot.urdf.xacro文件
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="bot">
<!-- 基础部分 -->
<xacro:include filename="$(find bot_description)/urdf/base.urdf.xacro"/>
<!-- 传感器部分 -->
<xacro:include filename="$(find bot_description)/urdf/sensor/imu.urdf.xacro"/>
<xacro:include filename="$(find bot_description)/urdf/sensor/camera.urdf.xacro"/>
<xacro:include filename="$(find bot_description)/urdf/sensor/laser.urdf.xacro"/>
<!-- 执行器部分 -->
<xacro:include filename="$(find bot_description)/urdf/actuator/wheel.urdf.xacro"/>
<xacro:include filename="$(find bot_description)/urdf/actuator/caster.urdf.xacro"/>
<!-- 调用宏 -->
<xacro:base_xacro length="0.12" radius="0.10"/>
<xacro:imu_xacro xyz="0.0 0.0 0.02"/>
<xacro:camera_xacro xyz="0.10 0.0 0.075"/>
<xacro:laser_xacro xyz="0.0 0.0 0.10"/>
<xacro:wheel_xacro wheel_name="left_wheel" xyz="0 0.10 -0.06"/>
<xacro:wheel_xacro wheel_name="right_wheel" xyz="0 -0.10 -0.06"/>
<xacro:caster_xacro caster_name="front_caster" xyz="0.08 0.0 -0.076"/>
<xacro:caster_xacro caster_name="back_caster" xyz="-0.08 0.0 -0.076"/>
</robot>
添加虚拟部件,使轮子贴合地面
打开base.urdf.xacro文件进行修改
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="base_xacro" params="length radius">
<!-- 虚拟部件,base投影到地面的点 -->
<link name="base_footprint"/>
<link name="base_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="${radius}" length="${length}"/>
</geometry>
<material name="white">
<color rgba="1.0 1.0 1.0 0.5"/>
</material>
</visual>
</link>
<joint name="joint_name" type="fixed">
<origin xyz="0.0 0.0 ${length/2.0+0.032-0.001}" rpy="0.0 0.0 0.0"/>
<parent link="base_footprint"/>
<child link="base_link"/>
</joint>
</xacro:macro>
</robot>
构建配置运行
将fixed frame选为“base_footprint”

可以看到轮子紧贴地面了

添加物理属性让机器人更真实
添加碰撞属性
修改base.urdf.xacro文件,给base_link添加碰撞属性
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="base_xacro" params="length radius">
<!-- 虚拟部件,base投影到地面的点 -->
<link name="base_footprint"/>
<link name="base_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="${radius}" length="${length}"/>
</geometry>
<material name="white">
<color rgba="1.0 1.0 1.0 0.5"/>
</material>
</visual>
<collision>
<!-- 将visual里面的内容拷贝一份,有时也可用规则图形近似不规则图形 -->
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="${radius}" length="${length}"/>
</geometry>
<material name="white">
<color rgba="1.0 1.0 1.0 0.5"/>
</material>
</collision>
</link>
<joint name="joint_name" type="fixed">
<origin xyz="0.0 0.0 ${length/2.0+0.032-0.001}" rpy="0.0 0.0 0.0"/>
<parent link="base_footprint"/>
<child link="base_link"/>
</joint>
</xacro:macro>
</robot>
用同样的方法分别给laser等三个传感器、两个执行器添加碰撞属性(下面仅展示laser)
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="laser_xacro" params="xyz">
<!-- 雷达支撑杆 -->
<link name="laser_cylinder_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="0.01" length="0.10"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 0.5"/>
</material>
</visual>
<collision>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="0.01" length="0.10"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 0.5"/>
</material>
</collision>
</link>
<!-- 雷达 -->
<link name="laser_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="0.02" length="0.02"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 1.0"/>
</material>
</visual>
<collision>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="0.02" length="0.02"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 1.0"/>
</material>
</collision>
</link>
<joint name="laser_joint" type="fixed">
<parent link="laser_cylinder_link"/>
<child link="laser_link"/>
<origin xyz="0.0 0.0 0.05" rpy="0.0 0.0 0.0"/>
</joint>
<joint name="laser_cylinder_joint" type="fixed">
<parent link="base_link"/>
<child link="laser_cylinder_link"/>
<origin xyz="${xyz}" rpy="0.0 0.0 0.0"/>
</joint>
<gazebo reference="laser_cylinder_link">
<material>Gazebo/Black</material>
</gazebo>
<gazebo reference="laser_link">
<material>Gazebo/Black</material>
</gazebo>
</xacro:macro>
</robot>
再重新构建配置,运行
取消勾选visual Enabled,点击“Collision Enabled”,可以看出碰撞可视化与机器人可视化一致

添加质量和惯性
质量可以用千克表示,而旋转惯性需要使用一个3*3的矩阵表述
在urdf目录下新建一个common_inertial.xacro文件(不用记,直接让豆包写就好了)
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="box_inertia" params="m w h d">
<inertial>
<mass value="${m}"/>
<inertia ixx="${m / 12 * (h*h + d*d)}" ixy="0.0" ixz="0.0" iyy="${m / 12 * (w*w + d*d)}" iyz="0.0" izz="${m / 12 * (w*w + h*h)}"/>
</inertial>
</xacro:macro>
<xacro:macro name="cylinder_inertia" params="m r h">
<inertial>
<mass value="${m}"/>
<inertia ixx="${m / 12 * (3*r*r + h*h)}" ixy="0.0" ixz="0.0" iyy="${m / 12 * (3*r*r + h*h)}" iyz="0.0" izz="${m / 2 * r*r}"/>
</inertial>
</xacro:macro>
</robot>
在base.urdf.xacro文件中引入刚刚创建的惯性宏(如下就添加好了惯性矩阵)
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:include filename="$(find bot_description)/urdf/common_inertial.xacro"/>
<xacro:macro name="base_xacro" params="length radius">
<!-- 虚拟部件,base投影到地面的点 -->
<link name="base_footprint"/>
<link name="base_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="${radius}" length="${length}"/>
</geometry>
<material name="white">
<color rgba="1.0 1.0 1.0 0.5"/>
</material>
</visual>
<collision>
<!-- 将visual里面的内容拷贝一份,有时也可用规则图形近似不规则图形 -->
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="${radius}" length="${length}"/>
</geometry>
<material name="white">
<color rgba="1.0 1.0 1.0 0.5"/>
</material>
</collision>
<xacro:cylinder_inertia m="1.0" r="${radius}" h="${length}"/>
</link>
<joint name="joint_name" type="fixed">
<origin xyz="0.0 0.0 ${length/2.0+0.032-0.001}" rpy="0.0 0.0 0.0"/>
<parent link="base_footprint"/>
<child link="base_link"/>
</joint>
</xacro:macro>
</robot>
构建配置,运行
关闭visual Enabled,打开Mass Properties里的Mass,这样就可以在rviz中的质量视角,再点下imertial就能可视化惯性

其它的部件同理添加质量和惯性属性,不再一一演示

在Gazebo中完成机器人仿真
安装Gazebo
sudo apt install gazebo
下载模型
mkdir -p ~/.gazebo
cd ~/.gazebo
git clone https://gitee.com/ohhuo/gazebo_models.git models
rm -rf ~/.gazebo/models/.git
在终端中输入gazebo就能打开gazebo了

可以点击左上角的图形,添加图形(shift+左键可以旋转视角)

右键可以Delete删除

也可以手动画墙
点击左上角的“Edit”,选择“Building Editor”,点击“Wall”

然后在右侧白色方格作画,就能画出指定形状的墙

保存构建
画好之后,点击左上角的“File”,退出文件

选择第一个,“保存并退出”

新建一个world文件夹存储刚刚的世界文件

命名为room

点击Insert,还可以选择插入各种各样的物品

点击左上角的移动图标,拖拉三个方向箭头,可分别向三个方向移动,将房间移动到靠近原点的位置(加载机器人时,机器人将出现在中间位置)

编辑完之后,点击左上角“File”,选择“Save World As”

以.world后缀保存文件

可以发现,sdf文件是urdf文件的扩展

在gazebo中加载机器人模型
需要将机器人建模使用的urdf格式转换成sdf格式
安装功能包
sudo apt install ros-humble-gazebo-ros-pkgs
在launch目录下新建一个gazebo_sim.launch.py文件
import launch
import launch_ros
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
urdf_package_path = get_package_share_directory('bot_description')
default_xacro_path = os.path.join(urdf_package_path, 'urdf', 'bot.urdf.xacro')
default_gazebo_world_path=os.path.join(urdf_package_path,'world','custom_room.world')
action_declare_arg_mode_path = launch.actions.DeclareLaunchArgument(
name='model',default_value=default_xacro_path,description='加载的模型文件路径'
)
substitutions_commend_result=launch.substitutions.Command(['xacro ',launch.substitutions.LaunchConfiguration('model')])
robot_description_value=launch_ros.parameter_descriptions.ParameterValue(substitutions_commend_result,value_type=str)
action_robot_state_publisher=launch_ros.actions.Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{'robot_description':robot_description_value}]
)
action_launch_gazebo=launch.actions.IncludeLaunchDescription(
launch.launch_description_sources.PythonLaunchDescriptionSource(
[get_package_share_directory('gazebo_ros'),'/launch','/gazebo.launch,py']
),
launch_arguments=[{'world',default_gazebo_world_path},{'verbose','true'}]
)
return launch.LaunchDescription([
action_declare_arg_mode_path,
action_robot_state_publisher,
action_launch_gazebo,
])
在CMakeLists.txt中添加拷贝

构建配置,运行
ros2 launch bot_description gazebo_sim.launch.py
能成功打开(第二遍才有图像)

现在我们将机器人加载到世界中
更新gazebo_sim.launch.py文件
import launch
import launch_ros
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
urdf_package_path = get_package_share_directory('bot_description')
default_xacro_path = os.path.join(urdf_package_path, 'urdf', 'bot.urdf.xacro')
default_gazebo_world_path=os.path.join(urdf_package_path,'world','custom_room.world')
action_declare_arg_mode_path = launch.actions.DeclareLaunchArgument(
name='model',default_value=default_xacro_path,description='加载的模型文件路径'
)
substitutions_commend_result=launch.substitutions.Command(['xacro ',launch.substitutions.LaunchConfiguration('model')])
robot_description_value=launch_ros.parameter_descriptions.ParameterValue(substitutions_commend_result,value_type=str)
action_robot_state_publisher=launch_ros.actions.Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{'robot_description':robot_description_value}]
)
action_launch_gazebo=launch.actions.IncludeLaunchDescription(
launch.launch_description_sources.PythonLaunchDescriptionSource(
[get_package_share_directory('gazebo_ros'),'/launch','/gazebo.launch.py']
),
launch_arguments=[('world', default_gazebo_world_path),('verbose', 'true')]
)
action_spawn_entity=launch_ros.actions.Node(
package="gazebo_ros",
executable="spawn_entity.py",
arguments=['-topic','/robot_description','-entity','bot']
)
return launch.LaunchDescription([
action_declare_arg_mode_path,
action_robot_state_publisher,
action_launch_gazebo,
action_spawn_entity
])
构建配置,运行
ros2 launch bot_description gazebo_sim.launch.py
能正常显示机器人

使用gazebo标签扩展urdf
可改变机器人在gazebo中的颜色等各属性(摩擦力)
打开urdf/sensor下的laser.urdf.xacro,加上gazebo标签

构建配置,运行
可以看到雷达和雷达支撑杆的颜色成功变为了黑色

使用两轮差速插件控制机器人
在plugin标签中使用两轮差速插件
在urdf目录下新建一个plugins文件夹,在里面创建gazebo_control_plugin.xacro文件
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- 声明宏-->
<xacro:macro name="gazebo_control_plugin">
<gazebo>
<!-- 插件标签:插件名 插件文件名字 -->
<plugin name="diff_drive" filename="libgazebo_ros_diff_drive.so">
<ros>
<namespace>/</namespace>
<remapping>cmd_vel:=cmd_vel</remapping>
<remapping>odom:=odom</remapping>
</ros>
<update_rate>30</update_rate>
<left_joint>left_wheel_joint</left_joint>
<right_joint>right_wheel_joint</right_joint>
<wheel_separation>0.2</wheel_separation>
<wheel_radius>0.064</wheel_radius>
<max_vel>20</max_vel>
<max_wheel_acceleration>1.0</max_wheel_acceleration>
<publish_odom>true</publish_odom>
<publish_odom_tf>true</publish_odom_tf>
<publish_wheel_tf>true</publish_wheel_tf>
<odometry_frame>odom</odometry_frame>
<robot_base_frame>base_footprint</robot_base_frame>
</plugin>
</gazebo>
</xacro:macro>
</robot>
然后在bot.urdf.xacro文件中引入插件并调用
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="bot">
<!-- 基础部分 -->
<xacro:include filename="$(find bot_description)/urdf/base.urdf.xacro"/>
<!-- 传感器部分 -->
<xacro:include filename="$(find bot_description)/urdf/sensor/imu.urdf.xacro"/>
<xacro:include filename="$(find bot_description)/urdf/sensor/camera.urdf.xacro"/>
<xacro:include filename="$(find bot_description)/urdf/sensor/laser.urdf.xacro"/>
<!-- 执行器部分 -->
<xacro:include filename="$(find bot_description)/urdf/actuator/wheel.urdf.xacro"/>
<xacro:include filename="$(find bot_description)/urdf/actuator/caster.urdf.xacro"/>
<!-- 插件 -->
<xacro:include filename="$(find bot_description)/urdf/plugins/gazebo_control_plugin.xacro"/>
<!-- 调用宏 -->
<xacro:base_xacro length="0.12" radius="0.10"/>
<xacro:imu_xacro xyz="0.0 0.0 0.02"/>
<xacro:camera_xacro xyz="0.10 0.0 0.075"/>
<xacro:laser_xacro xyz="0.0 0.0 0.10"/>
<xacro:wheel_xacro wheel_name="left_wheel" xyz="0 0.10 -0.06"/>
<xacro:wheel_xacro wheel_name="right_wheel" xyz="0 -0.10 -0.06"/>
<xacro:caster_xacro caster_name="front_caster" xyz="0.08 0.0 -0.076"/>
<xacro:caster_xacro caster_name="back_caster" xyz="-0.08 0.0 -0.076"/>
<xacro:gazebo_control_plugin/>
</robot>
大家可以再检测以下轮子的代码
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="wheel_xacro" params="wheel_name xyz">
<link name="${wheel_name}_link">
<!-- 可视化部分(RViz/Gazebo显示用) -->
<visual>
<origin xyz="0.0 0.0 0.0" rpy="1.57079 0.0 0.0"/>
<geometry>
<cylinder radius="0.032" length="0.04"/>
</geometry>
<material name="yellow">
<color rgba="1.0 1.0 0.0 0.8"/>
</material>
</visual>
<!-- 碰撞部分(Gazebo物理碰撞用) -->
<collision>
<origin xyz="0.0 0.0 0.0" rpy="1.57079 0.0 0.0"/>
<geometry>
<cylinder radius="0.032" length="0.04"/>
</geometry>
</collision>
<!-- 新增:惯性属性(Gazebo必需,没有则轮子不显示) -->
<inertial>
<mass value="0.1"/> <!-- 轮子质量0.1kg,可根据实际调整 -->
<!-- 圆柱型轮子的惯性矩阵(适配半径0.032、长度0.04的尺寸) -->
<inertia ixx="0.00002" ixy="0.0" ixz="0.0"
iyy="0.00001" iyz="0.0"
izz="0.00002"/>
</inertial>
</link>
<!-- 关节定义(原代码无问题,保留) -->
<joint name="${wheel_name}_joint" type="continuous">
<parent link="base_link"/>
<child link="${wheel_name}_link"/>
<origin xyz="${xyz}" rpy="0.0 0.0 0.0"/>
<axis xyz="0 1 0"/>
<!-- 新增:关节物理参数,防止Gazebo中关节异常 -->
<limit effort="50" velocity="50"/>
<dynamics damping="0.01" friction="0.01"/>
</joint>
<!-- 新增:Gazebo专属配置,优化轮子显示和物理表现 -->
<gazebo reference="${wheel_name}_link">
<!-- 指定Gazebo内置材质,兼容更好 -->
<material>Gazebo/Yellow</material>
<!-- 禁用自动惯性计算,使用我们定义的inertial -->
<auto_inertial>false</auto_inertial>
</gazebo>
</xacro:macro>
</robot>
再构建配置,运行
ros2 launch bot_description gazebo_sim.launch.py
为了方便显示,我把之前的方块删除了

在右侧可以看到有一个plugins

再打开一个终端,使用键盘控制节点控制机器人移动
ros2 run teleop_twist_keyboard teleop_twist_keyboard
光标在此终端,我们就可以使用ijkl等进行移动,w/x进行加减速

可能是我没设置摩擦力,前后移动比较缓慢,JK控制左右比较明细

也可以打开rviz查看TF移动
使用ros2_control驱动机器人
ROS 2 Control是ROS2中用于机器人硬件控制的核心框架,核心作用是解析上层速度指令→转换成电机能识别的控制信号→同时反馈硬件状态。
由硬件接口、控制器、控制器管理器3部分组成
安装ros2 control
sudo apt install ros-humble-ros2-control
安装控制器
sudo apt install ros-humble-ros2-controllers
使用gazebo接入ros2_control
安装gazebo ros2 control插件
sudo apt install ros-humble-gazebo-ros2-control
在urdf目录下新建一个bot.ros2_control.xacro文件
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- 定义宏 -->
<xacro:macro name="bot_ros2_control">
<!-- 标签里的内容会给插件解析 -->
<ros2_control name="BotGazeboSystem" type="system">
<hardware>
<plugin>gazebo_ros2_control/GazeboSystem</plugin>
</hardware>
<joint name="left_wheel_joint">
<command_interface name="velocity">
<param name="min">-1</param>
<param name="max">1</param>
</command_interface>
<command_interface name="effort">
<param name="min">-0.1</param>
<param name="max">0.1</param>
</command_interface>
<state_interface name="position"/>
<state_interface name="velocity"/>
<state_interface name="effort"/>
</joint>
<joint name="right_wheel_joint">
<command_interface name="velocity">
<param name="min">-1</param>
<param name="max">1</param>
</command_interface>
<command_interface name="effort">
<param name="min">-0.1</param>
<param name="max">0.1</param>
</command_interface>
<state_interface name="position"/>
<state_interface name="velocity"/>
<state_interface name="effort"/>
</joint>
</ros2_control>
<gazebo>
<plugin filename="/opt/ros/humble/lib/libgazebo_ros2_control.so" name="gazebo_ros2_control">
<parameters>package://bot_description/config/bot_ros2_controller.yaml</parameters>
</plugin>
</gazebo>
</xacro:macro>
</robot>
在bot_description目录下新建一个config文件夹,在里面新建一个bot_ros2_controller.yaml文件
controller_manager:
ros__parameters:
update_rate: 100
use_sim_time: true
在bot.urdf.xacro文件中调用宏
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="bot">
<xacro:include filename="$(find bot_description)/urdf/base.urdf.xacro"/>
<!-- 传感器部分 -->
<xacro:include filename="$(find bot_description)/urdf/sensor/imu.urdf.xacro"/>
<xacro:include filename="$(find bot_description)/urdf/sensor/camera.urdf.xacro"/>
<xacro:include filename="$(find bot_description)/urdf/sensor/laser.urdf.xacro"/>
<!-- 执行器部分 -->
<xacro:include filename="$(find bot_description)/urdf/actuator/wheel.urdf.xacro"/>
<xacro:include filename="$(find bot_description)/urdf/actuator/caster.urdf.xacro"/>
<!-- 插件部分:删除冲突的 gazebo_control_plugin include -->
<!-- <xacro:include filename="$(find bot_description)/urdf/plugins/gazebo_control_plugin.xacro"/> -->
<xacro:include filename="$(find bot_description)/urdf/bot.ros2_control.xacro"/>
<!-- 宏调用:顺序不变 -->
<xacro:base_xacro length="0.12" radius="0.10"/>
<xacro:imu_xacro xyz="0.0 0.0 0.02"/>
<xacro:camera_xacro xyz="0.10 0.0 0.075"/>
<xacro:laser_xacro xyz="0.0 0.0 0.10"/>
<xacro:wheel_xacro wheel_name="left_wheel" xyz="0 0.10 -0.06"/>
<xacro:wheel_xacro wheel_name="right_wheel" xyz="0 -0.10 -0.06"/>
<xacro:caster_xacro caster_name="front_caster" xyz="0.08 0.0 -0.076"/>
<xacro:caster_xacro caster_name="back_caster" xyz="-0.08 0.0 -0.076"/>
<!-- 调用 ros2_control 宏 -->
<xacro:bot_ros2_control/>
</robot>
再构建配置,运行(我这出错了,但我也没有发现问题在哪,好像就是接口不能连接)
ros2 launch bot_description gazebo_sim.launch.py
再打开一个终端,使用“ros2 control list_hardware_interfaces”命令列出所有硬件接口
我就到此结束了先,因为我肯定是会在硬件上操作的,这篇写的不是很好,大家嘴下留情。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)