当前位置: 首页 > news >正文

【花雕动手做】嵌入式 AI Agent 机器人实战——迷你小龙虾 MimiClaw 的架构与主程序概览

导语:在经历了小车硬件准备,部署烧录、飞书对接、TLS 握手失败等一系列真实世界的打磨后,我们终于可以把目光从“跑起来”收回到“看懂它”上。MimiClaw 用一块25元的开发板和总量5000行纯 C 代码,完整实现了 AI Agent 的全栈模块。这里将尝试拆开它的架构与主程序,读懂设计背后那些精巧的逻辑与哲学。

第三部分,MimiClaw 的架构与主程序概览

这是官方给出的一张手绘架构图

一、为什么你需要懂它的结构?

如果你的目标是让机器人真正理解你、记住你、自主行动,那你迟早要面对代码,进行符合实际使用场景的二次开发。好在 MimiClaw 并没有把复杂性隐藏在黑盒里,它的 mimi 像是精细编排的交响乐,每个函数都承载着一个明确的设计意图。

从之前系列测试与实验中,遇到的飞书连接成功、电机控制正常,再到调用 deepseek-chat 模型时因证书验证失败引发的系统重启,这些经验都指向同一个事实:架构决定了调试的方向。现在我们就结合架构图与 mimi.c 的源码,对 MimiClaw 进行一次彻底的解剖。

二、整体架构:四个层次,一个大脑

MimiClaw 的系统可划分为清晰的四层,各层职责明确、协同工作,共同构成机器人的完整“生命体”:

  1. 通道层 — 耳朵与嘴巴
    Telegram、WebSocket(含飞书)是消息的核心入口。所有用户发送的指令(如“你好[握手]”)都将通过这里进入系统,并送入消息队列排队,等待后续处理,是机器人与外界交互的“桥梁”。

  2. 智能体核心 — 大脑的思考与决策
    这是 MimiClaw 最核心、最精彩的部分。Agent Loop 作为系统的“心跳”,持续从消息总线拉取消息,结合记忆(SOUL.md、MEMORY.md 等)和可用的技能/工具,判断是直接回答用户指令,还是调用云端 LLM 生成响应,最终将回复推回消息总线。

Tool Registry(工具注册表)中注册了 25 个实用工具,涵盖 LED 控制、电机驱动、文件读写、定时任务等,这些工具相当于机器人的“手和脚”,让思考决策能够转化为实际动作。

  1. 增强模块 — 让感官更丰富
    该模块为机器人赋予了物理感知能力,使其不再只是对话框中的文字,而是一个有“感知”的物理存在:IMU(惯性测量单元)用于感知设备的摇动、倾角;RGB LED 用于展示系统状态和“情绪”;物理按钮可实现紧急打断或模式切换,提升交互的便捷性和安全性。

  2. 存储层 — 记忆的根
    存储层是机器人“记忆”的载体,保障数据不丢失:SPIFFS 文件系统存放在 16MB Flash 中,用于存储人格定义、对话历史、技能脚本等核心数据;NVS(非易失性存储)用于保存 Wi-Fi 密码和运行时修改的配置(如通过 set_model deepseek-chat 写入的模型配置)。即使设备意外断电,记忆和配置也能完整保留。

三、mimi() 源码全解:一场精心编排的启动舞步

MimiClaw 主程序 mimi.c 源代码如下:

