news 2026/5/8 15:43:19

保姆级教程:用ROS2 Humble和Gazebo Classic从零搭建一个能跑能停的差分AGV模型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
保姆级教程:用ROS2 Humble和Gazebo Classic从零搭建一个能跑能停的差分AGV模型

从零构建ROS2 Humble差分AGV:Gazebo仿真与键盘控制全指南

在工业自动化与机器人研究领域,自主导引车(AGV)的仿真开发是验证算法和系统设计的关键步骤。ROS2 Humble作为机器人操作系统的最新LTS版本,配合Gazebo Classic仿真环境,为开发者提供了强大的工具链。本文将手把手带你完成一个差分驱动AGV的完整开发流程,从URDF建模到Gazebo物理仿真,最终实现键盘控制移动——这不仅是ROS2入门的绝佳实践,更是掌握机器人仿真核心技能的必经之路。

1. 开发环境配置与工作空间搭建

在开始AGV建模前,需要确保基础环境正确配置。推荐使用Ubuntu 22.04 LTS系统,这是ROS2 Humble官方支持的最佳平台。通过以下命令安装核心组件:

sudo apt update sudo apt install ros-humble-desktop ros-humble-gazebo-ros-pkgs

创建工作空间是ROS2开发的起点,不同于ROS1的catkin工具,ROS2使用colcon构建系统。执行以下命令初始化工作空间:

mkdir -p ~/agv_ws/src cd ~/agv_ws colcon build

每次打开新终端时,需要source安装文件以激活工作空间环境。建议将以下命令添加到~/.bashrc文件中实现自动加载:

echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc echo "source ~/agv_ws/install/setup.bash" >> ~/.bashrc

创建功能包时需注意,ROS2支持多种构建类型。对于AGV描述文件,我们选择ament_cmake构建类型:

cd ~/agv_ws/src ros2 pkg create --build-type ament_cmake agv_description

功能包目录结构应包含以下关键文件夹:

  • urdf/: 存放机器人描述文件
  • meshes/: 存放3D模型文件(如有)
  • launch/: 存放启动文件
  • config/: 存放配置文件

2. AGV机械模型设计与URDF/Xacro编写

差分驱动AGV的机械结构通常包含以下核心组件:

  • 底盘(Chassis): 机器人的主体结构
  • 驱动轮(Drive Wheels): 左右各一,实现移动和转向
  • 万向轮(Caster Wheel): 提供稳定性支撑

使用Xacro(XML宏)编写模型比纯URDF更具优势,它能实现参数化设计和模块化编程。创建一个agv.xacro文件,首先定义机器人命名空间和基础参数:

<?xml version="1.0"?> <robot name="agv" xmlns:xacro="http://www.ros.org/wiki/xacro"> <!-- 基础参数定义 --> <xacro:property name="base_length" value="0.4" /> <xacro:property name="base_width" value="0.3" /> <xacro:property name="base_height" value="0.2" /> <xacro:property name="wheel_radius" value="0.05" /> <xacro:property name="wheel_width" value="0.02" /> <!-- 材料定义 --> <material name="blue"> <color rgba="0 0 0.8 1"/> </material> </robot>

底盘(Chassis)的Link定义需要包含视觉(Visual)、碰撞(Collision)和惯性(Inertial)属性:

<link name="base_link"> <visual> <geometry> <box size="${base_length} ${base_width} ${base_height}"/> </geometry> <material name="blue"/> </visual> <collision> <geometry> <box size="${base_length} ${base_width} ${base_height}"/> </geometry> </collision> <inertial> <mass value="5.0"/> <inertia ixx="0.1" ixy="0" ixz="0" iyy="0.1" iyz="0" izz="0.1"/> </inertial> </link>

驱动轮需要定义与底盘的连接关节(Joint)和自身的Link。以左轮为例:

