引言

四足机器人正从实验室走向真实场景——巡检、物流、陪护、赛事,每一个应用场景都对机器人的自主性提出了更高的要求。然而,搭建一套完整的四足机器人软件系统远非“装个ROS、跑个Demo”那么简单:底层要对接电机驱动与实时控制,中间层要解决SLAM建图与自主导航,上层还要实现语音交互、视觉感知与任务决策。每一层都有自己的坑,而真正让系统跑起来、跑得稳,考验的是工程化的系统集成能力。

本文以我参与开发的四足机器人智能伴侣系统为蓝本,从电机驱动语音导航,完整梳理这套基于ROS 2的端到端系统架构。文章将按照驱动层→感知层→决策层→建图层的纵向脉络展开,逐一拆解每个模块的设计思路、技术选型与踩坑经验。无论你是正在搭建ROS 2机器人系统的开发者,还是对四足机器人技术栈感兴趣的学习者,希望这篇文章能为你提供一份可参考的系统蓝图。


一、整体系统架构

整套系统运行在四足机器人的上装计算平台(通过网线连接机器狗本体),基于ROS 2构建,采用分层解耦的架构设计。自底向上分为四个层次:

这种分层设计的核心好处在于:每一层只做自己该做的事,层与层之间通过ROS 2的话题(Topic)和服务(Service)解耦。替换底层SDK不影响上层导航,更换SLAM算法不影响驱动层,这种模块化为后续的迭代和调试提供了极大的便利。


二、驱动与通信层:让机器狗“动起来”

驱动层是整个系统的基础,负责与机器人本体的SDK通信,将上层指令转化为实际的电机运动,同时将传感器数据反馈给上层。

2.1 底层控制与SDK封装

驱动节点(agibot_d1_driver_py)通过封装厂商提供的mc_sdk_zsl_1_py动态库,实现对机器人本体的底层控制。节点启动时完成SDK初始化并让机器人进入站立状态:python

self.app = mc_sdk_zsl_1_py.HighLevel()
self.app.initRobot(client_ip, client_port, dog_ip)
self.app.standUp()

这里有一个关键设计:IP参数的可配置性。代码中同时支持AP模式(192.168.234.x)和有线网线模式(192.168.168.x),通过YAML参数文件灵活切换。这种设计让开发者在调试阶段可以用WiFi连接,正式演示时切到有线保证稳定性,避免了硬编码带来的麻烦。

2.2 速度指令的看门狗机制

速度控制是机器人安全的核心。驱动节点订阅/cmd_vel话题接收Twist指令,并调用app.move()驱动电机。但有一个容易被忽略的问题:如果发布速度指令的节点崩溃了怎么办?

代码实现了一个看门狗定时器,每100ms检查一次指令接收时间:python

if time_diff >= 1e9 and not self.stopped_due_to_timeout:
    self.current_linear_x *= (1.0 - self.DECELERATION_RATE)
    # ... 平滑减速
    self.app.move(self.current_linear_x, self.current_linear_y, self.current_angular_z)

设计要点:不是立即急停,而是以DECELERATION_RATE = 0.3的速率平滑减速。急停会导致四足机器人重心不稳甚至摔倒,平滑减速给了机身姿态调整的时间。这个细节体现了从“功能实现”到“工程可靠”的进阶。

2.3 多传感器状态发布

驱动层同时以固定频率发布多路传感器数据:

  • 电池电量(1Hz):低于阈值时自动执行卧倒动作,防止电池过放损坏

  • IMU数据(100Hz):包含方向四元数、角速度、线加速度

  • 里程计(100Hz):发布odom话题及TF变换

  • 关节状态(100Hz):12个关节的角度位置

值得一提的是里程计的处理方式——驱动节点维护了一个TF缓冲区,通过lookup_transform查找从odombase_link的变换来填充里程计消息。这种基于TF的里程计发布方式,天然保证了与其他模块(如SLAM)的坐标系一致性。

2.4 离散动作服务接口

