资讯详情

基于ESP32的智能遥控小车:从硬件选型到PID闭环的完整实战指南

📅 2026/10/14 19:00:42 | 华诺云谱 👁 阅读
基于ESP32的智能遥控小车:从硬件选型到PID闭环的完整实战指南
简介面向嵌入式开发初学者与物联网爱好者的ESP32智能遥控小车项目以乐鑫ESP32为主控整合Wi-Fi/蓝牙通信、电机驱动、传感器采集与手机端控制覆盖从硬件选型、电路连接、固件编写到整机调试的完整流程。压缩包共19个文件体积仅1.39MB以16个Python源码为主包含主控制程序、红外遥控、样例与引脚调整版本等另有2张实物/接线参考图和1份Markdown项目说明方便对照接线与理解代码结构。目前已有4816人学习下载。通过该资源可获取可运行的遥控小车固件、核心功能模块的拆分思路、引脚定义修改方法以及超声波避障、加速度计/陀螺仪数据处理等常用传感器应用同时也能看到无线通信协议在真实硬件上的落地实现适合用于课程设计、竞赛练习或个人项目二次开发。1. 基于ESP32的智能遥控小车为什么说它是嵌入式入坑的最佳载体很多想学嵌入式的人第一步都卡在“不知道做什么项目”。做纯点灯太无聊做四轴飞行器成本又高而基于ESP32的智能遥控小车恰好卡在一个舒服的中间位置主板成本低、外设丰富、坏了换配件不心疼而且能一次性把WiFi、蓝牙、PWM、中断、PID这些嵌入式主力知识点串起来。我拆过几台类似的底盘也给人调过不少翻车现场结论很直接——如果你只有一块ESP32开发板和一辆玩具车底盘你离一台能手机遥控、能跑闭环速度的智能小车只差几个关键的接线和两百行代码。这篇笔记就按“硬件选型 → 电机控制 → 遥控链路 → 踩坑记录 → 进阶改造”的逻辑把整个落地过程压缩给你看。2. 硬件选型从H桥驱动到电源分配动手前的四步检查很多人买齐配件接线一插就发现板子冒烟问题几乎都出在没先做硬件层面的规划。ESP32本身是一块3.3V逻辑的芯片而小车电机是6V到12V的直流电机两者之间必须有驱动芯片做“功率放大”同时电源部分也得把电池电压分开处理。这一章先把选型理由说透再给你一套可照抄的接线方案。2.1 电机驱动选型为什么我推荐TB6612而不是L298N驱动直流电机最常用的方案是H桥电路本质上就是四个开关管控制电机两端电压极性换向就能反转。市面上给DIY玩家用的驱动模块里L298N和TB6612是出现频率最高的两块。L298N最大的问题是内部使用双极型晶体管饱和压降能达到2V左右。什么意思呢你电池电压7.4V经过L298N到电机两端就只剩5.4V左右动力被白白吃掉一大截。而且L298N发热明显模块体积大插在ESP32小车上占空间。TB6612FNG使用的是MOSFET导通阻抗低压降一般只有0.3V左右输出效率高得多模块尺寸也只有指甲盖那么大非常适合小车这类紧凑场景。接线逻辑其实不难。TB6612模块上有两个通道每个通道控制一个电机VM接电池正极2S锂电约7.4VVCC接逻辑电源5V即可PWMA与AIN1、AIN2控制电机A的转速和方向PWMB与BIN1、BIN2控制电机BGND必须和ESP32共地引脚功能接线目标说明VM电机供电电池7.4V直接决定电机输出功率VCC逻辑电平5V驱动芯片内部逻辑供电PWMA / PWMBPWM调速输入ESP32任意LEDC通道高电平比例决定平均电压AIN1 / AIN2方向控制两个GPIO一高一低正转反之反转AO1 / AO2电机A输出电机A两端没有极性要求接反就反转我一般会先用万用表量一遍模块各引脚对地阻值确认没有短路再上电。这一步能省掉后面80%的排查时间。2.2 引脚分配与PWM通道先用表格定好GPIO再接线ESP32不像Arduino UNO那样引脚随意用它有自己一套规则其中最容易坑人的是GPIO34到GPIO39这六个脚是纯输入脚不能输出PWM。另一个是部分引脚比如GPIO12、GPIO15在上电时有特殊状态可能导致电机在上电瞬间误动作需要避开或外加下拉电阻。我习惯把每个功能用的GPIO先用表格定下来再动手接线。下面是一份经过测试的分配方案功能引脚说明左电机方向AGPIO12AIN1注意上电状态左电机方向BGPIO13AIN2左电机PWMGPIO14LEDC通道0右电机方向AGPIO26BIN1右电机方向BGPIO27BIN2右电机PWMGPIO25LEDC通道1左编码器A相GPIO34PCNT脉冲输入左编码器B相GPIO35方向判断右编码器A相GPIO32PCNT脉冲输入右编码器B相GPIO33方向判断I2C预留GPIO21 / GPIO22可接OLED或传感器这里有两个关键点。GPIO12在部分开发板上电时是高电平状态如果你直接接TB6612的方向脚通电瞬间电机可能会抽动一下解决办法是方向脚不怕这个抖动、等初始化后再写电平或用10kΩ电阻下拉。另外把LEDC通道固定下来后后面所有代码都围绕这张表进行改起来也方便。编码器电机通常引出6根线其中两根是电机电源线四根是编码器信号线。编码器信号里A相输出脉冲B相用于判断正反转。如果你不用闭环控制编码器线可以不接但如果要做速度闭环这四根线必须在选型时就确认好。2.3 电源分配别让电机拉低电压把ESP32带崩电源是整个小车最容易隐蔽翻车的环节。纯电池供电时电机启动瞬间电流可能冲到1A以上电池电压被瞬间拉低。ESP32内部虽然有稳压但如果输入电压低于阈值会触发欠压复位表现就是小车一启动ESP32就重启串口反复打印乱码或brownout信息。我常用的做法是双层供电2S锂电池7.4V直接给TB6612的VM供电驱动电机。从电池并联出一个5V降压模块比如MP1584或AMS1117单独给ESP32的5V引脚供电。这样电机的大电流变化不会直接冲击ESP32的供电轨。如果不想再加稳压模块也可以把5V降压模块直接焊在电池接口上用热缩管包好。关键原则是电机电源和逻辑电源从源头分开只在GND处汇合。顺带一个容易被忽略的点降压模块和电池的正极之间最好串联一个自恢复保险丝或开关我第一次做的时候没有加有一次电机堵转直接把稳压模块烧了从那以后所有样机都强制加装保护。3. 电机控制PWM调速与编码器速度闭环的落地代码选好硬件之后真正的核心代码就集中在电机控制上。ESP32的PWM输出用的是LEDC控制器比Arduino UNO的analogWrite更灵活但API的使用方式略有区别。这一章从最基础的PWM生成开始一直写到能用的速度闭环每一段代码都附参数说明。3.1 用LEDC生成PWM初始化、调速与调速函数封装ESP32的LEDC支持16个通道频率可独立配置分辨率从1位到16位可调。对于直流电机来说我一般选择1kHz到20kHz之间的频率。频率太低电机转动时能听到明显的“哒哒哒”声频率太高MOSFET的开关损耗增加驱动模块发热明显。实测下来1kHz到10kHz是大部分人接受的区间。先看初始化代码#include driver/ledc.h #define PWM_FREQ 1000 // PWM频率1kHz人耳能感知轻微声音但可接受 #define PWM_RES 8 // 8位分辨率占空比0~255 #define PWM_CH_L 0 // 左电机LEDC通道号 #define PWM_CH_R 1 // 右电机LEDC通道号 #define PWM_GPIO_L 14 // 左电机PWM引脚 #define PWM_GPIO_R 25 // 右电机PWM引脚 void pwmInit() { ledcSetup(PWM_CH_L, PWM_FREQ, PWM_RES); ledcSetup(PWM_CH_R, PWM_FREQ, PWM_RES); ledcAttachPin(PWM_GPIO_L, PWM_CH_L); ledcAttachPin(PWM_GPIO_R, PWM_CH_R); }这段代码里ledcSetup的第一个参数是通道号第二个是PWM频率第三个是分辨率位数。8位分辨率对应的占空比范围是0到255够用了。分辨率越高频率可选范围越小因为底层计数器是同一个时钟源。ledcAttachPin把物理引脚绑定到通道上之后对通道写占空比对应引脚就会输出方波。接下来封装一个完整的电机控制函数同时处理方向和速度void motorControl(int pwmChannel, int pinA, int pinB, int speed) { // speed范围 -255 ~ 255正数正转负数反转 if (speed 0) { digitalWrite(pinA, HIGH); digitalWrite(pinB, LOW); } else if (speed 0) { digitalWrite(pinA, LOW); digitalWrite(pinB, HIGH); } else { digitalWrite(pinA, LOW); digitalWrite(pinB, LOW); } int duty abs(speed); // 取绝对值作为占空比 if (duty 255) duty 255; // 限幅保护 ledcWrite(pwmChannel, duty); }speed为0时把两个方向脚都拉低相当于让驱动模块输出高阻态电机自由滑行而不是急刹。如果你希望停车时立刻刹住可以把两根方向脚都拉高形成制动效果。不同场景有不同选择巡线小车建议用自由滑行爬坡车建议用制动。3.2 编码器接入用PCNT外设读取脉冲而不是傻傻写中断如果只是做遥控PWM调速就够了。但如果你想实现速度闭环就必须知道电机当前实际转速这里靠的是编码器。最常见的霍尔编码器电机内部有磁环和两个霍尔传感器输出两路90度相位差的方波脉冲。电机轴每转一圈会输出固定数量的脉冲减速齿轮后输出轴转一圈对应的脉冲数更多一般是减速比乘以编码器PPR。读取脉冲有两种思路。第一种是在GPIO上注册一个中断服务函数每来一个上升沿就计数一次。这种写法直观但在高转速下中断频繁触发会占用大量CPU时间甚至影响WiFi稳定。第二种是用ESP32自带的PCNT脉冲计数器外设它由硬件完成计数CPU完全不用干预效率高出几个量级。工程上我强烈建议用PCNT。PCNT初始化代码#include driver/pcnt.h #define PCNT_UNIT_L PCNT_UNIT_0 // 左电机计数器单元 #define PCNT_UNIT_R PCNT_UNIT_1 // 右电机计数器单元 #define PCNT_A_L 34 // 左编码器A相 #define PCNT_B_L 35 // 左编码器B相 void pcntInitLeft() { pcnt_config_t cfg {}; cfg.pulse_gpio_num PCNT_A_L; // 脉冲输入引脚 cfg.ctrl_gpio_num PCNT_B_L; // 方向控制引脚 cfg.lctrl_mode PCNT_MODE_REVERSE; // 低电平时计数方向取反 cfg.hctrl_mode PCNT_MODE_KEEP; // 高电平时保持计数方向 cfg.pos_mode PCNT_COUNT_INC; // 正沿加1 cfg.neg_mode PCNT_COUNT_DEC; // 负沿减1 cfg.counter_h_lim 32767; // 计数上限 cfg.counter_l_lim -32767; // 计数下限 cfg.unit PCNT_UNIT_L; cfg.channel PCNT_CHANNEL_0; pcnt_unit_config(cfg); pcnt_counter_clear(PCNT_UNIT_L); // 清零计数 }这段配置里最重要的是lctrl_mode和hctrl_mode它们决定了B相电平如何影响计数方向。标准正交编码器在电机正转时A相上升沿对应B相为高反转时B相为低所以设置成高电平保持、低电平反转。如果你的电机线序接反了表现是计数方向反了调整这两个模式即可不用拆线。读取当前计数值很简单int16_t leftCount 0; pcnt_get_counter_value(PCNT_UNIT_L, leftCount);每次循环读一次显式乘以符号方向即可获得相对位移。我通常在控制周期里定时读取和上一次差值相减就是该周期内的脉冲数。一段空白这里需要插入一个“小标题”作为过渡。如果你不打算做闭环PCNT可以跳过但做闭环的话务必在电机通电前先手动转动电机轴确认计数方向和实际旋转方向一致。3.3 速度闭环一个位置式PID就够别一上来就整复杂模型电机实际转速和PWM占空比并不是线性关系因为电池电压波动、负载变化、摩擦力都会影响转速。要实现稳定的直线行驶或巡线做速度闭环是必然选择。最常见的做法是PID控制根据目标转速和当前转速的误差调整PWM占空比输出。代码量不大用一个位置式PID即可typedef struct { float kp, ki, kd; float integral; float lastError; } PID_t; float pidUpdate(PID_t *pid, float target, float current, float dt) { float error target - current; pid-integral error * dt; pid-integral constrain(pid-integral, -100, 100); // 积分限幅 float derivative (error - pid-lastError) / dt; pid-lastError error; return pid-kp * error pid-ki * pid-integral pid-kd * derivative; }这个函数里dt是控制周期一般取10ms到50ms之间。积分限幅是必须的不然当电机堵转时积分项会无限累积PWM输出瞬间拉满恢复后产生剧烈超调。实际控制循环写成void loop() { static unsigned long lastTime 0; unsigned long now millis(); if (now - lastTime 20) { // 50Hz控制频率 float dt (now - lastTime) / 1000.0; lastTime now; int16_t rawLeft 0; pcnt_get_counter_value(PCNT_UNIT_L, rawLeft); float rpmLeft (rawLeft - lastLeftCount) / (PPR * REDUCTION_RATIO) / dt * 60.0; lastLeftCount rawLeft; int pwmOut (int)pidUpdate(pidLeft, targetRPM, rpmLeft, dt); motorControl(PWM_CH_L, DIR_L_A, DIR_L_B, pwmOut); } }PPR是编码器每圈脉冲数比如11REDUCTION_RATIO是减速比常见30:1那么输出轴一圈是330个脉冲。这个公式每次中断周期都能算出当前RPM从而让PID闭环生效。初调参数时我会先把ki和kd设为0只给kp一个较小的值比如1.0然后逐步加大直到电机转速不震荡为止再按固定流程整定积分项。4. 遥控端实现HTTP网页与经典蓝牙Serial两条路线小车能动之后遥控链路是第二步重点。ESP32同时支持WiFi和经典蓝牙。WiFi可以做网页遥控界面直观不依赖第三方App而经典蓝牙适合用手机上的串口助手直接发指令延迟低、调试方便。这一章分别给出两条路线的可行实现和适用场景。4.1 HTTP网页遥控手机浏览器直接操作无需安装AppHTTP方案的核心是让ESP32自己变成一个小的Web Server手机浏览器访问它的IP地址页面上的按钮触发HTTP请求ESP32收到请求后解析路径执行对应的动作。代码结构如下#include WiFi.h #include WebServer.h WebServer server(80); const char* ssid ESP32-Car; // 热点名称 const char* password 12345678; // 热点密码至少8位 void handleRoot() { String html !DOCTYPE htmlhtmlbody; html h2ESP32小车遥控/h2; html button ontouchstart\fetch(/cmd?dirf)\ ontouchend\fetch(/cmd?dirs)\前进/button; html button ontouchstart\fetch(/cmd?dirl)\ ontouchend\fetch(/cmd?dirs)\左转/button; html button ontouchstart\fetch(/cmd?dirr)\ ontouchend\fetch(/cmd?dirs)\右转/button; html button ontouchstart\fetch(/cmd?dirb)\ ontouchend\fetch(/cmd?dirs)\后退/button; html /body/html; server.send(200, text/html, html); } void handleCmd() { String dir server.arg(dir); if (dir f) { motorControl(PWM_CH_L, DIR_L_A, DIR_L_B, 200); motorControl(PWM_CH_R, DIR_R_A, DIR_R_B, 200); } else if (dir l) { motorControl(PWM_CH_L, DIR_L_A, DIR_L_B, 150); motorControl(PWM_CH_R, DIR_R_A, DIR_R_B, -150); } else if (dir r) { motorControl(PWM_CH_L, DIR_L_A, DIR_L_B, -150); motorControl(PWM_CH_R, DIR_R_A, DIR_R_B, 150); } else if (dir b) { motorControl(PWM_CH_L, DIR_L_A, DIR_L_B, -200); motorControl(PWM_CH_R, DIR_R_A, DIR_R_B, -200); } else if (dir s) { motorControl(PWM_CH_L, DIR_L_A, DIR_L_B, 0); motorControl(PWM_CH_R, DIR_R_A, DIR_R_B, 0); } server.sendHeader(Access-Control-Allow-Origin, *); server.send(200, text/plain, ok); } void setup() { WiFi.softAP(ssid, password); server.on(/, handleRoot); server.on(/cmd, handleCmd); server.begin(); } void loop() { server.handleClient(); }这里有个细节按钮用的是ontouchstart和ontouchend事件而不是传统的onmousedown与onmouseup。原因很简单在手机浏览器上触摸事件的响应更快而且手指按住不动会持续触发松开才发停车指令操作手感更接近摇杆。用fetch发请求比用window.location跳转更合适页面不会刷新MPU和WiFi负载也更小。server.on(/cmd, handleCmd)注册了路径处理器在浏览器里每次请求都会调用handleCmd通过server.arg(dir)解析参数。响应头加一条Access-Control-Allow-Origin: *是防止某些浏览器因为CORS策略拦截请求虽然同源场景下不需要但加上能省掉一部分兼容性排查时间。4.2 蓝牙串口遥控延迟更低更适合做底层调试蓝牙方案适合在开发阶段使用。ESP32的经典蓝牙支持SPP协议可以直接被手机上的蓝牙串口App识别连上之后就是一个透明串口发送字符串即可控制小车。代码比HTTP端更简洁#include BluetoothSerial.h BluetoothSerial SerialBT; void setup() { Serial.begin(115200); SerialBT.begin(ESP32-Car); // 蓝牙设备名 } void loop() { if (SerialBT.available()) { char cmd SerialBT.read(); switch (cmd) { case w: motorControl(PWM_CH_L, DIR_L_A, DIR_L_B, 200); motorControl(PWM_CH_R, DIR_R_A, DIR_R_B, 200); break; case a: motorControl(PWM_CH_L, DIR_L_A, DIR_L_B, -150); motorControl(PWM_CH_R, DIR_R_A, DIR_R_B, 150); break; case d: motorControl(PWM_CH_L, DIR_L_A, DIR_L_B, 150); motorControl(PWM_CH_R, DIR_R_A, DIR_R_B, -150); break; case s: motorControl(PWM_CH_L, DIR_L_A, DIR_L_B, -200); motorControl(PWM_CH_R, DIR_R_A, DIR_R_B, -200); break; case x: motorControl(PWM_CH_L, DIR_L_A, DIR_L_B, 0); motorControl(PWM_CH_R, DIR_R_A, DIR_R_B, 0); break; } } }蓝牙指令的延迟通常在几十毫秒量级比HTTP方案稳定不少而且不用担心网页连接被自动释放的问题。调试PID参数的时候我通常先用蓝牙方案配合串口助手发送指令因为改参数和观察速度数据都更顺手。需要额外注意的是BluetoothSerial库依赖于ESP32的蓝牙协议栈。如果你在固件里同时启用了WiFi和经典蓝牙在部分开发板上可能出现冲突表现是WiFi连接后蓝牙无法被发现。解决办法是在初始化蓝牙之前先WiFi.mode(WIFI_MODE_STA)并完成连接或者两者分时使用。实在不行可以用回调方式处理蓝牙接收避免loop里反复读取造成蓝牙丢包。4.3 通信协议别只用单个字符留好扩展余地上面两个方案的指令都比较简单每个字符代表一个动作。如果你后续要加入速度调节、灯光控制、舵机云台单字符协议马上就不够用了。我建议从一开始就把协议设计成“命令:参数”的格式比如SP:200表示设置速度200LT:90表示左转90度。void handleBluetoothCommand(String line) { int sep line.indexOf(:); if (sep 0) return; String cmd line.substring(0, sep); String val line.substring(sep 1); if (cmd SP) targetSpeed val.toInt(); else if (cmd SV) targetSpeed 0; }这样做的好处是扩展指令时只需添加新的else if分支而不用改动主循环。对于调试来说还能在蓝牙端随时查看当前速度、编码器计数等状态走一遍完整的“指令-反馈-确认”流程后期上传感器时也方便。5. 避坑与常见问题排查五条实测踩坑记录这一章写的都是我在实际测试中遇到过的问题每条按“现象 → 原因 → 解决”的方式列出应该能帮你省下不少排查时间。5.1 通电瞬间电机猛抽一下然后正常现象插上电池的一瞬间电机不受控制地转了一下持续不到一秒钟。原因方向控制引脚GPIO12在ESP32上电阶段处于高电平状态而PWM引脚在初始化前默认也是高电平TB6612两个通道同时有效相当于给了电机一个瞬时驱动信号。解决把方向引脚换到上电默认低电平的引脚比如GPIO14和GPIO27或者在TB6612的AIN1和AIN2引脚分别加上10kΩ下拉电阻保证上电瞬间保持低电平。5.2 电机一启动ESP32立刻重启串口乱码现象按下前进按钮电机还没转起来ESP32就重启了串口反复打印乱码。原因电机启动瞬间电流大电池电压跌落到ESP32稳压芯片的最小输入电压以下触发了欠压复位。常见于电源直接共用同一路。解决把锂电池正极接到TB6612的VM同时用单独的5V降压模块给ESP32供电GND共地。如果手头没有降压模块可以把电池换成电压更高的4S锂电加降压但体积和成本都上去了不如直接加降压模块。5.3 PWM占空比很低的时候电机不转调高一点又猛冲现象设置PWM占空比为30/255时电机完全不动占到80/255时转速又很高中间调节区很窄。原因直流电机存在启动电压占空比太低时平均电压低于电机的起始转动电压电机不转一旦超过启动电压转速又快速上升。比例关系不是线性的。解决外加控制时避开这个死区把低速段映射到PID输出的非线性曲线上。最简单的方法是设定一个最小占空比阈值比如低于50/255直接按0处理高于200/255按255处理进阶做法是用查表或分段线性映射效果更好。5.4 WiFi遥控时松手停车指令发送不及时车子冲出好远现象手机上松开按钮小车并没有马上停下而是滑行一小段距离才停。原因松手触发ontouchend事件后HTTP请求本身的延迟加上浏览器可能在保持TCP连接时有缓冲导致停车指令晚了几十毫秒才被ESP32执行。小车速度一旦快起来这几十毫秒就是几十厘米的误差。解决降低控制周期让ESP32内部有一个50ms级别的定时中断定时检查当前位置和目标位置如果距离太近就不等停车指令主动刹停或者直接用蓝牙方案替代HTTP蓝牙指令的延迟通常只有5ms左右。5.5 编码器计数方向反了导致PID越控越飞现象小车越走越偏PID调节看起来完全反向电机越调越快。原因编码器B相接入顺序与预期相反导致PCNT的计数方向和电机实际旋转方向不一致。误差信号被反向放大系统进入正反馈。解决把编码器B相和A相对调或者不改接线把PCNT配置里lctrl_mode和hctrl_mode对调一样能修正。验证方法很简单在PID闭合前先用手转动电机轮子观察串口打印的计数值方向是否和实际转向一致。6. 进阶PID参数整定与里程计统计两个用得上的实战技巧到这里小车的基本功能已经完整了。这一章给出两个立刻能用的进阶技巧正好把前面的硬件和控制知识串起来。6.1 PID参数整定的搜索顺序很多人第一次调PID上来就同时调三个参数结果越调越乱。我的方法是三步走先只调Kp再添加Ki最后加Kd。Kp从小往大加观察编码器反馈的RPM波形如果出现明显的周期性震荡就说明Kp过大回调到震荡前值的70%左右。接着加Ki用来消除稳态误差从0.01开始翻倍试探一旦出现低频摆动就减半。Kd最后加从0.1开始用于抑制超调。参数范围可以参考这张表但必须结合你自己的机械结构修正参数起始值观察现象调整方向Kp0.5转速跟不上目标误差大每次增加0.5直到出现震荡Ki0.01目标附近稳态误差每次翻倍直到低频摆动Kd0.1启动和停止时的超调量增加0.1直到响应变钝记住一个原则PID的最终目标不是让曲线完美而是让小车在不同负载下都够稳。如果只是固定地面跑P就够了想复杂地形再引入积分和微分。6.2 里程计把编码器脉冲换算成实际行驶距离有了编码器里程计几乎是白送的。每个控制周期里读取左右轮编码器计数差结合减速比和轮径就能算出移动距离。公式是距离 脉冲数 / (PPR × 减速比) × 轮周长比如编码器PPR11减速比30:1轮子直径65mm那么一个脉冲对应的距离约等于0.62mm。把左右轮的距离平均一下就是小车中心点位移。这个数据可以用来做直线校准也可以和陀螺仪数据融合做定位。float getDistance(int16_t pulseCount) { const float PULSE_PER_REV 11.0 * 30.0; // 输出轴一圈脉冲数 const float WHEEL_CIRC 3.14159 * 65.0; // 轮径65mm的周长 return (float)pulseCount / PULSE_PER_REV * WHEEL_CIRC; }我当初做直线测试时发现一个问题左右轮编码器读数总是不一致因为两轮机械阻力不完全相同。后来加了一个纯比例校正项让高速轮的PWM输出略低一些十几米直线就基本不偏了。这个补偿值不需要很大一般3到8个单位就够了必须在实测中慢慢标定没有通用值。从那以后我每做完一版小车都会先做“供电检查 → 引脚表核对 → 编码器方向确认 → PID整定 → 直线测试”这五步流程再考虑加传感器或做更复杂的控制基本没有再遇到那种查一个下午楞找不到原因的玄学问题。希望这份踩坑后的经验能让你少走几段弯路。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