第二周:主要完成了duo s和麦克风和扬声器的成功连接,解决网络问题,连接云端和个人agent

1. 项目背景与硬件架构

情感陪伴机器人:Milk-V Duo S 外设驱动与网络踩坑全记录。
系统核心架构如下:

  • 主控平台:Milk-V Duo S (基于 SG2000 芯片, 256M 内存)
  • 音频交互外设:Lenovo MCP01(USB 接口全向麦克风/扬声器一体机)
  • AI 核心驱动:阿里云 DashScope 平台 qwen3-omni-flash-realtime 全双工语音模型
  • 通信协议:基于 WebSocket 的实时音频流传输 (PCM 16k Mono)

在项目推进过程中,从底层硬件驱动到系统级环境配置,遇到了诸如网络配置、USB 接口模式冲突、ALSA 音频重采样等一系列嵌入式开发典型问题。本文将详细记录这些核心难点的排查思路与最终解决方案。


2. 核心难点一:网络连接与 Python 运行环境修复

2.1 解决无头设备(Headless)的网络配置与伪 IP 问题

在初始阶段,虽然已配置了 wpa_supplicant,但设备无法 Ping 通外网。
排查过程:
通过 ip a 命令查看网络接口状态,发现 wlan0 分配到了 169.254.7.48。这是一个典型的 APIPA(本地链路地址),意味着 Wi-Fi 模块虽然在物理层连接了路由器,但未能成功从 DHCP 服务器获取有效的局域网 IP。
解决方案:
手动触发 DHCP 客户端请求,强制网卡获取 IP 地址:

# 强制通过 wlan0 接口获取 IP
udhcpc -i wlan0

2.2 绕过 SSL 证书校验陷阱(系统时钟问题)

在尝试使用 pip3 安装依赖时,系统频繁抛出 SSLCertVerificationError 导致安装失败。
根因分析:
Milk-V Duo S 开发板没有板载 RTC(实时时钟)电池。每次断电重启后,系统时间都会回退到默认初始时间(例如 1970 年)。当 pip 尝试连接 Python 镜像站时,由于本地时间远早于服务器 SSL 证书的签发时间,触发了安全拦截机制。
解决方案:
利用板子已连接的网络,通过 NTP 服务向阿里云时间服务器请求对时:

ntpd -q -p ntp.aliyun.com

2.3 依赖包命名冲突 (WebSocket)

运行主程序时报 AttributeError: module 'websocket' has no attribute 'WebSocketApp'
这是一个经典的 Python 库命名坑。系统默认安装的 websocket 库仅包含基础功能,不具备长连接所需的 WebSocketApp 类。
解决方案:

pip3 uninstall websocket
pip3 install websocket-client

3. 核心难点二:USB 控制器独占与 Host 模式切换

在连接 Lenovo MCP01 麦克风后,使用 arecord -l 无法查看到任何 USB 音频设备。

3.1 硬件底层限制分析

Milk-V Duo S 的主控芯片内部仅包含一个 USB 控制器。在官方默认固件中,该控制器被强制分配给开发板左侧的 Type-C 接口,工作在 Device(从机)模式,以支持通过 USB 虚拟网卡进行 SSH 连接(IP: 192.168.42.1)。
这就导致:开发板右侧用于挂载外设的 USB Type-A 接口实际上处于未激活/断电状态。

3.2 “移花接木”动态无损切换法

要驱动 USB 麦克风,必须将 USB 控制器切换为 Host(主机)模式。但一旦切换,Type-C 的 SSH 连接会瞬间断开,导致设备失联。为了在调试音频的同时保持 SSH 控制,使用了以下动态操作方案:

  1. 建立双通道连接:先通过 USB 虚拟网卡 (192.168.42.1) SSH 登录,确认板子连接上 Wi-Fi,并记录其真实的 Wi-Fi 局域网 IP。
  2. 开启备用控制台:在电脑上新开一个终端,通过 Wi-Fi IP 重新 SSH 登录开发板。
  3. 动态执行切换脚本:如下
    duo s usb type a 接口使用说明
    DuoS USB Type A 接口与 Type C 接口的 USB 功能是二选一的,不可以同时使用。默认固件配置的是 Type C 口的 USB 网口(USB-NCM)功能,如果需要切换为 Type A 口的 USB 2.0 HOST 口接 U 盘等设备使用,需要执行以下命令:
