【花雕学编程】Arduino BLDC 之双电机差速转向控制(智能车)
本文介绍了基于Arduino的无刷直流电机(BLDC)双电机差速转向控制系统在智能车中的应用。系统采用左右轮独立调速实现转向,具有高效静音、长寿命等特点,支持闭环速度/位置控制,可扩展性强。文章详细阐述了该系统在智能车竞赛、教育科研、物流巡检等场景的应用,并重点分析了关键注意事项:包括BLDC驱动接口匹配、编码器信号处理、电源管理、机械对称性标定、控制算法鲁棒性以及电磁兼容设计。同时提供了红外循迹

Arduino BLDC 之双电机差速转向控制(智能车) 是一种基于无刷直流电机(BLDC)驱动、采用左右轮独立调速实现转向的移动机器人架构,广泛应用于教育、竞赛及轻型自主车辆原型开发中。该系统通过 Arduino 实现运动控制逻辑,结合传感器反馈完成路径跟踪、避障或循迹任务。以下从专业工程视角,系统阐述其主要特点、典型应用场景及需注意的关键事项。
一、主要特点
- 高效、静音、长寿命的驱动系统
BLDC 电机优势:
效率高达 85–90%,显著优于有刷电机,在电池供电场景下延长续航;
无电刷磨损,免维护,适合频繁启停的智能车应用;
运行噪声低(<50 dB),适用于对声学环境敏感的室内场景;
高功率密度支持快速加速与爬坡能力(如 10° 坡度)。 - 差速转向原理与运动灵活性
转向机制:通过调节左右 BLDC 轮的转速差实现转向:
同速 → 直线行驶;
左快右慢 → 右转;
反向旋转 → 原地转向(零转弯半径);
运动学模型简单:适用于非完整约束系统(unicycle model),便于路径规划与控制算法实现。 - 闭环速度/位置控制能力
反馈源:
霍尔传感器(内置 BLDC)提供粗略换相与速度估算;
外置增量式编码器(如 1000 PPR)经四倍频后实现高分辨率轮速测量;
控制策略:
双路独立 PID 控制器分别调节左右轮目标转速;
上层路径跟踪算法(如 Pure Pursuit、Stanley)输出期望速度差,下发至底层电机控制器。 - Arduino 平台的可扩展性与模块化
主控可选:
Arduino Mega 2560:多硬件中断支持双编码器计数;
Teensy 4.1:600 MHz Cortex-M7,支持 FOC + 高频编码器读取;
ESP32:双核 + Wi-Fi/BLE,支持远程监控与 OTA 更新;
易集成传感器:超声波、红外循迹、IMU(MPU6050)、摄像头等。
二、典型应用场景 - 大学生智能车竞赛(如全国大学生智能汽车竞赛)
使用 BLDC 提升速度与能效,在“直立组”“全向组”“信标组”中实现高速稳定运行;
差速控制配合陀螺仪实现抗侧滑直线行驶。 - 教育与科研实验平台
用于讲授:
移动机器人运动学(kinematics);
闭环速度控制(PID 调参);
路径跟踪算法(A* + 差速跟随);
成本远低于商用 AGV,适合本科/研究生课程设计。 - 轻型室内物流或巡检机器人原型
在仓库、实验室、医院等结构化环境中执行:
自主导航配送(结合二维码/UWB 定位);
定点巡检(搭载温湿度、气体传感器);
BLDC 的静音特性适合人机共存环境。 - 开源机器人社区项目(如 ROS 小车底层)
作为 ROS 2 / micro-ROS 的硬件执行层,Arduino 负责底层电机控制,上位机处理 SLAM 与导航。
三、需要注意的关键事项 - BLDC 驱动与 Arduino 接口匹配
关键问题:多数 BLDC 轮毂电机需通过 电子调速器(ESC) 驱动;
ESC 通常接受 标准 RC PWM 信号(1–2 ms 脉宽,50 Hz),而非 analogWrite() 的 490 Hz PWM;
必须使用 Servo.h 库或专用 ESC 库(如 ESC.cpp)生成正确信号;
高端方案:采用支持 FOC 的驱动板(如 SimpleFOC Shield + STSPIN32G4),通过 UART/CAN 接收目标速度指令,实现真正闭环。 - 编码器信号处理与实时性
中断资源限制:
Arduino Uno 仅有 2 个外部中断,无法同时处理双编码器 A/B 相;
推荐使用 Mega(6 个中断)或 Teensy(任意引脚中断 + 硬件 quadrature decoder);
防脉冲丢失:
高速时主循环若未及时读取计数器,会导致里程计误差;
使用硬件计数器(如 Teensy 的 Encoder 库)或 DMA(STM32)可避免此问题。 - 电源管理与电流冲击
启动/转向瞬间电流可达额定值 3–5 倍;
使用 高倍率锂电池(如 20C LiPo 或 LiFePO₄);
每个 ESC 输入端就近加 2200 μF 电解电容 + 100 nF 陶瓷电容 抑制电压跌落;
Arduino 与电机电源必须隔离:
逻辑部分使用 LM2596 或 AMS1117 稳压至 5V/3.3V;
避免电机启停导致主控复位。 - 机械对称性与系统标定
左右轮直径/摩擦系数不一致 会导致直线跑偏;
必须进行系统标定:
开环测试:同 PWM 下记录左右轮转速,计算补偿系数;
闭环校正:通过 IMU(如 MPU6050)检测航向角偏差,动态调整速度差。
轮胎打滑:在光滑地面易发生,建议使用高摩擦橡胶胎或增加自重。 - 控制算法鲁棒性
PID 参数整定:
内环(速度环)带宽应 > 外环(路径跟踪);
避免积分饱和(Integral Windup):加入 anti-windup 逻辑;
低速死区补偿:BLDC 在低占空比下响应非线性,需设置最小有效 PWM 阈值。 - 电磁兼容(EMC)与布线规范
电机线与信号线分离:
使用屏蔽线连接 ESC 与 BLDC;
编码器/IMU 信号线远离电机线,必要时加磁珠;
共地单点连接:电机地与逻辑地在电源处单点汇接,防止地环路噪声。