#include<stdio.h>#include<string.h>#include"freertos/FreeRTOS.h"#include"freertos/task.h"#include"esp_log.h"#include"esp_event.h"#include"esp_system.h"#include"esp_heap_caps.h"#include"esp_spiffs.h"#include"nvs_flash.h"#include"mimi_config.h"#include"bus/message_bus.h"#include"wifi/wifi_manager.h"#include"telegram/telegram_bot.h"#include"llm/llm_proxy.h"#include"agent/agent_loop.h"#include"memory/memory_store.h"#include"memory/session_mgr.h"#include"gateway/ws_server.h"#include"cli/serial_cli.h"#include"proxy/http_proxy.h"#include"tools/tool_registry.h"#include"display/display.h"#include"buttons/button_driver.h"#include"ui/config_screen.h"#include"imu/imu_manager.h"#include"rgb/rgb.h"#include"skills/skill_loader.h"staticconstchar*TAG="mimi";staticesp_err_tinit_nvs(void){esp_err_tret=nvs_flash_init();if(ret==ESP_ERR_NVS_NO_FREE_PAGES||ret==ESP_ERR_NVS_NEW_VERSION_FOUND){ESP_LOGW(TAG,"NVS partition truncated, erasing...");ESP_ERROR_CHECK(nvs_flash_erase());ret=nvs_flash_init();}returnret;}staticesp_err_tinit_spiffs(void){esp_vfs_spiffs_conf_tconf={.base_path=MIMI_SPIFFS_BASE,.partition_label=NULL,.max_files=10,.format_if_mount_failed=true,};esp_err_tret=esp_vfs_spiffs_register(&conf);if(ret!=ESP_OK){ESP_LOGE(TAG,"SPIFFS mount failed: %s",esp_err_to_name(ret));returnret;}size_ttotal=0,used=0;esp_spiffs_info(NULL,&total,&used);ESP_LOGI(TAG,"SPIFFS: total=%d, used=%d",(int)total,(int)used);returnESP_OK;}/* Outbound dispatch task: reads from outbound queue and routes to channels */staticvoidoutbound_dispatch_task(void*arg){ESP_LOGI(TAG,"Outbound dispatch started");while(1){mimi_msg_tmsg;if(message_bus_pop_outbound(&msg,UINT32_MAX)!=ESP_OK)continue;ESP_LOGI(TAG,"Dispatching response to %s:%s",msg.channel,msg.chat_id);if(strcmp(msg.channel,MIMI_CHAN_TELEGRAM)==0){telegram_send_message(msg.chat_id,msg.content);}elseif(strcmp(msg.channel,MIMI_CHAN_WEBSOCKET)==0){ws_server_send(msg.chat_id,msg.content);}elseif(strcmp(msg.channel,MIMI_CHAN_SYSTEM)==0){ESP_LOGI(TAG,"System message [%s]: %.128s",msg.chat_id,msg.content);}else{ESP_LOGW(TAG,"Unknown channel: %s",msg.channel);}free(msg.content);}}voidapp_main(void){/* Silence noisy components */esp_log_level_set("esp-x509-crt-bundle",ESP_LOG_WARN);ESP_LOGI(TAG,"========================================");ESP_LOGI(TAG," MimiClaw - ESP32-S3 AI Agent");ESP_LOGI(TAG,"========================================");/* Print memory info */ESP_LOGI(TAG,"Internal free: %d bytes",(int)heap_caps_get_free_size(MALLOC_CAP_INTERNAL));ESP_LOGI(TAG,"PSRAM free: %d bytes",(int)heap_caps_get_free_size(MALLOC_CAP_SPIRAM));/* Display + input */ESP_ERROR_CHECK(display_init());display_show_banner();ESP_ERROR_CHECK(rgb_init());rgb_set(255,0,0);button_Init();config_screen_init();imu_manager_init();imu_manager_set_shake_callback(config_screen_toggle);/* Phase 1: Core infrastructure */ESP_ERROR_CHECK(init_nvs());ESP_ERROR_CHECK(esp_event_loop_create_default());ESP_ERROR_CHECK(init_spiffs());/* Initialize subsystems */ESP_ERROR_CHECK(message_bus_init());ESP_ERROR_CHECK(memory_store_init());ESP_ERROR_CHECK(skill_loader_init());ESP_ERROR_CHECK(session_mgr_init());ESP_ERROR_CHECK(wifi_manager_init());ESP_ERROR_CHECK(http_proxy_init());ESP_ERROR_CHECK(telegram_bot_init());ESP_ERROR_CHECK(llm_proxy_init());ESP_ERROR_CHECK(tool_registry_init());ESP_ERROR_CHECK(cron_service_init());ESP_ERROR_CHECK(heartbeat_init());ESP_ERROR_CHECK(agent_loop_init());/* Start Serial CLI first (works without WiFi) */ESP_ERROR_CHECK(serial_cli_init());/* Start WiFi */esp_err_twifi_err=wifi_manager_start();if(wifi_err==ESP_OK){ESP_LOGI(TAG,"Scanning nearby APs on boot...");wifi_manager_scan_and_print();ESP_LOGI(TAG,"Waiting for WiFi connection...");if(wifi_manager_wait_connected(30000)==ESP_OK){ESP_LOGI(TAG,"WiFi connected: %s",wifi_manager_get_ip());/* Start network-dependent services */ESP_ERROR_CHECK(telegram_bot_start());ESP_ERROR_CHECK(agent_loop_start());cron_service_start();heartbeat_start();ESP_ERROR_CHECK(ws_server_start());/* Outbound dispatch task */xTaskCreatePinnedToCore(outbound_dispatch_task,"outbound",MIMI_OUTBOUND_STACK,NULL,MIMI_OUTBOUND_PRIO,NULL,MIMI_OUTBOUND_CORE);ESP_LOGI(TAG,"All services started!");}else{ESP_LOGW(TAG,"WiFi connection timeout. Check MIMI_SECRET_WIFI_SSID in mimi_secrets.h");}}else{ESP_LOGW(TAG,"No WiFi credentials. Set MIMI_SECRET_WIFI_SSID in mimi_secrets.h");}ESP_LOGI(TAG,"MimiClaw ready. Type 'help' for CLI commands.");}

