云天 发表于 6 天前

FireBeetle 2 ESP32-P4 和小智 AI 的智能小车项目

本帖最后由 云天 于 2025-7-28 13:52 编辑

【项目概述】

本项目旨在通过 FireBeetle ESP32-P4 开发板和小智 AI 平台实现一个智能小车,能够通过语音指令控制其运动状态。小车使用 TB6612FNG 微型双路直流电机驱动模块驱动两个 N20 减速电机,并通过行空板 K10 安装小智 AI 固件实现语音交互功能。整个项目结合了硬件开发、嵌入式编程和人工智能技术,具有较高的实用性和趣味性。
【项目亮点】

本项目的核心亮点之一是成功集成并应用了 xiaozhi-mcp库。该库是专为 ESP32 设备设计的,用于无缝接入小智 AI 平台的 MCP(模型上下文协议)服务器。通过 xiaozhi-mcp库,我们能够以极低的开发成本实现Esp32P4设备与小智 AI 平台的稳定通信,支持复杂的 JSON-RPC 协议,同时具备自动重连机制,确保了通信的高可靠性和稳定性。
【硬件设计】

1. FireBeetle ESP32-P4 开发板
FireBeetle ESP32-P4 是一款功能强大的开发板,具备丰富的 GPIO 引脚和强大的处理能力,适合用于物联网和嵌入式项目。在本项目中,我们使用其引脚 20、21、22 和 23 分别连接 TB6612FNG 驱动模块的控制引脚,用于控制电机的运动状态。

2. TB6612FNG 微型双路直流电机驱动模块
TB6612FNG 是一款双路全桥驱动芯片,单通道最大连续驱动电流可达 1.2A,峰值 2A/3.2A(连续脉冲/单脉冲),能够驱动微型直流电机。其控制逻辑简单,仅需 4 根管脚即可实现双路电机控制,适合与 Arduino 等控制器配合使用。

3. 行空板 K10
行空板 K10 是一款支持小智 AI 固件的开发板,具备语音交互功能。通过安装小智 AI 固件并配网,接入小智AI控制台配置智能体,从而将语音指令发送到小智 AI 服务器,实现对 ESP32-P4 小车的远程控制。

【软件设计】

1. Arduino IDE 编程
使用 Arduino IDE 2.3.6 编写 ESP32-P4 的控制程序,加载 `xiaozhi-mcp` 库以实现与小智 AI 平台的通信。程序通过 WebSocket 连接到小智 AI 的 MCP 服务器,并注册工具以接收语音指令,进而控制小车的运动状态。

2. 小智 AI 平台
小智 AI 提供了一个强大的语音交互平台,支持通过 MCP 协议与外部设备通信。通过在行空板 K10 上安装小智 AI 固件并配网,可以获取专属的 MCP 接入点,实现语音指令的发送和接收。

【项目实现】

1. 硬件连接
- 将 TB6612FNG 的 DIR1 引脚连接到 ESP32-P4 的引脚 20,PWM1 引脚连接到引脚 21。
- 将 TB6612FNG 的 DIR2 引脚连接到 ESP32-P4 的引脚 22,PWM2 引脚连接到引脚 23。
- 将两个 N20 减速电机分别连接到 TB6612FNG 的 M1 和 M2 接口。
- 将行空板 K10 安装小智 AI 固件并配网,获取 MCP 接入点。

2. 软件配置
- 在 Arduino IDE 中安装 `xiaozhi-mcp` 库。
- 编写 ESP32-P4 的控制程序,配置 WiFi 和 MCP 服务器地址,注册运动控制工具。
- 在小智 AI 平台上配置语音指令,将其映射到对应的 MCP 工具。




3. 程序逻辑
- 初始化 WiFi 和 MCP 客户端。
- 注册运动控制工具,接收语音指令。
- 根据语音指令控制电机的运动状态(前进、后退、停止、左转、右转)。
- 通过状态 LED 显示当前连接状态。

#include <Arduino.h>
#include <WiFi.h>
#include <WebSocketMCP.h>

/********** 配置项 ***********/
// WiFi设置
const char* WIFI_SSID = "sxs";
const char* WIFI_PASS = "smj080823";

