ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ROS2 Humble+Gazebo Classic差分AGV仿真:从URDF建模到键盘控制全流程

ROS2 Humble+Gazebo Classic差分AGV仿真:从URDF建模到键盘控制全流程 去年我第一次在ROS2 Humble环境里搭Gazebo Classic差分AGV仿真时以为最难的部分是写URDF模型结果真正折腾到凌晨的是Gazebo里那辆小车怎么都站不住——要么陷进地面要么按键没反应要么轮子原地空转。后来把差速驱动插件的参数、惯性矩阵、摩擦系数挨个捋了一遍才意识到这个“保姆级教程”看起来简单里面坑全是细节。这篇东西就把我整理好的完整流程写出来从环境安装、URDF建模、Gazebo Classic物理仿真到teleop_twist_keyboard键盘控制一条线走通。适合刚装好ROS2 Humble、想把差分运动小车先跑起来的朋友也适合想从零理解差速AGV坐标变换和话题通信的人。1. 为什么我推荐这个组合Humble与Gazebo Classic的搭配逻辑1.1 从新版Gazebo迁移回来的实际教训ROS2 Humble官方推荐的是新版Gazebo以前叫Ignition现在叫Gazebo Fortress/Garden之类很多教程上来就让你装ros_gz。说实话新版本画面确实好但如果你是第一次接触ROS2仿真我强烈建议先把目光放在Gazebo Classic上。原因很直接生态成熟。Gazebo Classic对应的是ROS2里的gazebo_ros_pkgs这个包在ROS1时代就已经被大量项目验证过。差速驱动、激光雷达、IMU、相机这些常见传感器插件网上能搜到的资料数量远多于新版Gazebo。对新手来说遇到一个报错能搜到答案和搜不到答案的差距是巨大的。我自己就是从新版Gazebo迁回Classic的在Fortress里光是搞定libgazebo_ros_diff_drive.so的加载就折腾了好几天回到Classic之后几乎一次通过。当然这不是说新版不好。如果你做的是多机器人协同、需要高级光照和更真实的传感器噪声新版值得投入。但本篇目标是“从零搭建一个能键盘控制的差分AGV模型”显然选Gazebo Classic更务实。1.2 这套组合能做什么做到什么程度用ROS2 Humble Gazebo Classic建一个差分AGV模型能做到的事情是这样的构建一个带物理属性的小车模型包含车身、左右主动轮、从动支撑轮在Gazebo Classic里加载模型获得重力和碰撞反馈通过差速驱动插件接收/cmd_vel话题上的线速度和角速度指令用teleop_twist_keyboard节点发布/cmd_vel消息实现键盘方向键控制同时输出里程计/odom和坐标变换/tf为后续导航、SLAM打基础这套链路是几乎所有移动机器人项目的起点。不管你是做巡检机器人、仓储AGV还是自己组一台树莓派小车最终代码结构都和这里展示的完全一样。把这条链路跑通后面接激光雷达做Cartographer、接Nav2做路径规划都是水到渠成的事。2. 环境准备安装ROS2 Humble与Gazebo Classic的几个关键细节2.1 安装命令与包清单如果你的系统是Ubuntu 22.04ROS2 Humble装好后Gazebo Classic默认是带过来的但我们需要额外安装ROS2与Gazebo的桥接包还有后续要用的建模和控制工具。sudo apt update sudo apt install ros-humble-gazebo-ros-pkgs sudo apt install ros-humble-gazebo-ros2-control sudo apt install ros-humble-xacro sudo apt install ros-humble-teleop-twist-keyboard sudo apt install ros-humble-rviz2 sudo apt install ros-humble-teleop-twist-keyboard这里我特别说一下ros-humble-gazebo-ros-pkgs。这个包里面包含了gazebo_ros节点、gazebo_ros_factory、gazebo_ros_diff_drive等插件是整个仿真的核心。没有它你就算手动启动了Gazebo也无法通过ROS2话题把机器人加载进去。很多教程还会让你装ros-humble-gazebo-ros2-control这是给ros2_control用的桥接包本篇暂时用不到但如果你后续想把仿真的关节控制权交给ros2_control最好提前装好。2.2 验证环境是否就绪的三板斧安装完不要急着写URDF先确认基础环境是通的。第一步检查Gazebo是否可以启动gazebo --version如果能看到Gazebo multi-robot simulator, version 11.x说明Gazebo本体没问题。第二步验证ROS2桥接能否加载ros2 pkg list | grep gazebo正常情况下应该能看到gazebo_ros、gazebo_ros_pkgs、gazebo_msgs这些包。第三步做一个快速冒烟测试手动启动Gazebo并提供ROS2接口gazebo --verbose -s libgazebo_ros_factory.so然后新开一个终端运行ros2 topic list如果能看到/clock、/parameter_events、/rosout这些话题说明Gazebo已经成功把ROS2节点跑起来了。这一步没通过的话后面一切免谈。2.3 不妨先用TurtleBot3验证链路如果你连URDF都还没写先跑一下TurtleBot3的Gazebo仿真可以快速确认整条链路通畅sudo apt install ros-humble-turtlebot3-gazebo ros-humble-turtlebot3-teleop export TURTLEBOT3_MODELburger ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py新终端里再运行export TURTLEBOT3_MODELburger ros2 run turtlebot3_teleop turtlebot3_teleop_key如果这个demo能正常用键盘控制那么你的环境装好了90%。剩下的问题集中在自定义URDF上。提示这个验证步骤非常值得做。它能把“环境问题”和“模型问题”快速切割避免你在排查时眉毛胡子一把抓。3. 差分AGV建模用xacro搭出可复用的小车骨架3.1 包结构设计与文件分工我们先创建一个ROS2功能包用来存放机器人的描述文件。这里我建议用ament_python因为后续launch文件、config文件都是Python生态最省事。ros2 pkg create agv_description --build-type ament_python包创建好之后手动添加目录结构agv_description/ ├── CMakeLists.txt ├── package.xml ├── urdf/ │ ├── agv.xacro │ └── agv.gazebo.xacro ├── launch/ │ ├── display.launch.py │ └── gazebo.launch.py ├── config/ │ └── (后续放rviz配置) └── setup.py这里把URDF拆成两个文件的目的是隔离关注点agv.xacro放几何、关节、传动等结构信息agv.gazebo.xacro放Gazebo的插件和物理参数。这样以后换仿真器时结构部分可以直接复用。3.2 轮子、车身、支撑轮的坐标设计差分AGV的坐标设计是整个模型的地基。先明确几个linkbase_footprint在地面上的投影点一般高度为0作为里程计推算的参考系base_link车身主体挂在base_footprint上方wheel_left_link、wheel_right_link左右主动轮caster_link后部从动轮用ball joint或fixed joint都可以简化处理我设计的关键参数如下参数数值说明车身长0.50 mx方向车身宽0.30 my方向车身高度0.10 mz方向轮子半径0.08 m主动轮轮子宽度0.025 m主动轮轮距0.36 m左右轮中心间距支撑轮半径0.03 m从动轮坐标计算的要点是base_link的底面不能直接贴地因为轮子有半径车身必须被轮子撑起来。我的做法是让base_link原点放在车身几何中心轮子中心位于base_link的左右两侧偏下位置。以左轮为例左轮中心相对base_link原点的位置是origin xyz0.11 0.18 -0.055 rpy0 0 0/这个-0.055怎么来的轮子半径0.08m车身底面相对base_link中心在z方向是-0.05轮子中心还要再低一点让轮子露出车身底面同时保证base_footprint到地面的关系正确。3.3 惯量与质量参数别让仿真飘起来很多新手写URDF时只写visual和collision不写inertial或者随便填一个质量。在RViz里看没问题一进Gazebo就出幺蛾子车子要么像纸片一样被弹飞要么倒在地上起不来。原因是Gazebo的物理引擎需要每个link有合理的质量和惯性张量。对于长方体车身的简化惯量计算公式是Ixx m * (w^2 h^2) / 12Iyy m * (l^2 h^2) / 12Izz m * (l^2 w^2) / 12其中m是质量l是长度w是宽度h是高度。假设车身质量3kg长0.5m、宽0.3m、高0.1m那么Ixx 3 * (0.3^2 0.1^2) / 12 3 * 0.1 / 12 0.025Iyy 3 * (0.5^2 0.1^2) / 12 3 * 0.26 / 12 0.065Izz 3 * (0.5^2 0.3^2) / 12 3 * 0.34 / 12 0.085轮子可以近似成圆柱体惯量公式略有不同但直径小、质量轻直接用0.001量级的数值也够用。关键是别漏掉inertial标签让物理引擎有据可依。下面是agv.xacro的核心骨架?xml version1.0? robot nameagv_diff xmlns:xacrohttp://www.ros.org/wiki/xacro xacro:property namewheel_radius value0.08/ xacro:property namewheel_width value0.025/ xacro:property namewheel_base value0.36/ xacro:property namechassis_length value0.5/ xacro:property namechassis_width value0.3/ xacro:property namechassis_height value0.1/ !-- base_footprint -- link namebase_footprint/ joint namebase_joint typefixed parent linkbase_footprint/ child linkbase_link/ origin xyz0 0 0.055 rpy0 0 0/ /joint !-- base_link -- link namebase_link visual geometry box size${chassis_length} ${chassis_width} ${chassis_height}/ /geometry material nameblue color rgba0.2 0.4 0.8 1.0/ /material /visual collision geometry box size${chassis_length} ${chassis_width} ${chassis_height}/ /geometry /collision inertial mass value3.0/ inertia ixx0.025 ixy0.0 ixz0.0 iyy0.065 iyz0.0 izz0.085/ /inertial /link !-- 车轮宏定义 -- xacro:macro namewheel paramsname parent_link xyz rpy link namewheel_${name} visual geometry cylinder radius${wheel_radius} length${wheel_width}/ /geometry material namedark color rgba0.1 0.1 0.1 1.0/ /material /visual collision geometry cylinder radius${wheel_radius} length${wheel_width}/ /geometry /collision inertial mass value0.2/ inertia ixx0.0002 ixy0.0 ixz0.0 iyy0.0002 iyz0.0 izz0.0004/ /inertial /link joint namewheel_${name}_joint typecontinuous parent link${parent_link}/ child linkwheel_${name}/ origin xyz${xyz} rpy${rpy}/ axis xyz0 1 0/ /joint transmission nametransmission_${name} typetransmission_interface/SimpleTransmission/type joint namewheel_${name}_joint hardwareInterfacehardware_interface/VelocityJointInterface/hardwareInterface /joint actuator namemotor_${name} hardwareInterfacehardware_interface/VelocityJointInterface/hardwareInterface mechanicalReduction1/mechanicalReduction /actuator /transmission /xacro:macro xacro:wheel nameleft parent_linkbase_link xyz0.11 0.18 -0.055 rpy0 0 0/ xacro:wheel nameright parent_linkbase_link xyz0.11 -0.18 -0.055 rpy0 0 0/ /robot注意车轮宏里的axis xyz0 1 0表示车轮绕y轴旋转。这是差分AGV的标准配置左右轮旋转方向相反时小车转向。3.4 差速传动的transmission配置transmission是URDF里经常被忽略但非常重要的部分。它把关节和执行器连接起来告诉Gazebo驱动插件哪个关节应该被速度控制。上面的代码里左右轮的transmission都指定了hardware_interface/VelocityJointInterface。这句话的意思是这个关节期望接收速度指令而不是位置指令或力矩指令。差速驱动插件就是通过这个接口去控制轮子转速的。有些教程会省略transmission然后在Gazebo插件里直接指定left_joint也能工作。但如果你想后续接ros2_controltransmission是绕不开的。所以从一开始就规范写好。4. Gazebo Classic下让小车真正动起来物理参数与驱动插件4.1 gazebo_ros_diff_drive插件参数逐项说明模型结构建好后要往agv.gazebo.xacro里写Gazebo插件。差速AGV最核心的就是libgazebo_ros_diff_drive.so。先看完整配置?xml version1.0? robot xmlns:xacrohttp://www.ros.org/wiki/xacro gazebo plugin namediff_drive filenamelibgazebo_ros_diff_drive.so ros namespace//namespace remappingcmd_vel:cmd_vel/remapping remappingodom:odom/remapping /ros left_jointwheel_left_joint/left_joint right_jointwheel_right_joint/right_joint wheel_separation0.36/wheel_separation wheel_diameter0.16/wheel_diameter max_wheel_torque20/max_wheel_torque max_wheel_acceleration5.0/max_wheel_acceleration publish_odomtrue/publish_odom publish_odom_tftrue/publish_odom_tf publish_wheel_tftrue/publish_wheel_tf odometry_frameodom/odometry_frame robot_base_framebase_footprint/robot_base_frame /plugin /gazebo !-- 添加简单颜色到所有link -- gazebo referencebase_link materialGazebo/Blue/material /gazebo gazebo referencewheel_left !-- 注意这里对应实际link名 -- materialGazebo/DarkGrey/material /gazebo /robot逐项说下关键参数的意义left_joint、right_joint指定差速驱动要控制的关节名必须和URDF里的joint name一致。wheel_separation左右轮中心距。这个值必须和URDF里的轮距一致否则转向半径计算会出错。wheel_diameter轮子直径。差速插件需要它把轮子角速度换算成线速度。max_wheel_torque轮子最大扭矩。太小的话稍微加载重一点车身就推不动。max_wheel_acceleration最大角加速度。太小会让起步很肉太大容易抖动。publish_odom_tf由插件自动发布odom - base_footprint的坐标变换省得自己写tf广播节点。robot_base_frame推荐用base_footprint而不是base_link。因为base_footprint是地平面投影和odom的z值更匹配不容易出现高度偏移。4.2 摩擦、阻尼与悬架参数对行走姿态的影响Gazebo的默认物理参数对普通小车来说往往过于“丝滑”轮子容易打滑。一个很有用的配置是在每个轮子的碰撞几何里加摩擦系数。gazebo referencewheel_left mu11.0/mu1 mu21.0/mu2 kp1000000.0/kp kd100.0/kd /gazebomu1是沿接触面运动方向的摩擦系数mu2是垂直方向的侧向摩擦系数。对差速AGV来说两个方向最好都大一点否则转弯时轮子会横向滑动。我实测mu11.0、mu21.0是比较稳的起点值。kp和kd是接触刚度与阻尼。默认值在有些版本里不够大轮子会轻微陷入地面或者跳动。如果看到模型在Gazebo里像在蹦床一样上下颠簸优先调大这两个值。如果车身弹跳明显还有一个简易方法把base_footprint - base_link的fixed joint改成continuous或floating不会解决物理问题正确做法是给base_link加一个gazebo标签里的implicitSpringDamper或者对应轮子joint增加阻尼。不过对新手我的建议是先把kp、kd调好其他先不动。4.3 一键启动launch文件里该写什么有了URDF和插件下一步要写launch文件把Gazebo、模型加载、RViz串起来。gazebo.launch.py内容如下import os from launch import LaunchDescription from launch.actions import DeclareLaunchArgument, ExecuteProcess, IncludeLaunchDescription from launch.conditions import IfCondition from launch.launch_description_sources import PythonLaunchDescriptionSource from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node from ament_index_python.packages import get_package_share_directory import xacro def generate_launch_description(): pkg_share get_package_share_directory(agv_description) urdf_model_path os.path.join(pkg_share, urdf, agv.xacro) robot_desc xacro.process_file(urdf_model_path).toxml() return LaunchDescription([ Node( packagerobot_state_publisher, executablerobot_state_publisher, parameters[{robot_description: robot_desc}], outputscreen ), ExecuteProcess( cmd[gazebo, --verbose, -s, libgazebo_ros_factory.so], outputscreen ), Node( packagegazebo_ros, executablespawn_entity.py, arguments[-topic, robot_description, -entity, agv], outputscreen ), ])这里有三个关键节点robot_state_publisher把URDF里的关节状态发布到ROS2同时根据fixed joint发布base_footprint - base_link的tf。gazebo进程以libgazebo_ros_factory.so插件启动让Gazebo具备通过ROS2话题生成实体的能力。spawn_entity.py从/robot_description话题读取URDF把模型生成到Gazebo世界里。编译并运行colcon build --packages-select agv_description source install/setup.bash ros2 launch agv_description gazebo.launch.py如果一切正常Gazebo窗口里会出现一辆蓝色车身、黑色轮子的小车。5. 键盘控制链路/cmd_vel与teleop_twist_keyboard的配合方式5.1 启动teleop并正确remap话题模型已经在Gazebo里站住了接下来接入键盘控制。ROS2官方提供了现成的teleop_twist_keyboard节点它会读取键盘输入发布geometry_msgs/msg/Twist消息。默认情况下teleop_twist_keyboard发布的话题名是/cmd_vel正好和我们的差速插件默认接收话题一致。所以最简启动方式就是ros2 run teleop_twist_keyboard teleop_twist_keyboard但如果你在一个复杂系统里多个机器人可能有不同的cmd_vel话题或者你的差速插件做了命名空间隔离就需要remap。例如把键盘节点的话题重映射到agv/cmd_velros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r cmd_vel:agv/cmd_vel启动后终端会显示按键说明Reading from the keyboard and Publishing to Twist! --------------------------- Moving around: u i o j k l m , . For Holonomic mode (strafing), hold down the shift key: ---------------------------按i键小车前进按,键后退按j和l左右转按k停止。这个按键布局继承自ROS1时代对差速AGV很顺手。5.2 /cmd_vel速度指令的坐标系约定在ROS2里Twist消息的线性速度linear.x表示沿机器人自身坐标系x轴的前进速度角速度angular.z表示绕z轴的旋转速度。差速驱动插件的内部计算逻辑是左轮速度 (v - ω * d / 2) / r右轮速度 (v ω * d / 2) / r其中v是线速度ω是角速度d是轮距r是轮子半径。也就是说当你按j键左转时teleop会发布一个linear.x为0、angular.z为正值的Twist插件收到后会让左轮速度降低、右轮速度升高实现逆时针旋转。这是差速AGV的标准运动学。理解这个关系很重要。如果你发现按前进键时车子反而后退或者按左转时车子右转不要急着改代码先检查两个轮子是否接反了以及URDF里左右轮的joint名和插件里的left_joint、right_joint是否对应。5.3 RVizGazebo双窗口验证方法键盘控制跑起来后打开RViz看tf和里程计是验证模型正确性的高效手段。ros2 run rviz2 rviz2在RViz里添加RobotModel显示看到小车模型再添加TF显示查看各个坐标系的变换关系。正常情况你应该能看到odom - base_footprint的变换由差速插件发布base_footprint - base_link的固定变换由robot_state_publisher发布base_link - wheel_left_link、base_link - wheel_right_link这是因为publish_wheel_tftrue这时按键盘的i键RViz里的小车会向前移动Gazebo里的小车也会同步移动。如果两个窗口都正常说明整条链路已经完整打通。我还习惯在同一个终端用ros2 topic echo /odom观察里程计数据确认linear.x和angular.z的变化和操作对应。这样能提前发现轮子接反、坐标系方向错误等问题。6. 实测过程中我踩过的坑与排查思路6.1 模型陷进地面从“悬浮”到“陷落”的坐标问题第一次用自定义URDF加载到Gazebo时最常见的现象是模型陷进地面或者轮子陷一半、车身跷跷板一样卡住。这个问题的根因基本都在base_footprint到base_link的高度设计上。我的设计里base_footprint的z0表示地面base_link原点在轮子中心高度附近。轮子半径0.08m而base_joint的origin z0.055意味着base_link底部在base_footprint上方0.005m。这个“刚好不擦地”的高度是经过计算的。如果你发现陷进地面可以按这个顺序排查检查base_footprint - base_link的z值是否为正值且大于等于车身底面相对高度检查wheel_left_joint、wheel_right_joint的z值让轮子中心和base_link原点的z差接近轮子半径在RViz里先看一遍模型确认几何关系后再进Gazebo注意RViz里模型显示正常不代表Gazebo物理正确。RViz只画几何不管碰撞Gazebo则会对每个collision做碰撞检测。所以URDF里collision的尺寸和位置必须和visual一致否则会出现“看得见摸不着”的诡异情况。6.2 车子只转不走差速插件参数不匹配另一个高频问题是按前进键车子原地打转不走直线。我当时排查了很久最后发现是wheel_separation参数填错了。URDF里左右轮中心在y方向分别是0.18和-0.18轮距是0.36m但我在插件里误填成了0.3导致运动学解算出来的左右轮速度比例错误车子一边转圈一边走。解决方法是确保URDF里的轮距和插件里的wheel_separation完全一致。最简单的方式是用同一个xacro变量在两个地方引用避免硬编码不一致。wheel_separation直接决定差速解算的角速度比例这个值不对线速度和角速度的耦合关系就会出问题。新手往往忽略它以为只是个标定参数实际上它必须和模型几何严格对应。6.3 键盘没反应先从话题连接查起“按键盘没反应”这种情况通常不是控制器问题而是话题没接上。我的排查顺序是这样的先确认teleop节点是不是真的在发布ros2 topic echo /cmd_vel按键盘时看有没有Twist消息刷出来。没有输出说明teleop没启动成功或话题名不对。确认差速插件是否订阅了正确的/cmd_velros2 node info /gazebo在订阅列表里找/cmd_vel。如果插件在某个命名空间里话题名可能会变成/namespace/cmd_vel。确认机器人模型确实被spawn进Gazebo了。有时候spawn_entity报错但launch不中断Gazebo里其实没有模型自然不会有任何运动。在Gazebo窗口里看是否有模型出现或者运行ros2 service call /spawn_entity gazebo_msgs/srv/SpawnEntity {name: agv}检查差速插件是否加载成功。启动Gazebo时加--verbose如果插件加载失败终端会打印Failed to load plugin之类的错误。6.4 车轮抖动与起步无力摩擦和加速度的平衡Gazebo里小车起步时明显抖动或者前进速度忽快忽慢这个问题的根源往往在于物理参数不够合理。先说抖动。如果max_wheel_acceleration设置过大轮子的速度指令变化太快contact solver跟不上就会出现高频抖动。我后来把max_wheel_acceleration从默认的10.0降到5.0抖动明显改善。再说起步无力。如果max_wheel_torque设置太小电机力矩不足以克服摩擦小车就会原地不动或者龟速爬行。我用于3kg车身、0.16m轮径的小车max_wheel_torque20是够用的。如果你的车更重可以适当加到50。此外还有一个隐藏因素kp、kd接触参数过低会导致轮子与地面之间的摩擦力不够起步时轮子空转。如果你看到轮子转但车不动优先检查mu1、mu2有没有配置上。下面是我最终稳定使用的Gazebo物理参数参考参数数值作用mu11.0纵向摩擦mu21.0横向摩擦kp1000000.0接触刚度kd100.0接触阻尼max_wheel_torque20.0轮子最大扭矩max_wheel_acceleration5.0轮子最大角加速度6.5 坐标变换冲突odom和map同时出现时的tf树最后说一个进阶但常见的问题。如果你未来接入SLAM或Nav2会同时出现map、odom、base_footprint三个坐标系。此时如果差速插件的publish_odom_tftrue没问题但如果你又启动了Nav2的robot_localization或者手写tf广播就可能出现odom - base_footprint变换被重复广播的冲突。我在后续做Nav2的时候遇到过这个问题症状是RViz里tf树不断报错机器人位置乱跳。解决方法是明确职责odom到base_footprint的tf只由差速插件或里程计节点发布不要两处都发。如果你用robot_localization融合IMU和轮式里程计最好把差速插件的publish_odom_tf设为false让robot_localization统一发布。这个坑虽然不是本篇键盘控制必须处理的问题但既然做AGV模型迟早会遇到。提前在URDF里留好开关比到时候大改省事很多。7. 从仿真到实体micro-ROS与ESP32的扩展思路7.1 仿真的终点不是停在仿真键盘控制跑通之后这个模型就成了一个比较完整的“数字底座”。给它加激光雷达模型就可以做SLAM加Nav2就可以做自主导航加到多机器人仿真里就可以测试调度算法。仿真模型的代码结构、坐标系设计、话题定义和实体小车基本一致。我个人的经验是仿真阶段一定要把话题名、坐标系、机器人描述集中管理形成规范。后面接实体车时你只需要把Gazebo插件替换成真实的电机驱动节点其余基本不动。7.2 把差速控制迁移到ESP32的管线如果你和我一样最终想把模型跑在真实小车上一个很顺的路线是用ESP32 micro-ROS。micro-ROS可以把ROS2的/cmd_vel订阅能力移植到单片机上让ESP32直接接受键盘节点发来的Twist消息然后输出PWM给电机驱动板。这个扩展里仿真部分给你的最大财富是运动学公式已经被验证过了。ESP32上的代码要做的只是接收线速度和角速度按差速公式算左右轮转速再转换成PWM占空比。公式一模一样只是执行器从Gazebo里的虚拟关节变成了真实电机。如果你已经跑到这一步强烈建议把URDF里的transmission和ros2_control配置保留着。真实小车如果用ros2_control框架URDF里的关节描述可以直接复用不会白写。7.3 一个值得保留的工程习惯无论你最后做的是仿真还是实体我建议你从第一天就给自己的AGV建立一个“配置中心”意识xacro里的尺寸参数统一用xacro:property定义launch里只用xacro变量不硬编码所有话题名只在一个地方配置命名空间标准化这样做的直接好处是昨天还在改轮距今天换个电机明天加个传感器不用翻遍整个工程找数字。我这台AGV从0.36m轮距改成0.42m时只改了xacro里的一行Gazebo插件、Nav2参数自动适配省下的时间非常可观。最后再分享一个小技巧启动Gazebo之前先跑一次xacro agv.xacro agv.urdf把生成的URDF打开看一眼。很多模型加载失败的问题在URDF里都能一眼看出来——比如某个link的坐标明显在天上某个joint的parent写错了。养成这个习惯你会少走很多弯路。
返回列表