1、红外循迹智能车
#include <AFMotor.h>
// 定义前后两路电机
AF_DCMotor leftMotor(1, MOTOR12_64KHZ); // 左轮电机
AF_DCMotor rightMotor(2, MOTOR12_64KHZ); // 右轮电机
const int IR_SENSOR_PIN = A0; // 红外循迹传感器
float targetLinePosition = 0.5; // 目标位置比例(0~1)
float currentDeviation = 0; // 当前偏离量
void setup() {
pinMode(IR_SENSOR_PIN, INPUT);
leftMotor.setSpeed(0);
rightMotor.setSpeed(0);
}
void loop() {
static uint32_t lastTime = 0;
if (millis() - lastTime > CONTROL_PERIOD) {
// 读取传感器值并归一化处理
int sensorValue = analogRead(IR_SENSOR_PIN);
float normalizedPos = map(sensorValue, MIN_ADC, MAX_ADC, 0, 1);
// PID控制器计算转向比例
currentDeviation = targetLinePosition - normalizedPos;
float turnRatio = Kp * currentDeviation + Ki * integral + Kd * derivative;
// 差速转向执行
int baseSpeed = BASE_SPEED;
int leftSpeed = constrain(baseSpeed * (1 - turnRatio), 0, MAX_SPEED);
int rightSpeed = constrain(baseSpeed * (1 + turnRatio), 0, MAX_SPEED);
leftMotor.setSpeed(leftSpeed);
rightMotor.setSpeed(rightSpeed);
lastTime = millis();
}
}
// 中断服务函数用于高速采样
void handleEncoderInterrupt() {
static int count = 0;
count++;
if (count >= ENCODER_TICKS_PER_UPDATE) {
updateOdometry();
count = 0;
}
}
技术要点解读:
PID参数整定方法:采用齐格勒-尼科尔斯法现场调试获得最优PID系数
非线性补偿机制:建立电机扭矩-转速特性曲线进行前馈补偿
电磁兼容设计:TVS二极管吸收反电动势尖峰保护H桥电路
热失效防护:NTC热敏电阻监测绕组温度自动降额运行
故障诊断树:分级报警机制区分传感器故障/电机堵转/电源异常
2、超声波避障智能车
#include <NewPing.h>
// 超声波传感器配置
#define TRIGGER_PIN 7
#define ECHO_PIN 8
#define MAX_DISTANCE 200 // cm
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
struct ObstacleData {
float frontDistance;
float leftDistance;
float rightDistance;
};
ObstacleData obs;
bool obstacleDetected = false;
void setup() {
pinMode(BUZZER_PIN, OUTPUT);
attachInterrupt(digitalPinToInterrupt(ECHO_PIN), captureEchoPulse, RISING);
}
void loop() {
static uint32_t lastScanTime = 0;
if (millis() - lastScanTime > SCAN_INTERVAL) {
// 多方向障碍物检测
obs.frontDistance = sonar.ping_cm(FRONT_SENSOR);
obs.leftDistance = sonar.ping_cm(LEFT_SENSOR);
obs.rightDistance = sonar.ping_cm(RIGHT_SENSOR);
// 动态窗口法局部路径规划
if (obs.frontDistance < SAFETY_DISTANCE) {
obstacleDetected = true;
executeAvoidanceManeuver();
} else {
obstacleDetected = false;
continueNormalOperation();
}
lastScanTime = millis();
}
}
// 复杂动作序列执行
void executeAvoidanceManeuver() {
switch (currentState) {
case DETECTION:
stopVehicle();
rotateHeadTowardsObstacle();
break;
case DECISION:
calculateOptimalPath();
break;
case EXECUTION:
performDifferentialTurn();
break;
default:
enterSafeMode();
}
}
// 模糊逻辑转向决策
float fuzzyTurnDecision(float distance, float rateOfChange) {
if (distance < CRITICAL_DISTANCE && rateOfChange > APPROACH_RATE) {
return HARD_TURN_RATIO;
} else if (distance < CAUTION_DISTANCE) {
return MEDIUM_TURN_RATIO;
} else {
return SOFT_TURN_RATIO;
}
}
技术要点解读:
时空一致性校验:时间戳同步保证多传感器数据对齐
概率占据栅格更新:贝叶斯法则融合多次测量结果降低误报率
能量最优轨迹生成:Dubins曲线算法规划最短路径转向
声光警示系统:LED闪烁频率随接近速度加快而增加
应急制动预案:当制动距离不足时触发机械抱闸装置
3、惯性导航+蓝牙遥控智能车
#include <Wire.h>
#include <MPU6050.h>
#include <SoftwareSerial.h>
// MPU6050惯性测量单元
MPU6050 imu;
SoftwareSerial bluetoothSerial(BT_RX_PIN, BT_TX_PIN);
struct CommandPacket {
uint8_t mode; // 工作模式选择
float headingAngle; // 目标航向角
float travelDistance;// 目标行驶距离
};
CommandPacket remoteCmd;
float actualHeading = 0;
float traveledDistance = 0;
void setup() {
imu.initialize();
bluetoothSerial.begin(BAUD_RATE);
delay(1000);
}
void loop() {
// 接收远程指令
if (bluetoothSerial.available()) {
parseIncomingPacket(&remoteCmd);
}
// 航位推算定位
updateHeadingEstimation();
updateDistanceTraveled();
// 双环PID控制
float headingError = remoteCmd.headingAngle - actualHeading;
float distanceError = remoteCmd.travelDistance - traveledDistance;
float angularVelocity = Kp_heading * headingError + Ki_heading * integral_heading;
float linearVelocity = Kp_distance * distanceError + Ki_distance * integral_distance;
setMotorSpeeds(linearVelocity, angularVelocity);
}
// 互补滤波姿态估计
void updateHeadingEstimation() {
int16_t ax, ay, az;
int16_t gx, gy, gz;
imu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
float dt = (millis() - lastUpdateTime) / 1000.0f;
float quaternion[4];
MadgwickAHRSupdate(gx, gy, gz, ax, ay, az, dt, quaternion);
actualHeading = atan2(quaternion[0], quaternion[1]) * RADIANS_TO_DEGREES;
}
// 里程计积分修正
void updateDistanceTraveled() {
int pulseCount = getEncoderTicks();
traveledDistance += (pulseCount * WHEEL_CIRCUMFERENCE) / ENCODER_RESOLUTION;
}
技术要点解读:
四元数姿态解算:Madgwick梯度下降法实现低成本IMU融合
非完整约束处理:考虑车轮打滑时的滑动因子校正
自适应卡尔曼滤波:根据运动状态动态调整过程噪声矩阵
数字孪生映射:将物理坐标系与虚拟地图坐标系关联
低功耗待机策略:无操作时进入深度睡眠模式仅保留RTC时钟

4、基于PWM调速的差速转向(使用SimpleFOC库)
#include <SimpleFOC.h>
// 左电机配置
BLDCMotor motorL = BLDCMotor(7); // 7对极
BLDCDriver3PWM driverL = BLDCDriver3PWM(9, 10, 11, 8); // PWM引脚+使能引脚
// 右电机配置
BLDCMotor motorR = BLDCMotor(7);
BLDCDriver3PWM driverR = BLDCDriver3PWM(5, 6, 7, 4);
// 编码器或传感器(假设使用无感FOC)
void setup() {
Serial.begin(115200);
// 初始化左电机
driverL.init();
motorL.linkDriver(&driverL);
motorL.voltage_sensor_align = 3; // 对齐电压
motorL.init();
motorL.initFOC();
// 初始化右电机
driverR.init();
motorR.linkDriver(&driverR);
motorR.voltage_sensor_align = 3;
motorR.init();
motorR.initFOC();
}
void loop() {
// 读取遥控器或传感器输入(这里用串口模拟)
if (Serial.available()) {
String input = Serial.readStringUntil('\n');
int commaIndex = input.indexOf(',');
float linear = input.substring(0, commaIndex).toFloat(); // 线性速度 (-1~1)
float angular = input.substring(commaIndex + 1).toFloat(); // 角速度 (-1~1)
// 差速模型:左轮速度 = 线性速度 - 角速度/2,右轮速度 = 线性速度 + 角速度/2
float speedL = linear - angular / 2.0;
float speedR = linear + angular / 2.0;
// 设置电机速度(单位:转/秒)
motorL.move(speedL * 10.0); // 缩放因子根据实际调整
motorR.move(speedR * 10.0);
}
motorL.loopFOC();
motorR.loopFOC();
motorL.update();
motorR.update();
}
5、基于串口指令的差速控制(使用Arduino标准PWM)
// 左电机引脚
#define ENA 5 // PWM速度控制
#define IN1 6 // 方向控制
#define IN2 7
// 右电机引脚
#define ENB 10
#define IN3 8
#define IN4 9
void setup() {
Serial.begin(9600);
pinMode(ENA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
}
void setMotorSpeed(int pwmPin, int dirPin1, int dirPin2, float speed) {
if (speed >= 0) {
digitalWrite(dirPin1, HIGH);
digitalWrite(dirPin2, LOW);
} else {
digitalWrite(dirPin1, LOW);
digitalWrite(dirPin2, HIGH);
speed = -speed;
}
analogWrite(pwmPin, constrain(speed * 255, 0, 255));
}
void loop() {
if (Serial.available()) {
String input = Serial.readStringUntil('\n');
int commaIndex = input.indexOf(',');
float linear = input.substring(0, commaIndex).toFloat(); // 0~1
float angular = input.substring(commaIndex + 1).toFloat(); // -1~1
// 差速计算
float speedL = linear - angular / 2.0;
float speedR = linear + angular / 2.0;
// 设置电机
setMotorSpeed(ENA, IN1, IN2, speedL);
setMotorSpeed(ENB, IN3, IN4, speedR);
}
}
6、带PID闭环的差速转向(使用MPU6050反馈)
#include <Wire.h>
#include <MPU6050.h>
#include <PID_v1.h>
// 电机控制引脚(同案例5)
#define ENA 5
#define IN1 6
#define IN2 7
#define ENB 10
#define IN3 8
#define IN4 9
// MPU6050配置
MPU6050 mpu;
double targetAngle = 0; // 目标航向角(度)
double currentAngle = 0;
double pidOutput = 0;
// PID参数
double Kp = 1.0, Ki = 0.1, Kd = 0.05;
PID pid(¤tAngle, &pidOutput, &targetAngle, Kp, Ki, Kd, DIRECT);
void setup() {
Serial.begin(115200);
Wire.begin();
mpu.initialize();
mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250);
// 电机引脚初始化
pinMode(ENA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
// PID初始化
pid.SetMode(AUTOMATIC);
pid.SetOutputLimits(-1, 1); // 输出范围对应电机速度
}
void loop() {
// 读取陀螺仪数据(简化处理,实际需积分或使用DMP)
int16_t gz;
mpu.getRotation(&gz, NULL, NULL);
currentAngle += gz * 0.01; // 假设10ms采样周期
// 更新PID(例如通过串口设置目标角度)
if (Serial.available()) {
targetAngle = Serial.parseFloat();
}
pid.Compute();
// 差速控制:pidOutput为转向修正量
float linearSpeed = 0.5; // 固定前进速度
float turnCompensation = pidOutput * 0.3; // 转向补偿系数
float speedL = linearSpeed - turnCompensation;
float speedR = linearSpeed + turnCompensation;
// 设置电机方向
setMotorSpeed(ENA, IN1, IN2, speedL);
setMotorSpeed(ENB, IN3, IN4, speedR);
delay(10);
}
void setMotorSpeed(int pwmPin, int dirPin1, int dirPin2, float speed) {
// 同案例5
}
要点解读
差速控制核心公式
左轮速度 = 线性速度 - 角速度/2
右轮速度 = 线性速度 + 角速度/2
此公式确保车辆以指定角速度绕瞬时中心旋转(阿克曼几何简化模型)。
电机驱动方式选择
无刷电机(BLDC):需专用驱动器(如案例4的SimpleFOC库),支持FOC控制,效率高但复杂。
有刷电机:可直接用PWM+方向引脚(案例5、6),适合低成本方案。
闭环控制的必要性
开环差速(案例4、5)依赖输入精度,易受摩擦、轮胎差异影响。
闭环(案例6)通过传感器(如MPU6050陀螺仪)反馈航向角,用PID修正偏差,适合高精度转向。
实时性优化
BLDC需高频调用loopFOC()(案例1),否则会抖动。
有刷电机需避免delay(),可用millis()实现非阻塞控制(案例5、6中未体现,但实际应优化)。
安全与调试
限制最大速度(如constrain(speed, -1, 1))防止失控。
通过串口输出实际速度/角度,便于调试(如案例4的Serial.print)。
急停功能:检测到异常时立即设置PWM为0。
注意,以上案例只是为了拓展思路,仅供参考。它们可能有错误、不适用或者无法编译。您的硬件平台、使用场景和Arduino版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)