ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

电赛轮腿式平衡小车:姿态解算与串级PID控制实战

电赛轮腿式平衡小车:姿态解算与串级PID控制实战

简介:这份资源是面向电子设计竞赛参赛者、自动控制与机器人方向学生的轮腿式自平衡小车完整项目包,聚焦动态平衡原理与嵌入式控制实践。压缩包共1311个文件,约49.48MB,以C源码(619个)和头文件(316个)为主体,辅以汇编、链接脚本、工程配置及数学库文件,覆盖从底层驱动到控制算法的完整代码链路。项目围绕轮式推进与腿部支撑相结合的创新结构展开,涉及陀螺仪与加速度计数据采集、传感器融合滤波、PID姿态控制、电机驱动逻辑及运动控制策略,并可能包含硬件原理图、PCB布局、元器件清单与调试测试记录。已有585人学习下载,适合希望深入理解自平衡控制、嵌入式系统开发与传感器应用的读者参考复现,也可作为电赛备赛与课程设计的实战素材。

1. 从一堆“趴窝”的轮腿代码说起:这份电赛平衡小车资源到底能跑通什么

如果你正在准备电赛控制类题目,或者手头有一个轮腿式平衡小车项目卡在“能直立但走不稳、一碰就倒”的阶段,这份《电赛平衡小车轮腿式平衡小车.zip》大概率能帮你省下不少从零搭框架的时间。轮腿式平衡小车和普通两轮平衡车最大的区别在于:它把轮式底盘的滚动效率和腿式结构的越障能力揉在一起,控制上要同时处理直立环、速度环、转向环,还要协调腿部关节的位姿补偿。很多人第一次做这个结构,代码写了几百行,结果小车要么高频抖动,要么缓慢漂移,调参调到怀疑人生。这份资源的核心价值,就是给出一套已经跑通基础平衡的工程框架,包含姿态解算、串级PID、电机驱动和腿部舵机控制这几块,适合已经学过STM32或Arduino、想直接上手轮腿结构验证算法的同学。它不是那种“点开就能用”的成品固件,但骨架清晰,改起来有迹可循。

2. 轮腿平衡的底层逻辑:姿态解算与串级PID怎么搭

2.1 为什么轮腿结构不能照搬两轮平衡车的控制思路

普通两轮平衡车的控制对象是一个倒立摆,重心高、支撑面窄,控制目标就是让轮子追着重心跑。轮腿式结构多了一对可伸缩或可摆动的腿,这就带来两个新问题:第一,腿的位姿会改变整体重心高度和转动惯量,直立环的PID参数不能固定不变;第二,腿在落地瞬间会给车身一个额外的冲击力矩,如果速度环响应太慢,这个冲击会直接反映成车身俯仰角的突变。常见做法是把腿的控制和轮的控制解耦成两个频率层:轮子负责快速平衡,控制周期1ms到2ms;腿负责慢速姿态调整,控制周期5ms到10ms。这样直立环不用频繁适应腿的动,调参难度会下降不少。

我一般会先让腿固定在一个中间位姿,把轮子的直立环和速度环调稳,再逐步加入腿的动态补偿。如果一上来就全开,波形会乱成一团,根本分不清是哪个环在振荡。

2.2 姿态解算:MPU6050原始数据到欧拉角的处理链

资源里用的是MPU6050这类六轴传感器,输出加速度和角速度。直接拿加速度算角度会有运动加速度干扰,直接积分角速度会漂移,所以必须做融合。常见方案是互补滤波或Mahony滤波,资源里给的是互补滤波的简化实现,计算量小,适合跑在STM32F1这类没有FPU的片子上。

// 互补滤波:加速度计低频可信,陀螺仪高频可信 // dt为控制周期,单位秒;alpha为滤波系数,通常取0.96~0.99 float alpha = 0.98f; float angle_acc = atan2f(acc_y, acc_z) * 57.29578f; // 加速度解算角度 angle = alpha * (angle + gyro_x * dt) + (1 - alpha) * angle_acc;

这段代码里,angle是融合后的俯仰角,gyro_x是陀螺仪x轴角速度,dt必须和实际控制周期严格一致。如果dt写死成0.01但实际中断是8ms,角度会缓慢漂移,小车会往一个方向慢慢加速。参数alpha越大,越信任陀螺仪,动态响应好但静态漂移明显;越小越信任加速度计,静态稳但运动时容易被干扰。我一般从0.98开始试,如果小车静止时角度跳动超过0.5度,就降到0.96。

注意:MPU6050的安装方向必须和代码里的轴定义一致,否则角度符号反了,直立环会变成正反馈,一上电就满速冲出去。

2.3 串级PID:直立环、速度环、转向环的嵌套关系