除了连续的速度控制,驱动层还通过/control服务提供了离散动作的调用接口,包括:

  • STAND_UP / LIE_DOWN / PASSIVE(站立/卧倒/被动模式)

  • JUMP / FRONT_JUMP / BACK_FLIP(跳跃/前跳/后空翻)

  • SHAKE_HAND / TWO_LEG_STAND(握手/双腿站立)

这些动作为上层的“表演”或“交互”场景提供了直接的控制能力。


三、视频感知层:让机器狗“看见”

感知层负责采集和处理环境视觉信息,为OCR识别和远程监控提供数据源。

视频发布节点(agibot_d1_video_py)从RTSP视频流读取摄像头画面,转换为ROS 2的Image消息并以100Hz发布到/video_frames话题。

self.cap = cv2.VideoCapture(rtsp_url)
# ...
img_msg = self.bridge.cv2_to_imgmsg(frame, encoding="bgr8")
self.publisher_.publish(img_msg)

技术细节:RTSP地址同样支持AP模式和有线模式切换。在实际部署中,有线连接(192.168.168.168)比WiFi(192.168.234.1)更稳定,丢帧率显著降低——这对后续的OCR识别精度至关重要。


四、决策与交互层:让机器狗“听懂”和“看懂”

这是系统最具“智能感”的部分——语音交互与视觉识别。

4.1 语音交互链路

competition_master节点构建了一条完整的语音交互链路:

录音 → 语音识别(ASR) → 指令解析 → 任务执行 → 语音播报(TTS)

录音模块通过sounddevice采集麦克风音频,调用百度语音API将音频转为文本。指令解析采用关键词匹配策略——例如识别到“执行识别”触发OCR流程,识别到“去A/去B/去C”触发导航。

播报模块使用edge-tts生成语音并播放,并在edge-tts不可用时降级到espeakpyttsx3。这种多级降级策略保证了在依赖缺失的环境下系统仍能给出语音反馈。

4.2 OCR视觉识别

OCR识别流程是系统的一个亮点:

  1. 语音触发后倒计时5秒(给操作员准备时间)

  2. 从RTSP视频流抓取一帧图像

  3. 使用PaddleOCR进行文字识别

  4. 识别数字/字母并检测颜色

  5. 将标注结果保存为图片

  6. 语音播报识别结果

颜色检测的实现值得注意——代码在HSV空间中通过阈值判断文字颜色(黑/白/红/橙/黄/绿/蓝/紫)。这种传统图像处理方法虽然不如深度学习精确,但在标识牌颜色相对固定的场景下,轻量、快速、无需训练的优势非常明显。

# 颜色判定逻辑(简化)
if vv < 30: return '黑色'
if vv > 220 and sv < 30: return '白色'
if hv < 10 or hv > 170: return '红色'
# ... 更多颜色阈值

4.3 语音播报队列防冲突

一个容易被忽视但实际非常重要的设计:播报队列与防重入机制

语音播报是耗时操作(合成+播放可能需要数秒),如果多个事件同时触发播报,会导致声音重叠或线程冲突。代码通过锁保护的队列和is_speaking标志实现了串行播报:

with self.speech_queue_lock:
    if text not in self.speech_queue:  # 防重复
        self.speech_queue.append(text)

五、导航与感知层:让机器狗“认路”

有了驱动层提供的运动能力和感知层提供的环境信息,系统还需要一套完整的导航框架来实现自主移动。

5.1 导航管理

navigation_manager节点负责将语音指令转化为实际的导航任务。它维护了一组预设目标点(A/B/C),从YAML配置文件中加载坐标

当收到“移动到目标点A”的语音指令时,节点调用Nav2的NavigateToPose动作客户端发送导航目标,并订阅导航结果反馈进行语音播报。

5.2 障碍物检测与分类

obstacle_detector节点基于激光雷达数据进行障碍物检测,它的核心能力在于区分静态和动态障碍物

实现思路很巧妙:通过连续帧之间障碍物位置的变化量来判断:

  • 静态障碍物:位置几乎不变,连续多帧确认后触发告警“前方发现障碍物,正在绕行”

  • 动态障碍物:位置变化明显,触发告警“注意,前方有移动物体,正在避让”

