AI Pulse

打造Reachy Mini的媒体栈:眼睛、耳朵和声音

打造Reachy Mini的媒体栈:眼睛、耳朵和声音

Reachy Mini 通过与摄像头和麦克风的感知来与环境互动。因此,音频和视频处理是这个小机器人的关键部分。Lite 和 Wireless 两个版本都可以在本地或远程使用,我们希望这对用户来说是无缝的:相同的代码在每种情况下都能工作。

这样做的回报是,你可以直接在 Reachy Mini 的音视频流之上构建 AI 应用,并在任何有意义的地方运行:机器人上、笔记本电脑上,或基于 GPU 的 Hugging Face Space 上。本文将介绍我们为实现这一目标所做的技术选择和设计。

硬件

在进入软件之前,我们先看看这些机器人用来感知世界的硬件。 Lite 和 Wireless 的组件列在各自文档页面上。它们大部分相同,但有一些值得注意的区别。

摄像头

在两个型号上,摄像头都是 Raspberry Pi Camera 3 Wide,具有以下特性: - Sony IMX708 图像传感器 - 12 MP - 自动对焦 - MIPI DSI 连接

两个摄像头都可以以 60 fps 的速度传输 1920×1080 的图像,这对于跟踪应用很有用。以下是 rpi cam 的特性: ` $ rpicam-vid --list-cameras Available cameras 0 : imx708_wide [4608x2592 10-bit RGGB] (/base/axi/pcie@1000120000/rp1/i2c@88000/imx708@1a) Modes: 'SRGGB10_CSI2P' : 1536x864 [120.13 fps - (768, 432)/3072x1728 crop] 2304x1296 [56.03 fps - (0, 0)/4608x2592 crop] 4608x2592 [14.35 fps - (0, 0)/4608x2592 crop] `

为了保证 USB 即插即用,Lite 的摄像头安装在一块定制电路板上,使其符合 UVC 标准。分辨率大致相似,但请注意,Lite 的摄像头输出的是压缩的 JPEG 图像,系统会自动解压给你。不过,根据你的应用,你可能更愿意直接访问 JPEG 帧。 ` v4l2-ctl --device=/dev/video4 --list-formats-ext ioctl: VIDIOC_ENUM_FMT Type: Video Capture

[0]: 'MJPG' (Motion-JPEG, compressed) Size: Discrete 3840x2592 Interval: Discrete 0.033s (30.000 fps) Size: Discrete 1920x1080 Interval: Discrete 0.017s (60.000 fps) Size: Discrete 3840x2160 Interval: Discrete 0.033s (30.000 fps) Size: Discrete 3264x2448 Interval: Discrete 0.033s (30.000 fps) `

在 macOS 上,GStreamer 的 avfvideosrc(AVFoundation)源会将帧率限制在 30 fps。

摄像头已使用 multical 进行标定,因此其参数可用于任何计算机视觉任务(请参见 look_at 示例)。

音频

麦克风是 Seeed 的 reSpeaker XVF3800 的定制版本,这是一个基于 XMOS XVF3800 语音处理芯片的麦克风阵列: - 4 个 PDM MEMS 数字麦克风,用硅胶垫隔离 - 16 kHz 最大采样率 / −26 dBFS 灵敏度 / 64 dBA 信噪比

我们为其搭配了一个 5 W / 4 Ω 扬声器。虽然有四个麦克风,但固件会将捕获的音频混合并清理成立体声信号。 该音频系统有两个突出特点: - 声学回波消除(AEC)滤除机器人自己扬声器发出的声音,这样 Reachy Mini 就不会自己跟自己说话。AEC 内置于麦克风中,始终开启。 - 到达方向(DoA)可在 180° 范围内定位声源。(麦克风阵列是线性的;需要圆形布局才能实现 360° 全覆盖。)DoA 通过 SDK 暴露,并在你的应用请求时按需读取。

