基于 ESP32-P4 + Sipeed MaixSense A010 ToF 深度相机的端侧 TinyML 自主导航小车复现项目。
原论文:TinyNav: End-to-End TinyML for Real-Time Autonomous Navigation on Microcontrollers(CUCAI 2026)
原作者仓库:https://github.com/regularpooria/tinynav
一个能在 ESP32 微控制器上实时运行的自动驾驶小车:
- 用 A010 ToF 深度相机看前方障碍物
- 23k 参数的量化 CNN 模型直接在 ESP32 上推理
- 输出转向(steering)和油门(throttle),控制小车避障行驶
- 推理延迟约 22ms,FPS ≈ 20
| 部件 | 型号 | 说明 |
|---|---|---|
| 主控 | ESP32-P4-WIFI6-M | 360MHz 双核,32MB PSRAM |
| WiFi | 板载 ESP32-C6 | P4 经 SDIO 用 esp_hosted 驱动(出厂固件即可),STA 联网/热点兜底 |
| 深度相机 | Sipeed MaixSense A010 | VL53L5CX,UART 输出 25×25 深度图 |
| 电机驱动 | DRV8833 | 双路 H 桥,软件 PWM 直驱(1kHz) |
| 遥控 | 富斯 FS-G7P + R11P | CH1 转向,CH2 油门,CH3 模式切换 |
| 电源 | 2S 7.2V 锂电池 + XL4015 降压 5V | 电机和逻辑分开供电 |
| 存储 | 32GB SD卡 | 采集数据用 |
⚡ 各外设实际接的引脚(DRV8833 AIN1/2=25/24、BIN1/2=2/3、RC CH1/2/3=27/33/32、WS2812=50、 A010 UART=20/21、SD=SDMMC 43/44/39-42)见 docs/p4_hardware_pins.md。 电机驱动实为软件 PWM 1kHz(MCPWM/LEDC 外设失效后已弃用),非早期「20kHz」。
TinyNav-main/
├── main/
│ ├── main_functions.cc # 模型推理、输入预处理、输出解析
│ ├── web_control.c # WiFi(STA 优先/AP 兜底)+ 网页遥控 HTTP 服务
│ ├── api_control.c # /api/control 动作 API(AKA-00/Blockly 平台兼容)
│ ├── drive_system/
│ │ ├── depth_sensor.cc # A010 初始化、AT 命令、运行模式
│ │ └── drive_system.cc # RC 遥控读取、电机 PWM 控制
│ ├── model.cc # 量化模型的 C 数组
│ └── sdcard/ # SD 卡读写
└── components/ # ESP-IDF 组件(SD卡、LED等)
训练代码在单独的 tinynav_cnn/ 目录:
train_full.py— 训练模型quantize.py— INT8 量化convert_to_c.py— 转 C 数组写入 model.ccgradcam.py— Grad-CAM 可视化
- 采集数据:切换到采集模式(mode=2),用遥控开着车跑,SD 卡记录每帧的深度图 + 转向 + 油门
- 训练:
python train_full.py,自动遍历 data/ 下所有 CSV- 预处理:旋转90° → 裁剪左上角24×24 → 除以2550归一化 → 20帧滑动窗口
- 模型:Conv2D(32) → Conv2D(48) → GAP → Dense(48) → 输出 steering/tanh + throttle/sigmoid
- 约 22k 参数
- 量化:
python quantize.py,INT8 量化,模型约 31KB - 转C:
python convert_to_c.py,自动写入main/model.cc
完整环境搭建步骤(ESP-IDF v6.0 安装、环境变量、踩坑清单、整片备份/恢复)见 docs/build_flash_guide.md。速览:
# PowerShell 里(Git Bash 会报 MSys 错误,不支持)
$env:IDF_PATH='D:\ESP-IDF-v6.0\esp-idf'
$env:IDF_TOOLS_PATH='D:\ESP-IDF-v6.0\tools'
$env:IDF_PYTHON_ENV_PATH='D:\ESP-IDF-v6.0\tools\python_env\idf6.0_py3.12_env'
& "$env:IDF_PATH\export.ps1"
idf.py build # 首次全量编译约 10~20 分钟
idf.py -p COM5 flash # COM 口按实际(设备管理器里 CH343)
idf.py -p COM5 monitor # Ctrl+] 退出或直接双击根目录 build_flash.bat(内置环境变量,默认 COM5,可传参换口)。VS Code + ESP-IDF 扩展方式也可用。
上电默认进入网页遥控模式(mode=4),串口会打印车拿到的 IP。
车联网后(STA 连 main/web_control.c 里配置的 WiFi,连不上自动回落开放热点 TinyNav-Car,地址 192.168.0.1 网段热点为 192.168.4.1):
网页遥控:浏览器访问 http://<车IP>/,虚拟方向键 + 速度滑块,长按走松手停。
动作 API(/api/control,兼容 chenlongos/AKA-00 契约与其实验室 Blockly 平台实测格式):
# 平台档位格式:speed 0~50 + time 毫秒
curl "http://<车IP>/api/control?action=up&speed=50&time=1000"
curl "http://<车IP>/api/control?action=right&speed=20&time=500"
curl "http://<车IP>/api/control?action=stop&speed=0"
# AKA-00 m/s 格式也兼容:speed 0.01~0.5,支持 distance(cm)/angle(°)(开环近似)
curl "http://<车IP>/api/control?action=up&speed=0.25&distance=30"接实验室 Blockly 编程平台:浏览器打开 http://labs.chenlongrobot.com/?ip=<车IP>(用 http 打开),拖积木即可编程控制本车。
| 指标 | 论文 | 本复现 |
|---|---|---|
| 模型参数量 | 23k | 22k |
| 推理延迟 | 30ms | 22.6ms |
| Steering Pearson r | ~0.6 | 0.69 |
| 模型大小(INT8) | - | 30.7KB |
- A010 深度相机会因电机 EMI 干扰间歇性输出固定无效值,导致小车直行爆冲。根治需在 A010 的 VCC-GND 焊 100uF 电解电容,并将电机线与 UART 信号线分开走线。
- 逆时针赛道表现不如顺时针,与训练数据中逆时针样本较少有关。
depth_sensor.cc 的 write_to_sd 变量控制默认模式(编译期改):
0= RC 手动遥控1= 串口输出2= SD 卡采集数据3= AI 推理模式4= 网页遥控 + /api/control(当前默认)
运行时切换:串口发 SET_MODE:n / 查询 GET_MODE(见 docs/serial_output_guide.md)。