四博 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 桌面终端。