公司动态
基于ROS 2与PuppyPi的家庭机器人:自主巡逻与视觉抓取实践
1. 项目概述打造一个会巡逻和取物的家庭机器人最近在捣鼓一个挺有意思的项目用PuppyPi这个开源的机器人平台结合ROS机器人操作系统做了一个能在家里自主巡逻还能根据指令去取个小物件的机器人。这玩意儿听起来有点像科幻片里的场景但其实用现有的开源硬件和软件自己动手完全能实现。核心就是让机器人具备两个核心能力一是能像保安一样在设定的家庭区域内无碰撞地自主移动巡逻二是能听懂“去把遥控器拿来”这样的简单指令然后规划路径、找到目标、抓取并带回。这个项目特别适合对机器人、ROS和嵌入式开发感兴趣的朋友无论你是想深入学习ROS的实战应用还是想给家里添个能跑腿的“小助手”都能从中获得不少乐趣和知识。整个过程会涉及到机器人底盘控制、传感器数据处理、SLAM建图、自主导航、视觉识别、机械臂控制等多个模块的集成算是一个比较综合的练手项目。下面我就把自己从零搭建这个“家庭巡逻取物机器人”的完整过程、踩过的坑以及一些实用技巧详细地分享出来。2. 核心硬件与软件栈选型解析2.1 为什么选择PuppyPi作为硬件平台PuppyPi是一个基于树莓派CM4Compute Module 4设计的开源机器人开发平台。我选择它主要基于以下几点考量首先集成度高开箱即用。PuppyPi主板直接集成了电机驱动、IMU惯性测量单元、多个USB和CSI接口甚至预留了机械臂和舵机的控制接口。这意味着我不需要再额外购买和连接一大堆驱动板、传感器扩展板省去了大量繁琐的硬件接线和调试工作可以把精力集中在软件和算法上。对于快速原型开发来说这一点至关重要。其次强大的核心与丰富的接口。其核心树莓派CM4性能足够强劲能够流畅运行ROS 2、OpenCV等较为复杂的软件。丰富的GPIO、I2C、UART接口也方便后续扩展其他传感器比如再加一个激光雷达或者深度相机。主板自带的IMU为机器人的姿态估计和里程计提供了基础数据。再者活跃的社区与生态。PuppyPi背后有一个比较活跃的开源社区提供了基础的固件、驱动和示例代码。虽然文档可能不如一些商业产品详尽但社区讨论和开源代码能解决大部分初期问题。这对于学习和调试非常有帮助。注意PuppyPi有不同的版本对应不同的底盘如两轮差速、四轮麦克纳姆轮。对于家庭环境我选择了两轮差速底盘因为它结构简单、控制直观、成本较低并且在平整地面如地板、瓷砖上移动性能足够。麦克纳姆轮虽然能横向移动但噪音相对较大对地面平整度要求更高不太适合安静的居家环境。2.2 ROS 2机器人系统的“大脑”与“神经系统”这个项目的软件核心是ROS 2Robot Operating System 2。你可以把它理解成机器人的“操作系统”或“中间件”它不直接提供具体的感知或控制算法而是提供了一套通信框架和工具集让各个功能模块节点能够高效、可靠地交换数据。我选择ROS 2而非ROS 1主要是因为ROS 2在实时性、跨平台支持和生产级可靠性方面有显著提升。它的通信底层采用DDS数据分发服务支持更灵活的 QoS服务质量策略这对于要求稳定性的自主导航和机械臂控制场景很重要。此外ROS 2对Windows、macOS的支持也更好。在这个项目中ROS 2主要扮演以下角色通信中枢机器人的各个部件如激光雷达、摄像头、IMU、电机、机械臂都通过ROS 2的“话题”、“服务”、“动作”进行数据交换和指令传递。功能模块化将巡逻、建图、导航、视觉识别、抓取等任务分解成独立的节点便于开发、调试和复用。工具链丰富RViz用于三维可视化Gazebo用于仿真测试ros2 bag用于数据记录与回放这些工具能极大提升开发效率。关于ROS 2的安装网络上的“鱼香ROS一键安装”脚本确实很方便它能自动处理依赖和源配置。但我个人的经验是对于想深入理解ROS 2系统的人来说建议在熟悉一键安装后尝试一次从官方源手动安装这个过程能让你更清楚系统的组成部分和可能的故障点。例如在Ubuntu 22.04上安装ROS 2 Humble你会遇到诸如[2/4] FETCHING PACKAGES... ERROR或error: certificate has expired这类网络或证书问题手动解决这些问题的经验非常宝贵。2.3 关键外设与传感器选型仅有PuppyPi主板和底盘还不够要实现巡逻和取物需要给机器人装上“眼睛”和“手”。感知部分眼睛激光雷达LiDAR这是实现自主导航的核心传感器。我选用的是RPLIDAR A1这类低成本二维激光雷达。它通过旋转激光束测量周围环境的距离生成二维点云图用于SLAM建图和实时避障。对于家庭环境其8-10米的测距范围和±2cm的精度完全够用。深度相机用于物品识别与定位。我选择了英特尔Realsense D435i。它同时提供RGB彩色图像和深度信息并且内置IMU。D435i的深度原理是主动红外结构光在室内光照条件变化时比纯双目相机更稳定。它的ROS驱动realsense2_camera也非常成熟。这里有个坑需要注意默认的驱动发布点云和深度话题的频率可能较高如果觉得处理不过来导致CPU占用高或延迟大可以在启动节点时通过参数降低发布频率例如ros2 run realsense2_camera realsense2_camera_node __params:config.yaml 然后在yaml文件里设置depth_fps: 15。执行部分手机械臂为了实现“取物”需要一个简单的机械臂。我选用了一款6自由度6-DOF的桌面级舵机机械臂如UArm或类似开源设计。选择它的原因是结构紧凑、重量轻、控制相对简单通过PWM信号控制舵机并且有现成的ROS控制包如uarm可以修改使用。对于家庭取物其负载通常500g以内和臂展30cm左右足以应对遥控器、手机、小瓶装水等常见物品。3. 系统搭建与基础功能实现3.1 系统环境搭建与PuppyPi固件刷写第一步是准备软件环境。我是在一台Ubuntu 22.04的台式机上进行主要开发的这台机器作为开发机。PuppyPi上的树莓派CM4则作为机器人本体机。开发机环境安装ROS 2 Humble。可以使用“鱼香ROS”提供的一键安装脚本但务必确认脚本来源可靠并注意脚本可能因网络问题报错如之前提到的证书错误通常更新系统证书或配置合适的网络环境即可解决。安装必要的ROS 2工具和功能包colcon构建工具、rosdep依赖管理、Navigation2、SLAM Toolbox、MoveIt 2等。PuppyPi本体机环境为树莓派CM4烧录Ubuntu Server 22.04或20.04镜像。PuppyPi官网通常提供了适配的镜像文件内含必要的内核驱动。通过SSH连接到PuppyPi同样安装ROS 2 Humble。由于PuppyPi性能足够可以直接在上面运行导航、视觉等节点。但为了减轻负载也可以考虑将视觉识别这类计算密集型任务放在开发机上通过ROS 2的网络通信功能实现分布式计算。刷写PuppyPi控制固件这是关键一步。需要从PuppyPi的GitHub仓库克隆固件代码编译并刷写到主板的微控制器通常是STM32中。这个固件负责底层电机控制、IMU数据读取、电池电压监测等。编译环境可能需要arm-none-eabi-gcc工具链。刷写通常通过USB转串口工具进行。务必仔细阅读仓库的README确认刷写引脚和步骤错误的操作可能导致主板变砖。网络配置为了让开发机和机器人本体机上的ROS 2节点能相互发现和通信需要配置ROS_DOMAIN_ID或设置多机通信。最简单的方法是让两者连接到同一个局域网Wi-Fi然后在本体机的~/.bashrc和开发机的~/.bashrc中设置export ROS_DOMAIN_ID同一个数字例如0-232之间的一个数字。这样两台机器上运行的ROS 2节点就能自动组网了。3.2 底盘控制与传感器驱动集成硬件准备好后就要让ROS 2能够“指挥”它们。发布机器人基础信息创建一个URDF统一机器人描述格式文件描述PuppyPi机器人的物理结构底盘尺寸、轮子位置与类型、激光雷达和相机相对于底盘中心的安装位置TF变换。这个文件是后续所有导航和感知的基础。使用robot_state_publisher节点发布这些静态TF关系。发布传感器数据激光雷达运行对应的ROS驱动包例如rplidar_ros。启动后它会发布/scan话题类型为sensor_msgs/LaserScan包含了激光测距数据。深度相机运行realsense2_camera_node。它会发布/camera/color/image_raw彩色图、/camera/depth/image_rect_raw深度图以及点云、IMU等众多话题。注意同时发布这么多数据流会占用大量带宽和CPU。在启动时通过参数如enable_pointcloud:false禁用暂时不需要的数据流。IMU与里程计PuppyPi的固件通常会通过串口发布IMU原始数据和编码器计数。需要编写一个ROS节点或用现成的micro-ros方案来订阅串口数据进行解析并发布成标准的sensor_msgs/Imu消息和nav_msgs/Odometry消息。里程计信息由轮子编码器积分得到是机器人定位的初始依据。控制底盘运动编写一个电机控制节点。这个节点订阅ROS 2的/cmd_vel话题类型为geometry_msgs/Twist包含线速度和角速度指令然后根据机器人运动学模型对于两轮差速底盘公式是左轮速度 (线速度 - 角速度 * 轮距/2) / 轮半径右轮速度同理计算出左右轮的目标转速。将这个目标转速通过特定的通信协议如PWM、CAN或串口指令发送给PuppyPi主板上的电机驱动芯片。PuppyPi的固件可能已经暴露了相应的控制接口如特定的ROS服务或话题需要查阅其API文档。3.3 自主巡逻功能实现SLAM建图与导航巡逻的本质是在已知地图中实现点对点的自主导航。SLAM建图首先需要让机器人在家里跑一圈构建一张环境地图。我选用的是SLAM Toolbox它比经典的gmapping更现代支持长期建图和优化。启动命令类似ros2 launch slam_toolbox online_async_launch.py。然后通过键盘遥控teleop_twist_keyboard控制机器人在家中缓慢、均匀地移动确保激光雷达能扫描到所有角落特别是门框、家具边缘等特征明显的地方。建图完成后使用map_saver节点将地图保存为PGM图像和YAML元数据文件。这张地图就是机器人后续巡逻的“蓝图”。Navigation2导航栈配置Navigation2是ROS 2的官方导航框架它负责处理从A点到B点的全局路径规划和局部避障。配置Navigation2是关键且繁琐的一步。你需要创建多个配置文件主要是nav2_params.yaml核心参数文件定义代价地图costmap的层如静态层、障碍层、膨胀层、全局/局部规划器如NavFn、TEB、控制器如DWB的行为参数。tb3_ros2_nav2.launch.py启动文件加载机器人模型、地图、Navigation2节点以及RViz配置。参数调优经验膨胀半径inflation_radius这个参数决定了机器人距离障碍物多远开始“绕行”。设置太小容易撞上太大则可能导致在狭窄通道如门廊无法通过。我一般从机器人半径的1.5倍开始调试。代价地图更新频率局部代价地图更新频率update_frequency建议设置在5-10Hz太高会增加计算负担太低则避障反应慢。规划器选择全局规划我用默认的NavFn局部规划则选择了TEBTimed Elastic Band本地规划器。TEB在动态避障和复杂地形下表现更灵活但参数更多。初次使用可以先从DWB开始它更简单稳定。配置好后在RViz中设置一个目标点2D Pose Estimate初始化机器人位置2D Nav Goal指定目标机器人就应该能规划路径并自主移动过去了。第一次测试一定要在空旷地方进行并随时准备用急停开关巡逻逻辑实现自主导航实现后巡逻就变成了“按顺序访问一系列预设点”。我编写了一个简单的巡逻管理节点。这个节点内部维护一个巡逻点列表每个点是一个geometry_msgs/PoseStamped包含位置和朝向。它通过ROS 2的NavigateToPose动作客户端依次向Navigation2发送目标点。当Navigation2反馈“目标已到达”或“路径规划失败”时巡逻节点再发送下一个点。可以加入一些简单的逻辑比如在某点停留几秒模拟检查或者遇到连续失败时跳过该点并记录日志。4. 视觉识别与抓取功能实现4.1 基于深度相机的物品识别与定位让机器人去“取物”首先得让它“看见”并“知道”物品在哪。物品识别对于像遥控器、水杯这类形状、颜色相对固定的物体我采用了传统的颜色轮廓识别方法使用OpenCV实现足够简单高效。流程订阅深度相机发布的RGB图像话题/camera/color/image_raw。将图像从BGR转换到HSV颜色空间更容易分离颜色。根据目标物体的颜色范围例如红色遥控器使用cv2.inRange()函数创建掩膜mask。对掩膜进行形态学操作如开运算、闭运算去除噪声。使用cv2.findContours()查找轮廓并过滤掉面积过小的轮廓可能是噪声。计算剩余轮廓的最小外接矩形或圆心这个位置就是物体在图像二维坐标系中的位置像素坐标u, v。定位同时订阅对齐的深度图像话题/camera/aligned_depth_to_color/image_raw。确保深度图和彩色图是像素对齐的。在深度图中读取物体中心点u, v对应的深度值z单位通常是毫米。利用相机内参焦距fx, fy 光心cx, cy将像素坐标(u, v)和深度z转换为相机坐标系下的三维坐标(Xc, Yc, Zc)Xc (u - cx) * z / fx Yc (v - cy) * z / fy Zc z最后利用相机与机器人底盘之间的静态TF变换将物体在相机坐标系下的位置(Xc, Yc, Zc)转换到机器人底盘坐标系base_link下。这个坐标就是机器人“眼中”物体的位置。发布目标位置识别并计算出物体位置后我的识别节点会发布一个自定义的消息例如/object_detected内容包含物体ID比如“red_remote”和其在base_link坐标系下的位置geometry_msgs/Point。4.2 简易机械臂控制与抓取动作设计我使用的6自由度舵机机械臂其ROS控制通常依赖于moveit或dynamixel_workbench这类包。但为了简化我直接采用了位置控制模式。建立机械臂模型使用MoveIt Setup Assistant为机械臂生成URDF和SRDF文件。这个过程会定义机械臂的连杆、关节、运动学组以及可能的碰撞体。虽然最终我们可能不用MoveIt的复杂规划但这个模型是控制的基础。单关节位置控制每个舵机对应机械臂的一个关节。通过ROS 2服务或话题向舵机驱动节点发送目标角度指令。例如可以创建一个/arm_control服务服务请求里包含6个关节的目标角度弧度制。服务端收到请求后将其转换为舵机对应的PWM脉宽值通过串口或总线发送给舵机控制板。关键点运动插值。不能直接让关节从当前位置瞬间跳到目标位置这会导致抖动甚至损坏机械臂。需要在代码中实现简单的线性插值或梯形速度曲线让关节平滑地运动到目标位。设计抓取姿态序列抓取一个物体通常需要一系列连贯的位姿。我通过“示教”的方式手动控制机械臂移动到几个关键位置记录下这些位置的关节角度形成一个动作序列。典型序列准备姿态机械臂抬起处于一个不碍事的安全位置。预抓取姿态移动到物体正上方一定高度末端执行器夹爪对准物体。抓取姿态垂直下降使夹爪到达物体抓取点闭合夹爪。提起姿态夹紧物体后垂直向上提起一段距离。放置姿态移动到目标放置点如机器人身上的一个小篮子上方松开夹爪。将这些关节角度序列保存为一个YAML文件。我的抓取控制节点在收到“抓取”指令后就会按顺序执行这个序列。4.3 “巡逻-取物”任务整合与逻辑编排现在巡逻、导航、识别、抓取各个模块都已就绪需要编写一个总控节点来串联整个流程。这个总控节点采用有限状态机FSM的设计思想逻辑清晰。我使用了smach一个ROS中用于创建状态机的Python库来组织但用简单的Python函数和状态变量也能实现。核心工作流程如下状态空闲等待。机器人处于待命状态。触发通过ROS服务或语音指令可以集成一个简单的语音识别节点接收到任务例如“去取遥控器”。状态前往目标区域。总控节点调用巡逻管理节点或直接向Navigation2发送目标点这个目标点是预先标定的“遥控器通常所在的区域”比如茶几附近的一个点。机器人开始自主导航。状态搜索与识别。机器人到达目标区域后总控节点启动视觉识别节点。机器人可能需要在原地缓慢旋转发布小的角速度cmd_vel让相机扫描周围环境直到识别到目标物体并获取其位置。如果超时未找到则进入“任务失败”状态。状态精细接近。识别到物体后物体位置是相对于机器人当前位置的。总控节点会根据这个相对位置计算出一个新的导航目标点这个点位于物体前方一个合适的抓取距离比如30厘米。然后再次调用Navigation2让机器人移动到这个精确位置。状态执行抓取。机器人到达精确位置后总控节点向机械臂控制节点发送“执行抓取序列”的指令。机械臂按预编程的动作完成抓取。状态返回交付。抓取成功后总控节点再次向Navigation2发送“返回起始点”或指定的“交付点”的指令。机器人带着物体返回。状态放置物体。到达交付点后总控节点发送“放置序列”指令给机械臂松开物体。状态任务完成。返回空闲状态等待下一个指令。在整个流程中总控节点需要监控每个子任务的状态成功/失败/超时并做出相应的异常处理比如重试、放弃任务或返回充电。5. 调试、优化与常见问题排查5.1 核心调试工具与技巧ROS 2强大的工具链是调试的利器。RViz可视化一切。这是最重要的调试工具。在RViz中你可以显示激光雷达点云LaserScan检查建图和障碍物检测是否正常。显示摄像头图像Image确认视觉识别算法的输入是否正确。显示TF坐标系TF检查机器人各部件底盘、雷达、相机、机械臂的坐标变换关系是否正确。TF树错误是导航和定位失败的常见原因显示导航的全局路径Path、局部代价地图OccupancyGrid直观地看路径规划是否合理。ros2 topic echo / ros2 service call查看任意话题的实时数据或手动调用服务是测试节点功能的最直接方法。ros2 bag record / play录制传感器数据如激光雷达、相机和命令数据可以离线、反复回放用于复现问题和调试算法无需每次都开着机器人跑。rqt图形化工具集。rqt_graph查看节点与话题的连接图rqt_console查看节点日志rqt_plot绘制数据曲线如速度、角度。5.2 导航功能常见问题与调优导航是问题高发区以下是我遇到的一些典型问题及解决方法问题现象可能原因排查与解决方法机器人原地打转不向目标移动1. TF配置错误base_link与odom或map的变换关系异常。2. 里程计数据异常编码器接线松动或计算错误。3. 全局规划器找不到路径目标点被放在障碍物上。1. 在RViz中检查TF树确保map-odom-base_link链条完整且方向正确。2. 使用ros2 topic echo /odom检查线速度和角速度数据是否合理。3. 在RViz中检查目标点是否在代价地图的可通行区域浅灰色。机器人撞上障碍物1. 激光雷达安装高度或角度不对未能扫描到低矮障碍物如桌腿、电线。2. 代价地图的膨胀半径设置过小。3. 局部规划器参数过于激进最大速度、加速度设置太高。1. 调整雷达安装位置确保其扫描平面能覆盖常见障碍物高度。2. 适当增大inflation_radius。3. 在local_costmap的TrajectoryPlanner参数中降低max_vel_x,max_vel_theta,acc_lim_x等值。规划路径非常奇怪绕远路1. 地图不准确存在幽灵障碍物因动态物体建图时留下。2. 全局代价地图的分辨率或更新策略有问题。1. 重新建图建图时确保环境是静态的。2. 检查global_costmap的static_layer地图源是否正确rolling_window参数是否合理。导航到目标点附近后不停震荡目标点容差xy_goal_tolerance,yaw_goal_tolerance设置过小机器人难以精确到达。在controller_server的配置中适当增大容差值例如xy_goal_tolerance: 0.1米yaw_goal_tolerance: 0.2弧度。5.3 视觉与抓取环节的稳定性提升识别不稳定光照变化会导致颜色识别失败。解决方法使用自适应阈值或更鲁棒的特征如ORB、SIFT进行匹配但计算量增大。在家庭环境下可以尝试在目标物体上粘贴一个简单的AprilTag或Aruco码。视觉识别AprilTag极其稳定和快速定位精度高是机器人抓取的常用辅助手段。抓取位置不准即使识别定位准确由于机械臂运动误差、相机标定误差等也可能抓空。多次平均在抓取前让识别节点连续输出多帧物体的位置取平均值作为最终抓取位置。加入反馈在夹爪上安装一个微动开关或触觉传感器。当夹爪闭合碰到物体时传感器触发停止闭合动作。这样即使位置稍有偏差也能可靠抓取。眼在手外Eye-to-Hand标定仔细进行相机与机器人基座的标定减少坐标系转换带来的误差。可以使用easy_handeye这样的ROS包来辅助标定。5.4 系统集成与性能优化当所有节点一起运行时树莓派CM4可能会感到压力。节点管理使用launch文件合理组织节点启动顺序和命名空间。资源监控使用htop或ros2 topic hz监控CPU和话题发布频率。对于深度图像这类大数据量话题务必在realsense节点中降低发布频率和分辨率。通信优化在同一台机器上使用Intra-Process Communication可以大幅降低消息传递的开销。对于/cmd_vel这类需要低延迟的命令话题设置合适的ROS 2 QoS策略如ReliableVolatileKeepLast深度为1。电源管理机器人移动、机械臂动作都是耗电大户。务必监控PuppyPi的电池电压话题编写一个低电量自动回充的逻辑如果接入了自动充电桩。