这个基于Arduino BLDC的动态调参平衡车项目,是我最近完成的一个相当有意思的嵌入式控制系统实践。不同于传统的PID平衡车,我在系统中引入了速度前馈机制和动态参数调整功能,使得这个小车的平衡能力有了质的提升。作为一个玩了多年嵌入式控制的工程师,我想分享一下这个项目的核心思路和实现细节。
平衡车本质上是一个倒立摆系统,属于典型的非线性、不稳定系统。传统PID控制器在这种系统上表现往往差强人意——要么响应太慢导致容易倾倒,要么过于敏感产生振荡。我在项目中采用的"PID+前馈+动态调参"复合控制策略,很好地解决了这些问题。实测表明,这套系统在受到外力推搡时,恢复速度比纯PID方案快了近40%,而且在加减速过程中姿态更加稳定。
我的平衡车硬件平台主要由以下几个关键部件组成:
主控制器:选用的是ESP32开发板,看中的是其双核240MHz主频和丰富的外设接口。相比传统的Arduino Uno,ESP32的运算能力足够应付复杂的控制算法和实时数据处理。
姿态传感器:MPU6050六轴惯性测量单元(IMU),提供三轴加速度和三轴角速度数据。这是平衡车的"内耳",负责感知车体倾斜状态。
执行机构:两个无刷直流电机(BLDC)配合FOC(磁场定向控制)驱动器。选择BLDC而非普通直流电机,主要是看中其高扭矩密度和精确的速度控制能力。
速度反馈:电机轴上安装了600线正交编码器,用于精确测量实际转速。
电源系统:3S锂聚合物电池(11.1V)配合大容量滤波电容,确保在电机大电流波动时系统供电稳定。
软件部分采用分层设计:
code复制┌───────────────────────┐
│ 应用层 │
│ - 动态参数调整逻辑 │
│ - 安全监控 │
├───────────────────────┤
│ 控制层 │
│ - PID控制器 │
│ - 前馈补偿器 │
├───────────────────────┤
│ 驱动层 │
│ - 电机FOC驱动 │
│ - 编码器接口 │
├───────────────────────┤
│ 感知层 │
│ - IMU数据处理 │
│ - 传感器融合 │
└───────────────────────┘
控制周期设置为5ms(200Hz),通过硬件定时器严格保证实时性。在这个周期内需要完成传感器数据读取、姿态解算、控制算法计算和电机输出更新等一系列操作。
姿态估计的准确性直接影响控制效果。我采用了互补滤波算法融合IMU数据:
cpp复制void updateAngleEstimate(float dt) {
// 读取原始传感器数据
float accX = mpu.getAccelerationX();
float accZ = mpu.getAccelerationZ();
float gyroY = mpu.getRotationY(); // 俯仰轴角速度
// 加速度计计算的角度(有高频噪声)
float accAngle = atan2(accX, accZ) * 180/PI;
// 互补滤波融合
// 0.98权重给陀螺积分(动态响应好),0.02给加速度计(长期稳定)
currentAngle = 0.98 * (currentAngle + gyroY * dt) + 0.02 * accAngle;
// 角速度直接使用陀螺仪数据(已去除零偏)
currentAngleRate = gyroY;
}
这个简单的互补滤波在实际测试中表现相当不错,角度估计误差在±1°以内,完全满足控制需求。对于要求更高的场合,可以考虑改用卡尔曼滤波。
我使用了级联PID结构:外环是角度环,负责维持平衡;内环是速度环,实现速度控制。
cpp复制// 角度环PID参数
float angleKp = 2.5, angleKi = 0.1, angleKd = 0.3;
PID anglePID(¤tAngle, &angleOutput, &targetAngle, angleKp, angleKi, angleKd, DIRECT);
// 速度环PID参数
float speedKp = 0.8, speedKi = 0.05, speedKd = 0.1;
PID speedPID(¤tSpeed, &speedOutput, &targetSpeed, speedKp, speedKi, speedKd, DIRECT);
void controlUpdate() {
// 角度环计算
anglePID.Compute();
// 角度环输出作为速度环的目标
targetSpeed = angleOutput * 0.2; // 缩放因子
// 速度环计算
speedPID.Compute();
// 最终电机命令
motorCommand = speedOutput;
}
速度前馈是提升系统响应速度的关键。我的实现方式如下:
cpp复制float calculateFeedforward(float targetSpeed, float currentSpeed) {
// 前馈增益,通过实验测定
const float Kff = 0.25;
// 速度变化率估算
static float lastSpeed = 0;
float acceleration = (currentSpeed - lastSpeed) / controlPeriod;
lastSpeed = currentSpeed;
// 前馈量 = 速度项 + 加速度项
float feedforward = Kff * targetSpeed + 0.1 * acceleration;
return feedforward;
}
在控制循环中,前馈量直接叠加到PID输出:
cpp复制float ff = calculateFeedforward(targetSpeed, currentSpeed);
motorCommand = speedOutput + ff;
固定PID参数难以适应所有工况,因此我实现了基于工作点的动态调参:
cpp复制void adjustAnglePID(float angle) {
float error = abs(angle - targetAngle);
// 大倾角时增加增益
if (error > 10.0) {
angleKp = 3.5;
angleKd = 0.5;
}
// 小倾角时减小增益防止振荡
else if (error < 2.0) {
angleKp = 1.8;
angleKi = 0.05; // 减小积分项
}
// 中等倾角使用默认参数
else {
angleKp = 2.5;
angleKi = 0.1;
angleKd = 0.3;
}
anglePID.SetTunings(angleKp, angleKi, angleKd);
}
cpp复制void adjustSpeedPID(float speed) {
// 高速时减小比例增益
if (abs(speed) > 1.0) { // m/s
speedKp = 0.5;
speedKi = 0.02;
} else {
speedKp = 0.8;
speedKi = 0.05;
}
speedPID.SetTunings(speedKp, speedKi, speedKd);
}
先调角度环:将速度环PID设为零,只使用角度环
再调速度环:固定角度环参数
最后调前馈:先关闭前馈,调好PID后再引入
问题1:小车总是向一边倾斜
问题2:快速振荡无法稳定
问题3:响应迟钝,容易被推倒
cpp复制void loop() {
static uint32_t lastControlTime = 0;
// 严格5ms控制周期
if (micros() - lastControlTime >= 5000) {
lastControlTime = micros();
// 1. 读取传感器
readIMU();
readEncoders();
// 2. 更新状态估计
updateAngleEstimate(0.005); // 5ms = 0.005s
updateSpeedEstimate();
// 3. 动态调整参数
adjustAnglePID(currentAngle);
adjustSpeedPID(currentSpeed);
// 4. 计算控制量
controlUpdate();
// 5. 应用前馈
float ff = calculateFeedforward(targetSpeed, currentSpeed);
// 6. 输出到电机
setMotorOutput(motorCommand + ff);
}
}
cpp复制void setMotorOutput(float output) {
// 限制输出范围
output = constrain(output, -MAX_OUTPUT, MAX_OUTPUT);
// 死区补偿(针对电机启动死区)
if (abs(output) < 0.1) output = 0;
else if (output > 0) output += 0.1;
else output -= 0.1;
// 设置电机输出
motorDriver.setPWM(output);
}
经过实际测试,这套控制系统表现出色:
可能的改进方向:
改用模型预测控制(MPC):当前的前馈补偿是基于简单模型,MPC可以提供更优的前瞻控制
增加负载自适应:通过观测电流变化自动识别负载变化,调整控制参数
无线监控接口:添加蓝牙或Wi-Fi遥测,方便参数调试和数据记录
能量回收:在下坡时启用再生制动,延长续航时间
这个项目最让我满意的不是最终实现了平衡功能,而是在整个开发过程中对控制理论有了更深入的理解。从最初的纯PID到引入前馈,再到实现动态调参,每一个改进都带来了明显的性能提升。这也再次验证了工程实践中的一条真理:好的控制系统一定是理论与实验的完美结合。