<link name="left_wheel"> <visual> <geometry> <cylinder radius="${wheel_radius}" length="${wheel_width}"/> </geometry> <material name="black"/> </visual> <collision> <geometry> <cylinder radius="${wheel_radius}" length="${wheel_width}"/> </geometry> </collision> <inertial> <mass value="0.5"/> <inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/> </inertial> </link> <joint name="left_wheel_joint" type="continuous"> <parent link="base_link"/> <child link="left_wheel"/> <origin xyz="0 ${base_width/2} -${(base_height/2-wheel_radius)}" rpy="0 1.5707 0"/> <axis xyz="0 1 0"/> </joint>

3. Gazebo仿真集成与物理特性配置

将URDF模型成功导入Gazebo需要添加Gazebo特定标签和插件。首先在Xacro文件中添加Gazebo命名空间:

<gazebo reference="base_link"> <material>Gazebo/Blue</material> </gazebo> <gazebo reference="left_wheel"> <material>Gazebo/Black</material> <mu1 value="1.0"/> <mu2 value="1.0"/> </gazebo>

差分驱动控制需要添加gazebo_ros_diff_drive插件,该插件负责将ROS控制命令转换为Gazebo中的车轮运动:

<gazebo> <plugin name="diff_drive" filename="libgazebo_ros_diff_drive.so"> <ros> <namespace>/</namespace> </ros> <update_rate>30</update_rate> <left_joint>left_wheel_joint</left_joint> <right_joint>right_wheel_joint</right_joint> <wheel_separation>${base_width}</wheel_separation> <wheel_diameter>${2*wheel_radius}</wheel_diameter> <max_wheel_torque>20</max_wheel_torque> <max_wheel_acceleration>5.0</max_wheel_acceleration> <command_topic>cmd_vel</command_topic> <odometry_topic>odom</odometry_topic> <odometry_frame>odom</odometry_frame> <robot_base_frame>base_link</robot_base_frame> </plugin> </gazebo>

创建启动文件agv_gazebo.launch.py,实现一键启动仿真环境:

import os from launch import LaunchDescription from launch.actions import ExecuteProcess, IncludeLaunchDescription, RegisterEventHandler from launch.event_handlers import OnProcessExit from launch.launch_description_sources import PythonLaunchDescriptionSource from launch_ros.actions import Node from ament_index_python.packages import get_package_share_directory def generate_launch_description(): pkg_path = get_package_share_directory('agv_description') urdf_file = os.path.join(pkg_path, 'urdf', 'agv.xacro') # 将Xacro转换为URDF robot_description = Command( ['xacro ', urdf_file]) # 启动Gazebo gazebo = IncludeLaunchDescription( PythonLaunchDescriptionSource([os.path.join( get_package_share_directory('gazebo_ros'), 'launch', 'gazebo.launch.py')]), ) # 生成机器人模型 spawn_entity = Node( package='gazebo_ros', executable='spawn_entity.py', arguments=['-topic', 'robot_description', '-entity', 'agv'], output='screen' ) # 发布机器人状态 robot_state_publisher = Node( package='robot_state_publisher', executable='robot_state_publisher', name='robot_state_publisher', output='both', parameters=[{'robot_description': robot_description}] ) return LaunchDescription([ RegisterEventHandler( event_handler=OnProcessExit( target_action=spawn_entity, on_exit=[robot_state_publisher], ) ), gazebo, spawn_entity, ])

4. 运动控制实现与调试技巧

Gazebo中的差分驱动AGV可以通过geometry_msgs/msg/Twist消息控制。ROS2提供了开箱即用的键盘控制节点:

ros2 run teleop_twist_keyboard teleop_twist_keyboard

该节点会发布到/cmd_vel话题,控制消息包含两个关键参数:

  • linear.x: 前进/后退速度(m/s)
  • angular.z: 旋转速度(rad/s)

