资讯动态

基于ROS2和Gazebo Classic的差分驱动AGV建模与键盘控制仿真

发布时间:2026/10/5 4:09:57 来源:尧图企业网站定制
很多刚接触ROS2的朋友心里装的都是“让一台小车动起来”这件事但卡在第一步就不知道该干嘛URDF怎么搭、Gazebo怎么进、为什么按了键盘没反应。这篇教程就把我实际跑通的一套流程完整写下来用ROS2 Humble配Gazebo Classic从零手写一个差分驱动AGV模型然后用键盘控制它在仿真环境里跑起来。适合刚入门ROS2的开发者、做移动机器人毕设的学生以及想在买硬件之前先把控制逻辑跑通的工程师。看完你就能自己复现也能在后面换车体、换传感器。1. 环境准备先把ROS2 Humble和Gazebo Classic配好1.1 ROS2 Humble安装与系统要求要做这个项目系统我建议直接用Ubuntu 22.04 LTS因为ROS2 Humble就是官方针对22.04发布的版本依赖关系最干净。Windows和macOS虽然能装Docker版但后面跟Gazebo的图形界面、硬件加速打交道时问题很多不建议新手折腾。安装命令其实就两条先加ROS2官方源sudo apt update sudo apt install software-properties-common sudo add-apt-repository universe sudo apt update sudo apt install curl -y sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release echo $UBUNTU_CODENAME) main | sudo tee /etc/apt/sources.list.d/ros2.list /dev/null然后安装桌面版sudo apt update sudo apt install ros-humble-desktop python3-colcon-common-extensions这里我强烈建议装上python3-colcon-common-extensions后面编译工作空间要用colcon别等到要编译了再补。装完记得把环境变量写进bashrc否则每次开终端都要手动sourceecho source /opt/ros/humble/setup.bash ~/.bashrc source ~/.bashrc验证一下ros2 --version能输出版本号就算成了。这台机器如果配置一般虚拟机里跑也没问题但Gazebo吃显卡能开3D加速就开3D加速别让小车卡成PPT。1.2 Gazebo Classic版本选择与安装这里有个特别容易踩的坑ROS2 Humble默认的“gazebo”软件包已经变成了新版的Gazebo现在叫Gazebo Fortress/Ignition但网上大量教程、插件、URDF配置都是围绕Gazebo Classic写的包括我们现在要用的差速驱动插件gazebo_ros_diff_drive也是Classic时代的产物。所以这个教程明确用Gazebo Classic 11。安装方式是通过ROS2的元包sudo apt install ros-humble-gazebo-ros-pkgs这个包会拉来gazebo11、gazebo_ros、gazebo_plugins这些关键组件。装完先启动一下看看能不能正常打开图形界面ros2 launch gazebo_ros gazebo.launch.py如果能看到一个空荡荡的灰色世界窗口并且终端不报错说明环境OK。第一次启动可能会卡几秒那是在加载模型库后面会逐渐快起来。1.3 后面要用的工具包一次装齐在动手写代码之前我还建议先把工具装齐免得做一步装一步思路容易断sudo apt install ros-humble-xacro ros-humble-robot-state-publisher ros-humble-joint-state-publisher ros-humble-teleop-twist-keyboard sudo apt install ros-humble-rviz2 ros-humble-gazebo-ros2-controlxacro用来写参数化的URDF模板robot_state_publisher负责发布TF变换joint_state_publisher在纯URDF没有Gazebo插件时用来驱动可动关节teleop_twist_keyboard就是我们最终用来控制小车的键盘工具。单独装这些包看起来不起眼但缺一个后面都会报莫名其妙找不到节点的错误。提示如果你用的是Docker或镜像环境记得检查容器有没有GPU支持和图形转发否则Gazebo窗口大概率起不来。2. 先摸清楚原理差分驱动模型和URDF的核心概念2.1 差分驱动AGV是怎么转向的差分驱动说白了就是左右两个轮子独立驱动通过两轮速度差来实现转向。完全同速就直行左轮比右轮快就向右转反过来向左转一正一反就是原地旋转。这种结构的AGV成本低、结构简单在室内仓储物流里非常常见也特别适合入门。在ROS2里速度指令通过geometry_msgs/msg/Twist消息发布到/cmd_vel话题消息里有linear.x线速度和angular.z角速度两个最关键的字段。你按一下键盘的“i”键teleop节点就会往/cmd_vel发一个linear.x0.5, angular.z0.0的消息小车就直行按“j”键则发linear.x0.0, angular.z0.5的消息小车就原地左转。底盘控制器收到之后会根据模型的几何参数换算成左右轮的目标速度v_left v - (w * wheel_separation) / 2 v_right v (w * wheel_separation) / 2这里的wheel_separation是左右轮中心距v是线速度w是角速度。这个公式在后面配置插件参数时还会用到你先有个印象。2.2 URDF里的link和joint到底在描述什么URDFUnified Robot Description Format是一个XML格式的机器人描述文件它用两个核心概念描述一台机器人link刚体部件比如底盘、左轮、右轮、传感器支架。每个link都有视觉外观visual、碰撞体积collision和惯性参数inertial。jointlink之间的连接关系描述父link和子link之间的相对位置和旋转关系以及关节类型fixed、continuous、revolute、prismatic、floating。对一台AGV来说我一般按这样的层次结构去搭建base_link车体底盘 ├── left_wheel_link左驱动轮 ├── right_wheel_link右驱动轮 ├── front_caster_link前从动万向轮 └── back_caster_link后从动万向轮这里有个很重要的坐标系约定base_link应位于车体对称中心、地面高度处这样后面的里程计和TF树才不会出现奇怪的偏移。轮子和底盘的连接关系用joint描述每个joint需要定义parent_link、child_link和origin相对位置和旋转驱动轮关节类型是continuous因为轮子可以无限旋转从动万向轮为了简化处理一般用fixed或者带旋转的continuous都行看你要做到什么程度。2.3 为什么用xacro而不是直接写纯URDF纯URDF文件写起来极其啰嗦。你要写4个轮子每个轮子的visual、collision、inertial全都要写一遍而且轮子半径、宽度这些参数如果后面要改你得全局搜索替换改漏一处就是灾难。xacro是URDF的“高级模板语言”支持属性property定义常量比如轮距、轮径、车体长宽高。宏macro把重复结构抽象成可复用的函数比如定义“一个轮子”的宏然后传入不同参数调用四次。数学表达式可以在标签属性里写${base_width/2}之类的表达式自动计算。后面的代码我会全部用xacro来写。这不是炫技是让你后面调参数时能少掉一半头发。3. 搭建AGV模型手写URDF/xacro的核心实操3.1 创建工作空间和ROS2功能包先建一个工作空间名字随意但建议用带下划线的小写形式。终端依次执行mkdir -p ~/agv_ws/src cd ~/agv_ws/src ros2 pkg create agv_bringup --build-type ament_cmake --dependencies rclcpp std_msgs geometry_msgs这里我给包名用了agv_bringup意思是“启动整个AGV系统”的包。--build-type ament_cmake是因为我们后面要往包里放launch文件和URDF文件CMakeLists需要被正确配置才能把这些文件安装到install目录。接下来在包目录里创建需要的子目录cd agv_bringup mkdir -p urdf launch config meshesurdf放模型文件launch放启动脚本config放参数配置meshes放3D模型我们这次用纯几何体表示不需要mesh文件但目录先留着。3.2 编写AGV模型的xacro文件在urdf/agv.xacro里写入模型。我先给一个完整可用的版本然后逐段解释。?xml version1.0? robot nameagv xmlns:xacrohttp://www.ros.org/wiki/xacro !-- 全局参数定义 -- xacro:property namebase_length value0.5 / xacro:property namebase_width value0.36 / xacro:property namebase_height value0.1 / xacro:property namebase_ground_clearance value0.05 / xacro:property namewheel_radius value0.1 / xacro:property namewheel_width value0.04 / xacro:property namewheel_separation value0.3 / xacro:property namecaster_radius value0.025 / xacro:property namecaster_offset_x value0.2 / xacro:property namecaster_offset_y value0.15 / xacro:property namebody_mass value5.0 / xacro:property namewheel_mass value0.5 / !-- 材质颜色定义 -- material nameblue color rgba0.1 0.1 0.8 1.0 / /material material namegray color rgba0.4 0.4 0.4 1.0 / /material material nameblack color rgba0.1 0.1 0.1 1.0 / /material !-- 底盘link -- link namebase_link visual geometry box size${base_length} ${base_width} ${base_height} / /geometry origin xyz0 0 ${base_ground_clearance base_height/2} rpy0 0 0 / material nameblue / /visual collision geometry box size${base_length} ${base_width} ${base_height} / /geometry origin xyz0 0 ${base_ground_clearance base_height/2} rpy0 0 0 / /collision inertial mass value${body_mass} / inertia ixx0.1 ixy0.0 ixz0.0 iyy0.1 iyz0.0 izz0.1 / /inertial /link !-- 驱动轮宏定义 -- xacro:macro namedriven_wheel paramsprefix parent_link x_pos y_pos link name${prefix}_wheel_link visual geometry cylinder radius${wheel_radius} length${wheel_width} / /geometry origin xyz0 0 0 rpy${pi/2} 0 0 / material nameblack / /visual collision geometry cylinder radius${wheel_radius} length${wheel_width} / /geometry origin xyz0 0 0 rpy${pi/2} 0 0 / /collision inertial mass value${wheel_mass} / inertia ixx0.001 ixy0.0 ixz0.0 iyy0.001 iyz0.0 izz0.001 / /inertial /link joint name${prefix}_wheel_joint typecontinuous parent link${parent_link} / child link${prefix}_wheel_link / origin xyz${x_pos} ${y_pos} ${base_ground_clearance wheel_radius} rpy0 0 0 / axis xyz0 1 0 / /joint /xacro:macro !-- 调用宏生成左右驱动轮 -- xacro:driven_wheel prefixleft parent_linkbase_link x_pos0.0 y_pos${wheel_separation/2} / xacro:driven_wheel prefixright parent_linkbase_link x_pos0.0 y_pos${-wheel_separation/2} / !-- 从动万向轮宏定义 -- xacro:macro namecaster_wheel paramsprefix parent_link x_pos y_pos link name${prefix}_caster_link visual geometry sphere radius${caster_radius} / /geometry material namegray / /visual collision geometry sphere radius${caster_radius} / /geometry /collision inertial mass value0.1 / inertia ixx0.0001 ixy0.0 ixz0.0 iyy0.0001 iyz0.0 izz0.0001 / /inertial /link joint name${prefix}_caster_joint typefixed parent link${parent_link} / child link${prefix}_caster_link / origin xyz${x_pos} ${y_pos} ${base_ground_clearance caster_radius} rpy0 0 0 / /joint /xacro:macro !-- 调用宏生成前后从动轮 -- xacro:caster_wheel prefixfront parent_linkbase_link x_pos${caster_offset_x} y_pos${caster_offset_y} / xacro:caster_wheel prefixback parent_linkbase_link x_pos${-caster_offset_x} y_pos${caster_offset_y} / /robot这个文件的几个关键点你需要特别留意。坐标系原点决定车能不能站稳。所有轮子的joint原点其z坐标都等于base_ground_clearance wheel_radius也就是让轮子最底部刚好和地面接触。如果你的base_ground_clearance设得和wheel_radius不匹配车在Gazebo里要么悬空要么陷进地里。这是我见过最多的翻车原因之一。rpy${pi/2} 0 0是让圆柱体轮子绕x轴旋转90度。因为URDF里的cylinder默认轴线是z轴但轮子应该绕y轴旋转即圆柱轴线朝y方向所以需要绕x轴转90度否则轮子是竖直立着的像一枚硬币而且会和你期望的运动方向完全不同。inertial是必须写的别偷懒。在纯URDF显示阶段不写inertial可能勉强能跑但一进Gazebo物理仿真缺inertial的link会被当成质量为零或被强制赋一个默认惯性值轻则警告刷屏重则小车原地起飞。如果想算更精确的惯性张量可以按规则几何体的公式算对实心圆柱I_zz 0.5 * m * r^2I_xx I_yy 1/12 * m * (3*r^2 h^2)。我给的0.001是近似值你按真实尺寸算会更稳。3.3 给模型配上Gazebo差速驱动插件这一步是整个项目的灵魂。没有插件模型就只是一堆静止的几何体加了差速驱动插件它才能真正接收/cmd_vel驱动轮子转动并在物理世界里面移动。在xacro文件末尾添加gazebo plugin namediff_drive_controller filenamelibgazebo_ros_diff_drive.so ros namespace//namespace remappingcmd_vel:cmd_vel/remapping /ros left_jointleft_wheel_joint/left_joint right_jointright_wheel_joint/right_joint wheel_separation${wheel_separation}/wheel_separation wheel_diameter${2 * wheel_radius}/wheel_diameter publish_odomtrue/publish_odom publish_odom_tftrue/publish_odom_tf odom_frame_idodom/odom_frame_id robot_base_framebase_link/robot_base_frame max_wheel_torque20/max_wheel_torque max_wheel_acceleration1.0/max_wheel_acceleration /plugin /gazebo这里逐个解释参数left_joint/right_joint告诉插件哪个joint是左驱动轮、哪个是右驱动轮。名字必须和URDF中定义的完全一致包括前缀。wheel_separation左右轮中心距和前面公式里的wheel_separation对应。插件要靠这个参数把/cmd_vel的线速度和角速度换算成左右轮各自的转速。如果这个值和你URDF里的实际轮距对不上小车转弯半径就不对。wheel_diameter轮子直径插件要用这个来换算“轮子转速”和“小车前进线速度”之间的关系。注意这里是直径别填成半径。publish_odom/publish_odom_tf发布里程计消息和odom - base_link的TF变换。如果后面你打算做导航或者SLAM这两项必须打开。odom_frame_id/robot_base_frameTF树中里程计坐标系和机器人基座的坐标系名字。通常就是odom和base_link。max_wheel_torque最大轮扭矩。设得太小车可能推不动自己尤其在地面摩擦系数比较大的时候设得太大瞬间加速太猛小车容易原地跳或者打滑。20对于桌面小车足够。max_wheel_acceleration最大轮加速度。这个值决定小车启动和停止的平顺程度1.0大概是每秒钟轮速允许变化1 rad/s实际手感比较稳。重要namespace//namespace是让插件的所有话题都挂在根命名空间。如果你发现/cmd_vel话题名不对优先检查这里和remapping标签是否写对。很多时候键盘按了没反应80%的问题出在这个地方——话题对不上。3.4 在RViz里先检查一下模型模型写好了先在RViz里看不进Gazebo方便排查坐标系和外观问题。在包目录下创建一个最小launch文件launch/display.launch.pyfrom launch import LaunchDescription from launch_ros.actions import Node from launch.substitutions import Command, FindExecutable from launch_ros.parameter_descriptions import ParameterValue import os def generate_launch_description(): urdf_path os.path.abspath(os.path.join( os.path.dirname(__file__), .., urdf, agv.xacro)) robot_desc ParameterValue( Command([FindExecutable(namexacro), , urdf_path]), value_typestr) return LaunchDescription([ Node( packagerobot_state_publisher, executablerobot_state_publisher, parameters[{robot_description: robot_desc}], outputscreen ), Node( packagejoint_state_publisher, executablejoint_state_publisher, outputscreen ), Node( packagerviz2, executablerviz2, outputscreen ), ])然后编译并运行cd ~/agv_ws colcon build --packages-select agv_bringup source install/setup.bash ros2 launch agv_bringup display.launch.pyRViz2打开后在左侧面板把Fixed Frame设为base_link然后点Add添加RobotModel插件就能看到你写的小车了。如果模型没出现检查终端有没有xacro报错如果轮子悬空或者位置不对回去检查joint的origin坐标。4. 把AGV请进Gazebo世界4.1 编写一个真正的启动launch文件display.launch.py只是一个预览用的轻量launch。要进Gazebo我们需要同时启动三个东西Gazebo服务端和客户端gazebo_ros的spawn_entity节点用来把URDF模型“放置”到仿真世界中robot_state_publisher发布机器人的描述和TF在launch/gazebo.launch.py里写import os from launch import LaunchDescription from launch.actions import ExecuteProcess from launch.substitutions import Command, FindExecutable, LaunchConfiguration from launch_ros.actions import Node from launch_ros.parameter_descriptions import ParameterValue from ament_index_python.packages import get_package_share_directory from launch_ros.actions import SetParameter def generate_launch_description(): pkg_share get_package_share_directory(agv_bringup) urdf_path os.path.join(pkg_share, urdf, agv.xacro) robot_desc ParameterValue( Command([FindExecutable(namexacro), , urdf_path]), value_typestr) return LaunchDescription([ # 启动Gazebo服务端和客户端 ExecuteProcess( cmd[gazebo, --verbose, -s, libgazebo_ros_factory.so], outputscreen, additional_env{GAZEBO_MODEL_PATH: pkg_share} ), # 发布机器人描述和TF Node( packagerobot_state_publisher, executablerobot_state_publisher, parameters[{robot_description: robot_desc}], outputscreen ), # 将机器人创建到Gazebo世界 Node( packagegazebo_ros, executablespawn_entity.py, arguments[-entity, agv, -topic, robot_description], outputscreen ), ])这里spawn_entity.py -topic robot_description表示从/robot_description话题拿模型描述这个话题正是robot_state_publisher发布的。-entity agv是给仿真中的实体起名你可以随意改但别和已有实体重名。编译运行cd ~/agv_ws colcon build --packages-select agv_bringup source install/setup.bash ros2 launch agv_bringup gazebo.launch.py如果一切正常Gazebo窗口会打开世界中央会出现在地面上的蓝色小车左右两个黑轮子和前后灰色万向轮。4.2 检查话题、TF和机器人状态小车在Gazebo里安静趴着不代表一切正常我每次启动后都会按顺序检查这几项ros2 topic list至少能看到/cmd_vel、/odom、/scan如果后面加雷达、/robot_description、/joint_states、/clock这些话题。重点看/cmd_vel和/odom在不在。ros2 topic echo /odom --once能看到odom里带着位置和姿态数据虽然现在都是0但说明插件已经实时发布里程计了。ros2 run rqt_tf_tree rqt_tf_tree打开TF树面板应该能看到odom - base_link - left_wheel_link / right_wheel_link / front_caster_link / back_caster_link这样一条完整链条。如果链条断裂机器人会在RViz里显示得乱七八糟排查思路通常是joint名字是否匹配、robot_state_publisher是否启动、插件里的robot_base_frame是否写对。4.3 常见问题模型掉落、翻车、卡住不动进了Gazebo之后问题就开始五花八门了我说几个我当年遇到的模型直接掉出世界或者穿透地面。一般就是collision没有定义好或者定义了但位置和visual不一致。Gazebo里用的是collision做碰撞检测如果你只写了visual没写collision模型就不具备物理实体直接往下掉。另外确认base_link底面的高度和base_ground_clearance的对应关系别让底盘面悬空太多。小车原地打滑轮子赚得飞起但车不走。这是摩擦参数的锅。在gazebo标签里给轮子link加摩擦参数比如gazebo referenceleft_wheel_link mu11.0/mu1 mu21.0/mu2 kp1000000.0/kp kd100.0/kd /gazebomu1和mu2是纵向/侧向摩擦系数给个1.0左右一般就够。如果完全没写摩擦轮子和地面之间近似于光滑冰面你怎么给油门车都只是原地打转。小车抖动翻滚。大概率是inertial写得太离谱或者轮子joint的axis写错方向。轮子绕y轴转axis xyz0 1 0别手滑写成1 0 0。5. 键盘控制让AGV按你的心意动起来5.1 安装和运行teleop_twist_keyboard现在到了最有成就感的阶段——按键盘让小车跑起来。ROS2有一个专门用于键盘控制的基础工具teleop_twist_keyboard我们在环境准备阶段已经装好了。直接运行ros2 run teleop_twist_keyboard teleop_twist_keyboard终端会打印一张按键映射表Reading from the keyboard and Publishing to Twist! --------------------------- Moving around: u i o j k l m , . q/z : increase/decrease max speeds by 10% w/x : increase/decrease only linear speed by 10% e/c : increase/decrease only angular speed by 10%按键逻辑很简单i前进,后退j左转l右转k停止u / o / m / .带弧线的斜向运动你按一下i终端会持续发布前进指令直到你按k或者松手其实这里不是“持续按住”每按一次都会发一条速度消息仿真里小车会一直保持那个速度所以按了i之后记得按k停下不然小车会一路狂奔撞出世界边界。5.2 打通数据流从键盘到轮子现在我们来验证整个数据链路。先开一个终端看小车当前运动状态ros2 topic echo /cmd_vel然后另一个终端运行teleop按下i键。你会看到/cmd_vel不断出现linear: x: 0.5 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0同时看Gazebo窗口小车应该开始向前移动轮子在转。如果/cmd_vel有消息但小车不动按这个顺序排查用ros2 topic list确认话题存在且名字是/cmd_vel。用ros2 topic info /cmd_vel看发布者和订阅者数量。发布者应该是两个teleop和gazebo插件里的diff_drive订阅者至少有一个gazebo插件。如果只有发布者没有订阅者说明插件没在听这个话题。检查URDF里插件的remapping和namespace配置确认cmd_vel话题路径一致。看Gazebo终端有没有报错比如找不到joint、找不插件之类。5.3 调节速度参数找到合适的操控手感teleop默认线速度0.5 m/s、角速度1.0 rad/s对室内小型AGV来说有点快。我一般会把线速度调到0.3左右角速度0.8左右这样操控起来不容易冲过头。可以在启动时直接指定ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -p linear.x:0.3 -p angular.z:0.8也可以在程序运行中用q/z、w/x、e/c来实时加减速分别对应整体速度、线速度、角速度各加减10%。这个在仿真里调手感非常方便你一边按方向键一边按q/z就行。心得键盘控制在仿真里跑起来之后一定要测试下“原地旋转”动作。因为差速AGV的灵活性核心就是能零半径转弯如果原地旋转很顺滑说明wheel_separation和左右轮速度换算是正确的如果转起来画圈多半是轮距写错了。6. 从仿真到实机的桥接思考与常见问题排查6.1 仿真和实机之间到底差了什么很多朋友跑通仿真之后会问我现在能直接把这套代码搬到实车上吗答案是“逻辑可以参数要重新调”。仿真里Gazebo帮你把物理引擎、摩擦、惯性的烂摊子都接住了但实机上有几个东西是仿真里没有的真实的电机驱动器和PID闭环。仿真里电机说转就转但实机电机需要驱动板、编码器反馈、速度环/电流环。微控制器和ROS2的通信桥梁。这时候就要用到micro-ROS在STM32、ESP32这类单片机上跑一个micro-ROS agent通过串口或WiFi和上位机运行ROS2 Humble的设备通信订阅/cmd_vel话题解码之后控制电机。热词里出现的docker microros ros2 humble vscode platformio esp32其实就是这一挂的典型组合用VSCodePlatformIO写ESP32固件再借助Docker起micro-ROS agent通过串口桥接ROS2和单片机。里程计漂移和轮子打滑。仿真里里程计是理想值实况里轮子一打滑里程计立刻漂。所以实机项目通常要加IMU或者雷达做融合校正。6.2 建模和调试过程中的高频坑位整理我把自己和学员在实际操作中频繁踩的坑整理成了一张速查表建议收藏现象可能原因解决方案小车轮子转但车不动轮子link没写摩擦参数在wheel的gazebo reference块里加mu1/mu2启动后模型直接掉出世界collision标签缺失或位置错误检查每个link是否定义collision且大小位置覆盖完整键盘按了没反应/cmd_vel有消息插件namespace或remapping不对检查插件namespace//namespace和topic名小车在Gazebo里抖动、弹跳inertial参数不合理或collision面重叠检查link质量、惯性张量避免多个碰撞体互相穿插TF树断了odom到base_link之间缺少连接插件publish_odom_tf没打开或robot_base_frame写错确保插件配置中publish_odom_tf为true且robot_base_frame为base_linkGazebo启动后又退出没有窗口环境变量或DISPLAY问题检查显示环境在纯服务器上用gzserver配合headless模式xacro解析报错宏名或参数引用错误单独跑xacro urdf/agv.xacro定位错误行编译launch时找不到资源包没install或CMakeLists没安装文件运行colcon build并source检查CMakeLists的install指令其中“Gazebo启动后又退出”这一条在Docker或者SSH远程开发环境中特别常见。如果你用非图形化环境可以在launch里把gazebo的GUI关掉用-s libgazebo_ros_factory.so配合--headless模式跑服务端再用远程RViz2来观察。但这属于进阶玩法新手还是老老实实在本地图形界面下操作。6.3 后续怎么扩展这个AGV模型基础版跑通之后扩展方向其实非常广你可以根据自己的项目需求往上加东西加一颗激光雷达在URDF里加一个laser_link在某个位置挂上一个LaserScan传感器模型配置Gazebo的gazebo_ros_ray_sensor插件就能输出/scan话题配合Nav2做自主导航。加相机在车头加一个RGB相机link挂载gazebo_ros_camera插件输出图像话题就能做视觉识别或者视觉SLAM。换麦克纳姆轮把驱动轮从差分换成四轮独立驱动插件从gazebo_ros_diff_drive换成gazebo_ros_skid_steer_drive或者直接用ros2_control的ackermann_steering_controller对应模型运动学完全不同。加入ros2_control框架用gazebo_ros2_control插件替代简单的diff_drive插件用controller_manager管理多个controller比如joint_state_broadcaster、diff_drive_controller这样仿真代码和实机代码的结构会非常接近后面移植实机少走弯路。我个人建议如果你最终目标是实机URDF模型和launch文件尽量一次性写成ros2_control风格而不是完全依赖gazebo_ros_diff_drive这种一把梭的插件。两者达到的仿真效果差不多但架构和实机差距很大。后者更适合快速Demo和入门理解前者适合做完整项目。我自己做这个项目时最大的体会是仿真看着是“假的”但它逼着我把每个坐标、每个joint、每一条话题关系都搞清楚。URDF里一个轴写错了轮子就跑飞插件里一个namespace没对齐键盘就是没反应。这些坑幸好是在仿真里踩而不是在带着电机和电池的实车上踩。所以如果你按这篇教程一步步走到了键盘控制小车跑起来那一步恭喜你ROS2里最基础也是最重要的一条数据链路——从命令到执行已经印在你脑子里了。后面无论是做导航、做机械臂、做视觉跟随思想都是相通的定义模型发布状态发布指令执行反馈。把这套弄熟了实机只是换一层皮而已。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