完整的 mimi.c 本质上是上述架构图的线性实现,我们将其拆分为 5 个有明确意图的启动阶段,清晰看懂机器人的“启动逻辑”。

阶段一:硬件感官就绪 —— Wi-Fi 未连,感官先活

display_init();display_show_banner();// 屏幕亮起,打出 Logorgb_init();rgb_set(255,0,0);// 红灯:系统启动中button_Init();// 物理按钮就绪imu_manager_init();// 运动传感器开始工作imu_manager_set_shake_callback(config_screen_toggle);// 摇一摇切配置页

这 5 行代码蕴含着重要的设计理念:机器人应像生物一样,即使“大脑”(联网功能)尚未上线,感官和基础反射也要先正常工作。在烧录固件后等待 Wi-Fi 连接的几十秒内,设备会通过红灯和屏幕向用户传递“正在启动、正常运行”的信号,提升用户体验。

阶段二:基础地基 —— 搭建系统骨架

init_nvs();// 1. 键值库:记住你的 ApiKeyesp_event_loop_create_default();// 2. 事件循环:组件间的邮局init_spiffs();// 3. 文件系统:灵魂和记忆的硬盘

这三步是整个系统的“骨架”,缺一不可:没有 NVS,Wi-Fi 密码每次重启都需重新输入;没有事件循环,飞书 WebSocket 无法实现异步通信;没有 SPIFFS,SOUL.md 等核心文件无处存放,机器人也就没有了“灵魂”和“记忆”。

阶段三:大脑初始化 —— 服务全部“插上电”

message_bus_init();// 神经:消息总线memory_store_init();// 记忆:加载 MEMORY.md 等skill_loader_init();// 技能:加载可热插拔的扩展session_mgr_init();// 会话:管理多轮对话上下文// ...llm_proxy_init();// LLM 代理:包装 API 调用tool_registry_init();// 工具:注册 25 个执行函数cron_service_init();// 定时:计划任务heartbeat_init();// 心跳:存活报告agent_loop_init();// 主循环:Agent 决策任务

注意此处仅执行“初始化(init)”,未执行“启动(start)”。这种“初始化与启动分离”的设计,使得系统可在离线状态下完成所有核心组件的初始化,待 Wi-Fi 就绪后,再一键启动所有依赖网络的功能,提升系统灵活性。

阶段四:离线先行,在线随后 —— 鲁棒性的核心