// WebSocket MCP服务器地址
const char* MCP_ENDPOINT = "wss://api.xiaozhi.me/mcp/?token=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyODIzNSwiYWdlbnRJZCI6MTkwMjQsImVuZHBvaW50SWQiOiJhZ2VudF8xOTAyNCIsInB1cnBvc2UiOiJtY3AtZW5kcG9pbnQiLCJpYXQiOjE3NTM2MjQ4NDV9.LRuzeBUypjuc-NFuMYJb8Q7aUTn3GLY9xPNYFHLS3KMrkvV9nKHMJt1GvLy36kEq_oqPMnNjdbG7jchF5ZaXbw";

// 调试信息
#define DEBUG_SERIAL Serial
#define DEBUG_BAUD_RATE 115200

// LED控制引脚定义(用于状态指示和工具控制)
#define LED_PIN 3// ESP32板载LED
const int motor1M = 20;// 电机1方向控制
const int motor1E = 21;// 电机1速度控制
const int motor2M = 22; // 电机2方向控制
const int motor2E = 23; // 电机2速度控制
/********** 全局变量 ***********/
WebSocketMCP mcpClient;

// 缓冲区管理
#define MAX_INPUT_LENGTH 1024
char inputBuffer;
int inputBufferIndex = 0;
bool newCommandAvailable = false;

// 连接状态
bool wifiConnected = false;
bool mcpConnected = false;

/********** 函数声明 ***********/
void setupWifi();
void onMcpOutput(const String &message);
void onMcpError(const String &error);
void onMcpConnectionChange(bool connected);
void processSerialCommands();
void blinkLed(int times, int delayMs);
void registerMcpTools();

void setup() {
// 初始化串口
DEBUG_SERIAL.begin(DEBUG_BAUD_RATE);
DEBUG_SERIAL.println("\n\n 初始化...");

// 初始化LED
pinMode(LED_PIN, OUTPUT);
pinMode(motor1M, OUTPUT);
pinMode(motor1E, OUTPUT);
pinMode(motor2M, OUTPUT);
pinMode(motor2E, OUTPUT);
pinMode(3, OUTPUT);
digitalWrite(LED_PIN, LOW);

// 连接WiFi
setupWifi();

// 初始化MCP客户端
if (mcpClient.begin(MCP_ENDPOINT, onMcpConnectionChange)) {
    DEBUG_SERIAL.println(" 初始化成功,尝试连接到MCP服务器...");
} else {
    DEBUG_SERIAL.println(" 初始化失败!");
}

// 显示帮助信息
DEBUG_SERIAL.println("\n使用说明:");
DEBUG_SERIAL.println("- 通过串口控制台输入命令并回车发送");
DEBUG_SERIAL.println("- 从MCP服务器接收的消息将显示在串口控制台上");
DEBUG_SERIAL.println("- 输入\"help\"查看可用命令");
DEBUG_SERIAL.println();
}

void loop() {
// 处理MCP客户端
mcpClient.loop();

// 处理来自串口的命令
processSerialCommands();

// 状态LED显示
if (!wifiConnected) {
    // WiFi未连接: 快速闪烁
    blinkLed(1, 100);
} else if (!mcpConnected) {
    // WiFi已连接但MCP未连接: 慢闪
    blinkLed(1, 500);
} else {
    // 全部连接成功: LED亮起
    digitalWrite(LED_PIN, HIGH);
}
}

/**
* 设置WiFi连接
*/
void setupWifi() {
DEBUG_SERIAL.print(" 连接到 ");
DEBUG_SERIAL.println(WIFI_SSID);

// 开始连接
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);

// 等待连接
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
    delay(500);
    DEBUG_SERIAL.print(".");
    attempts++;
}

if (WiFi.status() == WL_CONNECTED) {
    wifiConnected = true;
    DEBUG_SERIAL.println();
    DEBUG_SERIAL.println(" 连接成功!");
    DEBUG_SERIAL.print(" IP地址: ");
    DEBUG_SERIAL.println(WiFi.localIP());
} else {
    wifiConnected = false;
    DEBUG_SERIAL.println();
    DEBUG_SERIAL.println(" 连接失败! 将继续尝试...");
}
}

