你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32CubeIDE+MPU6050做的动量轮平衡自行车(一)

[复制链接]
STMCU小助手 发布时间:2023-2-9 17:00

+ ]+ V( r' A' G% C# b! E
, C2 L# d' \9 F( z$ q0 `
N1UI`V7SWCGTLM$D1GQ54.png
( f1 o0 t- G- ^! q) V5 w* {; s0 k8 R2 F* K

! [! f" j3 D2 g( ]3 F0 k背景

/ P$ P% H$ g, O        本文用于记录平衡自行车的制作过程,及制作中遇到的问题;总体方案如下:采用STM32F103C8T6作为主控单元、MPU6050作为位姿采集单元、无刷电机带动动量轮调节小车平衡、1S锂电池配合5V和12V升压模块作为电源、蓝牙模块用于和微信小程序进行无线遥控及PID调试、舵机用于控制行驶方向和支撑小车站立。
8 F$ o5 z# t, x+ b1 l" g( O3 j2 e! y+ l- q
MPU-6050简介' e/ L" N. W2 x: g, v) X$ I7 E
    MPU-6050集成了 3 轴陀螺仪、3 轴加速度计及温度传感器,并且预留一个IIC 接口,可用于连接外部磁力传感器,并利用自带的数字运动处理器(DMP: Digital Motion Processor)硬件加速引擎,通过主 IIC 接口,向应用端输出完整的 9 轴融合演算数据。& s1 |! }& {  A/ @7 @* g% d2 Z

3 Q$ |" Q9 A: a2 y    MPU-6050 对陀螺仪和加速度计分别用了三个16 位的ADC(0~65535),将其测量的模拟量转化为可输出的数字量。且传感器的测量范围都是可以根据需求设定的,陀螺仪可测范围为±250,±500,±1000,±2000°/秒(dps),加速度计可测范围为±2,±4,±8,±16g。+ h4 @+ g2 d1 x; J3 _. R$ r$ d7 H0 g
# r1 n- M, c# _
' A4 ^8 ^% O8 u- ^2 g1 e
Q(D3Y}_OBQFJ0FA501Q1PWI.png $ ?( D3 h2 d  ?/ {
4 N$ ?! R& }/ p. L
MPU-6050姿态获取与处理(惭愧,我也一知半解)% Q9 _1 n* L) l& S/ \8 d+ @0 k$ G5 K
    理论上只需要对3轴陀螺仪的角度进行积分,就可以得到MPU-6050的姿态数据。实际上由于陀螺仪受噪声的影响,只对陀螺仪积分并不能得到完全准确的姿态,所以需要用加速度计进行辅助矫正,常用的方法有三种:互补滤波、卡尔曼滤波、硬件DMP解算四元数。
$ c3 r+ }  D. Q4 W6 z
9 d1 q: J8 p: ^7 |
3M4ZAPN6Y61O]`Q(`F`%P%T.png 0 }$ G4 C8 _: I: B4 b0 }
互补滤波、卡尔曼滤波计算位姿示例
+ s% a9 `; H# L8 x9 J

( \) }  ?) D( L+ C LTC%Z6L{1J6%~U}3VE@]A$P.png
. H( [/ P2 V" C9 u" G9 v8 F0 }
5 ~7 k' M; U' p, o
互补滤波、卡尔曼滤波计算位姿示例0 q& }+ ^: U$ G8 `( H7 g! \! h2 i

9 _# x  X3 a! ?3 S7 L
    除了使用加速度计计算的噪声较大,卡尔曼滤波,一阶互补滤波,二阶互补滤波看着差不多,O(∩_∩)O哈哈~(四元数计算的示例就不演示了,有兴趣的可以自己研究一下。)
7 u8 [0 n) s* O8 c! E1 P4 }% A2 D    1)一阶互补滤波:因为加速度计有高频噪声,陀螺仪有低频噪声,需要互补滤波融合得到较可靠的角度值。* P. c$ e2 E  p2 e& p
    2)卡尔曼滤波:利用线性系统状态方程,通过系统输入输出观测数据,对系统状态进行最优估计的算法。由于观测数据中包括系统中的噪声和干扰的影响,所以最优估计也可看作是滤波过程(来源于百度词条,我也看不懂这说的啥)。! d2 b6 N, j. W6 U/ p
    3)硬件DMP解算四元数:DMP将原始数据直接转换成四元数输出,运用欧拉角转换算法,从而得到Yaw、Roll和Pitch(使用四元数计算位姿,好像会将初始化的位姿设定为零位)。