麦克风在设计过程中与 Seeed 合作进行了校准。理想的设置是声源距离机器人约 1 米。如果默认配置不能满足你的需求,XMOS XVF3800 芯片会暴露许多参数供你调节。

有了硬件,真正的工作在于软件:将这些流传输到你的应用中,无论它在何处运行。

技术挑战

如今,访问摄像头流应该是微不足道的。Reachy Mini Lite 有一个 USB 摄像头(定制模块上的 Raspberry Pi Camera 3 Wide),可以直接从主机读取。Reachy Mini Wireless 使用相同的 Raspberry Pi Camera 3 Wide,但它位于机器人的嵌入式计算模块上,因此必须远程访问。我们的 SDK 隐藏了这种差异:相同的 API 在两个机器人上都返回一帧图像。

但为什么要止步于此呢?如果我们能远程传输 Wireless 摄像头的流,我们也能对 Lite 以及任何平台做同样的事情。 音频也有同样的需求,但多了一个曲折:它是双向的。Reachy Mini 既能听也能说。

为了用单一、一致的设计处理所有这些配置,我们选择了 GStreamer。

GStreamer

GStreamer 是一个开源、文档完善、维护良好且跨平台的多媒体库。除其他功能外,它可以访问摄像头和麦克风,然后将数据转换为显示或处理所需的任何格式。GStreamer 还附带了它自己的 WebRTC 实现,这是当前低延迟网络媒体流的标准(好奇的读者可以在这里找到很好的介绍)。

WebRTC 是浏览器用于实时视频通话的技术。它在两个对等点之间建立一个直接的低延迟连接,并承载音频、视频和任意数据。我们用它来传输机器人的摄像头和麦克风,以及发送控制命令回来。

GStreamer 庞大的插件生态系统也使得扩展 Reachy Mini 的功能变得容易。例如,语音转文本和文本转语音插件都可以直接使用。

唯一缺少的是通过 pip 轻松安装。Reachy Mini SDK 必须易于安装:只需 pip install reachy-mini 即可。因此,我们与 Centricular 合作,让 GStreamer 以 Python wheels 的形式提供给所有人。

选定了 GStreamer,我们来看看如何将各个部分组装到 Reachy Mini 的媒体架构中。

架构

架构在官方文档中有详细说明。以下部分分解了服务器端(机器人)和客户端(你的计算机)。

服务器端(Reachy Mini)

服务器端的目标是让本地和远程客户端都能访问摄像头和麦克风。

“本地”涵盖两种情况。在 Lite 上,SDK 从主机上通过 USB 连接的摄像头读取帧。在 Wireless 上,应用可以直接在嵌入式 Raspberry Pi 计算模块上运行。Wireless 也设计用于远程使用(用户可能希望用 SDK 获取帧并在另一台计算机上处理),而远程访问对于 Web 应用(见下文“远程应用”)也是必需的。因此,Lite 也需要暴露其音视频流。

为了覆盖所有这些情况,我们围绕两个输出构建了一个单一的 GStreamer 管道。摄像头流会同时转发给 webrtcsink 元素(用于远程客户端)和一个本地 IPC 端点:Linux 和 macOS 上是 unixfdsink,Windows 上是 win32ipcvideosink。这样分流是为了避免将摄像头锁定给单一消费者:远程查看器和本地应用可以同时读取它。

IPC(进程间通信)只是同一机器上两个进程之间的快速本地通道。这里它让设备上的应用可以直接从守护进程接收原始摄像头帧,中间没有编码或解码。

音频则更简单,因为声卡可以被多个客户端同时打开。在服务器端,我们只需要将音频路由到 WebRTC 元素。如果本地客户端想要打开音频设备,它可以直接通过 GStreamer 进行,无需 IPC。

