当前位置: 首页 > news >正文

voice_control_smart_car(语音控制智能小车)

前言
前面学过了智能小车,也学习了小智AI ,这章是结合使用小智AI 语音控制智能小车

1:环境及硬件
esp-idf5.4.*
win11
小智AI全套(基于esp32s3(n16r8) 44pin )
小车 全套(2个层板,2个L298N,4个电机,4个轮子,2个节锂电池)
杜邦线 杜邦线 若干
低层
在这里插入图片描述
上层
在这里插入图片描述

2:代码
基于小智AI 1.9。2 修改
mp_server.cc

/** MCP Server Implementation* Reference: https://modelcontextprotocol.io/specification/2024-11-05*/#include "mcp_server.h"
#include <esp_log.h>
#include <esp_app_desc.h>
#include <algorithm>
#include <cstring>
#include <esp_pthread.h>#include "application.h"
#include "display.h"
#include "board.h"
#include "smartcar/smart_car_run.h"#define TAG "MCP"McpServer::McpServer() {
}McpServer::~McpServer() {for (auto tool : tools_) {delete tool;}tools_.clear();
}void McpServer::AddCommonTools() {// *Important* To speed up the response time, we add the common tools to the beginning of// the tools list to utilize the prompt cache.// **重要** 为了提升响应速度,我们把常用的工具放在前面,利用 prompt cache 的特性。// Backup the original tools list and restore it after adding the common tools.auto original_tools = std::move(tools_);auto& board = Board::GetInstance();// Do not add custom tools here.// Custom tools must be added in the board's InitializeTools function.AddTool("self.get_device_status","Provides the real-time information of the device, including the current status of the audio speaker, screen, battery, network, etc.\n""Use this tool for: \n""1. Answering questions about current condition (e.g. what is the current volume of the audio speaker?)\n""2. As the first step to control the device (e.g. turn up / down the volume of the audio speaker, etc.)",PropertyList(),[&board](const PropertyList& properties) -> ReturnValue {return board.GetDeviceStatusJson();});AddTool("self.audio_speaker.set_volume", "Set the volume of the audio speaker. If the current volume is unknown, you must call `self.get_device_status` tool first and then call this tool.",PropertyList({Property("volume", kPropertyTypeInteger, 0, 100)}), [&board](const PropertyList& properties) -> ReturnValue {auto codec = board.GetAudioCodec();codec->SetOutputVolume(properties["volume"].value<int>());return true;});auto backlight = board.GetBacklight();if (backlight) {AddTool("self.screen.set_brightness","Set the brightness of the screen.",PropertyList({Property("brightness", kPropertyTypeInteger, 0, 100)}),[backlight](const PropertyList& properties) -> ReturnValue {uint8_t brightness = static_cast<uint8_t>(properties["brightness"].value<int>());backlight->SetBrightness(brightness, true);return true;});}auto display = board.GetDisplay();if (display && !display->GetTheme().empty()) {AddTool("self.screen.set_theme","Set the theme of the screen. The theme can be `light` or `dark`.",PropertyList({Property("theme", kPropertyTypeString)}),[display](const PropertyList& properties) -> ReturnValue {display->SetTheme(properties["theme"].value<std::string>().c_str());return true;});}auto camera = board.GetCamera();if (camera) {AddTool("self.camera.take_photo","Take a photo and explain it. Use this tool after the user asks you to see something.\n""Args:\n""  `question`: The question that you want to ask about the photo.\n""Return:\n""  A JSON object that provides the photo information.",PropertyList({Property("question", kPropertyTypeString)}),[camera](const PropertyList& properties) -> ReturnValue {// Lower the priority to do the camera captureTaskPriorityReset priority_reset(1);if (!camera->Capture()) {return "{\"success\": false, \"message\": \"Failed to capture photo\"}";}auto question = properties["question"].value<std::string>();return camera->Explain(question);});}/////////////////////////////////////////////////////////////////////////////////auto carcontrol = board.GetSmartCarControl();if(carcontrol){//前进AddTool("self.car.go_forward","Control the car to move forward. Use this tool to make the car move in the forward direction.\n""Parameters:\n""  duration: Moving duration in milliseconds. Default is 500ms.\n""Usage scenarios:\n""1. When the user asks the car to move forward\n""2. When traveling straight on a road\n""3. When needing to advance to a specific location",PropertyList({Property("duration", kPropertyTypeInteger, 100, 5000)}),[carcontrol](const PropertyList& properties) -> ReturnValue {int duration = properties["duration"].value<int>();carcontrol->car_forward(duration);//  return CarControlTurnRight(angle, speed);ESP_LOGI(TAG, "self.car.go_backward %d",duration);return true;});
//后退
AddTool("self.car.go_backward", "Control the car to move backward. Use this tool to make the car move in the reverse direction.\n""Parameters:\n""  duration: Moving duration in milliseconds. Default is 500ms.\n""Usage scenarios:\n""1. When the user asks the car to move backward\n""2. When reversing from a parking space\n""3. When needing to back away from obstacles",PropertyList({Property("duration", kPropertyTypeInteger, 100, 5000)}),[carcontrol](const PropertyList& properties) -> ReturnValue {int duration = properties["duration"].value<int>();//  return CarControlTurnRight(angle, speed);carcontrol->car_backward(duration);ESP_LOGI(TAG, "self.car.go_backward %d",duration);return true;});AddTool("self.car.stop","Control the car to stop moving. Use this tool to bring the car to a complete halt.\n""Usage scenarios:\n""1. When the user asks the car to stop\n""2. When encountering obstacles or danger\n""3. When reaching the destination\n""4. In emergency situations requiring immediate stopping",PropertyList(),[carcontrol](const PropertyList& properties) -> ReturnValue {//  int duration = properties["duration"].value<int>();//  return CarControlTurnRight(angle, speed);carcontrol->car_stop();ESP_LOGI(TAG, "self.car.stop");return true;});AddTool("self.car.auto_cruise","Control the car to enter automatic cruise mode. Use this tool to enable autonomous driving with obstacle avoidance for 30 seconds.\n""Parameters:\n""  duration: Cruise duration in seconds. Default is 30 seconds.\n""Usage scenarios:\n""1. When the user wants hands-free driving\n""2. For long distance highway driving\n""3. When maintaining consistent speed is desired\n""4. When adaptive cruise control is needed",PropertyList({Property("duration", kPropertyTypeInteger, 100, 5000)}),[carcontrol](const PropertyList& properties) -> ReturnValue {int duration = properties["duration"].value<int>();//  return CarControlTurnRight(angle, speed);ESP_LOGI(TAG, "self.car.auto_cruise %d",duration);return true;});/////AddTool("self.car.turn_left","Control the car to turn rleftight. Use this tool to make the car turn to the left side.\n""Parameters:\n""  duration: Moving duration in milliseconds, range 0-5000. Default is 500ms.\n""  speed: Turning speed, range 0-1000. Default is 800 (half speed).\n""Usage scenarios:\n""1. When the user asks the car to turn left\n""2. When avoiding obstacles\n""3. When changing direction is needed",PropertyList({Property("duration", kPropertyTypeInteger, 0, 5000),Property("speed", kPropertyTypeInteger, 0, 1000)}),[carcontrol](const PropertyList& properties) -> ReturnValue {int duration = properties["duration"].value<int>();//  int speed = properties["speed"].value<int>();//  return CarControlTurnRight(angle, speed);carcontrol->car_turn_left(duration);ESP_LOGI(TAG, "self.car.turn_right %d",duration);return true;});AddTool("self.car.turn_right","Control the car to turn right. Use this tool to make the car turn to the right side.\n""Parameters:\n""  duration: Moving duration in milliseconds, range 0-5000. Default is 500ms.\n""  speed: Turning speed, range 0-1000. Default is 800 (half speed).\n""Usage scenarios:\n""1. When the user asks the car to turn right\n""2. When avoiding obstacles\n""3. When changing direction is needed",PropertyList({Property("duration", kPropertyTypeInteger, 0, 5000),Property("speed", kPropertyTypeInteger, 0, 1000)}),[carcontrol](const PropertyList& properties) -> ReturnValue {int duration = properties["duration"].value<int>();int speed = properties["speed"].value<int>();//  return CarControlTurnRight(angle, speed);carcontrol->car_turn_right(duration);ESP_LOGI(TAG, "self.car.turn_right %d-%d",duration,speed);return true;});///////////////////////////////////////////////////////////////////////////////////byeh add   20251020AddTool("设置舵机角度","Set the  servo angle.",PropertyList({Property("angle", kPropertyTypeInteger, 0, 180)}),[this](const PropertyList& properties) -> ReturnValue {ESP_LOGI(TAG, "设置舵机角度");int angle = properties["angle"].value<int>();//打印舵机角度ESP_LOGI(TAG, "[servo angle]舵机角度为%d",angle);return true;});AddTool("self.fan.turn_on","Turn on the fan.",PropertyList(),[this](const PropertyList& properties) -> ReturnValue {ESP_LOGI(TAG, "[Turn on the fan]打开风扇");//风扇逻辑return true;});//////////////////////////////////////////////////////////////////////////////////}// Restore the original tools list to the end of the tools listtools_.insert(tools_.end(), original_tools.begin(), original_tools.end());
}void McpServer::AddTool(McpTool* tool) {// Prevent adding duplicate toolsif (std::find_if(tools_.begin(), tools_.end(), [tool](const McpTool* t) { return t->name() == tool->name(); }) != tools_.end()) {ESP_LOGW(TAG, "Tool %s already added", tool->name().c_str());return;}ESP_LOGI(TAG, "Add tool: %s", tool->name().c_str());tools_.push_back(tool);
}void McpServer::AddTool(const std::string& name, const std::string& description, const PropertyList& properties, std::function<ReturnValue(const PropertyList&)> callback) {AddTool(new McpTool(name, description, properties, callback));
}void McpServer::AddUserOnlyTool(const std::string& name, const std::string& description, const PropertyList& properties, std::function<ReturnValue(const PropertyList&)> callback) {auto tool = new McpTool(name, description, properties, callback);tool->set_user_only(true);AddTool(tool);
}void McpServer::ParseMessage(const std::string& message) {cJSON* json = cJSON_Parse(message.c_str());if (json == nullptr) {ESP_LOGE(TAG, "Failed to parse MCP message: %s", message.c_str());return;}ParseMessage(json);cJSON_Delete(json);
}void McpServer::ParseCapabilities(const cJSON* capabilities) {auto vision = cJSON_GetObjectItem(capabilities, "vision");if (cJSON_IsObject(vision)) {auto url = cJSON_GetObjectItem(vision, "url");auto token = cJSON_GetObjectItem(vision, "token");if (cJSON_IsString(url)) {auto camera = Board::GetInstance().GetCamera();if (camera) {std::string url_str = std::string(url->valuestring);std::string token_str;if (cJSON_IsString(token)) {token_str = std::string(token->valuestring);}camera->SetExplainUrl(url_str, token_str);}}}
}void McpServer::ParseMessage(const cJSON* json) {// Check JSONRPC versionauto version = cJSON_GetObjectItem(json, "jsonrpc");if (version == nullptr || !cJSON_IsString(version) || strcmp(version->valuestring, "2.0") != 0) {ESP_LOGE(TAG, "Invalid JSONRPC version: %s", version ? version->valuestring : "null");return;}// Check methodauto method = cJSON_GetObjectItem(json, "method");if (method == nullptr || !cJSON_IsString(method)) {ESP_LOGE(TAG, "Missing method");return;}auto method_str = std::string(method->valuestring);if (method_str.find("notifications") == 0) {return;}// Check paramsauto params = cJSON_GetObjectItem(json, "params");if (params != nullptr && !cJSON_IsObject(params)) {ESP_LOGE(TAG, "Invalid params for method: %s", method_str.c_str());return;}auto id = cJSON_GetObjectItem(json, "id");if (id == nullptr || !cJSON_IsNumber(id)) {ESP_LOGE(TAG, "Invalid id for method: %s", method_str.c_str());return;}auto id_int = id->valueint;if (method_str == "initialize") {if (cJSON_IsObject(params)) {auto capabilities = cJSON_GetObjectItem(params, "capabilities");if (cJSON_IsObject(capabilities)) {ParseCapabilities(capabilities);}}auto app_desc = esp_app_get_description();std::string message = "{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{\"tools\":{}},\"serverInfo\":{\"name\":\"" BOARD_NAME "\",\"version\":\"";message += app_desc->version;message += "\"}}";ReplyResult(id_int, message);} else if (method_str == "tools/list") {std::string cursor_str = "";if (params != nullptr) {auto cursor = cJSON_GetObjectItem(params, "cursor");if (cJSON_IsString(cursor)) {cursor_str = std::string(cursor->valuestring);}}GetToolsList(id_int, cursor_str);} else if (method_str == "tools/call") {if (!cJSON_IsObject(params)) {ESP_LOGE(TAG, "tools/call: Missing params");ReplyError(id_int, "Missing params");return;}auto tool_name = cJSON_GetObjectItem(params, "name");if (!cJSON_IsString(tool_name)) {ESP_LOGE(TAG, "tools/call: Missing name");ReplyError(id_int, "Missing name");return;}auto tool_arguments = cJSON_GetObjectItem(params, "arguments");if (tool_arguments != nullptr && !cJSON_IsObject(tool_arguments)) {ESP_LOGE(TAG, "tools/call: Invalid arguments");ReplyError(id_int, "Invalid arguments");return;}DoToolCall(id_int, std::string(tool_name->valuestring), tool_arguments);} else {ESP_LOGE(TAG, "Method not implemented: %s", method_str.c_str());ReplyError(id_int, "Method not implemented: " + method_str);}
}void McpServer::ReplyResult(int id, const std::string& result) {std::string payload = "{\"jsonrpc\":\"2.0\",\"id\":";payload += std::to_string(id) + ",\"result\":";payload += result;payload += "}";Application::GetInstance().SendMcpMessage(payload);
}void McpServer::ReplyError(int id, const std::string& message) {std::string payload = "{\"jsonrpc\":\"2.0\",\"id\":";payload += std::to_string(id);payload += ",\"error\":{\"message\":\"";payload += message;payload += "\"}}";Application::GetInstance().SendMcpMessage(payload);
}void McpServer::GetToolsList(int id, const std::string& cursor) {const int max_payload_size = 8000;std::string json = "{\"tools\":[";bool found_cursor = cursor.empty();auto it = tools_.begin();std::string next_cursor = "";while (it != tools_.end()) {// 如果我们还没有找到起始位置,继续搜索if (!found_cursor) {if ((*it)->name() == cursor) {found_cursor = true;} else {++it;continue;}}// 添加tool前检查大小std::string tool_json = (*it)->to_json() + ",";if (json.length() + tool_json.length() + 30 > max_payload_size) {// 如果添加这个tool会超出大小限制,设置next_cursor并退出循环next_cursor = (*it)->name();break;}json += tool_json;++it;}if (json.back() == ',') {json.pop_back();}if (json.back() == '[' && !tools_.empty()) {// 如果没有添加任何tool,返回错误ESP_LOGE(TAG, "tools/list: Failed to add tool %s because of payload size limit", next_cursor.c_str());ReplyError(id, "Failed to add tool " + next_cursor + " because of payload size limit");return;}if (next_cursor.empty()) {json += "]}";} else {json += "],\"nextCursor\":\"" + next_cursor + "\"}";}ReplyResult(id, json);
}void McpServer::DoToolCall(int id, const std::string& tool_name, const cJSON* tool_arguments) {auto tool_iter = std::find_if(tools_.begin(), tools_.end(), [&tool_name](const McpTool* tool) { return tool->name() == tool_name; });if (tool_iter == tools_.end()) {ESP_LOGE(TAG, "tools/call: Unknown tool: %s", tool_name.c_str());ReplyError(id, "Unknown tool: " + tool_name);return;}PropertyList arguments = (*tool_iter)->properties();try {for (auto& argument : arguments) {bool found = false;if (cJSON_IsObject(tool_arguments)) {auto value = cJSON_GetObjectItem(tool_arguments, argument.name().c_str());if (argument.type() == kPropertyTypeBoolean && cJSON_IsBool(value)) {argument.set_value<bool>(value->valueint == 1);found = true;} else if (argument.type() == kPropertyTypeInteger && cJSON_IsNumber(value)) {argument.set_value<int>(value->valueint);found = true;} else if (argument.type() == kPropertyTypeString && cJSON_IsString(value)) {argument.set_value<std::string>(value->valuestring);found = true;}}if (!argument.has_default_value() && !found) {ESP_LOGE(TAG, "tools/call: Missing valid argument: %s", argument.name().c_str());ReplyError(id, "Missing valid argument: " + argument.name());return;}}} catch (const std::exception& e) {ESP_LOGE(TAG, "tools/call: %s", e.what());ReplyError(id, e.what());return;}// Use main thread to call the toolauto& app = Application::GetInstance();app.Schedule([this, id, tool_iter, arguments = std::move(arguments)]() {try {ReplyResult(id, (*tool_iter)->Call(arguments));} catch (const std::exception& e) {ESP_LOGE(TAG, "tools/call: %s", e.what());ReplyError(id, e.what());}});
}

application.cc
修改

void Application::CheckNewVersion(Ota& ota) {
//。。。。。。。。。省略
//不检查版本更新,免得下载新固件 被覆盖掉了const bool  bcheck = false;if(!bcheck){}if (bcheck && !ota.CheckVersion()) {retry_count++;if (retry_count >= MAX_RETRY) {ESP_LOGE(TAG, "Too many retries, exit version check");return;}//。。。。。。。。。省略          
}void Application::Start() {
//。。。。。。。。。省略  //########################by eh add  2025auto carcontrol = board.GetSmartCarControl();carcontrol->car_init_config();//########################AudioServiceCallbacks callbacks;

smart_car_run.h

#ifndef SMART_CAR_RUN_H
#define SMART_CAR_RUN_H
//void car_run_main();
class MotorControl;
class SmartCarControl{
public:SmartCarControl();~SmartCarControl();void car_init_config();void car_forward(int ms);void car_backward(int ms);void car_turn_left(int ms);void car_turn_right(int ms);void car_stop();void car_auto_cruise(int seconds); //自动巡航private:int  current_state;int  get_current_state();void set_current_state(int state);MotorControl* getMotorControl();};
#endif

smart_car_run.cc

#include "motor_control.h"
#include "smart_car_run.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"#define  MIN_DELAY_MS   300
#define MAX_DELAY_MS   2000
#define  CHECK_MS_COMMON(X) if(X<MIN_DELAY_MS){X=MIN_DELAY_MS;}else if(X>MAX_DELAY_MS){X = MAX_DELAY_MS;}#define MIN_AUTO_CRUISE_SEC   10
#define MAX_AUTO_CRUISE_SEC   50
#define  CHECK_SEC_AUTO_CRUISE(X) if(X<MIN_AUTO_CRUISE_SEC){X=MIN_AUTO_CRUISE_SEC;}else if(X>MAX_AUTO_CRUISE_SEC){X = MAX_AUTO_CRUISE_SEC;}
SmartCarControl::SmartCarControl(){current_state=0;
}
SmartCarControl::~SmartCarControl(){}
void SmartCarControl::car_init_config(){getMotorControl()->motor_control_init();
}
void SmartCarControl::car_forward(int ms){CHECK_MS_COMMON(ms)auto motor=getMotorControl();motor->car_control(CAR_FORWARD, 800); // 中等速度vTaskDelay(pdMS_TO_TICKS(ms)); //500motor->car_control(CAR_STOP, 0);vTaskDelay(pdMS_TO_TICKS(1000));
}
void SmartCarControl::car_backward(int ms){CHECK_MS_COMMON(ms)auto motor=getMotorControl();motor->car_control(CAR_BACKWARD, 800); // 中等速度vTaskDelay(pdMS_TO_TICKS(ms));motor->car_control(CAR_STOP, 0);vTaskDelay(pdMS_TO_TICKS(1000));
}
void SmartCarControl::car_turn_left(int ms){CHECK_MS_COMMON(ms)auto motor=getMotorControl();motor->car_control(CAR_LEFT, 800);vTaskDelay(pdMS_TO_TICKS(ms));motor-> car_control(CAR_STOP, 0);vTaskDelay(pdMS_TO_TICKS(1000));
}
void SmartCarControl::car_turn_right(int ms){CHECK_MS_COMMON(ms)auto motor=getMotorControl();motor->car_control(CAR_RIGHT, 800);vTaskDelay(pdMS_TO_TICKS(ms));motor-> car_control(CAR_STOP, 0);vTaskDelay(pdMS_TO_TICKS(1000));
}
void SmartCarControl::car_stop(){auto motor=getMotorControl();motor-> car_control(CAR_STOP, 0);vTaskDelay(pdMS_TO_TICKS(500));
}
void SmartCarControl::car_auto_cruise(int seconds)//自动巡航
{CHECK_SEC_AUTO_CRUISE(seconds)
}//private
int  SmartCarControl::get_current_state(){return current_state;
}
void SmartCarControl::set_current_state(int state){current_state=state;
}MotorControl* SmartCarControl::getMotorControl(){static MotorControl motor_control;return &motor_control;}

motor_control.h
引脚定义

//。。。。。。。。。省略  
#define SIMPLE_GPIO_CONTROL
#define ESP32_S3
//SIMPLE_MODE//////////////////////////////////////////////
// L298N模块1 (控制左侧两个电机):
// ENA  → GPIO_2 (PWM)
// IN1  → GPIO_4 (FWD_LEFT) → 同时连接到电机1和电机3的方向控制
// IN2  → GPIO_5 (BWD_LEFT) → 同时连接到电机1和电机3的方向控制  
// IN3  → GPIO_4 (FWD_LEFT) → 并联到IN1
// IN4  → GPIO_5 (BWD_LEFT) → 并联到IN2// L298N模块2 (控制右侧两个电机):
// ENA  → GPIO_3 (PWM) 
// IN1  → GPIO_6 (FWD_RIGHT) → 同时连接到电机2和电机4的方向控制
// IN2  → GPIO_7 (BWD_RIGHT) → 同时连接到电机2和电机4的方向控制
// IN3  → GPIO_6 (FWD_RIGHT) → 并联到IN1
// IN4  → GPIO_7 (BWD_RIGHT) → 并联到IN2
////////////////////////////////////////////////
// 电机引脚定义 - 请根据实际接线修改
#ifdef  SIMPLE_GPIO_CONTROL
// PWM控制引脚
#ifndef  ESP32_S3
#define LEFT_PWM_PIN    GPIO_NUM_0   //使能LEFT#define RIGHT_PWM_PIN   GPIO_NUM_10    //使能RIGHT// #define FWD_RIGHT_PIN   GPIO_NUM_3    // 右轮前进
// #define BWD_RIGHT_PIN   GPIO_NUM_2    // 右轮后退
// 方向控制引脚  
#define FWD_LEFT_PIN    GPIO_NUM_2    // 左边 IN1 IN3
#define BWD_LEFT_PIN    GPIO_NUM_3   // 左边 IN2 IN4
//
#define FWD_RIGHT_PIN   GPIO_NUM_18    // 右IN1 IN3
#define BWD_RIGHT_PIN   GPIO_NUM_12    // 右IN2 IN4// #define FWD_LEFT_PIN    GPIO_NUM_12    // 左轮前进
// #define BWD_LEFT_PIN    GPIO_NUM_18   // 左轮后退// PWM通道
#define LEFT_PWM_CH     LEDC_CHANNEL_1
#define RIGHT_PWM_CH    LEDC_CHANNEL_2
#else
#define LEFT_PWM_PIN    GPIO_NUM_9   //使能LEFT
// 方向控制引脚  
// #define FWD_LEFT_PIN    GPIO_NUM_10    // 左边 IN1 IN3
// #define BWD_LEFT_PIN    GPIO_NUM_11  // 左边 IN2 IN4#define FWD_LEFT_PIN    GPIO_NUM_11    // 左边 IN1 IN3
#define BWD_LEFT_PIN    GPIO_NUM_10  // 左边 IN2 IN4// #define RIGHT_PWM_PIN   GPIO_NUM_12    //使能RIGHT
// // 方向控制引脚 
// #define FWD_RIGHT_PIN   GPIO_NUM_13    // 右IN1 IN3
// #define BWD_RIGHT_PIN   GPIO_NUM_14    // 右IN2 IN4
//35->38 不要用 只有输入功能没输出功能
// #define RIGHT_PWM_PIN   GPIO_NUM_38    //使能RIGHT
// // 方向控制引脚 
// #define FWD_RIGHT_PIN   GPIO_NUM_37    // 右IN1 IN3
// #define BWD_RIGHT_PIN   GPIO_NUM_36    // 右IN2 IN4#define RIGHT_PWM_PIN   GPIO_NUM_45    //使能RIGHT
// 方向控制引脚 
#define FWD_RIGHT_PIN   GPIO_NUM_40    // 右IN1 IN3
#define BWD_RIGHT_PIN   GPIO_NUM_39    // 右IN2 IN4// PWM通道
#define LEFT_PWM_CH     LEDC_CHANNEL_0
#define RIGHT_PWM_CH    LEDC_CHANNEL_1
#endif#else
//c3  右边 GPIO
#define MOTOR1_IN1     GPIO_NUM_2
#define MOTOR1_IN2     GPIO_NUM_3
#define MOTOR1_ENA     GPIO_NUM_10#define MOTOR2_IN1     GPIO_NUM_6
#define MOTOR2_IN2     GPIO_NUM_7
#define MOTOR2_ENB     GPIO_NUM_5//左边 GPIO
#define MOTOR3_IN1     GPIO_NUM_0
#define MOTOR3_IN2     GPIO_NUM_1
#define MOTOR3_ENA     GPIO_NUM_12#define MOTOR4_IN1     GPIO_NUM_18
#define MOTOR4_IN2     GPIO_NUM_19
#define MOTOR4_ENB     GPIO_NUM_13// 电机通道定义
#define MOTOR1_PWM_CH           LEDC_CHANNEL_0
#define MOTOR2_PWM_CH           LEDC_CHANNEL_1
#define MOTOR3_PWM_CH           LEDC_CHANNEL_2
#define MOTOR4_PWM_CH           LEDC_CHANNEL_3#endif// PWM配置
#ifndef  ESP32_S3
#define LEDC_TIMER              LEDC_TIMER_1
#else
#define LEDC_TIMER              LEDC_TIMER_3
#endif
#define LEDC_MODE               LEDC_LOW_SPEED_MODE
#define LEDC_DUTY_RES           LEDC_TIMER_10_BIT
#define LEDC_FREQUENCY          5000
//。。。。。。。。。省略  

主要修改或增加文件
在这里插入图片描述
在这里插入图片描述

2:说明及建议
一个L298N 管一边的2个电机
只使用了3个GPIO
一个使能的 作为PWM
2个GPIO 管 方向
这里MCU 供电 直接用一个 l298n 的5V输出 供电
这样省GPIO,建议用2个MCU 更好,一个专门用于小智,一个控制车子,可以用UART 连接通信
L298N 寸尺大,建议改成 TB6612 尺寸小的,这样可以安装更多东西,
最好的是自己画原理图,打板,布置好元器件,还有这个亚克力板有3D打印的,最好自己制作,更省空间,孔位置更好布置

3:测试结果 如果对你又帮助,麻烦点个赞,加个关注
后续在上面板上增加一给摄像头,寻路及其他作用
在这里插入图片描述

voice_control_smart_car

工程下载
md5:0758ff11109f742ce74295a11c78b431

http://www.dtcms.com/a/590195.html

相关文章:

  • 如何做1个手机网站宜宾网站建设价格
  • ‘pyinstaller‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。
  • 2025年11月9日互联网技术热点TOP3及影响分析(乌镇核心消息)
  • 可以做问答的网站开题报告旅游网站建设
  • 公司的建设网站公司wordpress 首页显示文章数量
  • 郑州网站建设炉石wordpress 关闭feed
  • FPGA教程系列-Vivado中实现简单正弦波
  • 什么可以放置网站内容建工网论坛
  • 龙岩网站定制衡水seo营销
  • 西安阎良区建设局网站东莞建工集团
  • 南昌网站建设赣icp南昌搜索引擎优化的作用是什么
  • 济南传承网站建设公司iis关闭网站
  • 【STL——常用算数生成与集成算法】
  • 滨江建设交易门户网站VR网站建设价格
  • 网站开发项目实训装饰工程造价
  • 长沙麓谷建设发展有限公司网站phpcms 手机网站模板
  • 微网站如何做宣传搜索引擎搜索器
  • 科技网站设计模板页
  • 甘肃省城乡和建设厅网站外贸开发软件有哪些
  • 机器学习周报二十一
  • 第三次作业 网站搭建
  • Gorm的使用记录
  • 手工木雕网站建设策划书计算机专业主要学什么内容
  • 蒙古文政务网站群建设工作方案c 做网站性能怎么样
  • 海口专业网站建设最简单的wordpress主题
  • 厦门企业做网站怎样加入网站
  • 门户网站网站开发北京公司注册费用
  • 网站数据库制作seo高级优化技巧
  • 俄语网站建设注意事项thinkphp和wordpress区别
  • STM32F103学习笔记-16-RCC(第4节)-STM32 标准外设库函数命名规则总览(以stm32f10x_rcc.c/h为例)