平衡小车大家见过不少,但你是否想过亲手做一辆迷你平衡自行车?这不仅仅是简单的电机控制,还涉及到姿态感知、PID算法和一整套机电系统设计。本文将带你深入了解一个基于STM32的开源平衡自行车项目,从其背后的物理模型开始,逐步拆解机械结构、电路设计到核心代码实现,相信你能从中学到不少东西。
下图展示了本项目最终实现的简易迷你版平衡自行车模型。

自行车平衡理论
模型分析
1 倒立摆
我们都知道自行车在左右方向上是不稳定的,这其实是一个典型的物理模型——倒立摆。
顾名思义,倒立摆就是倒着放的摆,比如一根竖立的杆。

倒立摆的特性是:不稳定。一旦偏离平衡位置,就会有一个力(重力的分力)使系统更加偏离,导致偏差越来越大。
普通的倒立杆在前后左右都可能倒下,是二维不稳定;而自行车仅在左右方向可能倒下,是一维倒立摆,问题相对简化了。
2 自行车的平衡控制
自行车属于倒立摆模型,既然它不稳定,那我们该如何控制才能让它保持平衡呢?让我们把问题拆开来看:
- 怎样的状态才叫平衡?
- 我们能控制的是什么?
- 如何控制才能稳定平衡?
2.1 怎样的状态才叫平衡
我们需要对“平衡”进行数学描述。所谓平衡,其实就是倒立摆的倾角θ稳定在我们期望的数值上。
通常,我们希望平衡在θ = 0的位置。
2.2 我们能控制的是什么
对于倒立摆,我们通常能控制的是底端的力、速度或位置,不同控制量对应不同的控制方法。
但对于自行车而言,控制方式并非直接作用于底部,而是通过转向间接实现。当自行车以固定速度前进时,车把以一个角度α转向,自行车会做圆周运动,产生相应的“离心力”。
在这个非惯性系中看来,只要车把有转向角α,就会产生一个相应大小的横向回复力F,其大小是α的函数:F = f(α)。

这就是我们进行平衡控制时的实际控制量——车把转角α。控制它就等于控制了回复力。
2.3 如何控制才能平衡
上面我们已经能通过转向产生回复力,但这个力能直接把倒立摆“掰回”平衡位置吗?有了回复力就能稳定了吗?
并非如此。让我们回顾一下中学物理:
“过阻尼状态的摆会以较慢的速度回到平衡位置;欠阻尼状态的摆会很快回到平衡位置,但会在平衡位置来回摆动;临界阻尼状态的摆会以最快的速度稳定在平衡位置。”
对应到实际的自行车平衡中:
“如果回复力不够大,就无法矫正或矫正过慢,导致系统不稳定;如果回复力过大,就会矫正过度,同样导致不稳定。我们最希望的是回复力刚刚好,能让倒立摆快速回到平衡位置,又不至于矫枉过正。”
这是一个复杂的数学计算过程。在本平衡自行车项目中,回复力大小每20毫秒计算一次,核心算法是PID控制,这将在后文详细介绍。
3 自行车平衡需要解决的基本问题
- 获取左右方向倾角θ。
- 以合适的算法控制转角α,使系统稳定平衡。
下文将围绕这两个核心问题展开。
姿态检测
1. 检测的是什么?
检测的是自行车左右倾斜的角度θ。
2. 怎么检测?
使用一个名为GY521的模块,其核心是MPU6050芯片,集成了三轴陀螺仪和三轴加速度传感器。
GY521的具体使用将在实践部分介绍。这里需要明确的是,该模块提供的是各个轴向的加速度和角速度原始数据,并不能直接得到倾斜角度。需要通过算法对原始数据进行融合计算,才能得到准确的姿态角。常用的融合算法有互补滤波、卡尔曼滤波等。
PID算法
前面已经分析过,我们通过控制车把转角来控制回复力,需要实时计算一个“恰到好处”的回复力。为了理解PID,可以想象这样一个模型:

一个小球在光滑球面上,位置为x,球面顶点在L处。我们可以施加水平力F,目标是让小球稳定在x0处。
先看简单情况x0 = L,此时偏差为 e = L - x。
我们给出一个比例项(P):F = Kp * (L - x)。这样,只要存在偏差,就会有一个力将小球拉回L点。
但这样存在问题:当小球接近L点时仍有一定速度,比例力依然将其拉向L点,导致小球到达L点时速度过大,冲过平衡点,从而在L点附近来回振荡。这就是欠阻尼的不稳定状态。
为解决此问题,需要加入微分项(D):F = Kd * dx/dt = Kd * v。所谓“微分”即位置对时间的导数,也就是速度。其含义是:速度越大,就产生一个反向力使其减速,防止小球冲过头。
这一项具有“预测”功能,能根据当前速度预测下一时刻的状态并提前反应;也可视为一个与速度成正比的“阻尼力”。Kd调得过小会导致欠阻尼,过大则导致过阻尼。
积分项(I) 在平衡位置x0 ≠ L时作用显著。当小球静止在x0时,由于坡道存在恒定的横向分力,此时比例项(偏差为0)和微分项(速度为0)均无输出,小球无法在此平衡,会在更远处达到平衡,从而存在稳态误差。
积分项的作用就是对偏差进行累积(积分),针对这种长期存在的偏差进行补偿,使系统在长时间尺度上能精确稳定在设定点。
平衡自行车-实践篇
本文将介绍平衡自行车的具体制作过程,涵盖机械、电路和代码三大部分。
材料清单
机械部分
所需机械零件和基础工具如下表所示:

电路部分
核心电子元器件清单如下:

动力部分
传动方式
如图所示,项目采用了皮带(或齿轮)传动方式,这种方式相对容易实现。

电机选择
本DIY项目不考虑变速,所有平衡参数均基于一个固定车速进行调试。
因此,动力部分的作用是提供一个恒定且稳定的速度,尽可能不受负载变化影响。电机应选择扭力较大、转速稳定的减速电机。

电机供电
电机是直接供电还是使用升压模块,需根据电机特性决定。有些电机使用升压模块可提升功率,而有些大电流电机使用升压模块反而可能限制电流。
本项目中,使用升压模块将电压升至12V为N20电机供电。

另外,电机通过三极管受STM32控制,通过调节PWM占空比也可以限制电机的输出功率。
转向部分
转向部分使用一个舵机直接驱动车把转动即可。

电路设计
在GitHub工程里有详细的引脚连接表,你可以直接参考。这里概述几个关键部分的电路设计思路。
供电设计
- 采用3.3V稳压芯片(如LM1117-3.3)为整个控制系统供电,包括单片机、GY521模块、蓝牙模块。
- 采用5V稳压芯片为舵机供电。
- 采用12V升压模块为电机供电。
程序下载
本项目使用串口给STM32下载程序。

GY521模块连接
该模块通过I2C通信,仅需连接4根线:
- 3.3V
- GND
- PB0 — GY521 I2C SCL
- PB1 — GY521 I2C SDA (使用IO模拟I2C)
电机驱动电路
电机由12V升压模块供电。由于不需要反转,使用三极管即可直接驱动,电路图如下:

加入三极管是为了能通过调节PWM占空比来限制输出功率。但实际情况是,在本项目中100%占空比输出时动力才勉强够用。因此,如果不需要限制电机功率,或通过其他方式限制,也可以不用三极管和单片机控制。
舵机驱动电路
舵机使用5V供电,而单片机是3.3V电平。对于PWM控制脚,可以通过两个三极管实现同相的电平转换电路:

蓝牙模块
下图是使用的蓝牙串口模块,可实现串口透传,仅需连接4根线:VCC、GND、TXD、RXD。

蓝牙模块用于调试和遥控,没有它也能运行。但强烈建议加上,在调试PID参数时会非常方便。你可以在云栈社区的开源实战板块找到更多关于如何使用开源项目进行调试的经验分享。
代码结构
项目代码已开源在GitHub:https://github.com/nicekwell/balance_bike
代码主要分为3个部分:
- 基础驱动层:实现电机、舵机、GY521数据读取等硬件驱动。
- 平衡控制层:核心是一个20ms定时器,每20ms进行一次数据采集、PID算法计算和控制输出。
- 遥控调试层:实现日志输出、接收遥控指令等功能。

平衡控制流程
main函数会初始化一个20ms中断一次的定时器,每次中断调用main/balance.c里的balance_tick函数。平衡算法就在此文件中实现。
每20ms中断到来会顺序执行以下操作:
- 读取传感器(GY521)的加速度和角速度原始数据。
- 使用互补滤波算法融合数据,计算当前车身姿态角(倾角θ)。
- 使用PID算法根据当前倾角θ和角速度,计算出所需的前轮转向角α,并驱动舵机执行。
遥控与调试系统
该系统包含两部分:状态输出和指令接收。
状态输出
在main函数的while循环中,利用串口中断构建了一个简单的命令行界面,用于实时显示车身倾角、PID参数、电机状态等信息。
指令接收
串口接收到的数据会传递给main/control.c文件进行分析,将串口命令解释成相应的操作,主要用于实时调节PID参数。
这个项目完美地展示了如何将控制理论、C语言编程和嵌入式硬件相结合,解决一个实际的工程问题。如果你对嵌入式开发和自动控制感兴趣,不妨动手尝试一下,整个过程会让你对系统设计有更深刻的理解。也欢迎你到云栈社区分享你的制作经验和遇到的问题。