10浏览
查看: 10|回复: 0

[K10项目分享] 行空板 K10 + 小智 AI——打造“能听懂话”的两轮 AI小车

[复制链接]
本帖最后由 云天 于 2025-10-8 00:23 编辑

行空板 K10 + 小智 AI + IO 电机扩展板  
—— 打造“能听懂话”的两轮 AI 小车

【项目缘起】
       行空板 K10 是一块把 240×320 彩屏、摄像头、麦克风、Wi-Fi、ESP32-S3 全部塞进 51.6 mm×83 mm 的“魔方”。  
官方出厂集成麦克风、扬声器,可轻松实现离线语音识别、语音合****工智能项目。  
行空板 K10 + 小智 AI——打造“能听懂话”的两轮 AI小车图1
       行空板K10结合开源项目xiaozhi-esp32开启了新的可能。它充分利用行空板K10中ESP32-S3 的功能,并集成 DeepSeek 等模型的 API,实现了本地化的 AI 语音聊天。DF官网提供了免编译的固件,只需要简单的烧录上传即可让行空板 K10 摇身一变,成为一款响应式智能终端,支持多轮对话、唤醒词检测和实时翻译。
       但这个固件只有对话等基础功能,不能使用行空板上的引脚,如板载的 P0/P1 两个“全功能”引脚,通过“金手指”引出的多个引脚。
       于是有了这个项目:把 K10 变成一辆听得懂人话、看得见前方、跑得稳又停得准的两轮小车。”
行空板 K10 + 小智 AI——打造“能听懂话”的两轮 AI小车图4
【硬件清单 】
1. 行空板 K10(ESP32-S3)  
2. micro:bit 掌控 IO 扩展板
3.TB6612 双路电机驱动,电机驱动扩展板
4. N20电机 ×2 +
5.小车底盘 ×2(嘉立创)  
6. 18650 锂电池(3.7V)  
7. 3Pin 连接线×4  
行空板 K10 + 小智 AI——打造“能听懂话”的两轮 AI小车图3
【原理透析】
为什么“只剩”P0/P1位置保留了两个全功能GPIO(支持数字输入/输出、模拟输入、PWM输出)。
------------------------------------------------  
K10 的 ESP32-S3 原生 45 个 IO,但:  
- 摄像头 8 data + XCLK + PCLK + VSYNC + HREF + SIOC + SIOD = 14 根  
- 彩屏 SPI 4 data + DC + CS + RST + BACKLIGHT = 6 根  
- 麦克风 I²S 3 根  
- 扬声器 I²S 3 根  
- 喇叭功放使能、LED 灯、SD 卡……  

最后金手指引出的 20 根口线,全部由 **XL9535QF24**(16 bit I²C GPIO 扩展器)驱动,**只能做数字输入/输出**,不能 PWM/ADC。  
因此“电机驱动”这种“方向+PWM”需求,P0、P1负责"PWM",扩展板上的P8、P9负责“方向”数字信号。

【XL9535QF24 快速上手】  

1. 管脚定义  
   - SDA ←→ K10 金手指 P20  
   - SCL ←→ K10 金手指 P19  
   - 地址 0x20(A2/A1/A0 全部接地)  
2. 寄存器模型  
   与 PCA9555 完全寄存器级兼容:  
   - 方向寄存器:0x06/0x07  
   - 输出寄存器:0x02/0x03  
   - 输入寄存器:0x00/0x01  
3. ESP-IDF 驱动  
   官方已内置 `esp_io_expander_tca95xx_16bit.h`,实例化时把器件地址写成 `ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_000` 即可,一行代码不用改。
  1. esp_io_expander_new_i2c_tca95xx_16bit(i2c_bus_, ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_000, &io_expander);
复制代码

【软件架构】

```
APP 层:小智 AI 语音 ⇄ 语义槽位 “self.car.action”  
    ↓  
MCP Server(JSON-RPC)  
    ↓  
Df_K10Board::IoExpanderSetLevel(P8/P9, DIR)  
    ↓  
XL9535QF24 → TB6612 AIN1/AIN2 → 电机正/反/停  
```
定时器 `stop_timer_` 负责“运行 X 秒后自动停车”,防止语音没听到“停止”时小车冲出桌面。
行空板 K10 + 小智 AI——打造“能听懂话”的两轮 AI小车图5
【关键代码片段】