/**
* MCP输出回调函数(stdout替代)
*/
void onMcpOutput(const String &message) {
DEBUG_SERIAL.print(" ");
DEBUG_SERIAL.println(message);
}

/**
* MCP错误回调函数(stderr替代)
*/
void onMcpError(const String &error) {
DEBUG_SERIAL.print(" ");
DEBUG_SERIAL.println(error);
}

/**
* 注册MCP工具
* 在连接成功后注册工具
*/
void registerMcpTools() {
DEBUG_SERIAL.println(" 注册工具...");

// 注册LED控制工具
mcpClient.registerTool(
    "led_blink",// 工具名称
    "控制ESP32P4 车辆运动状态", // 工具描述
    "{\"properties\":{\"state\":{\"title\":\"车辆运动状态\",\"type\":\"string\",\"enum\":[\"前进\",\"后退\",\"停止\",\"左转\",\"右转\"]}},\"required\":[\"state\"],\"title\":\"MotorControlArguments\",\"type\":\"object\"}",// 输入schema
    [](const String& args) {
      // 解析参数
      DEBUG_SERIAL.println("[工具] 运动控制: " + args);
      DynamicJsonDocument doc(256);
      DeserializationError error = deserializeJson(doc, args);
      
      if (error) {
      // 返回错误响应
      WebSocketMCP::ToolResponse response("{\"success\":false,\"error\":\"无效的参数格式\"}", true);
      return response;
      }
      
      String state = doc["state"].as<String>();
      DEBUG_SERIAL.println("[工具] 运动控制: " + state);
      
      // 运动控制
      if (state == "前进") {
      digitalWrite(motor1M, HIGH);
      analogWrite(motor1E, 200);
      digitalWrite(motor2M, LOW);
      analogWrite(motor2E, 200);
      DEBUG_SERIAL.println("[工具] 运动控制: 已" + state);
      } else if (state == "后退") {
      digitalWrite(motor1M, LOW);
      analogWrite(motor1E, 200);
      digitalWrite(motor2M, HIGH);
      analogWrite(motor2E, 200);
      
      DEBUG_SERIAL.println("[工具] 运动控制: 已" + state);
      } else if (state == "停止") {
      digitalWrite(motor1M, LOW);
      analogWrite(motor1E, 0);
      digitalWrite(motor2M, LOW);
      analogWrite(motor2E, 0);
      DEBUG_SERIAL.println("[工具] 运动控制: 已" + state);
      } else if (state == "左转") {
      digitalWrite(motor1M, LOW);
      analogWrite(motor1E, 200);
      digitalWrite(motor2M, LOW);
      analogWrite(motor2E, 200);
      DEBUG_SERIAL.println("[工具] 运动控制: 已" + state);
      }else if (state == "右转") {
      digitalWrite(motor1M, HIGH);
      analogWrite(motor1E, 200);
      digitalWrite(motor2M, HIGH);
      analogWrite(motor2E, 200);
      
      DEBUG_SERIAL.println("[工具] 运动控制: 已" + state);
      }
      
      // 返回成功响应
      String resultJson = "{\"success\":true,\"state\":\"" + state + "\"}";
      return WebSocketMCP::ToolResponse(resultJson);
    }
);
DEBUG_SERIAL.println(" 运动控制工具已注册");

}

/**
* MCP连接状态变化回调函数
*/
void onMcpConnectionChange(bool connected) {
mcpConnected = connected;
if (connected) {
    DEBUG_SERIAL.println(" 已连接到MCP服务器");
    // 连接成功后注册工具
    registerMcpTools();
} else {
    DEBUG_SERIAL.println(" 与MCP服务器断开连接");
}
}