这种分类能力让语音播报更加智能和人性化——告诉用户“有移动的人”和“有固定的墙”,体验是完全不同的。同时,节点将障碍物检测日志写入文件,方便事后复盘。


六、SLAM建图层:让机器狗“有地图”

导航的前提是“知道自己在哪里”,这依赖SLAM(同步定位与建图)技术。系统同时集成了两套SLAM方案,各司其职。

6.1 Cartographer:大场景建图

Cartographer擅长闭环检测与大场景建图。其核心是基于图优化的SLAM框架——将机器人的位姿作为图的节点,传感器观测作为边,通过非线性优化求解全局一致的位姿估计。

配置文件中的几个关键参数体现了针对四足机器人的特殊调优:

TRAJECTORY_BUILDER_2D.use_imu_data = false -- 关键:弃用IMU 
odometry_sampling_ratio = 0.001 -- 几乎不用里程计

为什么弃用IMU? 四足机器人在行走时机身持续高频摆动,廉价IMU的积分漂移极其严重。强行融合IMU反而会污染激光雷达的纯匹配结果。这个决策体现了工程中的“减法”智慧——不是传感器越多越好,而是要根据实际硬件特性做取舍。

6.2 Slam Toolbox:长期定位

Slam Toolbox则专注于长期运行中的定位稳定性。它支持建图模式和纯定位模式的无缝切换——地图建好后切换到定位模式,不再更新地图,只追踪机器人的位姿。

配置参数同样针对四足机器人做了优化:

minimum_travel_distance: 0.1      # 移动超过10cm才更新
minimum_travel_heading: 0.1       # 旋转超过0.1rad才更新
stack_size_to_use: 40000000       # 40MB栈空间防溢出

stack_size_to_use这个参数容易被忽视,但在大规模闭环优化时,栈溢出会导致节点崩溃——给足栈空间是工程健壮性的保障

6.3 双SLAM架构的协作逻辑

两套SLAM不是同时运行的,而是分工协作

  1. 建图阶段:使用Cartographer在环境中走一圈,构建全局地图

  2. 部署阶段:加载地图,切换到Slam Toolbox的定位模式

  3. 导航运行:Slam Toolbox持续提供高频率的位姿估计,Nav2基于此做路径规划

这种“Cartographer建图 + Toolbox定位”的组合,兼顾了建图的全局一致性(闭环优化)和长期运行的轻量性(不维护大规模图优化)。


七、总结与思考

回顾整个系统的构建过程,有几点思考值得分享:

第一,分层架构是系统复杂度的解药。 驱动、感知、决策、建图各层通过ROS 2的通信机制解耦,每一层都可以独立调试和替换。没有这种分层设计,面对数百行代码和多节点协同,调试将是一场噩梦。

第二,工程化的核心是“容错”而非“完美”。 看门狗超时停止、语音播报多级降级、障碍物检测的动态/静态分类——这些都不是最“炫酷”的技术,但它们保证了系统在真实环境中不会因为一个节点的异常而整体崩溃。

第三,传感器融合要做“减法”。 四足机器人的IMU精度有限,强行融合不如弃用。技术选型要基于实际硬件特性,而非理论上的“越多越好”。

第四,ROS 2生态提供了强大的“乐高积木”。 Cartographer、Nav2、PaddleOCR、百度语音API——这些成熟的开源工具和云服务大大降低了开发门槛。开发者的核心价值在于如何把这些积木搭建成一个稳定、可用的系统


目前这套系统已在睿抗机器人开发者大赛等场景中完成了实机验证,实现了从语音指令到自主导航再到视觉识别的完整闭环。未来,我们计划引入大模型(VLA)实现更自然的语义理解和任务规划,让机器狗从“听话执行”进化到“理解意图”。

希望这篇文章能为你的ROS 2机器人开发之路提供一些参考。欢迎在评论区交流讨论!

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