1. 初始化 XL9535
  1. esp_io_expander_new_i2c_tca95xx_16bit(i2c_bus_, ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_000, &io_expander);
  2. uint16_t output_pins =  (1 << 8) | (1 << 9) ;  // 使用位掩码 (1 << 8) | (1 << 9) 表示引脚P8 和引脚P9。
  3. ret = esp_io_expander_set_dir(io_expander, output_pins,IO_EXPANDER_OUTPUT);
复制代码

2. 语音控制回调  
  1. void InitializeIot() {
  2.       
  3.         stop_timer_ = xTimerCreate("StopMotorTimer",pdMS_TO_TICKS(1000),pdFALSE,this,StopMotorCallbackStatic);
  4.         auto& mcp_server = McpServer::GetInstance();
  5.         motor_.init();
  6.         mcp_server.AddTool("self.car.action",
  7.             "可控制小车的运行动作,action(小车的动作):前进、后退、停止、左转、右转。speed:速度;run_time:执行时间(秒)",
  8.             PropertyList({Property("action", kPropertyTypeString),Property("speed", kPropertyTypeInteger, 200, 0, 255),Property("run_time", kPropertyTypeInteger, 2, 0, 60)}),
  9.             [this](const PropertyList& properties) -> ReturnValue {
  10.                 std::string action=properties["action"].value<std::string>().c_str();
  11.                 int speed = properties["speed"].value<int>();
  12.                 int run_time = properties["run_time"].value<int>();
  13.                 if (action=="前进"){
  14.                     motor_.forward(speed);
  15.                     SetPnumLevel(8,0);
  16.                     SetPnumLevel(9,1);
  17.                     if (stop_timer_) {
  18.                         xTimerStop(stop_timer_, 0);
  19.                         xTimerChangePeriod(stop_timer_, pdMS_TO_TICKS(run_time * 1000), 0);
  20.                         xTimerStart(stop_timer_, 0);
  21.                     }
  22.                 }else if(action=="停止"){
  23.                     motor_.stop();
  24.                 }else if(action=="后退"){
  25.                     motor_.backward(speed);
  26.                     SetPnumLevel(8,1);//P8引脚
  27.                     SetPnumLevel(9,0);//P9引脚
  28.                     if (stop_timer_) {
  29.                      xTimerStop(stop_timer_, 0);
  30.                      xTimerChangePeriod(stop_timer_, pdMS_TO_TICKS(run_time * 1000), 0);
  31.                      xTimerStart(stop_timer_, 0);
  32.                 }
  33.                 }else if(action=="左转"){
  34.                     motor_.turnLeft(speed);
  35.                     SetPnumLevel(8,0);
  36.                     SetPnumLevel(9,0);
  37.                     if (stop_timer_) {
  38.                         xTimerStop(stop_timer_, 0);
  39.                         xTimerChangePeriod(stop_timer_, pdMS_TO_TICKS(run_time * 1000), 0);
  40.                         xTimerStart(stop_timer_, 0);
  41.                     }
  42.                 }else if(action=="右转"){
  43.                     motor_.turnRight(speed);
  44.                     SetPnumLevel(8,1);
  45.                     SetPnumLevel(9,1);
  46.                     if (stop_timer_) {
  47.                         xTimerStop(stop_timer_, 0);
  48.                         xTimerChangePeriod(stop_timer_, pdMS_TO_TICKS(run_time * 1000), 0);
  49.                         xTimerStart(stop_timer_, 0);
  50.                     }
  51.                 }
  52.                 return ReturnValue(action);
  53.             
  54.          });
  55.                
  56.     }
复制代码

3. 自动停车回调函数  
  1. static void StopMotorCallbackStatic(TimerHandle_t xTimer)
  2.     {
  3.         auto* self = static_cast<Df_K10Board*>(pvTimerGetTimerID(xTimer));
  4.         self->StopMotorCallback();
  5.     }
  6.     void StopMotorCallback()
  7.     {
  8.         motor_.stop();
  9.     }
复制代码

