
STM32平衡小车大家应该见到过很多了,作为学生或者DIY爱好者做一辆小型的mini平衡小车还是可以实现的。给大家带来博主老倪制作的迷你的平衡自行车项目,相信我们能从中学到一些新东西。在本文将会介绍平衡自行车的具体制作过程,包括机械、电路和代码。 文末点击阅读原文,可以获取平衡自行车完整的代码。下图是本项目要说的简易mini版平衡小车。 ![]() 6 B! s4 }4 a/ E0 Q' O; b6 k7 V 自行车平衡理论 + Z/ d2 n$ f% ^% ] 模型分析- a8 v9 x7 t" g6 } 1 倒立摆 很显然我们知道自行车在左右方向上不稳定,这是一个很常见的物理模型——倒立摆。2 u9 \8 Q% N. V7 a7 h ![]() 顾名思义,倒立摆的意思就是倒着的摆,比如一个倒着的杆, ![]() 4 I2 r: f, X% o: f 倒立摆的特性:不稳定,只要偏离平衡位置,就会有一个力(重力的分力)使系统更加偏离平衡位置,这样偏差就会越来越大。 一般倒立的杆在前后左右方向都有可能倒下,在二维的平面上不稳定;而自行车仅在左右方向上可能倒下,是一维的倒立摆,这要简单一些。5 ?/ D- f# o. N' ^- c2 ` 以下是几个生活中常见的倒立摆例子:# I0 A7 `7 A- S6 C; K1 { % L2 I6 w3 v3 s3 M" {( y & @. N4 a: W& E; f ![]() 2 自行车的平衡控制 自行车属于倒立摆模型,倒立摆是不稳定的,那么倒立摆应该如何控制才能平衡呢?3 x) ]- v7 Q) f( B u& e; l- U 我们把问题拆分一下:' A" a5 R+ Y1 g. { * 怎样的状态才叫平衡?* _, D$ w; p$ N* |/ i% l * 我们能控制的是什么?! _ a) t1 V- H: B3 D+ z. o. V; S * 如何控制才能稳定平衡?% l/ A) P1 K5 N8 ?- F. E 2.1 怎样的状态才叫平衡 我们要对”平衡”进行数学描述,所谓的平衡其实就是倒立摆的倾角稳定在一个我们想要的值。0 H) E, d4 f5 A) F) x2 a8 E % K! ~( x3 D* a; b& R# \( T n! F 通常我们想要平衡在θ = 0处。* Y( a: ^2 M$ T. l 2.2 我们能控制的是什么5 k7 {( S2 V& ^, [+ E2 e 7 D' E! D7 d; N' f 对于倒立摆模型,通常我们能控制的是底端的力或速度或位置,不同的控制量对应的控制方法不同。 U% i, w. d( v |5 [% O 对于自行车来说,它的控制方式不像通常的倒立摆那样直接控制底部,而是间接地通过转向来控制,当自行车以一个固定的速度前进时,自行车把手以一定角度进行转向(设为α),自行车会做相应半径的圆周运动,产生相应大小的”离心力”。/ H0 R$ A! G$ F# h 在自行车这个费惯性系里看来,只要对把手进行一定角度的转向(α),就会产生一个相应大小的横向力: 3 n* `4 }3 P1 c5 i( T8 b0 v ![]() 这就是我们进行平衡控制时的实际控制量——把手转角α,只要控制它就能控制回复力。 . y, I6 J% Q: Z0 I 2.3 如何控制才能平衡 0 I8 q# f# t8 i 上面我们已经能够通过转向产生回复力,这个回复力可以把倒立摆”掰回”平衡位置,有往回掰的回复力就能稳定平衡了吗? 5 P5 U9 Y9 b5 o. L3 L/ I1 z% P 并不是这样,我们再来回顾一下中学物理: “过阻尼状态的摆会以较慢的速度回到平衡位置;欠阻尼状态的摆会很快回到平衡位置,但会在平衡位置来回摆动;临界阻尼状态的摆会以最快的速度稳定在平衡位置。”. K0 H1 K/ m' U# R" `7 M/ Y ; O; K4 L% Q1 B5 }) h 结合到实际的自行车平衡中就是: >“如果恢复力不够大,就无法矫正,或者矫正速度很慢,这会导致系统不稳定;如果回复力过大,就会导致矫正过度,这也会导致系统不稳定;我们最希望的状态就是回复力刚刚好,刚好使倒立摆快速回到平衡位置,又不至于矫正过度。 ”( V; [' u& V$ r * c2 x, E0 y& d3 s/ W m1 l 这是一个复杂的数学计算过程,回复力大小会在系统运行时不断地计算(本平衡自行车是20ms计算一次),用到的是PID算法,会在后面详细介绍。 % A8 @ }2 A5 d; B 3 自行车平衡需要解决的基本问题 * 获取左右方向倾角θ$ B- b. R; L8 u) F2 A. _# ] * 以合适的算法控制转角α使系统稳定平衡 这将会在下面详细讨论。 G0 {# O$ [8 V/ `: [6 w " T, X/ h0 Z3 A0 b% ?& v8 H 姿态检测$ Y2 h3 _* l' ]9 J1 _ 1 检测的是什么6 X7 G2 H* U6 s 检测的是自行车左右倾斜的角度。) r* F$ F8 [9 S4 X7 f 5 w$ m6 V& n2 ?' g( i 2 怎么检测; \* t; Z7 {0 ]" Q# c 用一个叫gy521的模块,里面用的是mpu6050芯片,带有陀螺仪和加速度传感器。 7 W4 Q: K5 ^4 Z5 n gy521的具体使用会在第三篇-实践篇介绍,这里我们知道通过这个模块我们可以得到自行车各个方向的加速度和角速度。注意哦,我们不能直接得到倾斜角度,我们的到的是各个方向的加速度和角速度,需要进行一些复杂的计算才能得到正确的倾斜角度。 常用的算法有互补平衡滤波、卡尔曼滤波。 PID算法: K( s+ G4 y6 S & [$ r( k) s8 C! A3 S* I 前面已经分析了,我们通过控制把手转角来控制回复力,我们需要实时计算一个合适的回复力使系统稳定平衡。 ![]() 有一个小球在光滑球面上,小球的位置是x,光滑球面顶端在L处,我们可以控制小球水平方向力F,现在要求让小球稳定平衡在x0处。 & q3 G9 o, p3 b$ _ j7 } 先看简单情况x0=L,此时偏差为L-x,5 d8 `' C: V0 Y5 Z 4 F6 [% z6 a+ `. P. h9 \: F 我们给出一个比例项(P) F = kp*(L-x),这样就会有一个回复力,当偏差存在时就会有一个力把小球拉回L处。 这存在的问题是,小球接近L时是会有一定速度的,小球越来越接近L,此时的力仍然是在把小球往L处拉,这会导致小球到达L时(我们想要的位置)速度很大,小球无法立刻停下来,而是会冲过去。/ j8 q: o3 z+ ~3 u( H- J7 L 这样小球就会在L附近来回摆动,这是不稳定的状态,属于欠阻尼状态。 ) M5 f6 W7 N- z 为了解决上述问题需要加一个微分项(D) F = kd*dx/dt = kd*v,所谓”微分”指的是位置x对时间的微分,说白了就是速度。7 W4 f; F* O y' ~! R 意思就是当速度越大,就产生一个反向的力使速度减小,这样就可以防止出现上面小球冲过去的。: f ?' ~6 P0 d, n7 \ 1 k3 q! \, `. X 可以认为这一项具有”预测”功能,预测小球下一时刻的状态从而提前做出反应,预测小球将要到达L处,提前减速。* k% \$ R0 X) t 7 {: T+ }( e' S* x( w9 i$ F' G 也可以认为这一项具有阻尼作用,相当于系统中有一个和速度成比例的阻尼力。/ b1 P2 l5 E$ B& }& ^& Y0 I2 T 这个”阻尼力”调得过小会导致欠阻尼状态,调得过大会导致过阻尼状态。0 Q. N* \8 @- r+ z* |2 p O 积分项此时可以不用,积分项是当平衡位置x0不等于L时使用的,6 O t: M7 ]3 s t5 Q, B) t / Q E$ T, t$ X* X+ h 当平衡位置不是L处,那么当小球静止在平衡位置x0时,由于在坡道上会有一个恒定的横向偏移力,此时比例调节作用为0(Δx=0),微分调节作用也是0(v=0),所以小球在该处无法平衡,会在更远离平衡位置处达到平衡,那么就会有一个长时间存在的偏差。5 t/ u7 \6 N6 ~. \: s0 n4 B6 C# T$ q 积分作用就是检测偏差进行累积,对于上面这个长时间存在的偏差进行积分(累积叠加),使系统在长时间范围可以稳定在要求的平衡位置。 平衡自行车-实践篇 在本文将会介绍平衡自行车的具体制作过程,包括机械、电路和代码。 T9 j4 v, i0 B# g" N4 a : k" w" k+ ~" {8 i3 f l 材料 机械; U R, j7 B' @% H- ~6 ? & W. I0 I! g6 V8 ?, }$ g ![]() # r8 o% x d1 V( F- B' W9 h9 P; l 电路 0 l; ^) o Q# r1 ^9 l0 Q3 m ![]() 2 X1 p- r* c, U8 {+ V. T 动力部分' z/ `9 y7 b( l3 d2 u2 {8 k 传动方式 如图,我用的是皮带或者齿轮传送的方式,因为比较好实现。 6 } R5 [7 e/ r" | ![]() 2 b+ p, y" p8 g 电机选择 这个DIY是不考虑变速情况的,平衡的参数都是按照一个固定速度调的。4 q; [( I' q4 {( b, W, f 所以动力部分的作用就是提供一个恒定的速度,并且这个速度尽可能稳定,尽可能不受外部影响。 " c R8 D1 j. E5 l( W# v% E 电机应选择扭力大一些、转速稳定的减速电机。: t4 L! ?4 T: ^! {0 d" l 2 \" ?4 _3 V' c) L4 J ![]() 电机供电 电机是直接供电还是使用升压模块供电要根据电机特性,有些电机用升压模块可以提高功率,有些大电流电机用升压模块反而可能限制了电流。 我这里用升压模块升到12v给N20电机供电的。 2 o7 X$ ?: d1 C! ? * B2 V$ D8 n. U/ I ![]() : V' ^' O/ R- I: I# I. I& T 另外,电机通过三极管受stm32控制,通过控制占空比也可以限制电机输出的功率。& W% }2 k2 q2 A) P. F 8 ]$ m' i6 K$ { 转向部分 转向部分用一个舵机带动把手转动即可。 ![]() 电路 在GitHub工程里有详细的引脚连接表 # E+ E$ M. P; j$ e; b# o* @' r1 F& H 供电# {$ V# G& z: ` * 用3.3v稳压芯片给整个控制系统供电,包括单片机、GY521模块、蓝牙模块。: v% M% s, Z# c+ M) c * 用5v稳压芯片给舵机供电。 * 用12v升压模块给电机供电。; @ Q- {, a! n* R2 R! m$ m 0 a+ I/ J% u# V O4 l' s2 J8 K 下载 4 I" J. h8 y* C+ _& H9 w& A 我是用串口给stm32下载程序的。 ![]() GY521 ) k" Z1 z& q7 i1 c6 J! Y/ P! K 这个模块通过i2c通信,只需要连接4根线。" W2 h( k+ _' A) _ * 3.3v * GND * PB0 GY521 I2C SCL * PB1 GY521 I2C SDA (用的是IO模拟i2c)# |' q9 @+ F, T5 \0 E 电机# @! ^* F- ?+ J5 j* g3 c 点击用12v升压模块供电,由于不需要反转,用三极管即可直接驱动,电路图如下:" g, G# T: p$ F8 m/ F9 {" r 0 J( _" M* ]$ x# } ![]() 加三极管的目的是为了可以通过调节PWM占空比来限制输出功率,但我的实际情况是100%输出时动力才勉强足够。所以如果你不需要限制电机输出功率,或者通过其他方式限制输出功率,也可以不要三极管,不通过单片机控制。' e, C! c. L) @, A 舵机 舵机是用5v供电的,而单片机是3.3v电平,对于[PWM]控制脚可以通过2个三极管实现同相的电平转换: 6 X0 i, Y2 f0 b ![]() u' Z X. k7 @% Q% x7 a; ~ 蓝牙模块 " c6 f" [* X2 E! W% P( O+ o+ J6 \4 D 下图是我使用的蓝牙串口模块,可以实现串口透传,只需要4根线连接:vcc、gnd、txd、rxd。7 W& f3 k2 @* a- _; ^ t 0 b/ O- D8 Z+ ^ ![]() / Z+ K6 X/ B. x: m2 ] 蓝牙模块是用来调试和遥控的,没有它也能跑。建议还是加上这个模块,在调试PID擦数时会非常方便。; ]/ b# l& z. `# P; j 8 _, Q( x( F$ R 代码结构3 x, @/ T" _' Z, V 9 ]' ^# \3 t7 M( i0 l7 ?( H9 Y M 代码提交在GitHub,点击阅读原文直达。 : y$ }+ L4 w3 }+ K+ a 主要分为3个部分:, v, `* Q! w$ q+ v * 基础的驱动程序,实现电机、舵机、gy521数据读取; * 平衡控制系统,核心是一个20ms定时器,每20ms进行一次数据采集、计算和响应;0 @4 [* Q4 K! `6 T! E * 遥控和调试系统,实现log输出、接收遥控信息。! n& o+ o8 n7 ~2 a0 C3 m) | ![]() . j5 B z2 O" ~- E. ^ 平衡控制- ?1 ]9 R3 k3 ~ # [7 @- a* M. L8 e' b( N main函数会初始化一个定时器20ms中断一次,调用 main/balance.c 里的 balance_tick 函数,平衡算法在 main/balance.c 实现。 6 R3 }+ N. ^ r# q% ?0 n2 p 每20ms到来会执行一次:0 u( H" J" W* E * 读取传感器加速度和角速度信息。/ l+ p: ?. t; s5 S * 互补平衡滤波计算当前姿态。7 U }, S# x6 U! n) V8 f7 a * 用PID算法计算出前轮转角。$ ` p5 h9 A0 Y, [' j9 m. }5 h 遥控和调试 ^ S; @ ^) D& {' ^& M 1 ]' B+ J8 [+ b* t/ Y$ s% w 两部分:状态输出和指令接收。 状态输出 , \% N2 p: y; T) l4 W& f 在main函数的while循环里,利用串口中断构建一个简单的界面显示状态。# A/ e n( D, ?! s- _0 ? , F' e5 s; w6 J: K" r - ?0 c* E" N& _7 v 指令接收7 j \7 K9 y- o; [1 s* h* z( l$ p , v! M* @# v2 M$ f 串口接收到的数据会传给main/control.c,该文件分析串口数据,解释成相应的操作。主要是PID参数调节。2 d* c+ {& x/ b+ [# o6 X. |8 m9 t + j& ?' m. [! |6 l5 w |