第13天 ROS Noetic Gazebo差速小车从零搭建|键盘遥控、雷达数据读取、Python自主节点全流程 📅 2026/7/2 7:32:58 第13天ROS控制小车运动-自动第13天ROS控制小车运动简介基于Ubuntu20.04ROS Noetic搭建完整机器人仿真平台从零手写差速小车URDF模型集成360°激光雷达实现Gazebo物理仿真、键盘遥控、RViz雷达可视化、Python自主控车雷达数据读取节点。全程无冗余操作代码可直接复制运行完美适配ROS入门学习、高校课程实验、机器人实训。一、前置环境说明1.1 软硬件环境配置本次实战基于稳定通用的ROS仿真环境适配绝大多数虚拟机、物理机设备操作系统Ubuntu 20.04 LTSROS版本Noetic Ninjemys仿真工具Gazebo 11系统自带适配版本一键安装全部依赖库包含仿真插件、键盘控制工具无需逐个安装sudo apt install ros-noetic-desktop-full ros-noetic-gazebo-ros ros-noetic-gazebo-plugins ros-noetic-teleop-twist-keyboard1.2 核心通信链路总览必懂原理整个小车仿真运行逻辑极简所有操作都遵循ROS话题通信机制两条核心链路贯穿全程小车运动链路键盘按键 → teleop遥控节点 → 发布/cmd_vel速度话题 → Gazebo差速驱动插件 → 计算左右轮转速 → 物理引擎驱动小车移动雷达传感链路Gazebo射线仿真雷达 → 激光插件解析数据 → 发布/scan雷达话题 → RViz可视化/自定义Python节点读取数据1.3 差速小车运动数学原理差速底盘是移动机器人最基础的底盘结构仅依靠左右两个主动轮的转速差即可实现前进、后退、转向无需额外转向机构。左轮速度 vL右轮速度 vR小车运动状态vv直线前进-v-v直线后退v-v原地右转-vv原地左转核心计算公式轮距L、小车线速度v、角速度ωGazebo差速插件会自动根据该公式计算轮速开发者只需发布线速度、角速度即可控车。1.4 核心通信消息 Twist 详解ROS所有地面移动机器人统一使用geometry_msgs/Twist消息通过/cmd_vel话题接收运动指令字段定义固定linear.x 前后线速度(m/s)前进为正、后退为负 linear.y/z 地面小车固定为0无上下、平移运动 angular.z 旋转角速度(rad/s)左转正、右转负 angular.x/y 地面小车固定为0无俯仰、横滚运动二、步骤1创建仿真功能包 工程目录新建ROS工作空间与仿真专用功能包自动配置依赖创建标准化工程目录# 新建工作空间已存在可跳过 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src # 创建仿真功能包导入核心依赖 catkin_create_pkg simple_car_sim urdf gazebo_ros gazebo_plugins rospy # 创建标准化目录 cd simple_car_sim mkdir urdf launch worlds scripts # 编译工作空间并刷新环境 cd ~/catkin_ws catkin_make source devel/setup.bash目录功能说明urdf存放机器人三维模型文件launch存放仿真启动脚本scripts存放Python控车、雷达读取节点worlds存放自定义仿真场景文件三、步骤2编写完整URDF机器人模型本次模型包含基准坐标系、蓝色底盘、左右驱动轮、万向支撑轮、360°激光雷达集成Gazebo物理仿真插件与雷达传感插件可直接仿真运行。文件路径~/catkin_ws/src/simple_car_sim/urdf/simple_car.urdf?xml version1.0? robot namesimple_car !-- 1. 材质定义 -- material nameblue color rgba0.2 0.4 0.8 1.0/ /material material nameblack color rgba0.1 0.1 0.1 1.0/ /material material namegray color rgba0.6 0.6 0.6 1.0/ /material !-- 2. base_footprint -- link namebase_footprint/ joint namebase_joint typefixed parent linkbase_footprint/ child linkbase_link/ origin xyz0 0 0.1/ !-- 修复抬高 轮子半径 0.05 车体半高 0.05 0.1m 确保车体在轮子正确支撑下底面恰好在轮子顶端 -- /joint !-- 3. base_link -- link namebase_link visual geometry box size0.3 0.2 0.1/ /geometry material nameblue/ /visual collision geometry box size0.3 0.2 0.1/ /geometry /collision inertial origin xyz0 0 0 rpy0 0 0/ mass value1.0/ inertia ixx0.0058 ixy0 ixz0 iyy0.0108 iyz0 izz0.0158/ /inertial /link !-- 4. 左驱动轮 -- link nameleft_wheel visual origin xyz0 0 0 rpy0 0 0/ geometry cylinder radius0.05 length0.04/ /geometry material nameblack/ /visual collision origin xyz0 0 0 rpy0 0 0/ geometry cylinder radius0.05 length0.04/ /geometry /collision inertial origin xyz0 0 0 rpy0 0 0/ mass value0.2/ inertia ixx0.000183 ixy0 ixz0 iyy0.000183 iyz0 izz0.00025/ /inertial /link joint nameleft_wheel_joint typecontinuous parent linkbase_link/ child linkleft_wheel/ !-- base_link 原点在车体中心z方向 轮子需要在车体底面z -0.05处让轮轴与地面平行 rpy1.5708 0 0绕X轴转90°圆柱竖轴变为Y轴方向即横向轮子 -- origin xyz-0.05 0.12 -0.05 rpy1.5708 0 0/ !-- 旋转轴在子坐标系已绕X转90°中z轴 全局Y轴方向即轮轴方向 ✅ -- axis xyz0 0 -1/ !-- 修复统一改为 0 0 -1方向由 rpy 决定左右轮对称由 y 坐标正负保证 -- /joint !-- 5. 右驱动轮 -- link nameright_wheel visual origin xyz0 0 0 rpy0 0 0/ geometry cylinder radius0.05 length0.04/ /geometry material nameblack/ /visual collision origin xyz0 0 0 rpy0 0 0/ geometry cylinder radius0.05 length0.04/ /geometry /collision inertial origin xyz0 0 0 rpy0 0 0/ mass value0.2/ inertia ixx0.000183 ixy0 ixz0 iyy0.000183 iyz0 izz0.00025/ /inertial /link joint nameright_wheel_joint typecontinuous parent linkbase_link/ child linkright_wheel/ origin xyz-0.05 -0.12 -0.05 rpy1.5708 0 0/ axis xyz0 0 -1/ !-- 修复与左轮统一为 0 0 -1差速驱动插件内部会处理左右轮的正反转 -- /joint !-- 6. 前万向轮 -- link namecaster_wheel visual origin xyz0 0 0 rpy0 0 0/ geometry sphere radius0.025/ /geometry material namegray/ /visual collision origin xyz0 0 0 rpy0 0 0/ geometry sphere radius0.025/ /geometry !-- 修复surface 不属于 URDF移到 gazebo reference 里 -- /collision inertial origin xyz0 0 0 rpy0 0 0/ mass value0.1/ inertia ixx0.000010 ixy0 ixz0 iyy0.000010 iyz0 izz0.000010/ /inertial /link joint namecaster_joint typefixed parent linkbase_link/ child linkcaster_wheel/ !-- 修复万向轮球心 z -(车体半高 球半径) -(0.05 0.025) -0.075 这样球体最低点 -0.075 - 0.025 -0.1与驱动轮最低点一致 ✅ -- origin xyz0.1 0 -0.075/ /joint !-- 7. 激光雷达 -- link namelaser_link visual origin xyz0 0 0 rpy0 0 0/ geometry cylinder radius0.04 length0.04/ /geometry material namegray/ /visual collision origin xyz0 0 0 rpy0 0 0/ geometry cylinder radius0.04 length0.04/ /geometry /collision inertial origin xyz0 0 0 rpy0 0 0/ mass value0.1/ inertia ixx0.000033 ixy0 ixz0 iyy0.000033 iyz0 izz0.000080/ /inertial /link joint namelaser_joint typefixed parent linkbase_link/ child linklaser_link/ origin xyz0 0 0.07/ /joint !-- 8. Gazebo 插件差速驱动 -- gazebo plugin namediff_drive_controller filenamelibgazebo_ros_diff_drive.so leftJointleft_wheel_joint/leftJoint rightJointright_wheel_joint/rightJoint wheelSeparation0.24/wheelSeparation wheelDiameter0.1/wheelDiameter !-- 修复添加 wheelTorque 限制最大输出扭矩防止速度阶跃 -- wheelTorque5.0/wheelTorque !-- 修复加速度限制平滑速度变化 -- wheelAcceleration1.0/wheelAcceleration commandTopiccmd_vel/commandTopic odometryTopicodom/odometryTopic odometryFrameodom/odometryFrame robotBaseFramebase_footprint/robotBaseFrame updateRate30/updateRate publishTf1/publishTf publishWheelTFfalse/publishWheelTF publishOdomTFtrue/publishOdomTF !-- 关键显式开启关节状态发布 -- publishWheelJointStatetrue/publishWheelJointState /plugin /gazebo !-- 9. Gazebo 插件激光雷达 -- gazebo referencelaser_link sensor typeray namelidar_sensor pose0 0 0 0 0 0/pose visualizetrue/visualize update_rate10/update_rate ray scan horizontal samples360/samples resolution1/resolution min_angle-3.14159/min_angle max_angle 3.14159/max_angle /horizontal /scan range min0.12/min max10.0/max resolution0.01/resolution /range noise typegaussian/type mean0.0/mean stddev0.01/stddev /noise /ray plugin namelidar_plugin filenamelibgazebo_ros_laser.so topicNamescan/topicName frameNamelaser_link/frameName /plugin /sensor /gazebo !-- 10. Gazebo 材质 摩擦力属性 -- gazebo referencebase_link materialGazebo/Blue/material /gazebo gazebo referenceleft_wheel materialGazebo/Black/material mu11.0/mu1 mu21.0/mu2 !-- 轮子与地面接触的最大横向修正速度减少数值抖动 -- maxVel1.0/maxVel minDepth0.001/minDepth /gazebo gazebo referenceright_wheel materialGazebo/Black/material mu11.0/mu1 mu21.0/mu2 maxVel1.0/maxVel minDepth0.001/minDepth /gazebo !-- 修复万向轮摩擦力必须在 gazebo reference 里设置才生效 -- gazebo referencecaster_wheel materialGazebo/Grey/material mu10.0/mu1 mu20.0/mu2 maxVel0.1/maxVel minDepth0.001/minDepth /gazebo gazebo referencelaser_link materialGazebo/Grey/material /gazebo /robotURDF语法校验启动前必做提前检测模型语法错误规避启动崩溃问题cd ~/catkin_ws/src/simple_car_sim/urdf check_urdf simple_car.urdf输出Successfully Parsed XML即为正常若报错检查标签闭合、参数格式即可。四、步骤3编写仿真启动Launch文件Launch文件一键启动Gazebo、加载机器人模型、生成仿真小车、发布TF坐标无需逐个启动节点。文件路径~/catkin_ws/src/simple_car_sim/launch/simple_car_gazebo.launchlaunch !-- 将URDF模型加载到ROS参数服务器 -- param namerobot_description commandcat $(find simple_car_sim)/urdf/simple_car.urdf / !-- 启动Gazebo空白仿真世界启用仿真时间 -- include file$(find gazebo_ros)/launch/empty_world.launch arg namepaused valuefalse/ arg nameuse_sim_time valuetrue/ arg namegui valuetrue/ /include !-- 在Gazebo原点生成机器人模型 -- node namespawn_urdf pkggazebo_ros typespawn_model args-urdf -model simple_car -param robot_description -x 0 -y 0 -z 0.1 outputscreen/ !-- 发布机器人TF坐标变换为RViz可视化提供支撑 -- node namerobot_state_publisher pkgrobot_state_publisher typerobot_state_publisher outputscreen param namepublish_frequency value30.0/ /node /launch五、步骤4启动仿真 基础功能测试5.1 启动Gazebo完整仿真环境cd ~/catkin_ws source devel/setup.bash roslaunch simple_car_sim simple_car_gazebo.launch首次启动耗时30秒左右加载完成后可见Gazebo窗口中出现蓝色小车车顶雷达带有绿色激光射线。5.2 键盘遥控小车运动新建终端执行命令启动键盘控制节点source ~/catkin_ws/devel/setup.bash rosrun teleop_twist_keyboard teleop_twist_keyboard按键说明重点必须鼠标选中该终端窗口按键才生效i前进、,后退j原地左转、l原地右转k紧急停止q/z增大/减小全局运动速度问题1teleop_twist_keyboard 可执行文件找不到精准报错解决报错现象执行rosrun teleop_twist_keyboard teleop_twist_keyboard提示Couldnt find executable named teleop_twist_keyboard仅检索到文件夹无可执行文件报错原因依赖安装不完整、软件包未编译、环境变量未刷新是Noetic版本高频隐性报错终极解决命令逐条执行# 1. 重新安装完整键盘控制依赖覆盖缺失文件 sudo apt install --reinstall ros-noetic-teleop-twist-keyboard # 2. 刷新ROS全局环境变量 source /opt/ros/noetic/setup.bash # 3. 回到工作空间刷新本地环境 cd ~/catkin_ws source devel/setup.bash # 4. 直接运行无需编译官方包自带可执行文件 rosrun teleop_twist_keyboard teleop_twist_keyboard彻底根治方案若依旧报错说明本地源码冲突删除冗余源码文件夹~/xxx_ws/src/teleop_twist_keyboard再重新安装依赖即可。5.3 查看ROS话题通信状态# 查看所有活跃话题 rostopic list # 实时打印小车速度指令 rostopic echo /cmd_vel # 实时打印激光雷达测距数据 rostopic echo /scan # 查看话题发布频率 rostopic hz /scan rostopic hz /cmd_vel5.4 RViz可视化小车与雷达点云新建终端输入rviz打开可视化工具按以下步骤配置将左侧Fixed Frame修改为base_footprint点击左下角 Add添加RobotModel显示小车三维模型再次 Add添加LaserScanTopic选择/scan将LaserScan的Size参数改为0.05放大雷达点云更清晰操控小车移动可实时观察RViz中雷达点云随障碍物变化。六、步骤5自主编写Python功能节点6.1 小车自动画圆节点自主控车原理固定线速度角速度让小车持续做圆周运动理解速度话题发布逻辑。文件路径simple_car_sim/scripts/move_circle.py#!/usr/bin/env python3 move_circle.py 让小车以固定的线速度和角速度做圆周运动。 目的理解 cmd_vel 的发布方式。 import rospy from geometry_msgs.msg import Twist def main(): # 初始化节点 rospy.init_node(move_circle, anonymousTrue) # 创建发布者发布到 /cmd_vel 话题 # queue_size10 表示消息队列最多缓存10条 pub rospy.Publisher(/cmd_vel, Twist, queue_size10) # 发布频率10Hz每秒发10次指令 rate rospy.Rate(10) rospy.loginfo(小车开始画圆按 CtrlC 停止...) while not rospy.is_shutdown(): # 构造 Twist 消息 msg Twist() # 线速度向前 0.3 m/s msg.linear.x 0.3 # 角速度绕 z 轴 0.5 rad/s左转 # 转弯半径 r v / ω 0.3 / 0.5 0.6 m msg.angular.z 0.5 # 发布消息 pub.publish(msg) # 按照设定频率休眠 rate.sleep() # 节点退出时发送停止指令 rospy.loginfo(停止小车...) stop_msg Twist() # 默认全0即停止 pub.publish(stop_msg) if __name__ __main__: try: main() except rospy.ROSInterruptException: pass添加权限并运行chmod x ~/catkin_ws/src/simple_car_sim/scripts/move_circle.py rosrun simple_car_sim move_circle.py6.2 激光雷达数据读取节点原理订阅/scan话题解析雷达数据打印小车前、左、右三方障碍物距离。文件路径simple_car_sim/scripts/read_laser.pyimport rospy from sensor_msgs.msg import LaserScan def laser_callback(msg): 每收到一帧雷达数据就调用这个函数。 msg.ranges 是一个列表包含 360 个距离值。 索引 0 对应正前方0°索引 90 对应左方90°以此类推。 注意inf 表示该方向没有测到障碍物超过最大量程。 # 取正前方索引0的距离 front_dist msg.ranges[0] # 取左方索引90的距离 left_dist msg.ranges[90] # 取右方索引270即-90°的距离 right_dist msg.ranges[270] rospy.loginfo( f前方: {front_dist:.2f}m | 左方: {left_dist:.2f}m | 右方: {right_dist:.2f}m ) def main(): rospy.init_node(read_laser, anonymousTrue) # 订阅 /scan 话题每次收到消息调用 laser_callback rospy.Subscriber(/scan, LaserScan, laser_callback) rospy.loginfo(开始读取激光雷达数据...) # spin() 让节点保持运行持续接收回调 rospy.spin() if __name__ __main__: try: main() except rospy.ROSInterruptException: pass chmod x ~/catkin_ws/src/simple_car_sim/scripts/read_laser.py rosrun simple_car_sim read_laser.py添加权限并运行chmod x ~/catkin_ws/src/simple_car_sim/scripts/read_laser.py rosrun simple_car_sim read_laser.py七、整体通信数据流总结7.1 小车运动数据流键盘输入 / Python节点 → 发布/cmd_vel(Twist)→ 差速驱动插件 → 计算左右轮速 → Gazebo物理引擎驱动小车运动7.2 雷达传感数据流Gazebo射线仿真采集距离 → 激光插件解析数据 → 发布/scan(LaserScan)→ RViz可视化 / 自定义节点数据读取八、常见报错与解决方案全覆盖问题1spawn_urdf 模型生成失败URDF文件路径错误核对$(find)功能包名与文件路径一致问题2robot_state_publisher 进程闪退所有link标签缺少inertial惯性参数本文代码已修复该问题问题3RViz不显示雷达点云Fixed Frame设置为base_footprint手动选择话题/scan问题4键盘控制无响应鼠标点击键盘控制终端激活窗口焦点问题5Gazebo启动卡顿、缓慢首次启动加载资源属于正常现象关闭多余后台程序即可九、项目拓展学习方向新增摄像头URDF模型实现机器人视觉仿真基于雷达数据编写自主避障、循迹行走算法订阅/odom里程计话题实现小车坐标定位使用Xacro拆分URDF文件实现机器人模型模块化搭建添加仿真障碍物场景完成自主导航实训文末福利本文全套可运行工程文件、完整源码已整理完毕适合ROS入门实训、期末课程设计、机器人仿真作业直接复制即可运行无报错、无删减标签#ROS #Gazebo仿真 #差速小车 #URDF建模 #机器人仿真 #Twist话题 #雷达传感器