资源里的控制结构是标准串级PID:外环速度环,内环直立环,转向环单独叠加。直立环的输入是目标角度和当前角度的偏差,输出是PWM占空比;速度环的输入是编码器反馈的车速和目标速度(通常为0),输出是目标角度的修正量。这样设计的好处是,当小车被推动时,速度环会通过改变目标角度让小车“追”回原位,而不是硬抗。

// 直立环PD控制:角度偏差 + 角速度阻尼 float angle_error = target_angle - current_angle; float gyro_rate = gyro_x; // 角速度直接作为微分项,避免微分噪声 float balance_out = Kp_angle * angle_error - Kd_angle * gyro_rate; // 速度环PI控制:编码器速度偏差,输出叠加到目标角度 float speed_error = target_speed - current_speed; speed_integral += speed_error * dt; float speed_out = Kp_speed * speed_error + Ki_speed * speed_integral; // 最终目标角度 = 机械中值 + 速度环修正 target_angle = mechanical_zero + speed_out;

这里有个关键点:直立环的微分项直接用陀螺仪角速度,而不是对角度做差分。角度差分噪声大,陀螺仪本身测的就是角速度,信噪比更好。速度环的积分限幅一定要加,否则小车长时间堵转时积分会饱和,松开的瞬间会猛冲。参数上,Kp_angle通常从200左右开始试(对应PWM满量程1000),Kd_angle从0.5到2之间调,Kp_speed和Ki_speed要小一个数量级,先调直立再调速度,顺序不能反。

3. 从压缩包到跑起来:工程导入与参数整定步骤

3.1 工程目录结构与编译环境确认

解压后一般能看到几个标准文件夹:User放主函数和中断,Hardware放电机、编码器、OLED驱动,Control放PID和姿态解算,Library是STM32标准库或HAL库。先看User/main.c里的初始化顺序,常见是:时钟→GPIO→定时器PWM→编码器接口→串口→MPU6050→OLED。如果用的是Keil,打开.uvprojx直接编译;如果是IAR,注意启动文件是否匹配。编译报错最多的是库文件路径不对,在工程设置里把Library目录加进Include Paths就行。

# 如果用的是Makefile工程,先确认交叉编译工具链 arm-none-eabi-gcc --version # 编译 make clean && make -j4 # 烧录,假设用st-flash st-flash write build/firmware.bin 0x8000000

这段命令适用于Makefile工程,-j4是四线程编译加快速度。烧录地址0x8000000是STM32F1的Flash起始地址,F4系列也是这个地址,但如果是F0或G0系列要查手册确认。烧录前最好先擦除全片,避免旧程序残留导致中断向量表错位。

3.2 电机死区与编码器极性检查

上电后先别急着调PID,把小车架起来让轮子悬空。给一个很小的PWM(比如5%占空比),看轮子转不转。如果嗡嗡响但不转,说明死区没补偿。资源里一般有Motor_DeadZone宏,改成实际值就行。编码器极性更关键:用手往前推轮子,串口打印的速度值应该是正数;如果推着往前走但读数是负的,把编码器计数取反,否则速度环会变成正反馈,小车会疯跑。

// 编码器读数处理:根据实际方向调整符号 int16_t encoder_raw = TIM_GetCounter(TIM3); int16_t speed = encoder_raw * ENCODER_DIR; // ENCODER_DIR为1或-1 TIM_SetCounter(TIM3, 0); // 每个控制周期清零,测的是增量

ENCODER_DIR这个宏就是用来翻转方向的,改一个符号比改硬件接线快得多。控制周期清零计数器是M法测速的常规操作,周期越短速度分辨率越低,但延迟小。平衡车一般用2ms到5ms的测速周期。

3.3 直立环参数整定:先P后D,看波形说话

把速度环和转向环先关掉,只开直立环。Kp_angle从0开始加,每次加20,直到小车能勉强站住但还在低频晃动。这时候加Kd_angle,从0.1开始,每次加0.1,直到晃动明显减弱但高频抖动还没出现。如果小车往一个方向加速倒下,说明机械中值不对,调整mechanical_zero直到松手后小车能原地静止几秒。这个过程最好用上位机看波形,没有上位机就看OLED上的角度值,目标角度和实际角度的偏差应该在±2度以内。

提示:调直立环时电池电压要充足,电压下降会导致PWM实际输出变小,参数看起来“变软”,调好的参数换块电池可能又不行了。

3.4 速度环与转向环的叠加顺序

直立环稳了之后,把速度环的Kp_speed从0.1开始加,Ki_speed从0.01开始。速度环的目标是让小车在原地不漂移,所以target_speed设为0。如果小车缓慢往一个方向跑,加大Kp_speed;如果小车前后低频摆动,减小Ki_speed。转向环最简单,遥控器给转向指令时,直接给左右轮差速,Kp_turn从50开始试,能顺畅转弯就行。三个环都开之后,如果小车出现高频抖,优先降直立环的Kd_angle;如果出现低频晃,优先降速度环的Ki_speed。