对于那些熟悉 GStreamer 管道的人来说,这转化为: ` gst-launch-1.0 webrtcsink run-signalling-server=true meta="meta,name=reachymini" name=ws libcamerasrc ! capsfilter caps=video/x-raw,width=1280,height=720,framerate=60/1,format=YUY2,colorimetry=bt709,interlace-mode=progressive ! tee name=t t. ! queue ! v4l2h264enc extra-controls="controls,repeat_sequence_header=1" ! 'video/x-h264,level=(string)4' ! ws. t. ! queue ! videoconvert ! video/x-raw,format=BGR ! unixfdsink socket-path=/tmp/reachymini_camera_socket alsasrc device=reachymini_audio_src provide-clock=false ! ws. ` 这是针对 Reachy Mini Wireless(Raspberry Pi)的。管道在其他平台上略有调整。

客户端(你的计算机)

客户端与服务器端镜像。有两种情况:本地客户端从本地 IPC 端点读取帧,远程客户端通过 WebRTC 接收帧。SDK 会自动检测适用哪种情况:如果守护进程的本地端点可达,它使用 IPC;否则回退到 WebRTC。无论哪种方式,你的代码都调用相同的 API。文档中有许多示例,例如 look_at 或声音播放。

到目前为止,我们已经介绍了 SDK 如何读取流。但 WebRTC 路径打开的不仅仅是这些:它让应用可以在远离机器人的地方运行,无论是在浏览器中还是在远程服务器上。让我们看看它带来了什么。

在本地消费流的 GStreamer 管道是: ` gst-launch-1.0 unixfdsrc socket-path=/tmp/reachymini_camera_socket ! videoconvert ! fakesink alsasrc device=reachymini_audio_src ! queue ! audioconvert ! audioresample ! fakesink ` 而对于远程客户端: ` gst-launch-1.0 webrtcsrc signaller::uri="ws://<ip_robot>:8443" connect-to-first-producer=true name=src src. ! videoconvert ! autovideosink src. ! autoaudiosink ` 在我们的 SDK 中,sink 是 appsink 元素,它们将帧和音频样本暴露给你的代码,但这些管道在调试时仍然很方便!

远程应用

WebRTC 的一大优势是应用可以从任何地方消费音视频流。除了媒体,WebRTC 还支持双向数据交换,因此我们在同一连接上暴露控制 API。因此,远程应用不仅可以观看机器人,还可以驱动它。

WebRTC 旨在在机器人和应用之间建立点对点连接,这保持了低延迟。我们仅使用你的 Hugging Face 账户来将你的机器人与信令服务器上的应用配对:它标识两个端点,以便它们可以相互找到。即使一切都在你的本地网络上运行,这两个对等点仍然需要这个初始介绍。一旦完成,媒体直接在它们之间流动,你的账户不再参与。媒体是否真的直接流动取决于应用运行的位置,如接下来的两节所述。

信令服务器。在两个对等点能够直接通信之前,它们首先必须相互找到并商定如何连接(网络地址、编解码器、加密密钥)。信令服务器是协调者,负责传递这个初始握手。它只承载设置信息,从不承载音频或视频本身。

JavaScript 应用

reachy-mini-sdk 包让你将 TypeScript/JavaScript 应用连接到 Reachy Mini。WebRTC 示例展示了视频流、音频和机器人控制。

结合 Transformers.js,你可以以非常小的努力将最先进的模型带到你的机器人上。得益于 WebGPU,先进的 AI 模型可以直接在浏览器中运行,并且由于连接保持点对点,数据永远不会离开你的本地网络。例如,睡前故事生成器依赖本地 LLM (Gemma 3) 和本地 TTS (Kokoro) 来编写和讲述故事。

在底层,我们的 reachy-mini-sdk.js 提供了消耗机器人音视频流的 WebRTC 管道。它还提供了一个主机模块,使用你的账户处理与 Hugging Face Spaces 的连接。

一切都在浏览器中运行很好,但 WebGPU 受限于你机器上的 GPU,这可能不足以处理更大的模型。这时远程计算就派上用场了。

Hugging Face Spaces

Space 简而言之就是一台远程机器,并且可以配备强大的 GPU。你可以将 Reachy Mini 连接到 Space,以卸载更耗 GPU 的任务。例如,物体跟踪示例通过 WebRTC 消耗视频流,并发送回头部姿态,以便机器人跟随选定的物体。