4.引脚P0、P1产生PWM——motor.cc文件
  1. #include "motor.h"
  2. #define PWM_FREQ_HZ     1000
  3. #define PWM_RESOLUTION  LEDC_TIMER_8_BIT
  4. MotorDriver::MotorDriver(int ain1, int ain2)
  5.     : _ain1(ain1), _ain2(ain2),
  6.       _ch_ain1(LEDC_CHANNEL_1), _ch_ain2(LEDC_CHANNEL_2)
  7. {}
  8. void MotorDriver::init()
  9. {
  10.     // 设置 PWM 定时器
  11.     ledc_timer_config_t timer_conf = {};
  12.     timer_conf.speed_mode       = LEDC_MODE;
  13.     timer_conf.timer_num        = LEDC_TIMER_1;
  14.     timer_conf.duty_resolution  = PWM_RESOLUTION;
  15.     timer_conf.freq_hz          = PWM_FREQ_HZ;
  16.     timer_conf.clk_cfg          = LEDC_AUTO_CLK;
  17.     timer_conf.deconfigure      = false;  // ESP-IDF 5.0+ 新字段,必须赋值
  18.     ledc_timer_config(&timer_conf);
  19.     // 配置每个通道
  20.         auto config_channel = [this](int gpio, ledc_channel_t ch) {
  21.                 ledc_channel_config_t channel = {};
  22.                 channel.gpio_num     = gpio;
  23.                 channel.speed_mode   = LEDC_MODE;
  24.                 channel.channel      = ch;
  25.                 channel.timer_sel    = LEDC_TIMER_1;
  26.                 channel.duty         = 0;
  27.                 channel.hpoint       = 0;
  28.                 channel.intr_type    = LEDC_INTR_DISABLE;
  29.                 ledc_channel_config(&channel);
  30.         };
  31.     config_channel(_ain1, _ch_ain1);
  32.     config_channel(_ain2, _ch_ain2);
  33. }
  34. void MotorDriver::setMotor(ledc_channel_t ch1, uint8_t duty1,
  35.                            ledc_channel_t ch2, uint8_t duty2)
  36. {
  37.     ledc_set_duty(LEDC_MODE, ch1, duty1);
  38.     ledc_update_duty(LEDC_MODE, ch1);
  39.     ledc_set_duty(LEDC_MODE, ch2, duty2);
  40.     ledc_update_duty(LEDC_MODE, ch2);
  41. }
  42. void MotorDriver::stop()
  43. {
  44.     setMotor(_ch_ain1, 0, _ch_ain2, 0);
  45. }
  46. void MotorDriver::forward(uint8_t speed)
  47. {
  48.     setMotor(_ch_ain1, speed, _ch_ain2, speed);
  49. }
  50. void MotorDriver::backward(uint8_t speed)
  51. {
  52.     setMotor(_ch_ain1, speed, _ch_ain2, speed);
  53. }
  54. void MotorDriver::turnLeft(uint8_t speed)
  55. {
  56.     setMotor(_ch_ain1, speed, _ch_ain2, speed);
  57. }
  58. void MotorDriver::turnRight(uint8_t speed)
  59. {
  60.     setMotor(_ch_ain1, speed, _ch_ain2, speed);
  61. }
复制代码

【语音指令实测】
唤醒词:“小智小智”  
用户:“前进 200 速度 3 秒”  
小车:立即直行,3 s 后自动停止,屏幕提示“前进完成”。  
用户:“左转 150 速度 2 秒”  
小车:原地左转 2 s 后刹车,环形 LED 流水灯提示“转向中”。
  行空板 K10 + 小智 AI——打造“能听懂话”的两轮 AI小车图2
【视频演示】
【踩坑记录 】

1. XL9535 上电默认输入,必须先把 P8/P9 设成输出,否则电机抖动。  
2. K10 的 3.3 V 电源最大 500 mA,千万别把电机电源接到 3.3 V 排针!  

【一句话总结】  

“当 K10 遇到 小智AI,语音不再只停留在‘对话’,而是直接对话语言(非命令词)‘驱动’世界—— 原来,AI 的终点不是回答问题,而是帮你把车开出去,再安全地开回来。”
【代码开源】
https://gitee.com/yuntian365/xiaozhi-k10-car1(替换小智AI对应文件)

注:main/CMakeLists.txt里找到 SOURCES这一行,确认有没有把 "motor/motor.cpp"加进去。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail