第 9 章 H 桥与刹车:反电动势、滑行和刹车的物理
本章把刹车讲到底层物理①为什么断电后电机还会溜惯性 反电动势②反电动势从哪来电磁感应③滑行和刹车在电路上分别做了什么、为什么刹车停得快④能量去哪了⑤代码stop()的真实实现以及本章最大的坑ENA。范围声明H 桥电路本身与完整真值表在 8.3 已讲清本章专攻刹车的物理文中引用的代码全部摘自 8.6 的完整工程是节选片段、不能单独编译——可抄可跑的全套文件在第 12 章。9.1 现象断电后电机为什么还会转分两层看。第一层是惯性转起来的东西带着动能越重、转得越快的越难停。断电只是撤走了推力动能不会瞬间消失电机继续转——这是还会溜的主因。第二层才是电磁转动的电机同时是一台发电机。这里最容易说反要抠一下字眼感应出来的电流不会继续推动自己转——恰恰相反按楞次定律闭合回路里的感应电流产生的力矩是阻碍它转动的下一节细讲。所以断电后是惯性让它继续转若回路闭合感应电流的力矩阻碍转动——这才是制动力。回路是断开的还是短接的决定了它是溜很久还是很快停——这正是本章两种停法的分野。转动的电机 一台小发电机 ├─ 惯性动能 → 继续转 └─ 反电动势感应出电压 → 若回路闭合感应电流的力矩阻碍转动制动力9.2 反电动势电磁感应原理电磁感应定律的直观版线圈在磁场里转动时穿过线圈的磁通在变化 → 线圈两端感应出电压。磁通把线圈想成雨里举着的一个呼啦圈“磁通就是穿圈而过的磁感线条数——条数一变圈上就感应出电压线圈转起来穿过的条数不停地在变所以它一直在发电”。这个电压的方向总是阻碍引起它的变化楞次定律电源想让它正转加速 → 感应电压方向相反所以叫反电动势缩写BEMF词汇表可查转速越高磁通变化越快 → 反电动势越大。正常转动时 电源电压(12V) ──► 克服 ──► 反电动势(~10V) ──► 剩下电压驱动电流所以启动瞬间转速 0反电动势 0 → 电流 V/R 很大第 8 章稳定时反电动势顶掉大部分电源电压电流小。9.3 两种停电路上的本质区别先纠正一张常见的误导性格子表。L298N 通道 A 的完整真值表有三个输入变量不是两个ENAIN1IN2结果0占空比 0任意任意输出高阻→ 电机自由滑行Coast1占空比 100%00两个输出端都接地绕组短接 →能耗制动Brake1占空比 100%11两个输出端都接 Vs绕组短接 →能耗制动Brake110正转101反转表里的ENA0/1指 ENA 的有效电平。实际使用中 ENA 是 PWM 脚占空比在 0~100% 之间就是调速0% 等效恒低100% 等效恒高。看出关键了吗拥有一票否决权的是 ENA占空比一到 0OUT1/OUT2直接高阻IN1/IN2 怎么摆都白搭。反过来IN 双 0也不是滑行——ENA 有效时 IN 双 0 是把绕组对地短接照样是刹车。一句话记法停不停、刹不刹看 ENA怎么转才看 IN。滑行Coast高阻能量没处去ENA 占空比 0 → 四个桥臂全部关断OUT1/OUT2 高阻等于电机两端不接线 反电动势存在但电流没处去 → 没有电磁制动力 只剩机械摩擦 风阻 → 溜着慢慢停刹车Brake短接能量快速变热ENA 占空比 100% 且 IN1IN2 → L298N 内 S1、S2 或 S3、S4 同侧导通 OUT1/OUT2 同电位 → 电机两端被短接 反电动势 → 在电机线圈 短接回路里驱动电流 → 这个电流产生 反向力矩阻碍转动→ 快速减速 动能 → 电能 → 电阻发热线圈、管子、导线关键认知刹车不是断电让它自然停而是把电机变成负载让反电动势自己发电、自己耗掉。所以停得快。9.4 能量守恒刹车时能量去哪了转动动能 → 反电动势发电电能 → 短路电流流过电阻 → 变成热线圈、功率管发热频繁大力刹车 → 驱动板/电机发热正常这是能耗制动dynamic braking——比机械刹车磨损小、控制精准。对比如果刹车时反向通电IN1/IN2 一反一正那就是反向制动电流会叠加电源 反电动势容易过流——这就是不能突然反转的物理原因8.7。真实代码在setSpeed()里替你把了这道关检测到目标方向与当前speed_相反时先stop(Brake)短接绕组再esp_rom_delay_us(cfg_.reverse_deadtime_us)默认 5000µs等转速被刹下来然后才换向通电。9.5 代码实现真实的 stop()以及最大的坑——ENA先坦白一件事本书早期草稿里的 stop() 是这样写的——“先把 PWM渐变到 0再把 IN1/IN2 拉高”。这套代码刹不住车。拿 9.3 的真值表一照就明白占空比归零后 ENA 恒为低电平L298N 输出已经是高阻了这时候再把 IN 拉高绕组根本没被短接——它实现的恰恰是滑行。下面是工程源码motor_control.cpp里的真实实现逐字贴上含原注释。片段接在 8.6.2 完整文件上不能单独编译esp_err_tDCMotor::stop(StopMode mode){if(!initialized_){returnESP_ERR_INVALID_STATE;}speed_0;if(modeStopMode::Brake){// 能耗制动ENA 必须保持有效占空比 100%再把 IN1/IN2 同时拉高短接绕组。// 只把占空比清零是不够的 —— ENA 无效时 L298N 输出高阻那是滑行不是刹车。esp_err_t errgpio_set_level(cfg_.in1,1);if(err!ESP_OK){returnerr;}errgpio_set_level(cfg_.in2,1);if(err!ESP_OK){returnerr;}returnapplyDuty(maxDuty());}// 自由滑行关断 PWM 输出方向引脚同时拉低esp_err_t errapplyDuty(0);if(err!ESP_OK){returnerr;}errgpio_set_level(cfg_.in1,0);if(err!ESP_OK){returnerr;}returngpio_set_level(cfg_.in2,0);}⚠️本章核心坑说三遍占空比 0 L298N 输出高阻——刹车必须把 ENA 占空比拉满maxDuty()即 100%再让 IN1/IN2同电平。只清占空比、只拉 IN都是滑行不是刹车。源码里那两行原话注释就是作者踩坑后留下的“只把占空比清零是不够的——ENA 无效时 L298N 输出高阻那是滑行不是刹车。”对照真值表读这段代码两个分支一目了然Brake 分支IN1、IN2 先同时拉高1/1 → 输出端同接 Vs短接绕组然后applyDuty(maxDuty())把 ENA 占空比打到 100%——两个动作缺一刹不住Coast 分支applyDuty(0)让 ENA 高阻这一刻就已经在滑行了再把 IN1/IN2 双双拉低只是把方向引脚收拾成干净的默认态。applyDuty()本身也值得看一眼片段接在 8.6.2 完整文件上不能单独编译真实代码逐字esp_err_tDCMotor::applyDuty(uint32_tduty){constuint32_tclampedstd::min(duty,maxDuty());// 用线程安全版本避免 set_duty 与 update_duty 之间被其他任务打断returnledc_set_duty_and_update(cfg_.speed_mode,cfg_.channel,clamped,0);}注意最后一个参数0——它是ledc_set_duty_and_update(mode, ch, duty, hpoint)的第 4 参hpoint相位偏移点0 立即生效不是渐变时长。别被早期草稿里第 4 参填个 1000 就是一毫秒渐变的写法骗了想按时长渐变 PWM 的是另一对 API——ledc_set_fade_with_time()ledc_fade_start()而且它们属于 LEDC fade 服务没装服务就用会直接返回ESP_FAIL服务安装与setSpeed()换向保护的细节在第 7、8 章。本项目用的是立即生效路线渐变靠剧本里的小步快走第 12 章你会看到。“先拉高 IN、再打满占空比”不是踩着油门拉手刹旧草稿还有一段论证“先短接再减 PWM 踩着油门拉手刹电源还在给电同时短接会产生大电流”。这段话本身也是错的错在对 H 桥电路的误读IN1IN21 时L298N 内部是 S1、S2同侧导通OUT1 和 OUT2被抬到同一个电位都等于 Vs——电源正极同时接到电机两端两端没有电位差电源根本不构成经电机短路的回路何来油门踩着拉手刹100% 占空比 绕组短接恰恰是标准的能耗制动手势。从正转 80%IN11、IN20、占空比 80%进入刹车的瞬间顺序是“先把 IN2 也拉高”这一下的状态是 IN1IN21、占空比还停在 80%——按真值表读这是80% 通断交替的制动ENA 每个 PWM 周期里 80% 的时间短接刹车、20% 的时间高阻瞬间滑行制动力就是这两段按时间比例混合出来的平均值和 PWM 调速平均电压是同一个机制比满刹车柔和但方向完全正确然后applyDuty(maxDuty())补到满刹车。全程没有电源短路这个中间态。把三个时刻列成小表时刻IN1IN2ENA 占空比桥臂状态① 刹车前正转 80%1080%80% 时间 S1S4 对角导通给电20% 时间高阻② 刚把 IN2 拉高1180%80% 时间 S1S2 同侧短接制动20% 时间高阻③ applyDuty(maxDuty()) 后11100%S1S2 持续导通绕组全程短接 满刹车setSpeed(0)固定刹车没有开关真实setSpeed()里对应 speed0 的分支只有三行片段接在 8.6.2 完整文件上不能单独编译逐字if(speed0){returnstop(StopMode::Brake);}没有brake_at_zero开关也没有cfg_.stop_mode字段——Config结构体里压根不存在这个东西。规则很简单传 0 就是能耗制动固定的想滑行请显式调用motor.stop(DCMotor::StopMode::Coast)。注意StopMode是定义在DCMotor类内部的嵌套枚举类外使用要带DCMotor::前缀第 1 章讲过作用域。所以演示程序里setSpeed(0)电机是果断停而不是溜着停——机制上就是走了上面那个固定分支。9.6 动手验证可重复实验前置说明和你现在的进度对齐本节需要电机真的在转也就是需要完整工程已编译烧录、L298N 已接线——这些在第 12 章 12.2抄main.cpp编译烧录和 12.3实物接线才给全。按顺序读到这里的你现在手里最多只有 8.6 的两个库文件做不了本实验这完全正常先略读本节、记住结论做完第 12 章后回来补上两分钟的事。程序全速 2s →stop(DCMotor::StopMode::Coast)→ 测多久停改stop(DCMotor::StopMode::Brake)→ 再测结果Brake 明显快可加秒表/看轮子。拆穿迷思实验强烈建议做同样以第 12 章 12.2/12.3 为前置把 Brake 分支里return applyDuty(maxDuty());临时改成return applyDuty(0);IN 双高保留重新编译烧录——电机会刹车失败、明显还在溜。因为占空比 0 时输出已高阻IN 拉得再高也没用。改回来又刹得稳稳的。一次实验顶读十遍真值表。注意那行代码与 8.6.2 逐字块对应实验做完务必原样改回再编译一次确认回到正常行为。观察发热反复 Brake 后触摸驱动板小心烫会明显热——能量变热的证明。9.7 常见问题速查现象原因解决刹车后还慢慢溜IN 没同时拉高没真正短接或 IN 对了但 ENA 占空比没到 100%核对 IN1/IN21/1再查占空比是否为 maxDuty()stop(Brake) 和 stop(Coast) 手感一模一样ENA 接线/通道错了占空比常年为 0 → 两种停法都退化成滑行查 ENA 走线与 LEDC 通道配置刹车瞬间重启电源扛不住感应电流换大电流电源频繁刹车发烫正常能耗制动减少猛刹频率9.8 想一想 小测验想一想用反电动势 回路解释为什么短接绕组能快速制动为什么刹车时 ENA 占空比必须拉满 100%占空比为 0 时把 IN 拉高会发生什么刹车时的电流来自哪里不是电源一行提示想不通就回看 9.1/9.3此刻电机自己就是电源——惯性带着线圈在磁场里转它就是台发电机电流只流通在绕组 短接回路这个自成的闭合圈里。小测验反电动势的方向与什么相反A. 转速 B. 电源电压 C. 电流滑行时电机两端是什么状态A. 开路/高阻 B. 短接 C. 接电源刹车时电机的动能最终变成什么A. 光 B. 热 C. 声音setSpeed(0)执行哪种停法9.9 本章总结反电动势BEMF 电机自己发电电磁感应回路一闭合感应电流的力矩阻碍转动——制动力从这里来滑行 ENA 占空比 0 → 两端高阻反电动势无回路能量靠摩擦慢慢耗刹车 ENA 占空比 100% IN1IN2 短接绕组反电动势驱动短路电流动能快速变热能耗制动——只清占空比不拉满 ENA 都是滑行setSpeed(0) 固定刹车想滑行显式stop(DCMotor::StopMode::Coast)换向有保护检测反向先 Brake 短接 5ms 死区再通电避免电源反电动势叠加过流9.4、8.7。下一章内存与 PSRAM——数据到底放在哪。