常见问题排查指南

  1. 模型在Gazebo中下陷或抖动

    • 检查所有Link的惯性参数是否正确设置
    • 调整万向轮的摩擦系数:
      <gazebo reference="caster_wheel"> <mu1 value="0.1"/> <mu2 value="0.1"/> </gazebo>
  2. 车轮打滑或控制响应迟缓

    • 增加差分驱动插件的max_wheel_torque
    • 调整车轮的摩擦系数:
      <gazebo reference="left_wheel"> <mu1 value="1.5"/> <mu2 value="1.5"/> </gazebo>
  3. TF坐标变换缺失

    • 确保robot_state_publisher节点正常运行
    • 检查URDF中所有关节定义是否正确

对于更高级的控制,可以创建自定义控制节点。以下Python示例实现了简单的目标点导航:

import rclpy from rclpy.node import Node from geometry_msgs.msg import Twist from nav_msgs.msg import Odometry import math class AGVController(Node): def __init__(self): super().__init__('agv_controller') self.cmd_vel_pub = self.create_publisher(Twist, '/cmd_vel', 10) self.odom_sub = self.create_subscription( Odometry, '/odom', self.odom_callback, 10) self.target_x = 2.0 self.target_y = 2.0 self.k_linear = 0.5 self.k_angular = 1.0 def odom_callback(self, msg): current_x = msg.pose.pose.position.x current_y = msg.pose.pose.position.y # 计算目标角度 angle_to_target = math.atan2(self.target_y - current_y, self.target_x - current_x) # 计算当前朝向(简化为2D平面) q = msg.pose.pose.orientation current_yaw = math.atan2(2*(q.w*q.z + q.x*q.y), 1-2*(q.y*q.y + q.z*q.z)) # 计算控制命令 cmd = Twist() distance = math.sqrt((self.target_x-current_x)**2 + (self.target_y-current_y)**2) if distance > 0.1: # 到达阈值 # 角度控制 angle_error = angle_to_target - current_yaw if angle_error > math.pi: angle_error -= 2*math.pi elif angle_error < -math.pi: angle_error += 2*math.pi cmd.angular.z = self.k_angular * angle_error # 距离控制 cmd.linear.x = self.k_linear * distance else: cmd.linear.x = 0.0 cmd.angular.z = 0.0 self.get_logger().info("Target reached!") self.cmd_vel_pub.publish(cmd) def main(args=None): rclpy.init(args=args) controller = AGVController() rclpy.spin(controller) controller.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()

在实际项目中,我发现差分驱动机器人的转弯半径控制需要特别注意车轮间距参数。当wheel_separation设置不准确时,会导致实际转弯半径与预期不符。建议通过多次实测调整这个参数,使用以下公式作为初始值参考:

转弯半径 = (轮间距/2) / tan(转向角)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 15:43:17

AI工具搭建自动化视频生成数字仪表盘录制

# 从手工搭建到AI驱动&#xff1a;自动化视频生成数字仪表盘录制的实践思考 过去这几年&#xff0c;我一直在处理各种数据可视化项目。最开始&#xff0c;仪表盘录制是个让人头疼的活儿——得手动操作浏览器、录屏、剪辑&#xff0c;还得保证每次录制的画面角度和时长一致。后来…

作者头像 李华
网站建设 2026/5/8 15:43:11

大模型微调和接口调用的理解

一、需求和技术1.企业对于大模型的不同类型个性化需求提高模型对企业专有信息的理解、增强模型再特定行业领域的知识——SFT&#xff08;有监督微调&#xff09;Supervised Fine-Tuning&#xff1a;通过提供的人工标注的数据&#xff0c;进一步进行训练预训练模型&#xff0c;让…

作者头像 李华
网站建设 2026/5/8 15:42:51

订阅经济时代:如何管理家庭数字服务账单与避免消费陷阱

1. 订阅经济浪潮下的家庭账单困局又到了月底&#xff0c;我盯着信用卡账单上那一长串的“月度订阅”扣款记录&#xff0c;感觉头有点大。这已经不是第一次了。从流媒体视频、音乐&#xff0c;到云端存储、新闻资讯&#xff0c;甚至家里的安防摄像头&#xff0c;似乎所有东西都变…

作者头像 李华