四博 AI 机械臂台灯智能音箱方案
四博 AI 机械臂台灯智能音箱方案
基于 ESP32-S3 打造带视觉感知、机械臂控制和学习陪伴能力的 AI 桌面终端
传统台灯只解决照明问题,传统音箱只解决语音交互问题。而四博 AI 机械臂台灯智能音箱,可以把照明、语音、视觉、机械臂、学习陪伴、环境感知、智能家居控制融合到一个桌面终端中。
它不是普通台灯,也不是普通 AI 音箱,而是一个具备“看、听、说、动、照明、陪伴、提醒、控制”能力的 AI 桌面机器人。
四博 AI 机械臂台灯 = ESP32-S3 AI 主控 + 摄像头视觉识别 + 远场语音交互 + TTS 语音播放 + 机械臂云台 / 灯头控制 + 智能照明调光 + 屏幕 / 双目屏显示 + 学习陪伴智能体 + 天气与环境识别 + MCP 工具调用 + OTA 在线升级一、产品定位
四博 AI 机械臂台灯智能音箱可以定位为:
AI 桌面学习陪伴终端 + 视觉感知台灯 + AI 语音音箱 + 智能照明控制器 + 桌面机械臂机器人 + 家庭 AI 助手 + CozyLife 智能家居入口它可以实现以下场景:
1. 看窗外天气:通过摄像头识别窗外光照、天空状态、雨雾情况 2. 看周边环境:识别桌面亮度、用户是否在座位、书本/作业区域 3. 学习陪伴:语音问答、英语陪练、古诗背诵、口算训练、学习提醒 4. 自动调灯:根据环境亮度和学习状态调节亮度 / 色温 5. 机械臂跟随:灯头自动转向书桌、书本、用户位置 6. AI 对话:接入小智、豆包、ChatGPT 或客户自有智能体 7. 智能家居控制:通过 MCP / MQTT / HTTP 控制灯具、空调、窗帘等设备 8. OTA 升级:持续更新固件、提示音、识别模型、表情素材和工具列表四博 AI 硬件选型资料里已经展示了 AI 智能相机、AI 智能小夜灯、AI 智能音响、AI-S3 双目双屏等形态,这些能力可以组合成“带视觉与机械臂控制的 AI 台灯音箱”产品形态。
二、硬件方案设计
推荐采用ESP32-S3R8 / ESPS3-32-N16R8作为主控。ESP32-S3 适合做 AI 音频、摄像头、LCD、触控和机械控制的组合方案。模组选型手册中 ESPS3-32 系列包含 N4、N8、N8R2、N16R2、N16R8 等多个规格,可按显示、视觉、音频缓存、OTA 空间需求选择不同版本。
推荐硬件配置
| 模块 | 推荐配置 |
|---|---|
| 主控 | ESP32-S3R8 / ESPS3-32-N16R8 |
| Flash | 16MB |
| PSRAM | 8MB |
| 摄像头 | OV2640 / GC2145 / USB Camera 方案可选 |
| 音频输入 | I2S 数字麦克风 / 双麦阵列 |
| 音频输出 | I2S Codec + Class-D 功放 |
| 喇叭 | 4Ω 3W |
| 屏幕 | 1.54 寸 / 2.0 寸 LCD,或双目屏 |
| 机械臂 | 2~4 自由度舵机 / 步进电机 / 云台结构 |
| 灯光 | PWM 调光,支持亮度和色温 |
| 传感器 | 环境光、温湿度、人体感应、三轴姿态可选 |
| 联网 | Wi-Fi + BLE,4G 可扩展 |
| 供电 | Type-C 5V / 12V 适配器,电池可选 |
| 升级 | HTTPS OTA / 在线烧录平台 |
三、系统总体架构
用户语音 / 桌面动作 / 周边环境 ↓ 麦克风 + 摄像头 + 环境传感器 ↓ ESP32-S3 AI 主控 ↓ 音频前处理 / 视觉采集 / 机械臂控制 / 屏幕显示 ↓ Wi-Fi / BLE / 4G ↓ ASR / LLM / TTS / 视觉识别 / MCP 工具调用 ↓ 台灯照明 / 机械臂动作 / 语音回复 / 学习陪伴 / 智能家居控制设备端负责:
1. 麦克风采集 2. 喇叭播放 3. 摄像头图像采集 4. 环境亮度检测 5. 机械臂运动控制 6. 灯光亮度 / 色温控制 7. 屏幕显示 8. Wi-Fi / BLE 联网 9. WebSocket AI 通信 10. MCP 工具执行 11. OTA 升级云端负责:
1. ASR 语音识别 2. LLM 大模型理解 3. TTS 语音合成 4. 图像识别 / 视觉理解 5. 天气判断 / 环境分析 6. 学习陪伴智能体 7. 知识库问答 8. MCP 工具调度四、软件模块设计
推荐工程结构如下:
doit_ai_arm_lamp/ ├── main/ │ ├── app_main.c │ ├── board_config.h │ ├── wifi_manager.c │ ├── audio_capture.c │ ├── audio_player.c │ ├── camera_service.c │ ├── vision_service.c │ ├── arm_servo.c │ ├── lamp_light.c │ ├── sensor_service.c │ ├── study_assistant.c │ ├── ai_ws_client.c │ ├── mcp_service.c │ ├── lcd_ui.c │ └── ota_service.c ├── components/ │ ├── camera_driver/ │ ├── lcd_driver/ │ ├── codec/ │ ├── servo_driver/ │ └── audio_afe/ ├── partitions_ota.csv └── sdkconfig.defaults系统状态机:
typedef enum { SYS_STATE_BOOT = 0, SYS_STATE_WIFI_CONFIG, SYS_STATE_IDLE, SYS_STATE_LISTENING, SYS_STATE_THINKING, SYS_STATE_SPEAKING, SYS_STATE_VISION_ANALYZE, SYS_STATE_ARM_MOVING, SYS_STATE_STUDY_MODE, SYS_STATE_SLEEP_MODE, SYS_STATE_OTA, SYS_STATE_ERROR } system_state_t; static volatile system_state_t g_sys_state = SYS_STATE_BOOT;五、板级配置示例
#pragma once #include "driver/gpio.h" #include "driver/i2c.h" #include "driver/spi_master.h" #include "driver/ledc.h" /* 产品名称 */ #define BOARD_NAME "DOIT_AI_ARM_LAMP_S3" /* I2S Audio */ #define PIN_I2S_BCLK GPIO_NUM_4 #define PIN_I2S_WS GPIO_NUM_5 #define PIN_I2S_DIN GPIO_NUM_6 #define PIN_I2S_DOUT GPIO_NUM_7 #define PIN_I2S_MCLK GPIO_NUM_16 /* I2C: Sensor / Codec */ #define I2C_PORT_MAIN I2C_NUM_0 #define PIN_I2C_SDA GPIO_NUM_8 #define PIN_I2C_SCL GPIO_NUM_9 /* Camera */ #define PIN_CAM_XCLK GPIO_NUM_10 #define PIN_CAM_PCLK GPIO_NUM_11 #define PIN_CAM_VSYNC GPIO_NUM_12 #define PIN_CAM_HREF GPIO_NUM_13 #define PIN_CAM_SIOD GPIO_NUM_8 #define PIN_CAM_SIOC GPIO_NUM_9 #define PIN_CAM_D0 GPIO_NUM_14 #define PIN_CAM_D1 GPIO_NUM_15 #define PIN_CAM_D2 GPIO_NUM_17 #define PIN_CAM_D3 GPIO_NUM_18 #define PIN_CAM_D4 GPIO_NUM_19 #define PIN_CAM_D5 GPIO_NUM_20 #define PIN_CAM_D6 GPIO_NUM_21 #define PIN_CAM_D7 GPIO_NUM_47 /* LCD SPI */ #define LCD_HOST SPI2_HOST #define PIN_LCD_MOSI GPIO_NUM_35 #define PIN_LCD_SCLK GPIO_NUM_36 #define PIN_LCD_CS GPIO_NUM_37 #define PIN_LCD_DC GPIO_NUM_38 #define PIN_LCD_RST GPIO_NUM_39 #define PIN_LCD_BL GPIO_NUM_40 /* Servo PWM */ #define PIN_SERVO_BASE GPIO_NUM_1 #define PIN_SERVO_SHOULDER GPIO_NUM_2 #define PIN_SERVO_ELBOW GPIO_NUM_3 #define PIN_SERVO_LAMP_HEAD GPIO_NUM_41 /* LED Light PWM */ #define PIN_LED_COLD GPIO_NUM_42 #define PIN_LED_WARM GPIO_NUM_45 /* User keys */ #define PIN_KEY_WAKE GPIO_NUM_0 #define PIN_KEY_MODE GPIO_NUM_46 /* Audio */ #define AUDIO_SAMPLE_RATE 16000 #define AUDIO_FRAME_MS 20 #define AUDIO_FRAME_SAMPLES (AUDIO_SAMPLE_RATE * AUDIO_FRAME_MS / 1000)六、主程序框架
#include "esp_log.h" #include "nvs_flash.h" #include "esp_event.h" #include "esp_netif.h" #include "board_config.h" #include "wifi_manager.h" #include "audio_capture.h" #include "audio_player.h" #include "camera_service.h" #include "vision_service.h" #include "arm_servo.h" #include "lamp_light.h" #include "sensor_service.h" #include "study_assistant.h" #include "ai_ws_client.h" #include "mcp_service.h" #include "lcd_ui.h" #include "ota_service.h" static const char *TAG = "AI_ARM_LAMP"; void app_main(void) { ESP_LOGI(TAG, "%s booting...", BOARD_NAME); ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); lcd_ui_init(); lcd_ui_show_status("四博AI机械臂台灯启动中"); wifi_manager_init(); audio_capture_init(); audio_player_init(); camera_service_init(); vision_service_init(); arm_servo_init(); lamp_light_init(); sensor_service_init(); study_assistant_init(); ai_ws_client_init(); mcp_service_init(); ota_service_init(); wifi_manager_start(); xTaskCreatePinnedToCore(audio_capture_task, "audio_capture", 8192, NULL, 5, NULL, 0); xTaskCreatePinnedToCore(ai_ws_client_task, "ai_ws_client", 10240, NULL, 6, NULL, 1); xTaskCreatePinnedToCore(camera_service_task, "camera_service", 8192, NULL, 4, NULL, 1); xTaskCreatePinnedToCore(vision_service_task, "vision_service", 8192, NULL, 4, NULL, 1); xTaskCreatePinnedToCore(sensor_service_task, "sensor_service", 4096, NULL, 3, NULL, 0); xTaskCreatePinnedToCore(ota_service_task, "ota_service", 6144, NULL, 3, NULL, 0); arm_servo_home(); lamp_set_brightness_color_temp(60, 4000); g_sys_state = SYS_STATE_IDLE; lcd_ui_show_status("AI台灯已就绪"); }七、视觉功能设计:看窗外天气和周边环境
1. 视觉能力定义
四博 AI 机械臂台灯的视觉功能可以分成三类:
1. 窗外天气观察 - 识别晴天 / 阴天 / 雨天 / 夜晚 - 判断窗外亮度 - 辅助决定是否需要开灯 2. 周边环境识别 - 识别用户是否在座位 - 识别桌面是否过暗 - 识别书本 / 作业区域 - 判断是否适合学习 3. 学习陪伴 - 拍摄书本或作业区域 - 提醒坐姿和学习时长 - 结合大模型进行语音讲解多模态 AI 开发宝典中的 DOIT_ESPS3_AI_EYE_Vision 方案已经提供摄像头、双目显示、触摸交互和 ESP32-S3 开发路径,适合作为视觉 AI 终端开发参考。
2. 摄像头初始化示例
#include "esp_camera.h" #include "board_config.h" esp_err_t camera_service_init(void) { camera_config_t config = { .pin_xclk = PIN_CAM_XCLK, .pin_pclk = PIN_CAM_PCLK, .pin_vsync = PIN_CAM_VSYNC, .pin_href = PIN_CAM_HREF, .pin_sscb_sda = PIN_CAM_SIOD, .pin_sscb_scl = PIN_CAM_SIOC, .pin_d0 = PIN_CAM_D0, .pin_d1 = PIN_CAM_D1, .pin_d2 = PIN_CAM_D2, .pin_d3 = PIN_CAM_D3, .pin_d4 = PIN_CAM_D4, .pin_d5 = PIN_CAM_D5, .pin_d6 = PIN_CAM_D6, .pin_d7 = PIN_CAM_D7, .pin_pwdn = -1, .pin_reset = -1, .xclk_freq_hz = 20000000, .pixel_format = PIXFORMAT_JPEG, .frame_size = FRAMESIZE_QVGA, .jpeg_quality = 12, .fb_count = 2, .grab_mode = CAMERA_GRAB_LATEST, }; return esp_camera_init(&config); }3. 拍照并上传 AI 视觉分析
esp_err_t vision_capture_and_upload(const char *task) { camera_fb_t *fb = esp_camera_fb_get(); if (!fb) { lcd_ui_show_status("摄像头采集失败"); return ESP_FAIL; } /* * task 可选: * "weather_view" 看窗外天气 * "desk_view" 看桌面环境 * "study_help" 学习陪伴识别 */ esp_err_t ret = ai_ws_send_image(task, fb->buf, fb->len); esp_camera_fb_return(fb); return ret; }4. 视觉任务调度
typedef enum { VISION_TASK_NONE = 0, VISION_TASK_WEATHER, VISION_TASK_DESK_ENV, VISION_TASK_STUDY_HELP, VISION_TASK_USER_POSITION } vision_task_t; void vision_run_task(vision_task_t task) { switch (task) { case VISION_TASK_WEATHER: lcd_ui_show_status("正在观察窗外天气"); vision_capture_and_upload("weather_view"); break; case VISION_TASK_DESK_ENV: lcd_ui_show_status("正在分析桌面环境"); vision_capture_and_upload("desk_view"); break; case VISION_TASK_STUDY_HELP: lcd_ui_show_status("正在分析学习内容"); vision_capture_and_upload("study_help"); break; case VISION_TASK_USER_POSITION: lcd_ui_show_status("正在检测用户位置"); vision_capture_and_upload("user_position"); break; default: break; } }八、机械臂控制设计
机械臂台灯的关键是“灯头可以动”。它可以自动转向用户、书本、窗户或指定方向。
1. 自由度设计
推荐机械结构:
2 自由度:底座左右旋转 + 灯头俯仰 3 自由度:底座旋转 + 大臂俯仰 + 灯头俯仰 4 自由度:底座旋转 + 大臂 + 小臂 + 灯头舵机控制结构:
typedef enum { SERVO_BASE = 0, SERVO_SHOULDER, SERVO_ELBOW, SERVO_HEAD, SERVO_MAX } servo_id_t; typedef struct { gpio_num_t gpio; int min_angle; int max_angle; int current_angle; ledc_channel_t channel; } servo_t; static servo_t g_servos[SERVO_MAX] = { [SERVO_BASE] = { .gpio = PIN_SERVO_BASE, .min_angle = 0, .max_angle = 180, .current_angle = 90, .channel = LEDC_CHANNEL_0, }, [SERVO_SHOULDER] = { .gpio = PIN_SERVO_SHOULDER, .min_angle = 20, .max_angle = 160, .current_angle = 90, .channel = LEDC_CHANNEL_1, }, [SERVO_ELBOW] = { .gpio = PIN_SERVO_ELBOW, .min_angle = 20, .max_angle = 160, .current_angle = 90, .channel = LEDC_CHANNEL_2, }, [SERVO_HEAD] = { .gpio = PIN_SERVO_LAMP_HEAD, .min_angle = 30, .max_angle = 150, .current_angle = 90, .channel = LEDC_CHANNEL_3, }, };2. 舵机 PWM 输出
#define SERVO_MIN_US 500 #define SERVO_MAX_US 2500 #define SERVO_PERIOD_US 20000 static uint32_t servo_angle_to_duty(int angle) { int pulse_us = SERVO_MIN_US + (SERVO_MAX_US - SERVO_MIN_US) * angle / 180; /* * LEDC 16bit 分辨率 */ return (uint32_t)((pulse_us * 65535) / SERVO_PERIOD_US); } void servo_set_angle(servo_id_t id, int angle) { if (id >= SERVO_MAX) { return; } servo_t *s = &g_servos[id]; if (angle < s->min_angle) { angle = s->min_angle; } if (angle > s->max_angle) { angle = s->max_angle; } uint32_t duty = servo_angle_to_duty(angle); ledc_set_duty(LEDC_LOW_SPEED_MODE, s->channel, duty); ledc_update_duty(LEDC_LOW_SPEED_MODE, s->channel); s->current_angle = angle; }3. 机械臂初始化
void arm_servo_init(void) { ledc_timer_config_t timer = { .speed_mode = LEDC_LOW_SPEED_MODE, .timer_num = LEDC_TIMER_0, .duty_resolution = LEDC_TIMER_16_BIT, .freq_hz = 50, .clk_cfg = LEDC_AUTO_CLK, }; ledc_timer_config(&timer); for (int i = 0; i < SERVO_MAX; i++) { ledc_channel_config_t ch = { .gpio_num = g_servos[i].gpio, .speed_mode = LEDC_LOW_SPEED_MODE, .channel = g_servos[i].channel, .timer_sel = LEDC_TIMER_0, .duty = 0, .hpoint = 0, }; ledc_channel_config(&ch); } arm_servo_home(); } void arm_servo_home(void) { servo_set_angle(SERVO_BASE, 90); servo_set_angle(SERVO_SHOULDER, 90); servo_set_angle(SERVO_ELBOW, 90); servo_set_angle(SERVO_HEAD, 90); }4. 常用动作
void arm_point_to_book(void) { servo_set_angle(SERVO_BASE, 90); servo_set_angle(SERVO_SHOULDER, 70); servo_set_angle(SERVO_ELBOW, 110); servo_set_angle(SERVO_HEAD, 80); } void arm_point_to_window(void) { servo_set_angle(SERVO_BASE, 150); servo_set_angle(SERVO_SHOULDER, 85); servo_set_angle(SERVO_ELBOW, 95); servo_set_angle(SERVO_HEAD, 75); } void arm_sleep_pose(void) { servo_set_angle(SERVO_BASE, 90); servo_set_angle(SERVO_SHOULDER, 130); servo_set_angle(SERVO_ELBOW, 140); servo_set_angle(SERVO_HEAD, 120); }九、灯光控制设计
台灯部分建议支持:
亮度调节:0~100% 色温调节:2700K~6500K 学习模式:高亮中性光 阅读模式:柔和暖白光 休息模式:低亮暖光 自动模式:根据环境光自动调节1. 灯光参数结构
typedef struct { int brightness; // 0~100 int color_temp; // 2700~6500 int auto_mode; } lamp_config_t; static lamp_config_t g_lamp = { .brightness = 60, .color_temp = 4000, .auto_mode = 0, };2. 冷暖光 PWM 控制
void lamp_light_init(void) { ledc_timer_config_t timer = { .speed_mode = LEDC_LOW_SPEED_MODE, .timer_num = LEDC_TIMER_1, .duty_resolution = LEDC_TIMER_12_BIT, .freq_hz = 2000, .clk_cfg = LEDC_AUTO_CLK, }; ledc_timer_config(&timer); ledc_channel_config_t cold = { .gpio_num = PIN_LED_COLD, .speed_mode = LEDC_LOW_SPEED_MODE, .channel = LEDC_CHANNEL_4, .timer_sel = LEDC_TIMER_1, .duty = 0, .hpoint = 0, }; ledc_channel_config_t warm = { .gpio_num = PIN_LED_WARM, .speed_mode = LEDC_LOW_SPEED_MODE, .channel = LEDC_CHANNEL_5, .timer_sel = LEDC_TIMER_1, .duty = 0, .hpoint = 0, }; ledc_channel_config(&cold); ledc_channel_config(&warm); }3. 设置亮度和色温
void lamp_set_brightness_color_temp(int brightness, int color_temp) { if (brightness < 0) brightness = 0; if (brightness > 100) brightness = 100; if (color_temp < 2700) color_temp = 2700; if (color_temp > 6500) color_temp = 6500; /* * 2700K 主要暖光,6500K 主要冷光。 */ float ratio = (float)(color_temp - 2700) / (6500 - 2700); int cold_percent = brightness * ratio; int warm_percent = brightness * (1.0f - ratio); uint32_t cold_duty = cold_percent * 4095 / 100; uint32_t warm_duty = warm_percent * 4095 / 100; ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_4, cold_duty); ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_4); ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_5, warm_duty); ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_5); g_lamp.brightness = brightness; g_lamp.color_temp = color_temp; }4. 学习模式 / 阅读模式 / 休息模式
void lamp_mode_study(void) { lamp_set_brightness_color_temp(85, 4500); arm_point_to_book(); lcd_ui_show_status("学习模式已开启"); } void lamp_mode_reading(void) { lamp_set_brightness_color_temp(70, 4000); arm_point_to_book(); lcd_ui_show_status("阅读模式已开启"); } void lamp_mode_rest(void) { lamp_set_brightness_color_temp(25, 3000); arm_sleep_pose(); lcd_ui_show_status("休息模式已开启"); }十、AI 语音与 WebSocket 通信
推荐采用 WebSocket 长连接,实现低延迟语音交互、视觉任务下发、TTS 音频回传和 MCP 工具调用。
1. 设备上线消息
{ "type": "hello", "device_id": "doit_ai_arm_lamp_001", "product": "doit_ai_arm_lamp", "firmware": "1.0.0", "features": { "audio": true, "vision": true, "arm": true, "lamp": true, "study_assistant": true, "weather_view": true, "mcp": true, "ota": true } }2. AI 消息处理
static void ai_ws_handle_json(const char *data, int len) { char *buf = calloc(1, len + 1); if (!buf) return; memcpy(buf, data, len); cJSON *root = cJSON_Parse(buf); if (!root) { free(buf); return; } cJSON *type = cJSON_GetObjectItem(root, "type"); if (cJSON_IsString(type)) { if (strcmp(type->valuestring, "tts_start") == 0) { audio_player_start(); lcd_ui_show_status("AI正在回答"); } else if (strcmp(type->valuestring, "tts_end") == 0) { audio_player_stop(); lcd_ui_show_status("等待指令"); } else if (strcmp(type->valuestring, "tool_call") == 0) { mcp_service_handle(root); } else if (strcmp(type->valuestring, "vision_result") == 0) { vision_handle_result(root); } } cJSON_Delete(root); free(buf); }十一、MCP 工具调用设计
用户可以直接说:
看一下窗外天气 帮我把灯调亮一点 进入学习模式 把灯头转向书本 看一下桌面环境 提醒我学习 30 分钟后休息 打开客厅灯AI 后端返回 MCP 工具调用:
{ "type": "tool_call", "tool": "lamp.set_mode", "arguments": { "mode": "study" } }1. MCP 工具定义
typedef enum { MCP_TOOL_LAMP_SET_MODE = 0, MCP_TOOL_LAMP_SET_LIGHT, MCP_TOOL_ARM_MOVE, MCP_TOOL_VISION_CAPTURE, MCP_TOOL_STUDY_TIMER, MCP_TOOL_HOME_CONTROL, MCP_TOOL_UNKNOWN } mcp_tool_id_t; typedef struct { const char *name; mcp_tool_id_t id; } mcp_tool_map_t; static const mcp_tool_map_t g_mcp_tools[] = { {"lamp.set_mode", MCP_TOOL_LAMP_SET_MODE}, {"lamp.set_light", MCP_TOOL_LAMP_SET_LIGHT}, {"arm.move", MCP_TOOL_ARM_MOVE}, {"vision.capture", MCP_TOOL_VISION_CAPTURE}, {"study.timer", MCP_TOOL_STUDY_TIMER}, {"home.device.control", MCP_TOOL_HOME_CONTROL}, };2. MCP 分发
static mcp_tool_id_t mcp_get_tool_id(const char *name) { for (int i = 0; i < sizeof(g_mcp_tools) / sizeof(g_mcp_tools[0]); i++) { if (strcmp(name, g_mcp_tools[i].name) == 0) { return g_mcp_tools[i].id; } } return MCP_TOOL_UNKNOWN; } void mcp_service_handle(cJSON *root) { cJSON *tool = cJSON_GetObjectItem(root, "tool"); cJSON *args = cJSON_GetObjectItem(root, "arguments"); if (!cJSON_IsString(tool) || !cJSON_IsObject(args)) { return; } switch (mcp_get_tool_id(tool->valuestring)) { case MCP_TOOL_LAMP_SET_MODE: mcp_handle_lamp_set_mode(args); break; case MCP_TOOL_LAMP_SET_LIGHT: mcp_handle_lamp_set_light(args); break; case MCP_TOOL_ARM_MOVE: mcp_handle_arm_move(args); break; case MCP_TOOL_VISION_CAPTURE: mcp_handle_vision_capture(args); break; case MCP_TOOL_STUDY_TIMER: mcp_handle_study_timer(args); break; case MCP_TOOL_HOME_CONTROL: mcp_handle_home_control(args); break; default: lcd_ui_show_status("未知工具调用"); break; } }3. 灯光模式 MCP
void mcp_handle_lamp_set_mode(cJSON *args) { cJSON *mode = cJSON_GetObjectItem(args, "mode"); if (!cJSON_IsString(mode)) { return; } if (strcmp(mode->valuestring, "study") == 0) { lamp_mode_study(); } else if (strcmp(mode->valuestring, "reading") == 0) { lamp_mode_reading(); } else if (strcmp(mode->valuestring, "rest") == 0) { lamp_mode_rest(); } }4. 视觉任务 MCP
void mcp_handle_vision_capture(cJSON *args) { cJSON *task = cJSON_GetObjectItem(args, "task"); if (!cJSON_IsString(task)) { return; } if (strcmp(task->valuestring, "weather") == 0) { arm_point_to_window(); vision_run_task(VISION_TASK_WEATHER); } else if (strcmp(task->valuestring, "desk") == 0) { arm_point_to_book(); vision_run_task(VISION_TASK_DESK_ENV); } else if (strcmp(task->valuestring, "study") == 0) { arm_point_to_book(); vision_run_task(VISION_TASK_STUDY_HELP); } }十二、学习陪伴设计
学习陪伴不是简单聊天,而是要形成“灯光 + 语音 + 视觉 + 计时 + 提醒”的闭环。
学习场景流程
用户:开始学习模式 ↓ 台灯自动转向桌面 ↓ 灯光调到 85% / 4500K ↓ 摄像头检测桌面环境 ↓ AI 开始学习陪伴 ↓ 每 30 分钟提醒休息 ↓ 发现环境过暗时自动补光学习任务结构
typedef struct { int active; int duration_min; int rest_interval_min; int elapsed_sec; } study_session_t; static study_session_t g_study = { .active = 0, .duration_min = 45, .rest_interval_min = 30, .elapsed_sec = 0, };学习模式启动
void study_assistant_start(int duration_min) { g_study.active = 1; g_study.duration_min = duration_min; g_study.elapsed_sec = 0; lamp_mode_study(); arm_point_to_book(); vision_run_task(VISION_TASK_DESK_ENV); audio_player_play_prompt("study_start.wav"); lcd_ui_show_status("学习陪伴已开启"); }学习提醒任务
void study_assistant_task(void *arg) { while (1) { if (g_study.active) { g_study.elapsed_sec++; if (g_study.elapsed_sec % (30 * 60) == 0) { audio_player_play_prompt("take_a_break.wav"); lcd_ui_show_status("学习30分钟了,休息一下眼睛"); lamp_set_brightness_color_temp(40, 3500); } if (g_study.elapsed_sec >= g_study.duration_min * 60) { g_study.active = 0; audio_player_play_prompt("study_finish.wav"); lcd_ui_show_status("本次学习完成"); lamp_mode_rest(); } } vTaskDelay(pdMS_TO_TICKS(1000)); } }十三、窗外天气识别逻辑
1. 本地初筛
本地可以先通过摄像头画面亮度做简单判断:
typedef enum { WEATHER_UNKNOWN = 0, WEATHER_SUNNY, WEATHER_CLOUDY, WEATHER_RAINY, WEATHER_NIGHT } visual_weather_t; visual_weather_t vision_estimate_weather_local(uint8_t *gray, int width, int height) { int64_t sum = 0; int pixels = width * height; for (int i = 0; i < pixels; i++) { sum += gray[i]; } int avg = sum / pixels; if (avg < 40) { return WEATHER_NIGHT; } else if (avg > 160) { return WEATHER_SUNNY; } else if (avg > 80) { return WEATHER_CLOUDY; } return WEATHER_UNKNOWN; }2. 云端视觉结果处理
void vision_handle_result(cJSON *root) { cJSON *task = cJSON_GetObjectItem(root, "task"); cJSON *result = cJSON_GetObjectItem(root, "result"); if (!cJSON_IsString(task) || !cJSON_IsString(result)) { return; } if (strcmp(task->valuestring, "weather_view") == 0) { char text[128]; snprintf(text, sizeof(text), "窗外环境:%s", result->valuestring); lcd_ui_show_status(text); audio_player_tts(text); } if (strcmp(task->valuestring, "desk_view") == 0) { if (strstr(result->valuestring, "过暗")) { lamp_set_brightness_color_temp(85, 4500); audio_player_tts("当前桌面偏暗,已为你调亮台灯。"); } } }十四、OTA 与量产升级
四博 AI 开发宝典中包含 ESP-IDF 开发环境搭建、开源工程编译、烧录、配网、OTA、实时打断、自定义唤醒词等开发章节,适合客户进行二次开发和量产前验证。
OTA 版本文件
{ "project": "doit_ai_arm_lamp_s3", "version": "1.0.3", "chip": "esp32s3", "url": "https://ota.customer.com/arm_lamp_s3_v1.0.3.bin", "asset_url": "https://ota.customer.com/assets/arm_lamp_assets_v3.bin", "md5": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "force": false, "note": "新增视觉天气识别、学习陪伴和机械臂动作优化" }OTA 执行代码
#include "esp_https_ota.h" #include "esp_http_client.h" void ota_check_update(void) { esp_http_client_config_t http_config = { .url = "https://ota.customer.com/arm_lamp/latest.bin", .timeout_ms = 10000, .keep_alive_enable = true, }; esp_https_ota_config_t ota_config = { .http_config = &http_config, }; lcd_ui_show_status("正在升级,请勿断电"); esp_err_t ret = esp_https_ota(&ota_config); if (ret == ESP_OK) { lcd_ui_show_status("升级成功,正在重启"); esp_restart(); } else { lcd_ui_show_status("升级失败"); } }十五、方案核心价值
四博 AI 机械臂台灯智能音箱方案的价值在于,它把原本分散的能力整合成一个桌面 AI 终端:
1. 视觉能力:能看窗外天气、桌面环境、学习状态 2. 语音能力:能对话、问答、提醒、陪伴 3. 照明能力:能调亮度、调色温、自动补光 4. 机械臂能力:能转向书本、窗户、用户位置 5. 学习陪伴:能做学习计时、休息提醒、知识讲解 6. 智能家居:能通过 MCP 控制灯具、窗帘、空调等设备 7. 二次开发:基于 ESP32-S3 和 ESP-IDF,方便客户定制 8. 量产升级:支持 OTA、素材更新、参数更新和客户系统接入一句话总结:
四博 AI 机械臂台灯不是普通台灯, 也不是普通 AI 音箱, 而是基于 ESP32-S3 打造的视觉感知、机械臂控制、 智能照明和学习陪伴一体化 AI 桌面终端。