ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

电动车跷跷板如何调稳?从力矩平衡到双闭环PID的完整控制方案

电动车跷跷板如何调稳?从力矩平衡到双闭环PID的完整控制方案 简介二〇二一年电子设计大赛校赛的“电动车跷跷板”完整工程文件包主要面向电子设计竞赛参赛者、嵌入式系统开发者和对平衡控制感兴趣的学习者。项目基于标准库开发整合电动车动力控制与跷跷板动态平衡两大核心涵盖定时器、片内存储器、时钟控制、模数转换、同步串行通信、控制器局域网总线等常用外设驱动并加入姿态传感器数据处理逻辑能够帮助阅读者快速理解传感器检测、闭环算法以及电机驱动方案。压缩包一共包含二百五十一个文件容量约八点九四兆字节文件构成以源程序文件、头文件、目标文件和汇编启动文件为主体同时附带工程配置文件、链接映射表、调试信息和批量清理脚本目录层次清晰既支持直接导入集成开发环境编译运行也适合按模块逐段阅读。现有七十人学习下载对备战同类校赛或入门单片机平衡控制类开发具有不错的参考价值。1. 电动车跷跷板的题眼不在车速在“角度-位置”的闭环“电动车跷跷板”这个赛题表面看是小车竞速实际考的是位置伺服控制。常见玩法是小车从一侧坡道驶上跷跷板通过前后移动让板面在水平位置停住然后原地保持或返回起点裁判看的不是谁先到而是板能不能稳定在水平状态。换句话说速度只是执行手段“让板面倾角归零”才是最终被控量。这个题目在电赛校赛里出镜率很高适合要交方案的学生也适合刚接触闭环控制的嵌入式工程师。下面从力矩关系讲起把传感器选型、双闭环 PID、状态机、联调排错和验收技巧一次性说清照着这套做法能把车调稳。2. 电动车跷跷板的物理模型力矩、倾角与传感器分工2.1 力矩平衡小车位置决定板的倾角拿到题目先不要碰代码把跷跷板的受力关系压成一条等式。设小车质量为 m车轮与板面接触点相对支点的水平投影距离为 x板面与水平面的夹角为 θ忽略轴摩擦和板的自重不对称小车对支点产生的力矩是τ m·g·x·cosθ只要小车不正好停在支点正上方合力矩不为零板就会向车所在的一侧倾斜直到车轮被限位结构挡住或板端落到地面。要让 θ 收敛到 0静止时唯一条件是 x 0即小车重心落在支点正上方。于是控制目标变成两个物理量用编码器积分得到 x用倾角传感器得到 θ让 x 跟随一个动态变化的参考值最终使 θ 回到水平。问题比听起来复杂的地方在于板和车互相耦合。车往前挪一点板跟着倾斜小车就处在坡道上重力沿坡面的分量 m·g·sinθ 反过来推动小车θ 和 x 构成一个串联回路。再加上小车从起点上板时板的一端压在地面上车要先爬一个变角度坡道轮子很容易打滑。所以完整的方案不能只有“停在中心”这一条逻辑必须有上板、寻点、保持、返回多个阶段每个阶段的控制目标不一样。2.2 倾角传感器MPU6050 的读取路径与互补滤波倾角测量用 MPU6050 这类六轴传感器常见安装位置是固定在车体 PCB 中央而不是跷跷板下方。静止或匀速时车体的俯仰角等于板面倾角所以只要把传感器装正读出的角度直接就是控制反馈量。角度可以用加速度计静止估计也可以用陀螺仪积分。加速度计低速稳定但噪声大陀螺仪动态响应快但会漂移常规做法是两者做互补滤波。读取加速度计算角度的最小代码如下// MPU6050 加速度计输出寄存器起始地址 0x3B6 字节按顺序为 X/Y/Z void read_acc_angle(float *angle_deg) { uint8_t data[6]; i2c_read(0x68, 0x3B, data, 6); // 0x68 为 AD0 接地时的地址 int16_t ax (data[0] 8) | data[1]; int16_t ay (data[2] 8) | data[3]; int16_t az (data[4] 8) | data[5]; // 量程 ±2g 时比例因子为 16384 LSB/g float acc_y (float)ay / 16384.0f; float acc_z (float)az / 16384.0f; *angle_deg atan2f(acc_y, acc_z) * 57.29578f; // 弧度转角度 }这里用 atan2f 而不是 asin 或 acos是因为 atan2f 在 ±90° 范围内的线性度明显更好并且不会在角度接近 90° 时出现导数爆炸。加速度计解法在静止时可信但车轮经过板面接缝或减速时会有明显跳动单纯用它做角度环会抖。实际使用时常加一层互补滤波// 陀螺仪 Z 轴或 Y 轴角速度根据安装方向选取这里以 Y 轴为例 angle 0.98f * (angle gyro_y * dt) 0.02f * acc_angle;0.98 和 0.02 是一组常用权值。权值越大越信任陀螺仪积分动态响应好但漂移明显车架震动大时把 0.98 降到 0.95让加速度计的修正更强。校赛这种低速场景权值在这个范围内不会差太多关键是每次上电都要重新做零点校准。2.3 位置与速度反馈编码器和超声波的定位分工角度环解决了“板偏了多少”的问题但还要知道“车在哪”。霍尔编码器是首选常见的是带减速箱的 TT 马达自带 AB 相霍尔输出。AB 两路脉冲相位差 90°通过判断 A 相跳变时 B 相的电平确定方向。编码器计数在定时器中断里累加volatile int32_t encoder_count 0; // 外部中断或定时器计数A 相上升沿触发 void encoder_isr_a(void) { if (read_gpio(B_PIN)) { encoder_count; } else { encoder_count--; } } // 编码器计数值换算成毫米位置 float read_pos_mm(void) { return (float)encoder_count * WHEEL_MM / (PPR * 4.0f); } // 10ms 采样一次得到毫米每秒速度 float read_speed_mm_s(void) { static int32_t last_count 0; float speed (float)(encoder_count - last_count) * WHEEL_MM * 100.0f / (PPR * 4.0f); last_count encoder_count; return speed; }注释里要求的 PPR 是电机编码器线数乘减速比再乘 4 是因为 AB 相四倍频。这个换算不要想当然最好实测推车走一段已知距离用串口打印编码器累计值反推每毫米对应的脉冲数比查手册可靠得多。编码器积分位置在轮子打滑时会失真所以小车还要加一个绝对位置参考。HC-SR04 超声波测距是低成本方案从车头向跷跷板远端挡板发射距离变化能反映车在板上的大致范围。超声波的刷新率只有 20 到 50 Hz不进控制环只用于状态机确认是否到达起点或终点。2.4 驱动方案比对与选型结论执行方式典型配置优点缺点直流减速电机编码器TT 马达、370 电机便宜、可测速、调速线性度够用齿轮回差大低速有死区带磁编码器的 N20 电机N20 减速电机体积小、精度高、便于布置扭矩偏小上坡吃力步进电机42 步进驱动器位置准确不丢步重量大、耗电不适合小车舵机拉线机构MG996R 等力矩足不受轮地摩擦影响行程有限结构复杂校赛场地通常只有两三米用 TT 马达加霍尔编码器足够。驱动芯片选 DRV8833 或 TB6612供电 7.4V 锂电池组PWM 频率设在 10 kHz 到 20 kHz既能降低电机电流尖啸又能让电流环更平滑。电池电压影响明显满电和快没电时同样 PWM 下的车速可能差 30%参数整定最好统一在满电状态下做。3. 电动车跷跷板的双闭环控制状态机与 PID 调参实现3.1 为什么角度环、位置环、速度环要串起来开环让电机以固定 PWM 转小车位置无法精确控制直接让角度误差映射成 PWM在平地上又会让小车以为已经平衡而不动作。常见做法是把控制拆成三级串联角度环在最外层输出的是目标位置偏移位置环接收这个偏移输出目标速度速度环执行速度输出 PWM。这样安排的核心原因是执行器特性。电机到位置本身是积分环节位置到角度又是一层因果最后系统带两个积分器单环比例控制也能勉强稳住但对齿轮回差、轮胎摩擦这类非线性很敏感会出现缓慢来回爬的低频极限环。把速度环放最内层等于先把电机和编码器包装成一个线性度较好的受控对象外环面对的模型简单很多。同时角度环的输出可以直接幅值限制间接限制车速避免上板时冲过头。整个信号链可以写成倾角给定 0 → 角度环 PID → 目标位置偏移 → 位置环 PID → 目标速度 → 速度环 PI → PWM → 直流电机。控制周期固定 1 kHz三个环共用同一个中断节拍。3.2 PID 的采样代码与微分先行处理先准备一个通用的 PID 结构体三个环复用但参数和限幅各自独立typedef struct { float kp, ki, kd; float integral; float last_y; // 上一次测量值用于微分先行 float out_max; // 输出限幅 float int_max; // 积分限幅 } PID; float pid_update(PID *pid, float err, float y, float dt) { pid-integral err * dt; if (pid-integral pid-int_max) pid-integral pid-int_max; if (pid-integral -pid-int_max) pid-integral -pid-int_max; // 微分项只对测量值求导不对误差求导 float d -(y - pid-last_y) / dt; pid-last_y y; float out pid-kp * err pid-ki * pid-integral pid-kd * d; if (out pid-out_max) out pid-out_max; if (out -pid-out_max) out -pid-out_max; return out; }这里特意把微分项写成对测量值求导而不是对误差求导。因为角度环的给定始终是 0速度快时误差会突然跳变普通微分会在阶跃瞬间输出巨大冲量板会被打得猛抖一下对测量值微分相当于只在被控量变化时起作用响应平稳得多。加速度计的角度噪声虽然经过互补滤波但仍有约 0.1° 量级的抖动所以角度环的 Kd 一开始从 0 加起不要急着给大。控制循环的骨架如下void control_loop(void) { float angle read_filtered_angle(); // 滤波后的板面倾角 float pos read_pos_mm(); // 相对起点的位置 float speed read_speed_mm_s(); float err_angle 0.0f - angle; float offset pid_update(angle_pid, err_angle, angle, DT); // 目标位置偏移 offset clamp(offset, -80.0f, 80.0f); // 离中心不超过 80mm float pos_target CENTER_MM offset; // CENTER_MM 为起点到支点的距离 float err_pos pos_target - pos; float v_ref pid_update(pos_pid, err_pos, pos, DT); v_ref clamp(v_ref, -250.0f, 250.0f); // 速度限幅单位 mm/s float pwm pid_update(speed_pid, v_ref - speed, speed, DT); set_motor_pwm(pwm); }注意 CENTER_MM 是起点到支点的水平距离不是板长的一半。这个值要在现场量好编码器标定后换算成毫米。整段程序里角度环的输出是位置偏移位置环的输出是速度速度环的输出才是 PWM三层职责不能混。符号问题必须现场验证用手给车一个朝向支点的位移观察 PWM 是否向反方向收紧方向不对就把角度环的误差取反或者交换编码器 AB 相接法。3.3 状态机划分与切换条件车不可能一直按平衡逻辑运行。开始上板时车在平地上角度传感器读到 0如果直接启用角度环位置误差会驱动车全速冲上跷跷板容易打滑和过冲。所以把整个流程拆成状态机typedef enum { STATE_IDLE, STATE_CLIMB, STATE_SEEK, STATE_HOLD, STATE_RETURN, STATE_STOP } CarState;状态进入条件退出条件IDLE上电复位收到启动指令CLIMB启动指令位移超过上板距离 D1且板倾角绝对值大于 2°SEEKCLIMB 退出角度误差绝对值小于 2° 且持续 500msHOLDSEEK 退出保持计时结束或收到返回指令RETURNHOLD 结束位移回到起点 ±50mmSTOPRETURN 退出断电上板距离 D1 不是板长而是从起点到板端支点的水平距离。车走到这个位置后轮子开始压上斜板传感器角度明显变化再切换到 SEEK。SEEK 阶段才启用完整的双闭环角度给定为 0让车自己找那个能保持水平的停车点。HOLD 阶段不能简单把 PWM 置 0否则编码器在低速下的死区会让车缓慢滑行一般把角度环输出限幅再压缩一倍让车以极低的速度微调。3.4 速度斜坡限幅与电机死区补偿跷跷板是柔性机械结构车突然加速或刹车板会弹动反馈又叠加到角度上形成振荡。给速度给定加一阶斜坡限幅是常见做法float ramp_limit(float target, float current, float max_delta) { float diff target - current; if (diff max_delta) diff max_delta; if (diff -max_delta) diff -max_delta; return current diff; }MAX_DELTA 取 300 到 500 mm/s² 比较合适车加速柔和又不会太慢。电机还有启动死区PWM 小到一定值后根本转不动直接在输出函数里跳过if (pwm 0 pwm DEADZONE) pwm DEADZONE; if (pwm 0 pwm -DEADZONE) pwm -DEADZONE;死区不能设太大否则车在小角度偏差下会像继电器一样来回吸合板会持续抖动。建议死区用实测最小启动 PWM 的一半测法是给一组递增 PWM 记下轮子刚转动的那个值。4. 电动车跷跷板联调排错从供电到串口波形一页纸4.1 分模块上电顺序与串口观测联调最忌直接上 PID。我习惯按“传感器 → 编码器 → 电机开环 → 速度环 → 角度环”这个顺序走前一步没有验证绝不进下一步。先写一个串口打印函数把角度、位置、PWM 三个值用逗号分隔发出来char buf[64]; sprintf(buf, %d,%d,%d\r\n, (int)(angle * 10), (int)pos, (int)pwm); uart_send_string(buf);电脑端用 Python 接收方便后续复制到电子表格或绘图import serial ser serial.Serial(/dev/ttyUSB0, 115200, timeout1) while True: line ser.readline().decode(errorsignore).strip() if not line: continue parts line.split(,) if len(parts) 3: angle float(parts[0]) / 10.0 pos float(parts[1]) pwm float(parts[2]) print(f{angle:.1f}\t{pos:.1f}\t{pwm:.0f})Windows 用户把设备名改成 COMx 即可。这样做的意义是板面的每一次抖动、电流的每一次变化都能在波形里看到而不是凭手感调参。先把传感器原始值打印出来确认角度随板面倾斜方向正确再用手推车确认编码器计数随前进方向增加最后固定 30% 占空比观察速度是否稳定。一步步排查后面调 PID 才会有的放矢。4.2 角度零点校准必须在水平地面做MPU6050 的零点受安装误差和温漂影响每次上电都要校准。校准程序在车静止且放在水平地面时执行float zero_offset 0.0f; void calibrate_zero(void) { float sum 0.0f; delay(2000); // 等待传感器稳定 for (int i 0; i 1000; i) { float a; read_acc_angle(a); sum a; delay(2); } zero_offset sum / 1000.0f; }之后所有读取都减去 zero_offset。这条看起来简单却常有人踩坑把车放在已经倾斜的板上校准等于把板当前的倾角当成了水平基准角度环会固执地把车往错误方向推。校准要在地面完成而且要保证小车四个轮子都在同一水平面。4.3 PID 参数整定顺序与初始参考值参数整定顺序比参数本身更重要。先整定内环速度环再整外环角度环不能反过来。速度环的 Kp 从 0.3 开始给一个速度阶跃观察转速响应Kp 增大到速度出现明显来回振荡然后退回一半再加入 Ki 消除静差。速度环整完角度环的 Kp 从 5 开始加用手轻轻压板的一端观察车轮是否朝板压下的反侧移动。Kp 太小车反应迟钝Kp 太大板会出现 3 Hz 以上的高频抖动。下面是一组可用的起点参数具体值以实际车重和机械为准环路KpKiKd限幅说明速度环0.50.050输出限幅 PWM 700位置环8.00.200.10输出限幅 250 mm/s角度环8.00.200.10输出限幅 80 mm角度环的积分不能给大否则车会把某一次的偏角误差累积成长期位置偏移表现为车反复在中心区域慢速爬。位置环输出限幅其实就是速度上限也是斜坡限幅的底座先设小一点保证不冲再逐步放开。4.4 常见故障排查表现象可能原因排查方法PWM 已饱和但车不动电机驱动方向反、编码器方向反、死区过大开环给固定 PWM看轮子与编码器计数方向是否一致板高频抖动角度环 Kp 过大、加速度计噪声串入控制降 Kp检查互补滤波及微分先行车在中心来回爬、找不到水平点齿轮回差、积分过弱或积分限幅太小提高位置环 Ki检查角度环积分是否被限幅截断上板瞬间车跳或突然反向状态机切换条件过于敏感角度阈值增大到 3°切换前加 50ms 去抖电池掉电后参数完全失效同一 PWM 下的速度随电压变化统一满电整定必要时用编码器测速做速度闭环兜底5. 让电动车跷跷板更快稳定的前馈补偿与验收技巧5.1 角速度前馈在板还没偏太多前收油角度环本质是偏差驱动偏差变大才输出修正量。跷跷板本身是柔性结构板面的弹性形变、车架减振都会让反馈信号滞后等到控制量输出时机械状态已经往前走了一截。一个有效的小技巧是把陀螺仪读到的角速度直接加到输出上// gyro_y 单位为 deg/skff 从 0.5 开始试 float ff kff * gyro_y; pwm_out pid_out ff;角速度前馈的意义是板正在快速回平时即使角度误差还很大前馈已经替 PID 提前收力抑制过冲。kff 的符号通过实验确定如果加上后超调变小符号就是对的如果板更加震颤且回程更快说明符号反了或增益太大。这个技巧对“从 SEEK 到 HOLD 的收敛时间”改善明显比单纯调 Kd 更平滑因为 Kd 会放大噪声而前馈用的是陀螺仪的短时角速度噪声谱不同。另一个实用细节是 HOLD 阶段的保速设置。车进入 HOLD 后把角度环输出限幅收缩到原来的三分之一速度给定保持一个小值比如 8 mm/s这样能解决编码器死区造成的缓慢爬行。同时要在 HOLD 状态不断累计角度绝对值的均值若超过阈值强制退回 SEEK。5.2 用量化指标验收不靠目测调车调到“看起来稳”是不够的实际评分看的是保持时间和偏差范围。建议固定两个指标平衡建立时间 T_settle指从 SEEK 进入那一刻开始到角度误差持续 2 秒以内小于 ±2° 的时间目标小于 3 秒保持漂移指 HOLD 阶段 20 秒内角度偏离不超过 ±3°、位置波动不超过 15mm。采集方法很简单控制循环每次把角度和位置发送到串口保留从启动到停止的完整数据用串口绘图工具或 Python 脚本计算进入阈值区间的时间点。如果 T_settle 总是超过 5 秒优先把角度环 Kp 翻倍同时把速度斜坡限幅的 MAX_DELTA 减半多数情况下比调 Kd 更有效如果保持漂移偏大先检查是否每次上电都做了零点校准再检查角度环积分限幅是否太小。这两条比盲目调高 PID 增益更能解释问题所在。本文还有配套的精品资源点击获取
返回列表