/**
* 处理来自串口的命令
*/
void processSerialCommands() {
// 检查是否有串口数据
while (DEBUG_SERIAL.available() > 0) {
    char inChar = (char)DEBUG_SERIAL.read();
   
    // 处理回车或换行
    if (inChar == '\n' || inChar == '\r') {
      if (inputBufferIndex > 0) {
      // 添加字符串结束符
      inputBuffer = '\0';
      
      // 处理命令
      String command = String(inputBuffer);
      command.trim();
      
      if (command.length() > 0) {
          if (command == "help") {
            printHelp();
          } else if (command == "status") {
            printStatus();
          } else if (command == "reconnect") {
            DEBUG_SERIAL.println("正在重新连接...");
            mcpClient.disconnect();
          } else if (command == "tools") {
            // 显示已注册工具
            DEBUG_SERIAL.println("已注册工具数量: " + String(mcpClient.getToolCount()));
          } else {
            // 将命令发送到MCP服务器(stdin替代)
            if (mcpClient.isConnected()) {
            mcpClient.sendMessage(command);
            DEBUG_SERIAL.println("[发送] " + command);
            } else {
            DEBUG_SERIAL.println("未连接到MCP服务器,无法发送命令");
            }
          }
      }
      
      // 重置缓冲区
      inputBufferIndex = 0;
      }
    }
    // 处理退格键
    else if (inChar == '\b' || inChar == 127) {
      if (inputBufferIndex > 0) {
      inputBufferIndex--;
      DEBUG_SERIAL.print("\b \b"); // 退格、空格、再退格
      }
    }
    // 处理普通字符
    else if (inputBufferIndex < MAX_INPUT_LENGTH - 1) {
      inputBuffer = inChar;
      DEBUG_SERIAL.print(inChar); // Echo
    }
}
}

/**
* 打印帮助信息
*/
void printHelp() {
DEBUG_SERIAL.println("可用命令:");
DEBUG_SERIAL.println("help   - 显示此帮助信息");
DEBUG_SERIAL.println("status   - 显示当前连接状态");
DEBUG_SERIAL.println("reconnect - 重新连接到MCP服务器");
DEBUG_SERIAL.println("tools    - 查看已注册工具");
DEBUG_SERIAL.println("其他任何文本将直接发送到MCP服务器");
}

/**
* 打印当前状态
*/
void printStatus() {
DEBUG_SERIAL.println("当前状态:");
DEBUG_SERIAL.print("WiFi: ");
DEBUG_SERIAL.println(wifiConnected ? "已连接" : "未连接");
if (wifiConnected) {
    DEBUG_SERIAL.print("IP地址: ");
    DEBUG_SERIAL.println(WiFi.localIP());
    DEBUG_SERIAL.print("信号强度: ");
    DEBUG_SERIAL.println(WiFi.RSSI());
}
DEBUG_SERIAL.print("MCP服务器: ");
DEBUG_SERIAL.println(mcpConnected ? "已连接" : "未连接");
}

/**
* LED闪烁函数
*/
void blinkLed(int times, int delayMs) {
static int blinkCount = 0;
static unsigned long lastBlinkTime = 0;
static bool ledState = false;
static int lastTimes = 0;

if (times == 0) {
    digitalWrite(LED_PIN, LOW);
    blinkCount = 0;
    lastTimes = 0;
    return;
}
if (lastTimes != times) {
    blinkCount = 0;
    lastTimes = times;
    ledState = false;
    lastBlinkTime = millis();
}
unsigned long now = millis();
if (blinkCount < times * 2) {
    if (now - lastBlinkTime > delayMs) {
      lastBlinkTime = now;
      ledState = !ledState;
      digitalWrite(LED_PIN, ledState ? HIGH : LOW);
      blinkCount++;
    }
} else {
    digitalWrite(LED_PIN, LOW);
    blinkCount = 0;
    lastTimes = 0;
}
}
【项目成果】

通过本项目,我们成功实现了一个可以通过语音指令控制运动状态的智能小车。用户可以通过行空板 K10 上的小智 AI 发出语音指令,如“前进”、“后退”、“左转”等,小车会根据指令做出相应的动作。项目结合了硬件开发、嵌入式编程和人工智能技术,具有较高的创新性和实用性。


【演示视频】

https://www.bilibili.com/video/BV1y285zGEue/?share_source=copy_web



页: [1]
查看完整版本: FireBeetle 2 ESP32-P4 和小智 AI 的智能小车项目