简介:本资源是面向嵌入式初学者与机器人爱好者的一站式平衡车开发套件,基于STM32F103 Cortex-M3主控,聚焦自平衡控制原理与工程实现,解决从硬件搭建、传感器融合到闭环算法调试的核心学习难点。压缩包共50.91MB,涵盖原理图PDF、多版本源码(库函数版/DMP版/卡尔曼滤波版/精简入门版)、算法说明文档、CCD/超声波/NRF24L01外设解析资料、开发笔记及芯片Datasheet等关键内容,类型包括电路设计文件、C语言工程代码、技术说明PDF与系统级文档。已有4032人学习下载,资源结构清晰,突出“理论—算法—硬件—调试”全链路闭环:既提供大功率版MiniBalanceV3.5完整硬件设计参考,又通过不同滤波策略源码对比展现姿态解算演进逻辑,辅以开发视频与问题记录笔记,显著降低平衡控制类项目的复现门槛与试错成本。
1. 项目概述:从“资料包”到一辆能站立的平衡车
如果你手头正好有一个名为“平衡小车之家平衡车全套资料STM32F103.rar”的压缩包,或者正想从零开始制作一辆属于自己的两轮自平衡小车,那么这篇内容就是为你准备的。这不仅仅是一个资料包的简单解压说明,而是一个完整的、从硬件到软件、从理论到实践的自平衡车项目深度解析。我们将以这个资料包中常见的STM32F103C8T6(俗称“蓝桥杯”或“最小系统板”)为核心控制器,拆解让两个轮子的小车像“不倒翁”一样站立起来的全部秘密。
自平衡车是嵌入式开发、自动控制理论(尤其是PID算法)和传感器融合的经典入门项目。它麻雀虽小,五脏俱全,涉及电机驱动、姿态感知、闭环控制、电源管理等多个关键模块。通过这个项目,你能实战掌握STM32的PWM输出、定时器编码器模式、中断、串口通信等核心外设,更关键的是,能深刻理解PID控制器“比例-积分-微分”三个参数是如何相互协作,将一个剧烈晃动的系统变得稳定如山的。接下来,我们将抛开空洞的理论,直接进入如何利用手头的资料和一块STM32,让小车真正“站”起来。
2. 核心硬件架构与元器件选型解析
一套完整的平衡车硬件系统,可以看作一个微型的机器人。我们需要感知自身的倾斜角度(姿态),需要驱动轮子产生运动来对抗倾斜,还需要一个“大脑”快速计算并下达指令。下面我们逐一拆解。
2.1 主控芯片:为什么是STM32F103C8T6?
资料包通常围绕STM32F103系列,尤其是C8T6这款型号。这不是偶然,而是由其极高的性价比和足够的性能决定的。STM32F103C8T6基于ARM Cortex-M3内核,主频72MHz,拥有64KB Flash和20KB RAM。对于平衡车项目,它的优势非常明显:
- 性能足够:平衡车的控制周期(即计算一次PID并更新电机输出)通常要求在5ms甚至更短(比如2ms)。72MHz的主频足以在短时间内完成姿态解算(涉及浮点或整数运算)、PID计算以及外设控制。
- 外设齐全:
- 定时器:需要至少2个高级定时器(如TIM1, TIM8)或通用定时器来生成驱动电机的PWM波,并且需要配置为互补输出带死区控制,以驱动常见的H桥电路,保护MOS管。同时,还需要定时器工作在编码器模式,来读取电机转速(用于速度环PID)。
- ADC:用于读取电池电压,实现低压报警或保护。
- USART:用于调试输出数据(角度、角速度、PID输出等),或者连接蓝牙模块(如HC-05)进行无线遥控和参数调试。
- I2C或SPI:用于连接最重要的传感器——陀螺仪和加速度计(通常集成在一个芯片里,如MPU6050,通过I2C通信)。
- 生态与成本:STM32的HAL库或标准库资料丰富,“平衡小车之家”等社区有大量成熟代码参考。C8T6核心板价格低廉,降低了项目门槛。
注意:虽然STM32F103没有硬件浮点单元(FPU),所有浮点运算都由软件模拟,但在优化得当的情况下(比如使用
arm_math库的定点数版本,或将浮点运算转化为整数运算),依然能满足实时性要求。这是初期调参时需要注意的。
2.2 姿态传感器:MPU6050的核心作用与数据融合
平衡车能站立的“眼睛”就是姿态传感器。MPU6050(或性能更好的MPU9250)几乎是标配,它集成了三轴加速度计和三轴陀螺仪。
- 加速度计:测量的是包括重力加速度在内的所有加速度。在静止或匀速运动时,可以通过计算各轴重力加速度的分量来估算车身相对于水平面的倾角。但它对运动加速度(比如小车突然启动)非常敏感,这会导致倾角估算产生巨大误差。
- 陀螺仪:测量的是绕各轴旋转的角速度。对角速度进行积分,就可以得到角度变化。但它存在“零漂”(静止时输出不为零),积分误差会随着时间累积,导致角度估算越来越偏离真实值。
因此,单一传感器无法获得稳定、准确的角度。必须采用传感器融合算法,最经典且高效的就是互补滤波或卡尔曼滤波。资料包里的代码大概率包含了这两种算法之一。
- 互补滤波:原理非常直观。利用加速度计估算出的角度(长期稳定,但短期噪声大)和陀螺仪积分出的角度(短期精确,但长期会漂移)进行加权融合。通常用一句核心代码实现:
angle = 0.98 * (angle + gyro * dt) + 0.02 * accAngle。其中0.98和0.02是滤波系数,dt是采样周期。它的计算量小,在STM32F103上运行绰绰有余,是入门首选。 - 卡尔曼滤波:一种最优估计算法,性能通常优于互补滤波,但数学模型和参数调整更复杂。资料包若提供,通常是已经调好的,理解其输入(加速度、角速度)和输出(融合后的角度、角速度)即可。
2.3 电机与驱动:动力系统的关键搭配
平衡车的“腿”是电机,而“肌肉”是电机驱动模块。
- 电机选型:通常使用直流减速电机。减速箱可以降低转速、增大扭矩,让小车更有力地抵抗倾斜。编码器是必选项,分为光电编码器和霍尔编码器。它安装在电机尾部,输出脉冲信号,用于测量电机实际转速,构成速度闭环。常见的是每圈输出13线(即13脉冲)的霍尔编码器,经过减速箱减速比换算后,得到轮子的转速。
- 驱动电路:最常用的是TB6612FNG或DRV8833这类双H桥驱动芯片。它们比传统的L298N效率高、发热小。STM32的PWM信号输入到驱动芯片的控制引脚,控制电机的转速和方向。这里就涉及到STM32的PWM输出配置,特别是如何产生频率可调、占空比可变的PWM。
- 频率选择:电机驱动PWM频率通常在5kHz到20kHz之间。频率太低,电机会有啸叫声;频率太高,开关损耗会增加。一般选择10kHz-15kHz是一个平衡点。
- 占空比控制:占空比直接对应电机两端的平均电压,从而控制转速。PID控制器的最终输出量,通常会映射到PWM的占空比值。
- 死区时间:当使用高级定时器的互补输出(控制H桥的上下管)时,必须设置死区时间。这是为了防止上下两个MOS管在切换瞬间同时导通,造成电源短路而烧毁芯片。死区时间通常设置在几百纳秒到几微秒。
2.4 电源管理:稳定是一切的前提
整个系统通常由一块7.4V或12V的锂电池供电。电源管理模块需要提供:
- 电机驱动电压:直接或经过稳压后供给电机驱动模块。
- 控制系统电压:需要通过降压模块(如LM2596、MP1584)稳定到5V,给STM32核心板、传感器、编码器等供电。STM32F103的工作电压是3.3V,通常由核心板上的AMS1117-3.3稳压芯片从5V转换而来。
- 电压监测:通过STM32的ADC通道,分压后采集电池电压。当电压低于设定阈值(如7.4V电池降到6.5V)时,应让小车缓慢停止或报警,防止电池过放。
3. 软件控制框架与PID算法深度剖析
硬件是躯体,软件是灵魂。平衡车的控制软件是一个典型的多环、嵌套的闭环反馈控制系统。
3.1 控制回路结构:环环相扣
平衡车的控制通常包含两个主要的PID环,它们以级联的方式工作:
- 内环(速度环):这个环的设定值是期望速度(通常由遥控器给定,直立时一般为0),反馈值是通过电机编码器计算出的实际速度。速度环PID的输出,是外环(角度环)的设定值增量。也就是说,速度环的作用是:如果你想让它加速前进,它就命令车身向前倾斜一点;想减速,就命令车身向后倾斜一点。
- 外环(角度环/直立环):这是最核心、最敏感的环。它的设定值是期望角度(平衡时就是0度,即竖直向上),反馈值是MPU6050融合后得到的车身当前倾角。角度环PID的输出,直接对应了需要给电机施加的扭矩大小(最终体现为PWM占空比)。它的作用是:发现车身前倾了,就命令电机向前转动,把车身“拉”回来;后倾则向后转动。
两个环的输出叠加在一起,共同决定了最终的PWM输出。代码结构上,在一个定时中断服务函数里(例如2ms中断一次),按顺序执行以下操作:
- 读取MPU6050原始数据,进行滤波融合,得到当前角度
angle和角速度gyro。 - 读取编码器值,计算当前速度
velocity。 - 执行速度环PID计算:
speed_out = PID_velocity(0, velocity)(期望速度=0)。 - 将
speed_out叠加到角度环的期望值上:angle_setpoint = speed_out(这里angle_setpoint通常就是0的偏移量)。 - 执行角度环PID计算:
pwm_out = PID_angle(angle_setpoint, angle)。 - 将
pwm_out值加载到定时器的比较寄存器,更新PWM输出。
3.2 PID参数整定:手动“调参”的艺术
PID控制器的性能完全取决于三个参数:比例系数Kp、积分系数Ki、微分系数Kd。调参是平衡车制作中最具“玄学”色彩但也最有规律可循的一步。必须遵循先内环后外环,先比例后微分再积分的原则,且每次只调一个参数。
角度环(直立环)调参:
- 只加
Kp:将Ki和Kd设为0。用手扶住小车,给定一个较小的Kp值(比如20),下载程序。你会发现松手后,小车会向一个方向加速跑出去,根本站不住。这是因为Kp的作用是“纠正偏差”,倾角越大,电机输出越大。但只有Kp,小车就像一个反应迟钝的倒立摆,总是“过冲”,无法稳定。 - 加入
Kd(微分):微分项关注的是角度变化的趋势(即角速度)。它的作用是抑制振荡,相当于给系统增加了“阻尼”。逐渐增大Kd,你会发现小车的振荡幅度变小,反应变“稳”了。当Kp和Kd配合适当时,小车应该能在你手扶的范围内快速摆动并试图保持直立,但可能无法长期静止,会缓慢地朝一个方向移动。这是因为还存在静态误差。 - 谨慎加入
Ki(积分):积分项是为了消除静态误差。但在直立环中,积分项要非常小,甚至为0。因为车身角度传感器本身非常敏感,微小的积分累积就可能导致输出突变,引起系统振荡。通常直立环只用PD控制器就够了。
- 只加
速度环(速度环)调参:
- 首先确保直立环已经能基本稳住小车(你可以一直用手轻微扶着)。
- 只加速度环的
Kp:让期望速度为0。增大Kp,你会发现小车开始抵抗你的推动。你推它,它会朝反方向“顶”你;你拉它,它会朝反方向“退”。这说明速度环开始工作了,它试图将速度维持在0。 - 加入速度环的
Ki:这是速度环的关键。积分项能累积速度的误差。如果小车在平地上因为微小的倾斜而缓慢移动,积分项会逐渐增加输出,改变直立环的设定点,让车身产生一个反方向的倾斜,从而产生一个力来抵消这个移动,最终让小车稳定在一点。调大Ki,小车“定住”的能力越强,但太大也会引起振荡。 - 速度环的
Kd通常不需要,或者加一个很小的值来抑制高频噪声即可。
实操心得:调参时,务必通过蓝牙模块和上位机(如匿名科创地面站、SerialPlot等)实时查看角度、角速度、PID输出等波形。观察阶跃响应(比如突然用手推一下小车),看它是否快速恢复且超调小、振荡少。“看波形调参”比“凭感觉调参”效率高十倍。
3.3 代码中的关键实现细节
定时器配置:
- 系统时钟节拍:使用
SysTick定时器或一个基本定时器产生一个精确的时基(如1ms),用于控制整个程序的运行周期和提供dt(时间微分)值。 - PWM定时器:配置一个高级定时器(如TIM1)的通道1和通道2为PWM输出模式,并启用互补输出和死区插入。关键代码是设置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;和TIM_BDTRInitStructure.TIM_DeadTime = 72;(死区时间,数值根据系统时钟计算)。 - 编码器定时器:配置两个通用定时器(如TIM2, TIM3)为编码器模式。
TIM_EncoderInterfaceConfig(TIMx, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);这样就能自动根据AB相脉冲计数。
- 系统时钟节拍:使用
MPU6050数据读取与融合:
// 示例:读取MPU6050原始数据(I2C通信) MPU_Get_Accelerometer(&acc_x, &acc_y, &acc_z); // 读取加速度 MPU_Get_Gyroscope(&gyro_x, &gyro_y, &gyro_z); // 读取陀螺仪 // 互补滤波融合(简化版) float acc_angle = atan2(acc_y, acc_z) * 57.29578f; // 加速度计计算的角度,转换为度 gyro_angle += gyro_x * dt; // 陀螺仪积分角度,gyro_x需转换为度/秒 // 互补滤波核心公式 float fusion_angle = 0.98f * (gyro_angle) + 0.02f * acc_angle; gyro_angle = fusion_angle; // 更新积分角度,用于下一次计算PID算法实现:建议将PID写成独立的结构体和函数,方便管理多个环。
typedef struct { float target; // 设定值 float actual; // 实际值 float err; // 当前误差 float err_last; // 上次误差 float integral; // 积分项 float Kp, Ki, Kd; // PID参数 float output; // 输出值 float integral_limit; // 积分限幅 float output_limit; // 输出限幅 } PID_TypeDef; float PID_Calc(PID_TypeDef *pid) { pid->err = pid->target - pid->actual; pid->integral += pid->err; // 积分限幅,防止积分饱和 if (pid->integral > pid->integral_limit) pid->integral = pid->integral_limit; if (pid->integral < -pid->integral_limit) pid->integral = -pid->integral_limit; pid->output = pid->Kp * pid->err + pid->Ki * pid->integral + pid->Kd * (pid->err - pid->err_last); pid->err_last = pid->err; // 输出限幅 if (pid->output > pid->output_limit) pid->output = pid->output_limit; if (pid->output < -pid->output_limit) pid->output = -pid->output_limit; return pid->output; }
4. 常见问题排查与实战调试技巧
即使有了完整的资料和代码,第一次组装调试也难免遇到各种问题。以下是一些典型的“坑”及其排查思路。
4.1 小车根本站不起来,直接倒向一边
- 可能原因1:电机输出方向错误。这是最常见的问题。角度环计算出的PWM输出符号,必须与电机实际转动方向匹配。例如,当小车前倾(角度为正),计算出的PWM输出应为正,这个“正”应该对应电机向前转动(将小车拉回)。如果接线相反,小车就会加速倒下。
- 排查:用手扶住小车,轻微前倾,观察轮子是否向前转动。如果不是,交换电机驱动芯片上控制该电机的两个PWM输入线(如AIN1和AIN2),或者直接在代码里对输出值取反。
- 可能原因2:MPU6050安装方向或数据符号错误。代码中定义的“前倾角度为正”必须与MPU6050物理安装的坐标系一致。如果传感器装反了或者坐标轴定义错了,反馈的角度符号就错了。
- 排查:通过串口打印出融合后的角度值
fusion_angle。用手将小车明确地向前倾斜,观察打印值是否增加(变正)。如果不是,检查MPU6050的安装,或者修改代码中加速度计和陀螺仪数据的符号。
- 排查:通过串口打印出融合后的角度值
- 可能原因3:PID参数极不合理。
Kp太小,无力纠正;Kd为负值,反而加剧振荡。- 排查:回归最基础的参数。将速度环PID参数全部设为0。角度环只给一个较小的
Kp(如20)和Kd(如0.5),Ki=0。然后用手扶住测试,根据小车反应慢慢调整。
- 排查:回归最基础的参数。将速度环PID参数全部设为0。角度环只给一个较小的
4.2 小车能站但剧烈振荡(“跳舞”)
- 可能原因1:微分项
Kd不足或比例项Kp过大。Kp过大会导致系统反应过激,Kd不足则无法抑制这种过激带来的振荡。- 排查:逐步减小
Kp,同时适当增大Kd。观察振荡频率和幅度是否降低。使用上位机观察角度波形,目标是让小车受到扰动后,角度波形能快速、平滑地回归零点,超调小。
- 排查:逐步减小
- 可能原因2:控制周期不稳定或过长。PID计算和传感器读取必须在严格固定的时间间隔内完成。如果因为中断嵌套、程序阻塞导致周期时快时慢,系统必然不稳定。
- 排查:在定时中断服务函数入口点翻转一个GPIO引脚,用示波器测量其波形,看中断周期是否稳定为设定的值(如2ms)。确保中断函数内代码执行时间远小于中断周期。
- 可能原因3:机械结构松动。轮子、电机安装不牢固,存在间隙,会导致控制指令和实际响应之间存在延迟和不确定性。
- 排查:用手晃动轮子和车身,检查是否有明显的旷量。紧固所有螺丝,确保电机轴与轮子连接紧密。
4.3 小车能站立但会缓慢漂移
- 可能原因1:速度环的积分项
Ki太小或为零。这是最主要的原因。直立环只能让小车不倒,但无法保证它的位置。速度环的积分作用才是抑制漂移的关键。- 排查:适当增大速度环的
Ki值。注意要非常缓慢地增加,因为Ki过大会引起低频振荡。观察小车在平地上从静止到开始缓慢移动的时间,这个时间越长,说明Ki的“定住”效果越好,但反应也会变慢,需要权衡。
- 排查:适当增大速度环的
- 可能原因2:MPU6050零点漂移。虽然融合算法能抑制大部分漂移,但如果陀螺仪零漂太大,融合后的角度仍会有一个缓慢的偏差,这个偏差会被速度环积分,导致小车朝一个方向走。
- 排查:小车静止放在水平面上,上电后不移动,通过串口观察融合角度值是否长期稳定在0附近。如果有缓慢的线性变化,可以在程序初始化时进行简单的校准:上电后静止2秒,计算这段时间内陀螺仪输出的平均值作为零偏,并在后续读数中减去它。
4.4 电机启动时抖动或异响
- 可能原因1:PWM频率不在电机合适的范围内。频率太低会听到明显的“滋滋”声(音频范围内的振动),频率太高可能导致驱动芯片发热严重或电机响应异常。
- 排查:尝试调整定时器ARR和PSC寄存器,将PWM频率调整到10kHz-15kHz范围内再测试。
- 可能原因2:死区时间设置不当。死区时间过长会减少有效电压输出,导致电机无力;死区时间过短则可能无法完全避免上下管直通的风险。
- 排查:使用示波器观察驱动芯片输入端的PWM波形,看互补的两路信号之间是否有明显的死区间隔。根据驱动芯片数据手册的推荐值进行设置(TB6612通常需要几百纳秒)。
5. 项目进阶与功能扩展思路
当你的平衡车能够稳稳站立并抵抗轻微干扰后,就可以考虑为其增加更多功能,这会让项目更有挑战性和趣味性。
5.1 无线遥控与姿态指令
通过蓝牙(HC-05/06)或2.4G无线模块(NRF24L01)接收遥控指令。可以将指令转化为速度环的目标速度。例如,手机APP或遥控器发送前进指令,就将目标速度设置为一个正值,小车便会自动向前倾斜并前进。关键在于建立一个平滑的目标速度斜坡函数,避免目标速度突变导致小车剧烈晃动甚至摔倒。
// 示例:速度目标斜坡处理 float target_velocity = 0; float remote_command = 0; // 从串口或无线接收的指令 float ramp_step = 0.5f; // 每控制周期增加的速度值 if (remote_command > target_velocity) { target_velocity += ramp_step; if (target_velocity > remote_command) target_velocity = remote_command; } else if (remote_command < target_velocity) { target_velocity -= ramp_step; if (target_velocity < remote_command) target_velocity = remote_command; } // 将平滑后的 target_velocity 赋给速度环PID的设定值5.2 使用HAL库进行重构与优化
很多资料包基于标准外设库(SPL)编写。你可以尝试用STM32CubeMX初始化项目,使用HAL库重新实现。这有助于你理解CubeMX的图形化配置工具,并熟悉HAL库的编程模式。虽然HAL库效率稍低,但可移植性更好。关键点在于用CubeMX正确配置定时器(PWM、编码器模式)、I2C(MPU6050)、USART等,并生成代码框架。
5.3 探索更高级的控制算法
PID虽然经典,但并非万能。当小车需要承载变化的负载、在复杂路面行驶时,单纯的PID可能表现不佳。可以研究:
- 模糊PID:将PID参数
Kp,Ki,Kd根据误差e和误差变化率ec的模糊规则进行在线调整,适应不同工况。 - 串级PID的改进:在速度环和角度环之间加入一个“转向环”,实现差速控制,让小车不仅能走直线,还能灵活转弯。
- 基于模型的控制:建立小车的动力学数学模型(倒立摆模型),设计状态观测器和LQR(线性二次型调节器)控制器,理论上能获得比PID更优的性能。
5.4 增加视觉或循迹功能
为小车加上摄像头(如OpenMV、ESP32-CAM)或红外/灰度传感器,就可以实现颜色追踪、物体跟随或循迹行驶。这需要引入另一个控制维度——转向控制。核心思路是:根据图像或传感器信息计算出偏离路径的误差,将这个误差通过一个单独的转向PID环,输出为左右轮的速度差。同时,整体的前进速度仍由遥控或定速控制。这就构成了一个双输入(速度、转向偏差)、双输出(左轮PWM、右轮PWM)的混合控制系统。
从解压一个“平衡小车之家”的资料包,到亲手调校出一辆反应灵敏、运行稳健的自平衡车,这个过程是对嵌入式系统开发能力的一次全面锻炼。它强迫你去理解硬件时序、传感器特性、控制理论以及软件架构的协同。最宝贵的经验往往来自于调试:当你看着波形图,一点点调整参数,最终看到小车从东倒西歪到稳稳立住的那一刻,那种对“反馈”、“闭环”、“稳定性”的理解,是任何教科书都无法给予的。建议你在基本功能实现后,不要止步,尝试去改动算法、增加功能,甚至重新设计机械结构,每一次挑战都会让你对这套系统的认识更深一层。
本文还有配套的精品资源,点击获取