serial_cli_init();// 串口 CLI 立刻开启,你随时可以插 USB 调试if(wifi_manager_wait_connected(30000)==ESP_OK){// 联网成功后,才启动这些需要网络的服务telegram_bot_start();agent_loop_start();cron_service_start();heartbeat_start();ws_server_start();// 创建出站派发任务,把回复正确地送回给你xTaskCreatePinnedToCore(outbound_dispatch_task,...);}

这是 MimiClaw 鲁棒性设计的核心:即使联网失败(如之前遇到的 TLS 崩溃),串口 CLI 依然可用,Wi-Fi AP 热点也能正常工作,不会出现“彻底死机”的情况。这种“离线底座”设计,为系统设置了不可突破的底线,确保调试和基础操作始终可用。

阶段五:双核任务分配 —— 高效协同,避免卡顿
ESP32-S3 拥有两个 CPU 核,MimiClaw 对双核进行了合理分工,保障核心功能的流畅性:

  • Core 0:运行 Wi-Fi 协议栈、LWIP、飞书 WebSocket 客户端的网络 I/O 以及串口 CLI,负责“对外通信”和“基础调试”。
  • Core 1:运行 Agent 决策循环和消息派发任务,负责“核心思考”和“动作执行”。

这种分工确保了对话响应和电机控制不会被网络波动卡住,因为最核心的智能体决策的运行环境是独立的,不受网络组件的干扰。

四、从调试经验回看架构

之前遇到的 TLS 错误,恰好印证了架构设计的合理性,也让我们更深刻地理解了各模块的关联:

E(1291766)esp-tls-mbedtls:Failed to set client configurations Guru Meditation Error:Core1panic'ed(IllegalInstruction)

该错误证实了架构图中 LLM Proxy 模块直接依赖 mbedTLS 组件,而错误的根源是 menuconfig 中未开启 TLS server verification。当我们理解了 llm_proxy_init() 与 SSL 库的关联后,修复这类问题就不再是盲目搜索,而是有明确方向的配置检查。

同样,飞书消息“Message from ou_… : 你好[握手]”能被成功处理,正是 outbound_dispatch_task 从消息总线取到回复后,根据 channel 字段正确路由回飞书的结果,这也体现了消息总线和出站派发任务的协同作用。

五、给你的动手路线图

在拥有可正常运行的设备、看懂系统架构后,可通过以下实践内化理解,二次开发亲手改造 MimiClaw:

  1. 离线模式实验
    故意留空 Wi-Fi 密码,刷入固件后观察:红灯是否正常亮起?屏幕是否有显示?串口 CLI 可输入哪些命令?通过该实验,能真切体会 MimiClaw“不联网也能正常工作”的架构优势。

  2. “摇一摇”触发配置切换
    拿起开发板快速晃动(需连接 IMU 和屏幕),观察配置页面是否会切换。config_screen_toggle 回调函数,正是架构图中“IMU → User Interface”这条交互链路的具体实现。

  3. 写一个自定义技能
    在 skills/ 目录下模仿已有 .c 文件,编写一个“情绪灯”技能,实现 skill_init(初始化)、skill_execute(执行)、skill_deinit(销毁)三个核心函数,再将其注册到 skill_loader 中。无需修改框架其他代码,即可为机器人新增一种行为。

  4. 调校机器人的灵魂
    通过串口 CLI 或直接编辑 SPIFFS 中的 SOUL.md 文件,定义机器人的人格。例如设置“用喵星人口吻回答,句尾必须加上‘咔嚓咔嚓’”,几分钟后,一个符合你预期人设的机器人就会诞生。

  5. 纯本地闭环
    假如编写一个 FreeRTOS 任务,每秒检查一次温度传感器数值。若温度超过 28°C 且 Agent 当前未处于移动状态,则直接驱动风扇。这是真正端侧智能的雏形,响应延迟可控制在毫秒级。

结语

MimiClaw 的真正价值,不在于它能让 ESP32 发出一条消息,而在于它提供了一个轻量、模块化、可离线的 AI Agent 开发底座。理解它的四层架构和启动流程,就相当于拿到了这个底座的“说明书”,从此可以尝试地添加传感器、执行器与大模型,而不会破坏系统原有的智能结构。