与浏览器应用不同,Space 在数据中心运行,而不是在你的本地网络上,因此机器人和 Space 通常无法直接相互到达。在这种情况下,流会通过 TURN 服务器中继(我们依赖 fastrtc TURN 服务)。与完全点对点的浏览器路径相比,这增加了一跳。

TURN 服务器。当两个对等点无法建立直接连接时(由于防火墙、NAT 或其中一个位于数据中心),TURN 服务器作为中继介入:双方都连接到它,它负责在它们之间转发媒体。它保证了连接性,但代价是额外的一跳。

这种方法的一大优点是最终用户无需安装或下载任何东西。一切,包括可能重达数 GB 的模型,都存在于 Space 中。这使得 Spaces 成为共享 AI 驱动的 Reachy Mini 应用的绝佳方式:任何人都可以从浏览器尝试,机器人参与其中,计算由他们处理。

本地或远程计算是在功耗、延迟和隐私之间的权衡。

流式传输延迟

视频流的一个关键属性是帧到达之前你所经历的延迟。越低越好。对于任何闭环交互(如面部跟踪),帧需要尽可能快地到达。

我们端到端测量延迟,即玻璃到玻璃延迟:图像从摄像头镜头(第一块玻璃)到屏幕(最后一块玻璃)所需的时间。为了捕捉这一点,我们构建了一个简单的设备,由一个 LED 和一个光敏电阻组成。由于设备位于我们的系统之外(它测量 LED 闪烁到达光敏电阻所需的时间),它给出了一个真实的端到端测量。

本次测试我们使用了 Reachy Mini Wireless 和一个在 Windows 笔记本电脑上的 Chrome 中运行的 JavaScript 应用。笔记本电脑的 GPU 是 Intel® UHD Graphics (TGL GT1),带有硬件 H.264 解码器。机器人和笔记本电脑位于同一本地网络上,使用消费级 Wi-Fi(5 GHz),显示器为 140 Hz。

我们测量到总延迟约为 100 毫秒。

这个数字可能因你的设置而有很大变化。大致上,延迟来自三个方面: - 发送端(机器人)。硬件是固定的,但可以通过在低层级调整摄像头驱动、编码器或 GStreamer 管道来节省几毫秒,主要是通过避免额外的拷贝和不必要的处理。 - 网络。这很大程度上取决于连接质量以及从机器人到客户端的路径。在本地网络上,通过 Wi-Fi 连接 Reachy,并用网线将电脑连接到路由器会有所帮助。在长距离(跨互联网)上,WebRTC 的拥塞控制可以通过降低视频比特率来补偿延迟,不过在 Raspberry Pi 上,这只有在我们最近推送的一个补丁后才能生效。这里一个值得调整的参数是抖动缓冲区,它可以吸收数据包到达时间的变化:网络越不稳定,它就需要越大,以增加一点额外延迟换取更平滑的播放。展望未来,为低延迟挑战而设计的 QUIC 协议可能成为 WebRTC 的替代方案,而且已经有一个 GStreamer 插件存在。不过我们在本地网络条件下的初步测试并未显示出显著改善。 - 客户端。一旦帧到达,它需要被解码,所需时间取决于你的硬件:独立 GPU 比集成 GPU 更快。之后,将帧显示到屏幕上也有其自身的延迟。

结论

本文概述了使 Reachy Mini 的 Lite 和 Wireless 版本在本地或远程无缝工作的设计选择。结果是一个单一、一致的 API,位于你可以从 Python 或浏览器消费的音视频流之上,并为在其上构建 AI 应用提供了清晰的路径,无论你的模型是在机器人上、笔记本电脑上还是 Hugging Face Space 上运行。

像往常一样与 Pollen 一样,所有代码都是开源的。

阅读原文
📚 相关主题 工程开源

📬 订阅 AI Pulse

每天三次更新,不错过重要信号

▲ 回到顶部