为Open Duck Mini创建一个动画仪表板,使用Gemini Live API与机器人进行交互
我认为人工智能的下一个重大突破将出现在机器人领域,即将大语言模型(LLMs)的推理能力赋予实体身体。虽然关注机器人执行任务或日常事务的实用性是极具吸引力的发展方向,但我们常常忽视了人类用户与机器之间的互动。与其只关注机器人能做什么,我们更应思考机器人运动和行为如何引发使用者特定的期待和社会规范。
最近我一直在尝试使用Open Duck Mini,虽然终于让机器人能够移动头部、行走,甚至做一些其他事情,本身已经是一种令人惊叹的体验,但我还想花些时间探索人机交互(HRI)和角色塑造的主题。为此,我开发了一个定制的动画工具,用于定义机器人可以执行的动画动作,使机器人能够以人类自然识别为传达信息的方式进行动作,例如点头或摇头。
在定义了一些动画后,我将其与Gemini Live API结合使用。该API是人机交互(HRI)的优秀工具,它允许人们直接与机器人对话,并触发这些动作,从而使机器能够以恰当且可识别的方式与外界互动。本项目的所有代码,包括动画工具客户端、Flask API服务器以及播放脚本,均可在GitHub上获取。
需要注意的是,我在此录制时将所有持续时间都设置为300毫秒,因此动画会快速移动,然后直接进入下一个关键帧的插值。你可以通过延长步骤的持续时间并调整缓动函数来减慢动画速度,这一点我将在本文后续部分详细介绍。
对于今天要讨论的大多数中间件,我都能使用 Gemini 和 AI Studio 快速生成所需代码,从而在硬件控制与高级AI推理之间搭建桥梁,而无需陷入繁琐的设置过程。这让我从最初花费几天时间随意构思想法——通常是在园艺或居家做些事情时——到用不到一天的时间完成最终项目。不过,我也密切协调了生成的代码,并进行了审查,以避免出现意外问题。
以人为本:迪士尼在人机交互中的方法
我们可以从迪士尼的动画师和创意团队中学到很多东西,我非常欣赏他们在角色创作和人机交互方面所做的出色工作(如果你还不了解这方面的内容,一定要去了解一下他们“活体角色”(Living Characters)的概念以及他们在这一领域的实践;虽然有些混乱,但我非常喜欢)。迪士尼开发的角色有时已经在电影或电视剧中有所设定,但有时候它们是全新的,需要进一步细化,以明确角色在特定情境中的互动方式、身体上的特殊特征、说话习惯、是否带有特定口音或方言,以及其他诸多细节。他们会先塑造角色的性格,并确定角色表达快乐、好奇或挫败等情绪的具体方式,然后设计出能够体现这些特质的机器人外形。
虽然机器人在运行时并不一定需要个性,但在设计初期就明确这些特质有助于改善机器与周围人类之间的互动。通过在构建过程中确定机器人应表现出的个性特征,我们可以确保其物理行为符合人类社会的期待。迪士尼的作品,例如他们发布的自主行走的奥拉夫机器人,以及关于如何实现脚跟与脚掌正确步态的研究过程,都展示了将艺术动画原理与机器人技术相结合,能够创造出既富有生命力又具响应性的机器,而不仅仅是机械化的装置。
除了在机器人领域的研究工作外,多年来我一直将《生命幻象》放在书架上,因为它是一本关于动画的绝佳参考书(我从事绘画作为爱好已有几年),而且多年前我在担任安卓开发人员时也经常使用它。其中一些概念,比如卡通中表达现实的各种方式,尤其是关键帧之间插值时需要考虑的运动速度变化问题,对这个项目非常有帮助。
由于这个开源机器人平台基于《星球大战》中的机器人(作为第501师和反抗军军团服装慈善团体的一员,我清楚地知道“需要”制作一个),因此它的性格可以专注于可爱的嘟哝声和旋转动作,而非语言表达。再加上它两条腿的摆动方式类似于鸭子(因此得名“机器人”),我从早期唐老鸭动画中夸张的动作中汲取了灵感。
中间件:我们为何要自主研发
设计物理动作需要在动画师的意图与机器人的硬件之间建立直接联系。由于针对定制硬件开发的公共动画工具几乎不存在,我不得不自行开发一套流程,手动定位鸭子的位置,将这些电机配置记录为关键帧,并整合成结构化的动作计划,使其能够与音频文件同步播放,从而丰富机器人响应的人格特征。为了快速实现这一目标,我使用AI Studio生成了一个自定义的Flask API服务器,用于与机器人进行交互,同时还开发了一个配套的小程序,用于修改关键帧的设置。
使用动画工具设计手势
工作流程围绕一个运行在笔记本电脑上的配套小程序展开,该程序通过网络与机器人内部搭载的树莓派Zero 2W上的Flask服务器进行通信。这个小程序让我能够在实际调整机器人姿态时,实时读取电机位置(重点关注电机ID 30、31、32和33,即Open Duck Mini的颈部和头部电机),决定应启用哪些其他情绪化功能,例如眼睛和投影灯以及天线方向,并将整个动作序列保存为JSON格式的动作脚本,以便后续由机器人的Python代码读取执行。
使用SSH而非直接通过USB连接机器人有两个原因:首先,Raspberry Pi Zero 2W只有一个USB接口,我通过加装一个小型集线器扩展了它,从而添加了一个USB麦克风(本可以使用i2s麦克风,但我希望有更多选择)。其次,线缆管理相当麻烦,因此我希望减少对机器人的物理连接。此外,这个USB麦克风要求机器人处于USB主机模式,而不是外设模式,为了避免破坏现有的工作配置,我决定采用纯软件方案。同时,由于我已将树莓派配置为连接家庭网络,如果它无法找到该网络,就会自动开启热点,我可以连接到这个热点,从而从笔记本电脑建立连接,方便在家中远程操作机器人。
这是一个由工具生成的动作脚本示例,姿势处于中性位置,但每一帧都会成为关键帧数组中的另一个节点:
脚本定义了带有目标电机位置、灯光和天线角度的关键帧。它还指定了一个全局声音(如 beep1.wav),用于在运动过程中播放。
Flask API 服务器
运行在树莓派上的API服务器充当了高层动画设计与物理硬件之间的桥梁。它提供端点,用于在录制阶段读取当前电机位置,并执行已保存的JSON文件的回放以进行测试(另有独立的Python代码片段,可在机器人正常使用时直接回放动画)。
这段代码是自动生成的,但我已经规划好它的结构并检查了质量,现在让我们分小块来看一下。
1. 初始化与设置
首先,我们搭建Flask应用程序,启用跨域资源共享(CORS),以便笔记本电脑小程序能够与之通信,并初始化用于读取电机的硬件接口(HWI)。同时,我们还定义了LED灯、投影仪/手电筒、音频播放扬声器以及控制鸭子天线的小型舵机的GPIO引脚。
关于导入的说明:您会注意到我们是从 mini_bdx_runtime 进行导入的。这是原始 BDX 机器人项目中的核心运行时包,它封装了底层电机通信(通过 rustypot 与 Feetech 伺服使用的 Dynamixel 协议 2.0 进行交互),从而免去了我们自己编写原始串行字节数据包的麻烦。
2. 硬件辅助与减震功能
接下来,我们定义了一些辅助函数来控制物理组件、用于贝塞尔插值的数学辅助函数,以及硬件阻尼函数。
虽然软件中的贝塞尔插值处理的是数学路径,但我们还通过 rustypot 库动态调整电机的内部硬件限制(加速度和速度曲线),从而能够直接写入电机寄存器,实现诸如“粘滞”(缓慢、沉重)或“锁定”(快速但平滑)等运动效果。
3. 读取电机位置
要在小程序中记录关键帧,我们需要读取机器人的当前状态。/read 端点会查询硬件接口,并返回四个头部电机的当前位置。
4. 播放循环(手势与音频)
/play 端点是实现关键功能的地方。它接收 JSON 格式的动作脚本,异步触发音频播放(假设存在关联的音频文件),然后按严格的 30Hz 更新频率逐帧推进,并通过插值方式计算电机位置,以避免抖动。
硬件阻尼与软件时序
你可能会疑惑:既然我们已经在 Python 中计算贝塞尔曲线,为什么还需要写入电机的硬件寄存器?
在机器人技术中,最佳效果来自于软件定时与硬件阻尼的结合:
•软件时序(持续时间与缓动):Python 循环计算精确且同步的时间位置(例如每 30Hz),并将其发送至电机。这确保了所有关节(如颈部和头部)在完全相同的毫秒内到达目标位置,从而保持动作的协调性。同时,它还支持复杂的缓动曲线(如过冲或预判),而这些功能硬件无法自行计算。
•硬件阻尼(平滑与物理控制):Feetech STS3215 电机内置 PID 控制器和运动曲线。通过使用 rustypot 库对“曲线加速度”(地址 108)和“曲线速度”(地址 112)进行写入,我们可以调节硬件的物理响应方式。这相当于一个物理低通滤波器,可消除因 USB 到串口通信(即树莓派零连接电机的方式)造成的微小抖动,从而避免电机出现嗡鸣或抖动现象。
例如,贝塞尔粘性(bezier_viscous)会将硬件加速和速度限制,迫使电机在软件指令后方“拖拽”,从而产生沉重、流畅的“水下”感觉。相反,贝塞尔夹持(bezier_clamped)则能实现对快速动作的精准追踪,同时保持足够的加速度限制,以防止齿轮发生摩擦。当使用这些不同的插值器时,您需要考虑指定电机到达目标位置所需的时间,或提供足够长的帧后等待时间,使动画能够及时跟上,避免播放过程中出现中断。
与 Gemini Live API 实时交互
为了将这些静态动作转化为响应式行为,我将 Gemini Live API 集成到运行在机器人上的 Python 脚本中。Live API 特别适合人机交互,因为它支持低延迟、低开销的双向音频流传输,使用户能够自然地与机器对话。
我并未将模型配置为返回语音回复,而是通过结构化交互方式,让机器人完全依靠物理动作进行沟通。通过使用系统指令和函数调用,我指示Gemini充当一个无声的物理代理,将用户的语音转化为手势触发信号。该模型配置了以下系统指令:
你是一个实体机器人。请勿说话,仅使用工具作答。使用 trigger_action 命令,输入 'yes' 表示同意,'no' 表示不同意,或 'beep1' 表示中立回应。
这种设置确保当用户说话时,Gemini能够分析输入内容并选择最合适的动作来触发,从而强化社交规范,例如点头表示同意,而无需依赖合成语音。在更完善的项目中,我们可以添加数十种不同情绪和心情的动画,真正赋予机器人独特的个性。
让我们来看看如何构建 Live API 集成脚本。
1. 进口设备、硬件、音频及减震设置
我们首先导入必要的库(包括 google-genai SDK),配置音频参数,并设置硬件阻尼寄存器和辅助函数。脚本需要同时处理麦克风输入和扬声器输出,以及实时管理物理电机的阻尼。
2. 实时API配置
在此处,我们初始化 Gemini 客户端并定义 LiveConnectConfig 对象。我们将系统指令传递给该对象,并定义 trigger_action 工具,用于告知 Gemini Live API 可以执行哪些操作。
注意:您需要通过 pip install -q -U google-genai 安装依赖,并使用 export GEMINI_API_KEY="your_key" 导出您的 API 密钥。有关 Gemini API 密钥的更多信息,请参见此处。
3. 音频输入处理
这两个异步任务负责从物理麦克风捕获音频并发送到当前的 Gemini Live 会话中。由于 API 预期音频为 16kHz,而我的麦克风运行在 48kHz,因此我通过切片 numpy 数组来对 listen_audio 中的音频进行下采样。由于这取决于我自己的硬件修改,您的项目可能需要在此处做相应的调整。
如果你在树莓派上运行此程序时遇到 PyAudio 错误,很可能是 MIC_INDEX 或 SPEAKER_INDEX 与你的硬件不匹配。例如,当我同时使用两台硬件设备时,USB麦克风和扬声器的索引位置发生了交换,因此我不得不重新检查并更新代码中的索引值。你可以通过在树莓派上运行一个简单的 Python 脚本来找到具体的索引:
(注:此处为示例说明,实际应根据具体情况进行调整)
直接运行一下,查找你的USB麦克风和扬声器名称,并更新设置配置中的索引号。
4. 接收响应与执行操作
接收并触发任务会监听 Gemini 的响应。当模型决定触发某个操作时,它会发送一个工具调用请求。脚本拦截该请求,创建一个后台线程来播放通过动画工具生成的动画 JSON,并将成功响应返回给 Gemini。
5. 播放动画和音频输出
当触发某个动作时,play_animation_task 会加载对应的 JSON 文件,处理电机序列,并加载音频文件。由于 Live API 连接处于活动状态,我们使用共享的扬声器队列(audio_queue_output)和统一的 play_audio 任务将字节写入扬声器硬件。
由于这段代码较长,我们来将其分解为关键部分。
A. 负载行动计划
首先,该函数会查找并加载定义动画的JSON文件。如果文件不存在,将提前退出以防止崩溃。
B. 音频处理与队列管理
如果动画配有声音,我们将加载WAV文件。由于我们的硬件可能需要与文件不同的采样率,因此会进行实时重采样。此外,我们还会应用数字归一化和3倍增益提升,以便在将音频转换为PCM字节并推送到输出队列之前,能够清晰地听到鸭子的声音,同时屏蔽电机噪音。
C. 电机初始化
在移动电机之前,我们需要先启用扭矩并读取它们的当前位置,以便确定插补的起始位置。
D. 序列化循环
这是核心循环。对于每个关键帧,我们设置外围设备状态(灯光、投影仪、天线),并使用贝塞尔插值计算中间的电机位置。该循环以目标速率运行,根据需要进入睡眠状态,以保持严格的定时并避免抖动。
E. 清理与重置
动画完成后,我们关闭灯光和投影仪,对天线进行居中,并禁用电机的扭矩以防止过热。如果您的应用需要,您也可以在此处更改这些默认设置。
6. 统一的讲话任务与主循环
最后,play_audio 任务会持续监控 audio_queue_output,并将任何传出的音频字节写入扬声器硬件。run 函数负责连接会话并协调所有并发的任务。
未来探索
尽管Open Duck Mini是一款小型机器人,但该项目所涉及的工程挑战,如延迟管理、音频重采样、实时同步以及软硬件协同设计,对于了解大规模机器人开发中面临的难题具有重要意义。
利用生成式人工智能开发必要的中间件,使我能够在传统方式所需时间的极短时间内构建出完整的人机交互(HRI)流程。通过将Gemini等灵活模型与自定义的物理手势相结合,我们能够开始打造不仅能执行指令,还能以可识别的方式参与社交空间的机器人。
我有一个想法,就是将来想对这个机器人进行改进,让它变得更耐用一些。我想加装霓虹灯作为眼睛,根据机器人的“心情”来显示不同颜色;升级从树莓派Zero 2W到树莓派5或Jetson Orin Nano,并可能用12V电机替换原有的电机(包括必要的电源升级)。不过到时候我可能会干脆造一个全新的BDX机器人,虽然钱包不会太开心,但毕竟太酷了,我们拭目以待吧 :)
我期待进一步扩展这一设置,并希望这能鼓励其他开发者探索大语言模型与物理具身之间的交叉领域。为此,接下来要探索的工具包括用于感知和任务协调的 Gemini Robotics-ER 模型,以及 MuJoCo 模拟工具。
本文编译自hackster.io