ln -sf /mnt/system/usb-host.sh /mnt/system/usb.sh
sync

然后执行 reboot 命令或重新上电使其生效。

比如 USB A 口接入 U 盘后,可以用 ls /dev/sd* 查看是否有检测到设备。

想恢复 Type C 口 的 USB 网卡(USB-NCM)功能时,执行:

rm /mnt/system/usb.sh
ln -sf /mnt/system/usb-ncm.sh /mnt/system/usb.sh
sync

执行完毕后,Type-C 网卡断开,但 Wi-Fi SSH 会话仍然存活。此时 Lenovo MCP01 麦克风成功通电亮灯,系统底层成功挂载该外设。

3.3 连接方式的改变

使用wifi功能后,电脑和duo板的连接发生改变。
之前是ssh root@192.168.42.1
现在根据相应连接的热点分配的ip地址改变ssh root@192.168.xx.xx

例如用手机连接,手机热点里可以看到连接设备的ip
电脑和开发板要在一个局域网下


4. 核心难点三:ALSA 音频子系统适配与调试

4.1 确认外设挂载节点

设备通电后,通过 ALSA 工具链确认其具体的系统映射节点:

[root@milkv-duo]~# arecord -l
**** List of CAPTURE Hardware Devices ****
card 2: MCP01 [MCP01], device 0: USB Audio [USB Audio]

确认麦克风和扬声器均挂载于 card 2, device 0

4.2 硬件直通 (hw) 与软件重采样 (plughw) 的抉择

在编写 Python 录播脚本时,需要指定音频设备名称。

  • 如果使用 hw:2,0:程序会直接请求底层硬件。由于阿里云大模型 API 强制要求 16000Hz 采样率单声道音频,若 USB 麦克风原生仅支持 48000Hz 双声道,程序会直接因格式不兼容而崩溃(抛出 Invalid argument)。
  • 最终方案:采用 ALSA 的 plughw:2,0 接口。
    plughw 在应用层与硬件层之间构建了一个插件层,能够在后台自动执行软件级的**重采样(Resampling)**和格式转换。这使得上层 Python 代码可以无视底层不同麦克风的硬件差异,稳定输出符合大模型要求的音频流。

4.3 音频通道激活

部分 USB 声卡初始接入时通道处于锁定或静音状态,使用命令行工具直接拉满增益并解除静音(unmute):

# 将 card 2 的 Speaker 音量设为 100% 并解除静音
amixer -c 2 sset 'Speaker' 100% unmute

5. 核心交互代码整合

经过上述底层调优,最终主程序配置区的核心代码整合如下:

# ================= 配置区 =================
# API 鉴权
API_KEY = "sk-xxxxxxxxxxxxxxxxxxxxxxxx"
WS_URL = "wss://[dashscope.aliyuncs.com/api-ws/v1/realtime](https://dashscope.aliyuncs.com/api-ws/v1/realtime)"
# 使用极速响应的 flash-realtime 模型
MODEL_NAME = "qwen3-omni-flash-realtime"

# 采用 plughw 接口保障 16kHz 采样率的自适应转换
RECORD_DEVICE = "plughw:2,0"   # 麦克风录音设备
PLAY_DEVICE = "plughw:2,0"     # 扬声器播放设备
# =========================================

6. 项目阶段性总结

项目重点难点集中在软硬件交互的底层逻辑上。通过解决网络时钟同步、单控制器下的 USB Host/Device 动态调度,以及 ALSA 音频子系统的重采样配置,“物理麦克风收音 -> 阿里云大模型实时处理 -> 物理扬声器发声”的全链路闭环。不仅加深了对嵌入式 Linux 系统(尤其是外设驱动与网络栈)的理解,也为后续复杂行为树的开发奠定了坚实的底层硬件基础。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