“迷你小龙虾”已经活了,它有了心跳、眼睛和手脚。接下来要做的,就是按照设想,逐步进行二次开发,一点一点教会它如何感知世界、如何自主行动。

MimiClaw 的架构与主程序概览相关参考资料
1、《【花雕学编程】全球首款 MCU 级 AI 智能体开盒:MimiClaw 架构全解析,把 “智能龙虾” 跑在 ESP32 上》
https://blog.csdn.net/weixin_41659040/article/details/159711424
2、《【花雕学编程】 AI 迷你小龙虾(MimiClaw)主程序:综合分析 + 深度解读 + 总体功能》
https://blog.csdn.net/weixin_41659040/article/details/159758142
3、《【花雕学AI】打破AI轻量化极限!MimiClaw:国产EP32S3 芯片上跑的纯C轻量AI Agent》
https://blog.csdn.net/weixin_41659040/article/details/152157480
4、《【花雕学编程】MimiClaw 嵌入式AI Agent系统架构剖析——基于FreeRTOS的多线程通信与外设协同机制》
https://blog.csdn.net/weixin_41659040/article/details/160352124

http://www.jsqmd.com/news/708062/

相关文章:

  • 奇异矩阵不止是数学错误:从数据质量到模型稳定的深度排查指南
  • WPF样式覆盖总失效?可能是你没搞懂MergedDictionaries的加载顺序
  • AWS无服务器网站搭建终极指南:S3+CloudFront静态托管教程
  • OBS-VST:在直播中实现专业音频处理的完整指南
  • 2026 年录音转文字工具亲子教育场景适配性横评:用记录优化亲子沟通
  • 在VSCode里跑OpenCV-Python,遇到Qt的‘xcb‘插件加载失败?一个环境变量就搞定
  • 基于LLM的智能数据分析:Streamline Analyst项目全解析
  • VisionMaster SDK 4.2 + C#避坑指南:从环境配置到结果获取的10个常见错误与解决方案
  • IDM插件拖不动?手把手教你用CRX文件搞定Chrome/Edge浏览器卡死问题
  • Zephyr CI/CD实战:用Twister自动化测试脚本,让你的每次提交都更安心
  • MiniCPM-o-4.5-nvidia-FlagOS实操手册:模型微调数据格式与LoRA适配器接入
  • 2025新范式:DeepSeek云资源智能管控,每年为企业节省60%云成本
  • Windows安装oracle19c oracle创建用户导入dmp
  • 移动端优化总结
  • 避坑指南:Geoserver 2.13/2.14版本为何与达梦DM8不兼容?附详细错误分析与替代方案
  • 桥接模式终极指南:如何实现抽象与实现的完美分离
  • CoreFreq开发者指南:如何扩展新的处理器架构支持
  • 深入理解 asyncio 跨线程调度:call_soon_threadsaf与 run_coroutine_threadsafe
  • 华硕笔记本性能优化新选择:G-Helper轻量级控制工具全面解析
  • Docker Compose一键部署TeamCity 2023.05.2(含MySQL/无MySQL两种配置)
  • DownKyi完整指南:快速掌握B站视频下载终极教程
  • 别再只会console.log了!用Node.js的os模块写个系统监控小工具(附完整源码)
  • 网盘直链下载助手:免费解锁八大主流网盘高速下载的完整指南
  • RAG系统构建全流程:从数据分块、向量化到检索优化与评估
  • 终极指南:如何使用jq流式处理大型JSON文件的内存优化技巧
  • 如何使用PyTorch Image Models构建高效特征存储:从提取到集成的完整指南
  • 从一次线上事故复盘:聊聊‘Duplicate entry’背后被忽略的并发问题与锁
  • 别再怕截图泄密!用PIMoG噪声层手把手教你打造抗屏摄的深度学习水印模型
  • 【Java】使用playwright来实现canvas前端画板UI自动化
  • React TypeScript Cheatsheet:侧边栏配置和文档组织终极指南