达妙43系列直流减速电机选型、驱动与PID闭环控制实战指南
1. 项目概述为什么是达妙43系列电机如果你刚开始接触机器人、智能小车或者各种自动化小装置那么“电机”这个词对你来说可能既熟悉又陌生。熟悉是因为它无处不在陌生是因为面对市面上五花八门的型号比如直流有刷、直流无刷、步进、舵机还有各种奇怪的参数新手很容易一头雾水。今天我们不谈那些复杂的理论就从一个非常具体、也非常经典的产品入手——达妙Dameow的43系列直流减速电机。我第一次接触这个系列是在一个校园机器人比赛里。当时我们需要一个动力足、体积小、价格还亲民的电机来驱动小车底盘在淘宝上翻了半天最终锁定了它。为什么是它因为“43”这个数字在创客和机器人爱好者圈子里几乎成了一个标准尺寸的代名词。它指的是电机机身直径约为43毫米这个尺寸在性能和安装空间上取得了很好的平衡。达妙作为国内一个比较知名的电机品牌其43系列产品线丰富从普通的直流有刷电机到带编码器的版本从不同减速比到不同电压规格几乎覆盖了从教学实验到竞赛机器人的大部分需求。简单来说这篇指南就是帮你把“达妙43电机”这个盒子从货架上拿下来拆开包装告诉你里面每个零件是干什么的怎么把它接上线让它转起来怎么控制它转得快慢和方向以及当你遇到它不听话时该怎么排查。无论你是想做个自动窗帘、一个循线小车还是一个机械臂的关节这篇指南都能给你一个扎实的起点。我们会避开那些深奥的磁场理论和复杂的微积分专注于“怎么做”和“为什么这么做”让你在动手的过程中自然而然理解背后的逻辑。2. 核心需求解析你到底需要什么样的电机在淘宝搜索“电机”你会看到成千上万的结果。直接下单一个达妙43电机可能并不是最优解因为同是43系列里面还有不少门道。选择错误轻则动力不足重则烧毁驱动模块。所以在动手之前我们必须先搞清楚自己的项目到底需要什么。2.1 明确负载与速度要求这是选型的第一步也是最关键的一步。你需要问自己两个问题我要驱动多重的物体比如你的小车自重含电池是2公斤预计最大载重1公斤总重就是3公斤。电机需要克服轮子与地面的摩擦力来推动这3公斤的重量。我需要多快的移动速度是希望小车慢速平稳地巡逻还是需要它快速冲刺这两个问题直接决定了你需要电机的扭矩和转速。扭矩好比是肌肉的力量决定了电机“能不能拉动”转速好比是肌肉收缩的速度决定了“拉得多快”。对于轮式机器人通常我们更关心扭矩因为足够的扭矩才能保证小车能在有坡度或者粗糙的地面上启动和运行。一个实用的估算方法对于在平整地面行驶的两轮差速小车每个驱动轮所需的扭矩T可以粗略估算为T μ * m * g * r。其中μ是摩擦系数平地上可取0.1-0.3m是单轮承载的质量总质量的一半g是重力加速度9.8r是轮子半径。假设总重3kg轮子半径0.03米摩擦系数取0.2那么单轮扭矩 T ≈ 0.2 * 1.5 * 9.8 * 0.03 ≈ 0.088 N·m。注意这是输出轴所需的扭矩。电机本身的扭矩很小需要经过减速箱放大。2.2 减速比扭矩与转速的转换器达妙43系列电机通常指“直流减速电机”它由一个高速但扭矩小的直流电机和一个齿轮减速箱组合而成。减速比比如1:30 1:48 1:210是这个组合的灵魂。减速比越大输出转速越慢输出扭矩越大。适合需要大力气、慢速度的场景比如机械臂、履带车、卷帘门。减速比越小输出转速越快输出扭矩越小。适合需要快速移动但对力量要求不高的场景比如高速竞速小车。达妙43常见的减速比有1:30 1:48 1:75 1:210等。假设电机空载转速是10000转/分钟RPM经过1:30的减速箱后输出轴转速约为333 RPM扭矩则理论上放大30倍。你之前看到的网络热词“PID闭环控制电机转速”这里的“转速”通常就是指这个经过减速后的输出轴转速。选择错误的减速比就像开手动挡汽车永远用1档跑高速或者用5档起步要么憋熄火要么干吼不走。2.3 电压与电流动力之源达妙43电机常见的额定电压有6V、12V、24V。请务必按照电机标称电压供电用低于额定电压的电源电机无力用高于额定电压的电源轻则发热严重、寿命缩短重则瞬间烧毁线圈。比电压更需要注意的是电流。电机在堵转轴被卡住不转时电流会急剧上升达到堵转电流这个值可能是正常工作电流的5-10倍。你的电源和电机驱动模块必须能承受这个瞬间的冲击电流。例如一个标称12V的电机空载电流可能只有0.1A正常工作电流0.5A但堵转电流可能高达3A。如果你用一个最大输出只有1A的驱动模块一旦轮子卡住模块就很可能烧毁。网络热词中的“L298N”、“TB6612”都是常见的电机驱动模块它们的最大持续输出电流分别是2A和1.2A每通道选型时必须留有余量。2.4 编码器电机的“眼睛”这是普通电机和智能电机的分水岭。编码器安装在电机输出轴上可以实时反馈电机转了多少圈、转速多快。没有编码器你只能控制电机“转”或“不转”、“快一点”或“慢一点”但无法精确知道它到底转了多少度。有了编码器你才能实现精确的位置控制和速度闭环控制。达妙43系列有带霍尔编码器如MG310霍尔编码电机的版本。霍尔编码器通过感应磁铁变化来计数精度通常为每转几十到几百个脉冲PPR。它对于实现巡线小车走直线、机械臂转到特定角度至关重要。后面要讲的PID控制其反馈信号就来源于编码器。注意选购时一定要确认编码器的输出类型通常是AB双相正交脉冲和供电电压通常是3.3V或5V以便与你主控板如Arduino, STM32, ESP32的编码器接口或计数器接口匹配。3. 驱动与控制方案选型选好了电机下一步就是让它听你的话。直接把它接到电池上它只会全速单向旋转这显然不能满足我们的控制需求。我们需要一个“指挥官”主控板和一个“执行官”驱动模块。3.1 电机驱动模块详解驱动模块的核心作用有两个放大控制信号电流和控制电机转向。主控板如Arduino的IO口输出电流很小约20mA根本无法直接驱动电机。驱动模块接收主控板微弱的方向和速度信号然后从电源取电输出足够大的电流给电机。1. L298N经典但低效的“老将”这是最经典、最入门的双H桥驱动芯片。它的优点是简单、皮实、驱动能力强单桥峰值电流可达3A。但缺点非常明显压降大每个H桥约有2V的导通压降。如果你用12V供电电机两端可能只有8V动力打折。发热严重正因为压降大电能以热量的形式消耗掉了必须加装大型散热片。需要外接续流二极管早期模块需要自己焊接现在集成的模块一般都焊好了。 它适合新手学习H桥原理或者驱动一些对效率不敏感、电压余量充足的电机。2. TB6612FNG高效小巧的“新星”这是目前创客项目中的主流选择。相比L298N它的优势是压倒性的效率高采用MOSFET导通内阻小压降低典型值0.5V发热极小通常不需要散热片。集成度高内部集成了热关断和短路保护电路更安全。控制简单两个控制引脚AIN1/AIN2决定方向一个PWM引脚控制速度。 对于达妙43这种中小型电机TB6612是更优、更现代的选择。网络热词中“TB6612电机驱动原理图”和“TB6612驱动两个电机”都指向了它的广泛应用。3. 其他驱动方案DRV8833类似TB6612体积更小电流能力稍弱1.5A常用于微型机器人。分立MOSFET搭建H桥当驱动电流很大5A或电压很高时需要自己用MOSFET搭建驱动电路。这需要一定的电子知识但灵活性和性能可以做到最优。热词中“使用N-MOS的高边有刷电机驱动电路”讨论的就是这种方案。选型建议对于达妙43电机工作电流通常在1A以内优先选择TB6612驱动模块。它性能好、易用、价格也不贵能让你避开很多因驱动模块发热或效率低下导致的问题。3.2 速度控制PWM的本质我们不能直接改变电池的电压来调速那样效率太低。通用的方法是脉冲宽度调制PWM。你可以把它想象成一个高速开关的水龙头。开关频率固定比如1kHz即一秒钟开关1000次但我们控制每次打开的时间长度脉宽。打开时间长关闭时间短平均水流就大电机转速快打开时间短关闭时间长平均水流就小电机转速慢。PWM频率通常选择1kHz到20kHz之间。频率太低如几十Hz电机会听到刺耳的啸叫声因为线圈在通断频率太高驱动模块的开关损耗会增加。对于有刷直流电机5kHz-10kHz是一个不错的起点。PWM分辨率决定了速度控制的细腻程度。比如Arduino的analogWrite()函数提供8位分辨率0-255共256级。那么analogWrite(电机引脚, 128)就代表50%的占空比电机以大约一半的速度运行。网络热词中“PWM控制电机”、“7段PWM”、“Dshot和PWM控制电机在硬件上有啥区别”都与此相关。简单说Dshot是数字协议主要用于航模无刷电调精度和抗干扰性更好而PWM是模拟信号简单通用。对于我们有刷直流电机用PWM就足够了。3.3 方向控制H桥的魔法单个开关只能让电机通断。要让电机正反转就需要H桥电路。它由四个开关通常是MOSFET组成形状像字母“H”电机位于中间。闭合S1和S4电流从左流向右电机正转。闭合S2和S3电流从右流向左电机反转。绝对不能同时闭合S1和S2或S3和S4这会直接短路电源这就是“桥臂直通”是毁灭性的。好的驱动芯片如TB6612内部有死区控制逻辑来防止这种情况。像L298N、TB6612这类模块已经把完整的H桥和逻辑保护电路集成好了。我们只需要给方向控制引脚如IN1, IN2高/低电平信号就能安全地控制转向。热词“用二个雨刮继电器控制雨刮电机”其实就是一个用继电器搭建的简易H桥思路但继电器速度慢、有机械寿命不适合做PWM调速。4. 基础接线与 Arduino 示例理论说再多不如接上线看看。我们以最典型的组合为例达妙43直流减速电机12V TB6612驱动模块 Arduino Uno。4.1 硬件连接清单与步骤所需材料达妙43电机12V x 1TB6612电机驱动模块 x 1Arduino Uno开发板 x 112V直流电源或电池组 x 1 注意电流能力≥2A5V或3.3V直流电源为TB6612逻辑部分供电可由Arduino的5V引脚提供x 1杜邦线若干接线步骤务必在断电下操作连接电源将12V电源的正极VCC接TB6612模块的VM电机电源端子。将12V电源的负极GND接TB6612模块的GND端子。重要再将这个GND与Arduino的GND用杜邦线连接起来让整个系统共地。连接电机将电机的两根线分别接到TB6612模块的A和A-输出端子。正反转可以通过调换这两根线来改变我们先随意接。连接控制信号TB6612的STBY待机引脚接Arduino的5V让模块始终处于工作状态。如果想用程序控制启停可以接一个数字IO口。AIN1接 Arduino 数字引脚8。AIN2接 Arduino 数字引脚9。PWMA接 Arduino 数字引脚10这是一个支持PWM输出的引脚旁边有波浪线~标志。供电逻辑部分TB6612的VCC引脚逻辑电压接Arduino的5V引脚。这为模块内部的逻辑电路供电。至此硬件连接完成。请再次仔细检查特别是电源正负极不要接反。4.2 基础驱动代码解析下面是一个让电机经历“正转加速 - 匀速 - 减速 - 停止 - 反转”循环的示例程序。我们将通过代码详细讲解每个参数的意义。// 定义TB6612控制引脚 const int AIN1 8; // 方向引脚1 const int AIN2 9; // 方向引脚2 const int PWMA 10; // PWM速度控制引脚 void setup() { // 初始化所有控制引脚为输出模式 pinMode(AIN1, OUTPUT); pinMode(AIN2, OUTPUT); pinMode(PWMA, OUTPUT); // 初始状态停止电机 digitalWrite(AIN1, LOW); digitalWrite(AIN2, LOW); analogWrite(PWMA, 0); // PWM占空比为0电机不转 } void loop() { // 阶段1正转加速 (2秒) Serial.println(正转加速); digitalWrite(AIN1, HIGH); // 设置转向为正转 digitalWrite(AIN2, LOW); for(int speed 0; speed 255; speed 5){ // 速度从0到255线性增加 analogWrite(PWMA, speed); delay(20); // 每次加速后等待20ms使加速过程持续约2秒 } // 阶段2全速正转 (2秒) Serial.println(全速正转); analogWrite(PWMA, 255); // PWM满占空比 delay(2000); // 阶段3正转减速 (2秒) Serial.println(正转减速); for(int speed 255; speed 0; speed - 5){ // 速度从255到0线性减少 analogWrite(PWMA, speed); delay(20); } // 阶段4短暂停止 (1秒) Serial.println(停止); digitalWrite(AIN1, LOW); // 方向引脚也置低确保停止 digitalWrite(AIN2, LOW); analogWrite(PWMA, 0); delay(1000); // 阶段5反转加速 (2秒) Serial.println(反转加速); digitalWrite(AIN1, LOW); // 设置转向为反转 digitalWrite(AIN2, HIGH); for(int speed 0; speed 200; speed 5){ // 反转速度最大到200演示非满速 analogWrite(PWMA, speed); delay(20); } delay(2000); // 阶段6反转减速停止 Serial.println(反转减速停止); for(int speed 200; speed 0; speed - 5){ analogWrite(PWMA, speed); delay(20); } digitalWrite(AIN1, LOW); digitalWrite(AIN2, LOW); delay(3000); // 停止3秒后重新开始循环 }代码关键点解读方向控制AIN1和AIN2是一对互补的逻辑。(HIGH, LOW)为正转(LOW, HIGH)为反转(LOW, LOW)或(HIGH, HIGH)为刹车快速停止。我们这里停止用的是(LOW, LOW)。速度控制analogWrite(PWMA, value)中的value范围是0-255对应0%-100%的占空比。值越大平均电压越高转速越快。加速过程使用for循环逐步增加PWM值并配合delay模拟出一个平滑的加速效果。在实际的机器人控制中我们不会这样用delay因为它会阻塞程序这里仅用于演示。实操心得第一次上电前可以把电机轴悬空不要装轮子防止意外飞车。同时用手轻轻捏住电机轴感受不同PWM值下的扭矩变化。你会发现即使在较低的PWM值如100下电机也能转动但稍微施加阻力就容易停转这是因为扭矩不足。这就是开环控制的局限性系统不知道负载变了它只会傻傻地输出固定的PWM。5. 进阶控制引入编码器与PID基础驱动让电机转起来了但这只是“开环”控制。就像蒙着眼睛踩油门你不知道车到底跑了多快。要实现精确控制必须“睁开眼睛”这就是编码器的作用。结合编码器的反馈使用PID算法我们才能让电机抵抗负载变化稳定在预设的转速或位置上。5.1 编码器信号读取达妙43的霍尔编码器一般输出AB两相正交脉冲。电机旋转时A、B两路信号会产生相位差90度的方波。通过检测A、B的边沿和相位关系我们既能计数知道转了多少也能辨向知道正转还是反转。Arduino读取编码器使用中断 Arduino Uno有两个外部中断引脚2和3我们可以将编码器A相接至中断引脚实现精准计数。// 定义编码器引脚 const int encoderPinA 2; // 必须接2或3号引脚支持外部中断 const int encoderPinB 4; volatile long encoderPos 0; // 使用volatile因为它在中断函数中被修改 void setup() { Serial.begin(9600); pinMode(encoderPinA, INPUT_PULLUP); // 启用内部上拉电阻 pinMode(encoderPinB, INPUT_PULLUP); // 当encoderPinA发生改变上升沿或下降沿时触发中断执行updateEncoder函数 attachInterrupt(digitalPinToInterrupt(encoderPinA), updateEncoder, CHANGE); } void loop() { Serial.println(encoderPos); // 循环打印编码器计数值 delay(100); } // 中断服务函数 void updateEncoder() { // 读取A相和B相的电平 int a digitalRead(encoderPinA); int b digitalRead(encoderPinB); // 根据A、B相位关系判断方向并计数 // 这是一种简单的判断逻辑更稳健的写法需要根据编码器真值表判断 if (a b) { encoderPos; // 假设A相变化时若AB则正转 } else { encoderPos--; // 否则反转 } }这段代码可以让你在串口监视器里看到电机转动时encoderPos值的变化。正转时递增反转时递减。编码器的分辨率PPR决定了电机转一圈这个值会变化多少。假设编码器是13线单相每转13个脉冲经过减速箱1:30减速后电机输出轴转一圈编码器实际测量的是电机输入轴转了30圈所以总脉冲数 30 * 13 390。这就是我们进行位置控制的基础。5.2 PID速度闭环控制原理PID是比例P、积分I、微分D控制的合称。对于电机速度控制我们可以这样通俗理解目标速度你希望电机达到的转速例如通过编码器计数换算成每秒多少转。当前速度编码器实际测量到的转速。误差 目标速度 - 当前速度。P比例误差乘以一个系数Kp。误差越大纠正的力度就越大。就像开车发现速度慢了就多踩点油门。但纯P控制容易在目标值附近震荡永远停不下来。I积分把一段时间内的误差累加起来再乘以一个系数Ki。它能消除静态误差。比如纯P控制下电机最终速度可能总是比目标慢一点由于摩擦积分项会随着时间推移慢慢增加输出最终把这个“一点”追回来。D微分计算误差的变化率乘以系数Kd。它能预测未来的误差趋势抑制震荡。比如速度正在快速接近目标微分项会提前减小输出防止“超调”。PID输出 Kp * 误差 Ki * 误差积分 Kd * 误差微分。这个输出值就是我们最终要送给电机的PWM值。5.3 Arduino PID速度控制代码实现下面是一个简化的、离散化的PID速度控制循环示例。我们假设已经能够通过编码器定期计算当前速度。#include PID_v1.h // 使用Arduino经典的PID库 // 电机驱动引脚定义同前 const int AIN1 8; const int AIN2 9; const int PWMA 10; // 编码器引脚与变量定义同前 const int encoderPinA 2; volatile long encoderPos 0; long lastPos 0; unsigned long lastTime 0; float currentSpeed 0; // 当前速度 (脉冲数/秒) // PID相关变量 double Setpoint, Input, Output; // 目标值输入值当前速度输出值PWM // 创建PID对象参数为(输入, 输出, 目标值, Kp, Ki, Kd, 方向) PID myPID(Input, Output, Setpoint, 2.0, 5.0, 0.1, DIRECT); // 参数需调试 void setup() { Serial.begin(115200); // 初始化电机驱动引脚... pinMode(AIN1, OUTPUT); pinMode(AIN2, OUTPUT); pinMode(PWMA, OUTPUT); digitalWrite(AIN1, HIGH); digitalWrite(AIN2, LOW); // 初始化编码器中断... pinMode(encoderPinA, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(encoderPinA), updateEncoder, CHANGE); // 初始化PID Setpoint 100.0; // 设定目标速度为100脉冲/秒这个单位需要根据你的编码器PPR和测量间隔换算 myPID.SetMode(AUTOMATIC); // 开启PID myPID.SetOutputLimits(0, 255); // 限制输出在PWM范围内 myPID.SetSampleTime(50); // 设定PID计算周期为50毫秒 lastTime millis(); } void loop() { unsigned long now millis(); unsigned long timeChange now - lastTime; // 计算时间间隔 if (timeChange 100) { // 每100ms计算一次当前速度 noInterrupts(); // 暂时关闭中断安全地读取encoderPos long posChange encoderPos - lastPos; lastPos encoderPos; interrupts(); // 计算速度脉冲变化量 / 时间间隔 (秒) currentSpeed (posChange / (timeChange / 1000.0)); Input currentSpeed; // 将当前速度作为PID的输入 lastTime now; } // 每次循环都执行PID计算库内部会根据SetSampleTime决定是否真正计算 myPID.Compute(); // 将PID的输出Output作为PWM值写入电机 analogWrite(PWMA, (int)Output); // 串口打印调试信息 Serial.print(Set:); Serial.print(Setpoint); Serial.print( | Cur:); Serial.print(currentSpeed); Serial.print( | Out:); Serial.println(Output); } // 编码器中断函数同前 void updateEncoder() { // ... 简化的方向判断逻辑 encoderPos; }调试PID参数的心得 这是一个“炼丹”过程没有绝对的标准值。先调PKp将Ki和Kd设为0。逐渐增大Kp直到电机速度能跟随目标值但开始出现明显的震荡在目标值上下摆动。此时将Kp减小到震荡刚好消失的值的60%-70%。再调IKi逐渐增加Ki观察电机速度是否能稳定在目标值上消除静态误差比如目标100最终稳定在98。Ki太大会引起超调和震荡。最后调DKd如果系统在调整P和I后响应速度够快且稳定D可以设为0或很小。如果系统有震荡可以适当增加Kd来抑制。但D项对噪声编码器测量噪声很敏感加得不好反而会引入不稳定。重要提示上面的代码中速度单位是“脉冲数/秒”。你需要根据你的编码器PPR和减速比将其转换为更有意义的“转/分钟RPM”或“米/秒”。例如如果电机输出轴转一圈对应390个脉冲那么转速(RPM) (脉冲数/秒) / 390 * 60。6. 常见问题与排查技巧实录在实际操作中你一定会遇到各种各样的问题。下面是我和学生们在项目中踩过的坑和解决方案希望能帮你节省大量时间。6.1 电机完全不转这是最常见的问题。请按照以下清单逐项排查像医生问诊一样症状可能原因排查方法电机无声驱动模块指示灯不亮主电源未接通或接反用万用表测量驱动模块VM和GND之间是否有电压如12V。检查电源开关、电池电量。驱动模块逻辑电源灯亮但电机不转控制信号未正确连接或设置1. 检查STBY引脚是否接高电平5V。2. 检查AIN1/AIN2方向引脚电平是否正确用digitalWrite设置HIGH/LOW。3. 检查PWMA引脚是否有PWM信号用analogWrite设置非零值。可用Arduino的blink示例程序测试该引脚是否正常。电机有轻微震动或嗡嗡声但不旋转PWM频率过低或驱动电流不足1. 尝试提高PWM频率对于Arduino可使用Timer库修改频率。2. 电源或驱动模块提供的电流不足无法启动电机。尝试减小负载或更换更大电流的电源/驱动。电机发热严重很快停转堵转或短路1. 立即断电用手转动电机轴检查是否有机械卡死。2. 用万用表通断档测量电机两线间电阻正常应为几欧姆到几十欧姆。如果电阻为0或无穷大电机可能内部短路或开路损坏。6.2 电机转动方向与预期相反这个问题很简单。解决方法有两个软件修正交换程序中控制AIN1和AIN2的HIGH/LOW逻辑。硬件修正交换接到电机驱动模块A和A-端子的两根电机线。通常建议在软件中修正这样更灵活。6.3 电机转速不稳定、抖动电源问题使用电池时在大负载下电池电压会下降导致PWM等效电压不稳。务必为电机驱动电路单独供电或使用大容量、低内阻的电池如18650锂电池组并确保电源线足够粗。Arduino的USB口或板上稳压器无法提供电机所需的大电流。机械问题检查联轴器、轮子是否安装牢固齿轮箱内是否有异物或损坏。不规则的机械阻力会导致转速波动。控制问题如果是开环控制负载变化必然导致转速变化。这是正常的需要引入带编码器的PID闭环控制才能解决。PWM干扰如果PWM频率落在音频范围几百Hz到几kHz电机线圈会因振动发声也可能引起轻微抖动。尝试将频率提高到10kHz以上人耳听不到。6.4 编码器计数不准或混乱接线干扰编码器信号线尤其是A、B相应使用双绞线或屏蔽线并远离电机电源线避免电磁干扰。电源噪声为编码器通常是5V或3.3V供电的电源要干净。可以在编码器电源引脚就近对地加一个0.1uF的瓷片电容滤波。中断冲突在Arduino上避免在中断服务函数updateEncoder()内做复杂操作或使用delay()。确保该函数执行时间极短。代码逻辑不严谨上面示例的中断判断逻辑是简化的。更稳健的写法是同时检测A、B两相的上升沿和下降沿并根据状态变化查表判断方向。许多社区库如Encoder.h已经实现了更可靠的算法建议直接使用。物理损坏霍尔编码器对磁铁位置敏感。拆卸电机后如果磁铁移位会导致编码器失效。6.5 驱动模块发热甚至烧毁电流超载这是最常见原因。确认电机堵转电流是否超过驱动模块的持续输出电流不是峰值电流。为驱动模块加装散热片是必须的。散热不良确保驱动模块放置在通风处不要被其他元件或外壳紧贴包裹。桥臂直通虽然TB6612等芯片有内部保护但程序错误导致短时间内方向控制信号快速翻转仍可能产生危险。确保你的控制逻辑中改变方向前有短暂的停止状态或软件死区。电源电压过高检查电机额定电压不要超压使用。7. 项目实战制作一个PID调速小车现在我们把所有知识串联起来做一个简单的双轮差分驱动小车并实现速度闭环控制。这个项目将涉及两个带编码器的达妙43电机。7.1 系统架构与物料清单系统框图[ 12V锂电池 ] -- [ TB6612驱动模块 (通道A) ] -- [ 达妙43编码电机 (左轮) ] | | [ Arduino Uno ] --|-- [ TB6612驱动模块 (通道B) ] -- [ 达妙43编码电机 (右轮) ] | |-- [ 编码器A相/B相 (左) ] -- [ 中断引脚2, 4 ] |-- [ 编码器A相/B相 (右) ] -- [ 中断引脚3, 5 ]物料清单Arduino Uno x1TB6612驱动模块 x2 (或一个双通道模块)达妙43直流减速编码电机12V 1:30减速比x212V锂电池组带DC插头或XT60接头x1小车底盘、轮子、万向轮套件 x1若干杜邦线、导线可能需要的电容、开关等7.2 核心代码框架与双电机PID控制我们需要为两个电机分别建立PID实例和编码器计数器。#include PID_v1.h // 假设使用Encoder库更稳定 #include Encoder.h // 定义左右电机驱动引脚 const int L_AIN1 8, L_AIN2 9, L_PWMA 10; const int R_AIN1 7, R_AIN2 6, R_PWMA 5; // 创建编码器对象参数为(A相引脚, B相引脚) Encoder leftEnc(2, 4); // 左电机编码器接引脚2,4 Encoder rightEnc(3, 5); // 右电机编码器接引脚3,5 // PID变量 - 左电机 double leftSetpoint 50.0; // 目标速度 (脉冲/秒) double leftInput, leftOutput; PID leftPID(leftInput, leftOutput, leftSetpoint, 2.0, 5.0, 0.1, DIRECT); // PID变量 - 右电机 double rightSetpoint 50.0; double rightInput, rightOutput; PID rightPID(rightInput, rightOutput, rightSetpoint, 2.0, 5.0, 0.1, DIRECT); // 速度计算相关变量 long leftOldPos 0, rightOldPos 0; unsigned long lastSpeedCalcTime 0; const long speedCalcInterval 100; // 速度计算间隔100ms void setup() { Serial.begin(115200); // 初始化所有电机驱动引脚... pinMode(L_AIN1, OUTPUT); digitalWrite(L_AIN1, HIGH); digitalWrite(L_AIN2, LOW); pinMode(R_AIN1, OUTPUT); digitalWrite(R_AIN1, HIGH); digitalWrite(R_AIN2, LOW); // ... 其他引脚模式设置 // 初始化PID leftPID.SetMode(AUTOMATIC); leftPID.SetOutputLimits(0, 255); leftPID.SetSampleTime(50); // PID计算周期50ms rightPID.SetMode(AUTOMATIC); rightPID.SetOutputLimits(0, 255); rightPID.SetSampleTime(50); lastSpeedCalcTime millis(); } void loop() { unsigned long now millis(); // 1. 定期计算当前速度 if (now - lastSpeedCalcTime speedCalcInterval) { long leftNewPos leftEnc.read(); // 读取编码器累计值 long rightNewPos rightEnc.read(); // 计算速度 (位置变化量) / (时间间隔单位秒) leftInput (leftNewPos - leftOldPos) / (speedCalcInterval / 1000.0); rightInput (rightNewPos - rightOldPos) / (speedCalcInterval / 1000.0); // 更新旧位置 leftOldPos leftNewPos; rightOldPos rightNewPos; lastSpeedCalcTime now; // 串口打印调试信息 Serial.print(L-Set:); Serial.print(leftSetpoint); Serial.print( Cur:); Serial.print(leftInput); Serial.print( | R-Set:); Serial.print(rightSetpoint); Serial.print( Cur:); Serial.println(rightInput); } // 2. 执行PID计算 leftPID.Compute(); rightPID.Compute(); // 3. 将PID输出应用到电机PWM analogWrite(L_PWMA, (int)leftOutput); analogWrite(R_PWMA, (int)rightOutput); // 这里可以添加其他逻辑比如通过串口接收新的目标速度指令 // if (Serial.available()) { ... } }7.3 调试与让小车走直线即使两个电机的型号、齿轮间隙完全一样在相同的PWM下转速也可能有细微差别导致小车跑偏。PID闭环控制可以极大改善这个问题但前提是两个PID环的参数要分别调试。单独调试将小车架起让轮子悬空。分别设置左右电机相同的目标速度如50脉冲/秒通过串口监视器观察它们的实际速度。微调其中一个电机的PID参数通常是Kp使它们在空载下能基本保持一致。地面测试将小车放在地上设定一个较低的目标速度让其直行。观察跑偏方向。如果向左偏说明右轮实际速度慢或扭矩不足。可以稍微提高右轮的目标速度rightSetpoint或者微调右轮的PID参数使其输出更大一些。反之亦然。引入更高级策略更优的方法是加入“转向控制”。将两个编码器的读数差作为误差用一个额外的PID控制器来动态调整左右轮的目标速度差从而主动纠正航向。这就是“差速转向”的基础。通过这个项目你不仅学会了驱动和控制单个电机更掌握了如何协调多个电机完成一个实际任务。这其中的调试过程——观察现象、分析数据、调整参数——才是嵌入式开发和机器人技术中最有价值的经验。