2 b+ h1 r$ C7 S9 q. S) Y) g! a% s2 ~' A" X) J# t
~3FY21~}NEG2BK(M@DI]NBR.png
$ D8 c! U- s5 `+ Q% w, w. N) `/ [" f# H) K9 q7 Q
+ \+ N0 i3 c0 h- m
一、一阶互补滤波算法

6 b2 e2 w# J5 Y    MPU-6050 的加速度计和陀螺仪各有优缺点,三轴的加速度值没有累积误差,通过简单的计算即可得到倾角,但是它包含的噪声太多(因为待测物运动时会产生加速度,电机运行时振动会产生加速度等),不能直接使用;陀螺仪对外界振动影响小,精度高,通过对角速度积分可以得到倾角,但是会产生累积误差。所以不能单独使用MPU-6050的加速度计或陀螺仪来得到倾角,需要二者进行互补。一阶互补算法的思想就是给加速度和陀螺仪不同的权值,把它们结合到一起进行修正,通过加速度和角速度就可以计算 Pitch 和 Roll 角(单靠 MPU6050 无法准确得到 Yaw 角,需要和地磁传感器结合使用)。
) \, k' ^% @8 X& ?6 N: M3 l( A0 |7 Y; j

! t- k) b- l4 A/ c一阶互补算法如下:

