四博AI台灯方案升级:基于 ESP32-S3 的摄像头拍照答题、语音陪伴与双目交互智能音箱方案
1. 项目背景
传统 AI 音箱主要以"语音输入 + 云端大模型 + 喇叭输出"为核心,适合问答、聊天、播放音乐等场景。但在儿童学习、桌面陪伴、智能台灯、AI 玩具等产品中,仅有语音交互还不够。用户更希望设备能够"看得见、听得懂、能表达、会互动"。
因此,四博 AI 台灯/AI 智能音箱方案可以在原有语音大模型基础上增加摄像头能力,实现:
- 拍照答题
- 作业识别
- 图像理解
- 桌面生活陪伴
- 双眼屏表情反馈
- 触摸交互
- 震动反馈
- 灯光联动
- 舵机头部转动
- Wi-Fi / 后续 4G 联网扩展
从四博现有资料看,ESP32-S3 系列定位于音视频/AI 市场,支持外挂 PSRAM、DVP 摄像头接口、SPI/RGB/I8080 LCD 显示等能力,更适合作为"带摄像头 + 屏幕 + 语音交互"的主控平台。
四博 AI 开发宝典中也提到 DOIT_ESPS3_AI_EYE_Vision 是基于 ESP32-S3 的多模态 AI 开发板,集成摄像头、双目显示、触摸交互,并可对接小智云端或本地智能服务,适合情感陪护和 AI 应用开发。
2. 推荐硬件方案
2.1 主控选型
推荐主控:
主控:ESP32-S3 / ESPS3-32 系列
建议规格:N16R8 或 N16R2
原因:
1. 支持摄像头数据采集
2. 支持 LCD / 双目屏显示
3. 支持 I2S 音频输入输出
4. 支持 Wi-Fi 联网
5. 支持 PSRAM,适合 JPEG 图像缓存、LVGL UI、音频缓存
四博模组选型手册中,ESPS3-32 系列覆盖 N4、N8、N8R2、N16R2、N16R8 等子型号,属于 ESP32-S3 通用模组,兼容 ESP32-S3-WROOM-1 系列模组。
2.2 系统硬件框图
┌──────────────────────────┐
│ 云端AI服务 │
│ ASR / LLM / VLM / TTS │
└─────────────▲────────────┘
│ WebSocket / HTTPS
│
┌──────────────┐ ┌────────┴────────┐ ┌──────────────┐
│ 摄像头 │──────▶│ ESP32-S3 │──────▶│ 双目屏/LCD │
│ OV2640/GC系列 │ DVP │ ESPS3-32 N16R8 │ SPI │ 表情/答题显示 │
└──────────────┘ └────────┬────────┘ └──────────────┘
│
┌──────────────┬────────┼─────────┬──────────────┐
│ │ │ │ │
┌────▼────┐ ┌─────▼────┐ ┌─▼───┐ ┌──▼───┐ ┌────▼────┐
│ 麦克风 │ │ 喇叭功放 │ │触摸 │ │ LED │ │ 舵机/马达 │
│ I2S/PDM │ │ I2S/DAC │ │GPIO │ │PWM │ │ PWM/GPIO │
└─────────┘ └──────────┘ └─────┘ └──────┘ └─────────┘
3. 功能设计
3.1 拍照答题流程
用户对台灯说:
"小博,帮我看看这道题。"
设备执行流程:
1. 唤醒词触发
2. 语音识别为"拍照答题"意图
3. 摄像头拍照
4. 图片压缩为 JPEG
5. 通过 HTTPS 上传到视觉大模型服务
6. 云端返回题目解析、答案、解题步骤
7. 设备通过 TTS 播报
8. 屏幕显示重点答案
9. 双眼屏切换"思考/回答/开心"表情
3.2 生活陪伴流程
典型交互:
用户:小博,我今天有点累。
设备:辛苦啦,要不要听一首轻松的歌?我也可以陪你聊一会儿。
陪伴模式不仅是语音问答,还可以结合:
1. 摄像头判断用户是否在桌前
2. 触摸传感器触发安抚反馈
3. 双目屏显示不同表情
4. 灯光亮度根据场景自动调节
5. 舵机控制头部轻微转动
6. 振动马达提供触觉反馈
4. 软件架构
main
├── app_main.c
├── camera
│ ├── app_camera.c
│ └── app_camera.h
├── ai_client
│ ├── ai_ws_client.c
│ ├── ai_http_vision.c
│ └── ai_protocol.h
├── audio
│ ├── app_audio.c
│ ├── wakeup.c
│ └── tts_player.c
├── display
│ ├── eye_anim.c
│ ├── lvgl_ui.c
│ └── answer_page.c
├── lamp
│ ├── lamp_pwm.c
│ └── scene_light.c
├── touch
│ └── touch_key.c
├── motor
│ ├── servo_ctrl.c
│ └── vibration.c
└── net
├── wifi_manager.c
├── blufi_config.c
└── mqtt_client.c
5. ESP-IDF 工程配置
四博 AI 开发宝典中建议使用 VSCode + ESP-IDF 扩展搭建开发环境,并在安装时选择 ESP-IDF v5.5 版本;文档中也提供了构建、烧录、串口监视等流程说明。
推荐配置:
idf.py set-target esp32s3
idf.py menuconfig
建议开启:
Component config → ESP PSRAM → Support for external SPI RAM
Component config → ESP32S3-specific → Support for LCD / Camera
Component config → Wi-Fi
Component config → LWIP
Component config → ESP HTTP Client
Component config → mbedTLS
Component config → FreeRTOS
6. 摄像头初始化代码
下面以 ESP32-S3 + OV2640 为例。实际 GPIO 需要根据四博 PCBA 原理图调整。
// app_camera.h
#pragma once
#include "esp_err.h"
#include "esp_camera.h"
esp_err_t app_camera_init(void);
camera_fb_t *app_camera_capture(void);
void app_camera_return(camera_fb_t *fb);
// app_camera.c
#include "app_camera.h"
#include "esp_log.h"
static const char *TAG = "app_camera";
#define CAM_PIN_PWDN -1
#define CAM_PIN_RESET -1
#define CAM_PIN_XCLK 15
#define CAM_PIN_SIOD 4
#define CAM_PIN_SIOC 5
#define CAM_PIN_D7 16
#define CAM_PIN_D6 17
#define CAM_PIN_D5 18
#define CAM_PIN_D4 12
#define CAM_PIN_D3 10
#define CAM_PIN_D2 8
#define CAM_PIN_D1 9
#define CAM_PIN_D0 11
#define CAM_PIN_VSYNC 6
#define CAM_PIN_HREF 7
#define CAM_PIN_PCLK 13
esp_err_t app_camera_init(void)
{
camera_config_t config = {
.pin_pwdn = CAM_PIN_PWDN,
.pin_reset = CAM_PIN_RESET,
.pin_xclk = CAM_PIN_XCLK,
.pin_sccb_sda = CAM_PIN_SIOD,
.pin_sccb_scl = CAM_PIN_SIOC,
.pin_d7 = CAM_PIN_D7,
.pin_d6 = CAM_PIN_D6,
.pin_d5 = CAM_PIN_D5,
.pin_d4 = CAM_PIN_D4,
.pin_d3 = CAM_PIN_D3,
.pin_d2 = CAM_PIN_D2,
.pin_d1 = CAM_PIN_D1,
.pin_d0 = CAM_PIN_D0,
.pin_vsync = CAM_PIN_VSYNC,
.pin_href = CAM_PIN_HREF,
.pin_pclk = CAM_PIN_PCLK,
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_VGA,
.jpeg_quality = 12,
.fb_count = 2,
.fb_location = CAMERA_FB_IN_PSRAM,
.grab_mode = CAMERA_GRAB_LATEST,
};
esp_err_t ret = esp_camera_init(&config);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "camera init failed: %s", esp_err_to_name(ret));
return ret;
}
sensor_t *s = esp_camera_sensor_get();
if (s) {
s->set_brightness(s, 0);
s->set_contrast(s, 1);
s->set_saturation(s, 0);
s->set_framesize(s, FRAMESIZE_VGA);
}
ESP_LOGI(TAG, "camera init ok");
return ESP_OK;
}
camera_fb_t *app_camera_capture(void)
{
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) {
ESP_LOGE(TAG, "camera capture failed");
return NULL;
}
ESP_LOGI(TAG, "capture image: %d bytes, %dx%d",
fb->len, fb->width, fb->height);
return fb;
}
void app_camera_return(camera_fb_t *fb)
{
if (fb) {
esp_camera_fb_return(fb);
}
}
7. 拍照上传视觉大模型
7.1 JSON 协议设计
{
"device_id": "sibo_ai_lamp_001",
"type": "vision_qa",
"prompt": "请识别图片中的题目,并给出详细解题步骤,适合小学生理解。",
"image_format": "jpeg",
"image_base64": "..."
}
7.2 HTTP 上传代码示例
// ai_http_vision.h
#pragma once
#include "esp_err.h"
#include "esp_camera.h"
esp_err_t ai_vision_send_image(camera_fb_t *fb, const char *prompt);
// ai_http_vision.c
#include "ai_http_vision.h"
#include "esp_http_client.h"
#include "mbedtls/base64.h"
#include "cJSON.h"
#include "esp_log.h"
#include <string.h>
#include <stdlib.h>
static const char *TAG = "ai_vision";
#define VISION_API_URL "https://your-server.com/api/v1/vision/qa"
#define DEVICE_ID "sibo_ai_lamp_001"
static char *base64_encode_image(const uint8_t *data, size_t len)
{
size_t out_len = 0;
mbedtls_base64_encode(NULL, 0, &out_len, data, len);
char *out = calloc(1, out_len + 1);
if (!out) {
return NULL;
}
int ret = mbedtls_base64_encode((unsigned char *)out, out_len, &out_len, data, len);
if (ret != 0) {
free(out);
return NULL;
}
out[out_len] = '\0';
return out;
}
esp_err_t ai_vision_send_image(camera_fb_t *fb, const char *prompt)
{
if (!fb || !prompt) {
return ESP_ERR_INVALID_ARG;
}
char *image_b64 = base64_encode_image(fb->buf, fb->len);
if (!image_b64) {
ESP_LOGE(TAG, "base64 encode failed");
return ESP_ERR_NO_MEM;
}
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "device_id", DEVICE_ID);
cJSON_AddStringToObject(root, "type", "vision_qa");
cJSON_AddStringToObject(root, "prompt", prompt);
cJSON_AddStringToObject(root, "image_format", "jpeg");
cJSON_AddStringToObject(root, "image_base64", image_b64);
char *json = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
free(image_b64);
if (!json) {
return ESP_ERR_NO_MEM;
}
esp_http_client_config_t config = {
.url = VISION_API_URL,
.timeout_ms = 30000,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
if (!client) {
free(json);
return ESP_FAIL;
}
esp_http_client_set_method(client, HTTP_METHOD_POST);
esp_http_client_set_header(client, "Content-Type", "application/json");
esp_http_client_set_post_field(client, json, strlen(json));
esp_err_t ret = esp_http_client_perform(client);
if (ret == ESP_OK) {
int status = esp_http_client_get_status_code(client);
ESP_LOGI(TAG, "vision api status=%d", status);
} else {
ESP_LOGE(TAG, "vision api failed: %s", esp_err_to_name(ret));
}
esp_http_client_cleanup(client);
free(json);
return ret;
}
8. 语音指令触发拍照答题
typedef enum {
AI_EVENT_NONE = 0,
AI_EVENT_WAKEUP,
AI_EVENT_TAKE_PHOTO_QA,
AI_EVENT_COMPANION_CHAT,
AI_EVENT_LIGHT_ON,
AI_EVENT_LIGHT_OFF,
} ai_event_t;
static QueueHandle_t s_ai_event_queue;
void ai_event_post(ai_event_t event)
{
if (s_ai_event_queue) {
xQueueSend(s_ai_event_queue, &event, 0);
}
}
static void ai_main_task(void *arg)
{
ai_event_t event;
while (1) {
if (xQueueReceive(s_ai_event_queue, &event, portMAX_DELAY) == pdTRUE) {
switch (event) {
case AI_EVENT_TAKE_PHOTO_QA: {
eye_anim_set_state(EYE_STATE_THINKING);
lamp_set_scene(LAMP_SCENE_STUDY);
camera_fb_t *fb = app_camera_capture();
if (fb) {
ai_vision_send_image(
fb,
"请识别图片中的题目,并给出清晰、分步骤的讲解。"
);
app_camera_return(fb);
}
eye_anim_set_state(EYE_STATE_ANSWERING);
break;
}
case AI_EVENT_COMPANION_CHAT:
eye_anim_set_state(EYE_STATE_SMILE);
lamp_set_scene(LAMP_SCENE_COMPANION);
break;
default:
break;
}
}
}
}
9. 台灯 PWM 调光代码
#include "driver/ledc.h"
#include "esp_err.h"
#define LED_PWM_GPIO 38
#define LEDC_TIMER LEDC_TIMER_1
#define LEDC_MODE LEDC_LOW_SPEED_MODE
#define LEDC_CHANNEL LEDC_CHANNEL_1
#define LEDC_DUTY_RES LEDC_TIMER_13_BIT
#define LEDC_FREQUENCY 5000
void lamp_pwm_init(void)
{
ledc_timer_config_t timer = {
.speed_mode = LEDC_MODE,
.timer_num = LEDC_TIMER,
.duty_resolution = LEDC_DUTY_RES,
.freq_hz = LEDC_FREQUENCY,
.clk_cfg = LEDC_AUTO_CLK,
};
ledc_timer_config(&timer);
ledc_channel_config_t channel = {
.gpio_num = LED_PWM_GPIO,
.speed_mode = LEDC_MODE,
.channel = LEDC_CHANNEL,
.timer_sel = LEDC_TIMER,
.duty = 0,
.hpoint = 0,
};
ledc_channel_config(&channel);
}
void lamp_set_brightness(uint8_t percent)
{
if (percent > 100) {
percent = 100;
}
uint32_t max_duty = (1 << 13) - 1;
uint32_t duty = max_duty * percent / 100;
ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, duty);
ledc_update_duty(LEDC_MODE, LEDC_CHANNEL);
}
10. 舵机控制头部转动
#include "driver/ledc.h"
#define SERVO_GPIO 39
#define SERVO_TIMER LEDC_TIMER_2
#define SERVO_CHANNEL LEDC_CHANNEL_2
#define SERVO_FREQ_HZ 50
#define SERVO_DUTY_RES LEDC_TIMER_14_BIT
static uint32_t servo_angle_to_duty(int angle)
{
if (angle < 0) angle = 0;
if (angle > 180) angle = 180;
// 0.5ms ~ 2.5ms, period 20ms
const uint32_t max_duty = (1 << 14) - 1;
float pulse_ms = 0.5f + (angle / 180.0f) * 2.0f;
return (uint32_t)(max_duty * pulse_ms / 20.0f);
}
void servo_init(void)
{
ledc_timer_config_t timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = SERVO_TIMER,
.duty_resolution = SERVO_DUTY_RES,
.freq_hz = SERVO_FREQ_HZ,
.clk_cfg = LEDC_AUTO_CLK,
};
ledc_timer_config(&timer);
ledc_channel_config_t channel = {
.gpio_num = SERVO_GPIO,
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = SERVO_CHANNEL,
.timer_sel = SERVO_TIMER,
.duty = servo_angle_to_duty(90),
.hpoint = 0,
};
ledc_channel_config(&channel);
}
void servo_set_angle(int angle)
{
uint32_t duty = servo_angle_to_duty(angle);
ledc_set_duty(LEDC_LOW_SPEED_MODE, SERVO_CHANNEL, duty);
ledc_update_duty(LEDC_LOW_SPEED_MODE, SERVO_CHANNEL);
}
11. 触摸/按键触发拍照
如果使用外置触摸 IC,可通过 GPIO 中断触发:
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#define TOUCH_KEY_GPIO 3
static void IRAM_ATTR touch_isr_handler(void *arg)
{
ai_event_t event = AI_EVENT_TAKE_PHOTO_QA;
xQueueSendFromISR(s_ai_event_queue, &event, NULL);
}
void touch_key_init(void)
{
gpio_config_t io_conf = {
.pin_bit_mask = 1ULL << TOUCH_KEY_GPIO,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_NEGEDGE,
};
gpio_config(&io_conf);
gpio_install_isr_service(0);
gpio_isr_handler_add(TOUCH_KEY_GPIO, touch_isr_handler, NULL);
}
12. 双目屏状态设计
双目屏可以设计为几个状态:
typedef enum {
EYE_STATE_IDLE = 0,
EYE_STATE_WAKEUP,
EYE_STATE_LISTENING,
EYE_STATE_THINKING,
EYE_STATE_ANSWERING,
EYE_STATE_SMILE,
EYE_STATE_SLEEP,
EYE_STATE_ERROR,
} eye_state_t;
void eye_anim_set_state(eye_state_t state)
{
switch (state) {
case EYE_STATE_IDLE:
lv_img_set_src(eye_left, &eye_idle_left);
lv_img_set_src(eye_right, &eye_idle_right);
break;
case EYE_STATE_THINKING:
lv_img_set_src(eye_left, &eye_think_left);
lv_img_set_src(eye_right, &eye_think_right);
break;
case EYE_STATE_ANSWERING:
lv_img_set_src(eye_left, &eye_answer_left);
lv_img_set_src(eye_right, &eye_answer_right);
break;
case EYE_STATE_SMILE:
lv_img_set_src(eye_left, &eye_smile_left);
lv_img_set_src(eye_right, &eye_smile_right);
break;
default:
break;
}
}
四博资料中提到双目设备支持 1.28 寸和 0.71 寸屏幕,并可定制眼睛样式固件,适合把台灯做成"有表情的 AI 陪伴设备"。
13. 云端接口设计
13.1 视觉答题接口
from fastapi import FastAPI
from pydantic import BaseModel
import base64
app = FastAPI()
class VisionQARequest(BaseModel):
device_id: str
type: str
prompt: str
image_format: str
image_base64: str
@app.post("/api/v1/vision/qa")
async def vision_qa(req: VisionQARequest):
image_bytes = base64.b64decode(req.image_base64)
# 这里可以接入豆包、通义千问、GPT、Qwen-VL 等视觉模型
answer = call_vlm_model(
image_bytes=image_bytes,
prompt=req.prompt
)
tts_url = call_tts_service(answer)
return {
"code": 0,
"device_id": req.device_id,
"answer": answer,
"tts_url": tts_url,
"display": {
"title": "拍照答题",
"content": answer[:120]
},
"action": {
"eye": "answering",
"lamp": "study",
"servo": 90
}
}
13.2 生活陪伴接口
class CompanionRequest(BaseModel):
device_id: str
text: str
scene: str = "desk_lamp"
@app.post("/api/v1/chat/companion")
async def companion_chat(req: CompanionRequest):
system_prompt = """
你是四博AI台灯中的生活陪伴助手。
你需要用温和、简短、自然的方式回应用户。
当用户学习时,要鼓励用户独立思考;
当用户情绪低落时,要给出陪伴和安慰;
当用户请求答题时,不只给答案,要给步骤。
"""
reply = call_llm(system_prompt, req.text)
tts_url = call_tts_service(reply)
return {
"reply": reply,
"tts_url": tts_url,
"eye": "smile",
"lamp": "warm",
"vibration": "short"
}
14. 产品落地建议
14.1 标准版
ESP32-S3 + 摄像头 + 麦克风 + 喇叭 + 台灯 PWM
适合:拍照答题、语音问答、台灯控制
14.2 陪伴版
ESP32-S3 + 摄像头 + 双目屏 + 触摸 + 震动马达 + 舵机
适合:儿童陪伴、桌面机器人、AI玩具、学习台灯
14.3 高配联网版
ESP32-S3 / AI-C5 + Wi-Fi + 4G 扩展
适合:不依赖家庭 Wi-Fi 的移动陪伴设备、车载 AI 音箱、户外 AI 玩具
四博产品资料中已有 AI 语音机器人、AI 智能小夜灯、AI 智能音响等产品方向,其中 AI 语音机器人强调儿童早教、外语交流、全科答疑、智能夜灯和快速响应;AI 智能音响则强调大模型、蓝牙音响、闹钟、声音克隆、声纹识别、APP/小程序和唤醒词定制等能力。
15. 总结
四博 AI 台灯方案的核心升级方向,是从"语音 AI 音箱"升级为"视觉 + 语音 + 表情 + 灯光 + 动作"的多模态 AI 终端。
推荐使用 ESP32-S3 作为核心主控,配合摄像头、双目屏、I2S 音频、PWM 灯光、触摸传感器和舵机模块,实现拍照答题、学习陪伴、生活聊天、智能夜灯、情绪反馈等功能。
该方案适合以下产品:
1. AI学习台灯
2. AI儿童陪伴机器人
3. AI智能音箱
4. AI桌面宠物
5. AI早教机
6. AI拍学机
7. AI语音夜灯
从产品化角度看,四博方案的优势在于:模组成熟、开发资料完整、已有小智 AI 开源适配基础、支持云端或本地大模型接入,并且可以根据客户外壳、屏幕、灯光、摄像头和交互方式进行快速二次开发。