4. 避坑与排查:轮腿平衡车最容易翻车的五个地方

4.1 上电就满速冲出去

现象:一开电机,小车直接往一个方向全速跑,根本来不及反应。原因:姿态解算的轴方向反了,或者直立环的符号写反,负反馈变成了正反馈。解决:先不接电机,用手倾斜车身,看OLED上的角度变化方向是否和实际倾斜方向一致。如果反了,把angle取反,或者把直立环输出取反。另外检查MPU6050的acc_y和acc_z是否对应代码里的轴。

4.2 小车原地高频抖动,电机发热严重

现象:小车能站住,但一直高频抖,电机烫手。原因:Kd_angle太大,或者陀螺仪数据没滤波,噪声被微分放大。解决:先降Kd_angle,每次降0.2;如果还抖,在陀螺仪读数后加一个低通滤波,截止频率设50Hz左右。另外检查控制周期是否稳定,如果中断里做了OLED刷新或串口打印,周期会抖动,把耗时操作挪到主循环。

4.3 速度环积分饱和导致“松手猛冲”

现象:用手按住小车几秒再松开,小车会猛冲一下才恢复。原因:速度环积分项在堵转时持续累加,饱和后松开瞬间输出很大。解决:给积分项加限幅,比如speed_integral限制在±500以内;同时加一个“堵转检测”,如果PWM很大但编码器速度为0超过200ms,强制清零积分。

4.4 腿部位姿变化导致平衡点漂移

现象:腿伸直时小车能站住,腿弯曲后小车往前倒。原因:腿的位姿改变了重心,机械中值需要动态补偿。解决:在代码里根据腿部舵机角度查表修正mechanical_zero,或者简单点,把腿固定在一个常用位姿,先不做动态补偿。如果比赛要求腿必须动,那就把腿的控制周期放慢,让轮子有时间响应。

4.5 电池电压下降后参数失效

现象:刚充满电能站住,跑十分钟后小车开始晃甚至倒。原因:电机PWM输出和电池电压成正比,电压降了之后同样的PID输出对应的实际力矩变小。解决:在PWM输出前乘以电压补偿系数,用ADC测电池电压,actual_pwm = pid_out * (nominal_voltage / current_voltage)。这个补偿不加,调好的参数只能在满电附近有效。

5. 进阶技巧:用SD卡记录波形做离线调参

调平衡车最痛苦的是“看不到内部状态”,OLED只能显示一两个变量,串口波形又受传输速率限制。我后来养成一个习惯:在控制中断里把关键变量打包成结构体,每10ms写一次SD卡,跑完一圈把卡拔下来用Python画图。这样能同时看角度、角速度、PWM、速度四条曲线,哪个环在振荡一目了然。

// 每10ms调用一次,数据打包写入SD卡缓冲区 typedef struct { float angle; float gyro; int16_t pwm; int16_t speed; } LogFrame; LogFrame frame = {current_angle, gyro_x, balance_out, current_speed}; f_write(&file, &frame, sizeof(frame), &bytes_written);

LogFrame结构体四个变量刚好16字节,对齐后写入效率高。SD卡用SPI模式,初始化时先降速到400kHz,识别成功后再切到高速。注意不要在中断里直接写SD卡,写入耗时可能几毫秒,会打乱控制周期。正确做法是中断里把数据放进环形缓冲区,主循环慢慢写。

# 上位机解析:用numpy读二进制文件并画图 import numpy as np import matplotlib.pyplot as plt data = np.fromfile('log.bin', dtype=np.dtype([ ('angle', '<f4'), ('gyro', '<f4'), ('pwm', '<i2'), ('speed', '<i2') ])) fig, ax = plt.subplots(4, 1, sharex=True) ax[0].plot(data['angle']); ax[0].set_ylabel('Angle') ax[1].plot(data['gyro']); ax[1].set_ylabel('Gyro') ax[2].plot(data['pwm']); ax[2].set_ylabel('PWM') ax[3].plot(data['speed']); ax[3].set_ylabel('Speed') plt.show()

这段Python代码用np.fromfile直接读二进制,dtype要和C结构体严格对应,<f4是小端四字节浮点,<i2是小端两字节整数。画出来的四条曲线如果角度和PWM相位差接近180度,说明直立环在正确工作;如果速度曲线有低频包络,说明速度环积分太强。从那以后我每次调新车都强制走一遍SD卡记录,哪怕只是换个电机,也先跑一圈看波形再改参数,省得靠猜。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进