0 t! L  L3 R+ a1 W) k: |
  1. //一阶互补滤波
    " f# q! o! c) ^
  2. float K1 =0.1;         // 对加速度计取值的权重+ S) {7 N* F2 _8 f0 R
  3. float FirstOrder_Pitch;
    ! Y0 h6 i* P( n0 @# @

  4. % m& }. c) n# j7 j8 }* a, w
  5. float FirstOrder(float newAngle, float newRate, float dt)//采集后计算的角度和角加速度
    " O* [% J5 p. O7 M( o! j5 c- W8 E, n
  6. {' K! X9 {# k  k- N% q  }0 {! |$ ?
  7.   FirstOrder_Pitch = K1 * newAngle + (1-K1) * (angle + newRate * dt);( l6 R, Z9 I+ C( G4 b: e
  8.   return FirstOrder_Pitch;
    ! d; M' v* J1 @7 j/ G" Y+ }
  9. }
复制代码

' I. {' U# ~+ I  ~, [8 F0 Z( ]二阶互补算法如下:
5 h& B9 y' x# S3 q
  1. //二阶互补滤波
    2 ~! Z, ]/ O6 y/ {2 ]
  2. float K2 =0.2; // 对加速度计取值的权重3 B5 ~. L9 c% `' f% W7 R
  3. float x1,x2,y1;" k1 r9 G  R" m* y  j  p2 X
  4. float SecondOrder_Pitch;3 D3 U* X5 y$ p9 \

  5. & j9 `7 D$ S6 M& H. ]
  6. float SecondOrder(float newAngle, float newRate, float dt)//采集后计算的角度和角加速度
    , R# e+ C* W' Y% W; c
  7. {
    * r9 y+ B# N" E
  8. x1=(newAngle-SecondOrder_Pitch)*(1-K2)*(1-K2);% ^( o& h) @" R6 m# A/ o; Z
  9. y1=y1+x1*dt;
    ( h4 W4 X3 |* e4 k: h. ~
  10. x2=y1+2*(1-K2)*(newAngle-SecondOrder_Pitch)+newRate;1 G- l0 E% R: A4 W' x. G
  11. SecondOrder_Pitch=SecondOrder_Pitch+ x2*dt;( U8 n* C& X* A
  12. return SecondOrder_Pitch;
    - M! @5 ?3 q7 a" h: F2 m2 c
  13. }
复制代码
) o! S/ @" L) n$ |8 q4 j* [$ ^
二、卡尔曼滤波  [5 U4 `  l- J2 u& n
  1. /* Kalman filter variables */
    ( {+ d& ~* A  X  i  z; G
  2.         float Q_angle = 0.001; // Process noise variance for the accelerometer  {2 E8 I- U2 N$ t; E0 s& s
  3.         float Q_bias = 0.003; // Process noise variance for the gyro bias
    : ^. f' {; h3 d6 k0 ?/ ~
  4.         float R_measure = 0.03; // Measurement noise variance - this is actually the variance of the measurement noise
    / N5 [6 p1 ^6 b' @0 L% ?0 j
  5. " a+ y: G! o2 W+ H; P5 G6 Q3 [
  6.         float angle = 0.0; // The angle calculated by the Kalman filter - part of the 2x1 state vector
    , [# v+ E; D' M/ B
  7.         float bias = 0.0; // The gyro bias calculated by the Kalman filter - part of the 2x1 state vector& y+ s+ l  k/ F4 I( @# ]
  8.         float rate; // Unbiased rate calculated from the rate and the calculated bias - you have to call getAngle to update the rate! d- p2 S# I) W5 t# W: z

  9. - H, B8 Z$ A! a' Z7 O, T
  10.         float P[2][2]={0.0,0.0,0.0,0.0}; // Error covariance matrix - This is a 2x2 matrix
    $ n& w1 r, Q9 c/ u3 u1 F

  11.   ?2 n- m6 q, \% w, ~# n4 }
  12. & y; V# [" l8 O, r6 q8 Z
  13. // The angle should be in degrees and the rate should be in degrees per second and the delta time in seconds# |( |; n0 M! Z5 ^6 b# G9 Y5 k/ M
  14. float Kalman_filter(float newAngle, float newRate, float dt) {4 h; }4 s: {  h: _% j
  15.     // KasBot V2  -  Kalman filter module - http://www.x-firm.com/?page_id=145
    * Z" t( @  A3 a: \
  16.     // Modified by Kristian Lauszus
    0 W. ^3 l6 Q, x4 Y
  17.     // See my blog post for more information: http://blog.tkjelectronics.dk/2012/09/a-practical-approach-to-kalman-filter-and-how-to-implement-it
    5 e3 P, i9 n) h8 a
  18. ! T2 e2 v/ l4 d& H: [
  19.     // Discrete Kalman filter time update equations - Time Update ("Predict")
    " r$ e2 l1 g4 a8 s3 V
  20.     // Update xhat - Project the state ahead
    ( H. f3 Z9 b4 V3 E
  21.     /* Step 1 */' R9 A% @) l1 `* F& L. l
  22.     rate = newRate - bias;1 A! i8 G* W+ I( h
  23.     angle += dt * rate;
    ; i0 E8 \  P1 z

  24.   W3 }/ \7 ]: \! w/ g
  25.     // Update estimation error covariance - Project the error covariance ahead5 ~( k7 B" O+ A+ m( ~8 S
  26.     /* Step 2 */# x6 n! S2 @8 g4 G' k
  27.     P[0][0] += dt * (dt*P[1][1] - P[0][1] - P[1][0] + Q_angle);4 [, b2 d/ L8 a( n
  28.     P[0][1] -= dt * P[1][1];7 M' Z+ a( _9 b
  29.     P[1][0] -= dt * P[1][1];' G! E" G* Y7 ^( U; j: R6 L
  30.     P[1][1] += Q_bias * dt;
    ( W0 @# J; j6 O6 l
  31. 7 S* B, D" o+ n4 ], m$ ?
  32.     // Discrete Kalman filter measurement update equations - Measurement Update ("Correct")
    * L" q- L. i% k/ G
  33.     // Calculate Kalman gain - Compute the Kalman gain
    ) S) m4 _# J3 E
  34.     /* Step 4 */) `$ ?" Q3 [7 H6 l
  35.     float S = P[0][0] + R_measure; // Estimate error
    $ K! B6 p; W3 t/ {: w
  36.     /* Step 5 */
    " ?7 d, y( x, f# O1 H0 G
  37.     float K[2]; // Kalman gain - This is a 2x1 vector
    $ O* u9 {3 u: K' n5 @
  38.     K[0] = P[0][0] / S;
    2 t% K& B6 C! k  @! i
  39.     K[1] = P[1][0] / S;1 Y  Y7 y. r0 ]1 J8 }- d. O! b

  40. ! N' n: w2 m, s" p5 i. n
  41.     // Calculate angle and bias - Update estimate with measurement zk (newAngle)
    7 y& F/ `* X) z/ b* V
  42.     /* Step 3 */
    . i8 E" D) S  C2 E. g
  43.     float y = newAngle - angle; // Angle difference# T- i8 B0 J& G) ~5 w* T  O
  44.     /* Step 6 */
      n9 l6 h8 A7 D9 e' I
  45.     angle += K[0] * y;" z+ O! y. S; ], y
  46.     bias += K[1] * y;
    9 }0 l- P5 g' u% n. i

  47.   P9 |0 S3 c# \( t  y
  48.     // Calculate estimation error covariance - Update the error covariance) ^1 F4 Z/ w6 d
  49.     /* Step 7 */; P8 Q# y' V* w% C4 `, `8 \
  50.     float P00_temp = P[0][0];! |$ i( w3 B) O! V3 J9 H/ L$ }# u8 N
  51.     float P01_temp = P[0][1];
    " K: x$ E1 i4 U8 a8 \

  52. & @7 A1 _) y( I$ H9 [) y% L+ l$ L
  53.     P[0][0] -= K[0] * P00_temp;$ j9 c* \  R+ V3 R( R
  54.     P[0][1] -= K[0] * P01_temp;4 k! B& h0 ?% A
  55.     P[1][0] -= K[1] * P00_temp;
    % y; Y0 g& \: {. f
  56.     P[1][1] -= K[1] * P01_temp;
      C' _8 n/ s# K% B1 A# R
  57. : {/ I+ K  x1 A* S  q9 i
  58.     return angle;' k8 J$ b1 c1 L9 c
  59. };
复制代码
: G  k1 W8 Z8 P; C  U
三、四元数法2 R9 ~! p# k: [
4 c0 k* N/ O" D+ T
     MPU-6050 自带了数字运动处理器,即 DMP,并且InvenSense公司 提供了一个 MPU-6050 的嵌入式运动驱动库,结合 MPU-6050 的 DMP,可以将我们的原始数据直接转换成四元数输出,而得到四元数之后,可以很方便的计算出欧拉角,从而得到 Yaw、Roll 和Pitch。
9 z: m: L7 T1 K8 _# c+ j% q2 u, Y
       使用内置的 DMP,大大简化了代码设计,且 MCU 不用进行姿态解算过程,大大降低了 MCU 的负担,从而有更多的时间去处理其他事件,提高系统实时性。
  |6 `7 k. m9 m1 x, u) X3 [
  1. void Read_DMP(float *Pitch,float *Roll,float *Yaw)
    + {$ j# e4 ~& }3 i  \
  2. {% \; }" ]7 J5 c/ O1 d5 h2 g1 c2 l
  3.         unsigned long sensor_timestamp;" X0 S- y) Y0 B" Q
  4.         unsigned char more;( I9 }' R' b* y: o
  5.         long quat[4];
    : n; H9 n" H4 L8 B  o$ V
  6. ' X- k+ b2 P$ j
  7.         dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors, &more);
    1 N* W6 |: ~% j* ]) l( e
  8.         if (sensors & INV_WXYZ_QUAT )* S# D% e; [, @  c" H% I# f
  9.         {
    : j1 Z# P5 [1 |0 V+ z
  10.                 q0=quat[0] / q30;
    + X3 E& K7 c  ~' L) {* \
  11.                 q1=quat[1] / q30;( i1 ^4 G4 Q( g) ]6 a8 ]
  12.                 q2=quat[2] / q30;+ r9 C7 w( F& W9 E( k! [3 Z
  13.                 q3=quat[3] / q30;4 T+ W% ~, ^, |' P
  14.                 *Pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3;% u+ N2 `% B( u6 R3 k+ S1 @9 ?
  15.                 *Roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll
    " e" F+ S9 f: L& b1 R
  16.                  *Yaw = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3;        //yaw
    4 ~, \/ M+ }# W5 l4 A" N
  17.         }, w- R9 h4 r/ `
  18. / J; Q' Q) f+ {9 b  \( F
  19. }
复制代码

+ g4 o! E/ r/ R$ h8 l( H9 }) o    以下是我测试一阶互补滤波和卡尔曼滤波项目的搭建过程,老鸟请忽略。
6 k" v5 A% Q$ @# E+ |3 b  e7 a7 B4 i  J, v; h( A% h$ O
    固件开发采用STM32CubeIDE开发,对于我这种菜鸡来说还是很友好的。
4 G+ e5 s, A. ]* J. n2 O
# c& k2 N7 V2 X0 j& l  I
    我使用的是STM32CubeIDE1.9.0,打开软件后,依次点击 File-->New-->Stm32 Project,弹出如下界面,输入MCU型号-->选择封装形式-->Next
8 L: F3 n$ d& `3 D3 D1 H8 Q
, o% v( X9 l+ T/ }) P& t$ X
6Z1)SWD0)NGR@]O%J3~_A`G.png . Y* W5 r! x& y* o4 E  Z& v: Q

7 X- b/ [# S7 f: G输入工程名字,点击Next. S  D2 r# C/ m8 k' |, t8 Z

% g7 L9 k- S& S# T3 _5 h/ }
KU9167BQ{6[RN@@Z{Q}@K7B.png   ?; r( a: p. {9 M  ?& j

1 @  b, [6 R& w4 M* |7 ~设置完成后,点击Finish$ }8 c' k" Z1 c. w: E% j
5 d3 A- \! S" @1 [, g
)W%A{MBY6WZ9DC`3B@F1KM8.png 0 u- c- |5 y# F' ~4 q+ ~7 h0 u% i
% c# d7 A5 I# E$ G" c' u
进入MCU配置界面& t3 A) S* ]& c8 H
* G. z0 ~+ e, _  u- A
LG2T6_XWK%N3B66H$BJS(D0.png
: O' ]& g" v* x& I' y
7 k0 _( Y" q  s+ N, z6 D
设置I2C与MPU-6050通讯。! z+ O  \: G' l( }  O
# J' G6 d# }( G: ?9 T, `) O3 B
CO(V62(7YX0CZ4}@DZ)$Q.png
4 |$ Q* I$ @( x- C' Y) m, Q

7 A4 a& ]. C- m& a设置串口与上位机通讯,实时显示MPU-6050位姿。8 ^9 k8 k0 U7 f. k8 \0 W

) v) u7 `: F1 l  Q0 W' @: H
ZJQU$P3UR1R4)729_G][%{S.png
; q# y: @& c6 I$ `! N% I5 _& B
* |- ~$ J& E0 `; X' q  P
设置系统时钟及调试模式。
) e+ E9 `) }% O  b6 b* v& w4 l2 V; m6 Y3 H" J# O: ?6 ~
(~J`34%%TXA1IXKWT(T@WN7.png
/ ^; w3 \. q. s6 e: I% b0 f/ w- V" x  f( p" h5 |+ _+ W
设置时钟源。# S6 |, P" N2 b& o
# k4 P3 f$ g# R0 S4 e: i; J3 G! N
~VCI0F0AO}W_{IQM08)~J44.png
3 r: Y3 i2 c: U* w3 D$ i$ U8 e  @- u* M3 F* ?. ~% G
全部设置好后,芯片引脚的分配情况。
4 i; F9 Y; M- Y, s8 n- M0 c7 ]- _7 L* q
SL6GUUTZ2Q)$R5{_){T9V@T.png
9 b" j  f) U5 l/ r7 @. I  ~/ Q6 }: x
9 n# [  {8 v, l$ f$ S在时钟配置里设置时钟最大频率。/ s. j0 n' U. B$ m5 _

- F; T/ d4 g. Q, X/ I: ?4 m- R
3_0F%FN3W27QQvC$O5S$I.png
4 y+ e4 d( Z, U  ^! k( o6 ?- j# n- P+ K" V6 w
在工程管理——>代码生成器中进行设置
4 ~( c0 E$ T: [! q6 Z/ Q
% H1 S, N6 e& U' i
2ET9LUB057L`M4NNOJ@@HRK.png   R) p) ^8 N5 M* s% a# }/ a# B

) p$ ]: s) n0 T! D$ }2 Y- S( U3 Q点击此处,生成代码
! L5 `& a7 K$ \) J6 [* |& v# }' [
* d6 K6 z% S4 |* {2 O! P: z4 v
NB$AP684`5LM@ZCOK85OHM6.png . J1 O& u; V( J8 o4 _
  ~1 W" {6 `0 E, E! b
在工程文件夹中创建iCode文件。
0 c  D# [' q+ i9 }: @- c; ?2 u0 s. ?3 [  K0 {4 Z' U
~3XUPXUKJ3X311X9L%MRRSX.png
3 Q/ ?, P" D0 i' C8 P' b6 N
& c+ L" m" v; Z9 u将MPU-6050、卡尔曼滤波库文件复制到iCode文件中。
& J# X! j" p  |8 q. w/ y. C  ]" P' X0 u" X. ~8 i) O. \
XFBF%G7M)$$FIFDRGQDM6W1.png
8 h' A$ p9 l2 g7 Q' S% S7 h
8 d2 h  K  I9 [# o- A* ^点击Debug,刚刚添加的驱动文件全部在工程文件中显示出来。
+ r; @( D# o' F2 g, e1 P; k: [; {: i0 p, ?# F: i! d9 D
Y%XW13SG7SJ%JBPZA1JIEAH.png 9 V' C1 [% f; l, w
2 o) ~% @/ w& I$ Y6 n# W( Z2 ]1 Y8 Q) c
按照下列方式设置MPU-6050、卡尔曼滤波驱动文件的编译路径。! Z, i  [) @+ x, j( v/ r+ u& [
% i) A2 b$ j; o1 ]' H5 F; p3 Y' A  E
DJZL)$_$FQL20QWJM(8)A.png # w6 o3 U; `* t. O

! _4 C+ t8 f; [: k: k4 o
WQUT]3V991@5_$%1Q%8XS`S.png 1 Y+ u" V* A0 i- L* g( }# u* y
5 v" [9 d* G) O. A5 L/ g1 Y6 v
然后再main.c文件中添加如下代码,用于重定义printf函数,用于串口输出;获取系统时间,用于滤波。
* ^1 Q# ]0 E8 T- [5 |
  1. /* USER CODE END Header */
    ) K3 o* z2 ]% h- h6 E* B
  2. /* Includes ------------------------------------------------------------------*/" f& V0 [8 y3 K* Z: ?/ H! ?) L
  3. #include "main.h", @5 c1 G( A7 t+ F2 r7 X
  4. #include "i2c.h"3 B/ [3 d0 M$ J( d* A
  5. #include "usart.h"
    * Q) ^. R4 Y+ _/ q- O3 T
  6. #include "gpio.h"
    ; ^2 |+ s: a/ `; H# m% v8 r
  7. . X, E7 b: Q2 {) A
  8. /* Private includes ----------------------------------------------------------*/3 ~2 w# k- P! A/ e! v1 E5 N
  9. /* USER CODE BEGIN Includes */
    8 N3 M! i( d0 r- T
  10. #include "mpu6050.h"
    5 t" f" r7 S) \* x) B' _) i
  11. #include <stdio.h>, Y5 z# I- Y+ b2 t- r& ~% q
  12. /* USER CODE END Includes */% z5 q# v; F$ Q0 }8 s) D( R

  13. 0 V" m3 l! {7 Z/ T  x" ]& f  E
  14. /* Private typedef -----------------------------------------------------------*/  O5 d( {) a: D- t, i& U( Z1 Y
  15. /* USER CODE BEGIN PTD */
    " P- h7 s5 H4 y# ^4 g+ O

  16. 2 b$ f# S3 m( D; l
  17. /* USER CODE END PTD */
    4 f; I; r+ c* G
  18. . S6 ]" c- ?2 t
  19. /* Private define ------------------------------------------------------------*/' K0 Q. Y2 y; i
  20. /* USER CODE BEGIN PD */3 O% Q2 H- {1 ~2 \: E' ]
  21. /* USER CODE END PD */
    6 W* J' e0 H4 O+ ?3 C$ t1 P

  22.   {3 Z* v, O: N) e3 L6 d$ [
  23. /* Private macro -------------------------------------------------------------*/
      P' l; V4 V+ Y% X5 k: K
  24. /* USER CODE BEGIN PM */
    - D' O+ T) B& j* ^
  25. 7 y* y# Z( f+ s7 h# k4 x
  26. /* USER CODE END PM */8 S4 s) d5 w: E3 u% E
  27. 1 m4 G3 \1 C4 g' [9 u: I0 r+ Q5 \4 `
  28. /* Private variables ---------------------------------------------------------*/
    . m$ |& o# D0 J2 t  r; \4 h
  29. ) z0 I! a+ c2 R; q, W7 f( W  Q' s3 C
  30. /* USER CODE BEGIN PV */. E8 V$ |) R! h6 q. o
  31. 2 Q( p. N& l( ~+ A  X4 O, R
  32. //重定义printf函数,用于串口输出
    ) T. I* F* V) \. [$ m) y& A
  33. #ifdef __GNUC__, Q4 P! H9 E( B5 f% H" w8 M- u
  34. #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    + n8 \) O) j. \' S2 A
  35. #else7 c2 E& S4 J2 S' l3 D: ]; G
  36. #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    " o; }4 u: k: u* l; f  m
  37. #endif5 ]" ^  ^, a% [/ u; c  V4 [3 @
  38. . T9 H# {$ B, @
  39. PUTCHAR_PROTOTYPE
    ! X9 Q' f* U- F# D5 `0 F
  40. {
    7 I; V" R. f: [6 T3 b( t
  41.         HAL_UART_Transmit(&huart1, (uint8_t*)&ch,1,HAL_MAX_DELAY);
    ; ^: U: F. d* S! K: H* s3 b
  42.     return ch;/ ^, q' B, K0 U1 g2 N
  43. }
    / S+ ^; C+ B, E& X0 E
  44. /* ---------------------------------------------------------------------------*/% a# s0 _" W  Z, e; _! L

  45. : S9 O" J; t. M$ T& u
  46. //获取系统时间,用于卡尔曼滤波
    # P" @" ^  s- q7 C$ k( y
  47. __STATIC_INLINE uint32_t GXT_SYSTICK_IsActiveCounterFlag(void)
    ) E( e/ ]) i, s. v7 @2 [: |. X7 v7 B
  48. {
    8 O% x! ^3 y9 d3 k/ ?
  49.   return ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == (SysTick_CTRL_COUNTFLAG_Msk));- u; ?  q, U0 P
  50. }, F9 G$ m5 x- F3 F. T
  51. static uint32_t getCurrentMicros(void)
    ; j; Y/ R$ q* f. u
  52. {
    0 S- \1 |# t; o9 @
  53.   /* Ensure COUNTFLAG is reset by reading SysTick control and status register */* m( ?; d% A5 |3 q' J: m' I
  54.   GXT_SYSTICK_IsActiveCounterFlag();
    ( ]+ Y2 V1 M/ H5 z& v+ c1 a9 D
  55.   uint32_t m = HAL_GetTick();5 |" v2 g* ^3 P% Q
  56.   const uint32_t tms = SysTick->LOAD + 1;1 }# I, D1 _% q3 H: C; g
  57.   __IO uint32_t u = tms - SysTick->VAL;
    $ v7 M$ S( U4 |$ q* b1 o
  58.   if (GXT_SYSTICK_IsActiveCounterFlag()) {: r# Q2 n3 `( L# }$ t) d- E
  59.     m = HAL_GetTick();' A0 L/ E: q" {4 V" Z2 x4 X
  60.     u = tms - SysTick->VAL;
    2 d' f) n( ^3 `% H3 {7 E7 j) h
  61.   }
    # k/ m3 x* h+ b
  62.   return (m * 1000 + (u * 1000) / tms);% h5 h1 n" i( K' [; J
  63. }/ Q& w3 _+ p) ^" K( B
  64. //获取系统时间,单位us
    ; U# C3 W# E5 e/ q  O+ c+ ~' e
  65. uint32_t micros(void)
    / b, w' k0 `! e4 G0 n8 U+ i
  66. {
    : V" b% o0 [* W; Q; Z1 e
  67.   return getCurrentMicros();* J2 H5 ]0 y( E4 r' i
  68. }! D. q0 f" ^; R4 w# r
  69. * g' b9 q% B- K5 E
  70. ! {% T7 D& B" P* Z$ \! g
  71. /* USER CODE END PV */
复制代码
1 `( `6 C! T9 ]( s) X4 S; `
使用printf()函数,还需进行如下设置:点击菜单栏 Project-->properties,弹出如下界面
6 o0 {% ^: ]9 q# r9 z+ _! u! ^# x
[I](O`R0}YFTDA{2SX~2%)J.png 0 b/ v9 y, h2 _
* K* H! y1 {4 i3 n& S
在main()函数中添加MPU6050初始化代码和发送位姿数据代码
( I* |/ P% W) |* Z+ ?
  1. int main(void)3 _, r4 o1 B/ M6 y6 @
  2. {( P# X! U) ]8 z  P( Q& K' x
  3.   /* USER CODE BEGIN 1 */3 J: f9 r) t# ~% G. G7 x& [7 `
  4. , f2 N2 I- A1 L+ U7 M6 |) A
  5.   /* USER CODE END 1 */
    : A+ L/ @8 S; u; a2 o' q3 f  i
  6. ) ^0 u' P/ }* R% i( X& o1 X$ u
  7.   /* MCU Configuration--------------------------------------------------------*/
    : D$ n6 d" T/ n2 B1 r
  8. 4 Z- I4 F/ G+ T3 B( N1 ]
  9.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    % Z6 ~! t) N1 O- h# D" T
  10.   HAL_Init();( |9 p7 d9 m( b& X
  11. - B2 t/ N0 a/ S6 D" p
  12.   /* USER CODE BEGIN Init */
    6 L& `# V0 ]* V0 n/ `

  13. / r8 C- l3 x; O5 S
  14.   /* USER CODE END Init */
    4 Z6 K2 g- Q9 R; K
  15. ) x+ O& T( S+ c0 W; }3 h
  16.   /* Configure the system clock */, p0 z( f  \' X& [, L1 M9 K' [
  17.   SystemClock_Config();
    # M1 A1 t" V1 x, S
  18. 9 i5 M. \! x/ h4 j
  19.   /* USER CODE BEGIN SysInit */9 K6 Z/ w! y+ P- R$ f

  20. ( X% O# ?# X) `! {
  21.   /* USER CODE END SysInit */$ C  R# |9 L- ~4 p6 I" Z' M& b
  22. - y; F$ ~; r; Q) D0 i, R
  23.   /* Initialize all configured peripherals */
    7 d! \& n7 S7 {9 p+ ?) v1 L/ ~* L5 R
  24.   MX_GPIO_Init();
    8 b- _8 p$ \+ `/ a% D
  25.   MX_I2C1_Init();8 D/ M1 r5 s4 G9 f! o; Y2 R' {, k
  26.   MX_USART1_UART_Init();
    . m& E+ L/ F# O7 i* U
  27.   /* USER CODE BEGIN 2 */' L0 R& N2 _& E/ M% C1 }
  28. / q& r9 R0 }4 `( G
  29.   while (MPU_Init()){                //如果初始化失败,继续初始化- U! H* K/ R9 c7 t8 f
  30.           printf("MPU_Init_Fail...");9 p6 a8 x+ M1 s
  31.   }
    - C$ y$ P7 I+ B2 Q, c
  32. ! [& E: c' f7 c  x
  33.   /* USER CODE END 2 */
    " w9 ~9 n: R! v4 F  a4 F
  34. ) e, W+ {/ `5 N- ~
  35.   /* Infinite loop */; @" z# g$ O* i& C9 J, u, P
  36.   /* USER CODE BEGIN WHILE */
    0 \+ r7 x7 T! ]" V
  37.   while (1)
    4 w, Y0 i: I% f* p* w, o
  38.   {' v. W* O* [1 Y( e
  39.           GetAngle();2 h( Y2 d& O% F. P
  40.           GetPitch();
    / z+ |( P9 r9 ]3 b% v1 h4 ^  [" ~" @
  41.           HAL_Delay(10);- D' e" w, N% V4 |! R4 |. g0 Y. i
  42.     /* USER CODE END WHILE */
    - s8 U; }& x( d) x" a) B! `* F
  43. 8 _/ i3 L  e6 @
  44.     /* USER CODE BEGIN 3 */" ]- t( z" i& j. }
  45.   }' Z: t( j* j. t" r6 x0 A, d
  46.   /* USER CODE END 3 */
    ' M5 P0 f# b! ]8 x
  47. }
复制代码

& I' L+ C- k3 |9 ?+ M1 k4 f点击,下载程序
9 N. C; y- y+ u- w
3 `- w0 j6 ]0 F$ C# t3 Z2 f' Y
{90_LT8S2ACLQ4(5~ZX943B.png ; n: [" H8 j* G" F8 @

9 g7 Q+ I* w# x程序烧录好后就会通过串口通讯向上位机发送位姿数据(加速度计、卡尔曼、一阶二阶互补滤波计算的位姿),上位机我用的是Arduino编译器来接受并显示数据。设置好波特率后即可显示数据。
" l% W" }' _) L* f+ {5 z( c5 _6 j9 ]9 ?* l% `4 o4 b# B+ f
H5PKUP6Y02TULI6(N)Y_NZ5.png ) o3 a5 K) C6 I' d7 y& x0 D

# ^- d7 N( R  Q2 C+ y4 r 作者:DIY攻城狮
4 u% }# H7 I- {* }/ K2 u) y5 h) D" r" E8 `; o, U
9 k1 ]$ r& [8 z
收藏 评论1 发布时间:2023-2-9 17:00

举报

1个回答
比奇堡成功人士 回答时间:2024-10-23 18:15:57

MPU-6050、卡尔曼滤波库在哪里呀

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版