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

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

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

7 `( \3 F2 l3 D# q
4 ?9 ]2 w; x) `# R# _0 w  D, b1 w
N1UI`V7SWCGTLM$D1GQ54.png 4 }- t& p* O" a& M  [& C% C! {
" j; ~% i& S/ G) {: p  J; K

4 E( b2 X% r- k# N4 A6 H背景
+ C% g8 G6 J' v
        本文用于记录平衡自行车的制作过程,及制作中遇到的问题;总体方案如下:采用STM32F103C8T6作为主控单元、MPU6050作为位姿采集单元、无刷电机带动动量轮调节小车平衡、1S锂电池配合5V和12V升压模块作为电源、蓝牙模块用于和微信小程序进行无线遥控及PID调试、舵机用于控制行驶方向和支撑小车站立。
9 p# ~1 }2 r, I8 ^5 d& o0 ?- [# \9 i' x0 Q2 a' Z  E
MPU-6050简介
: X' \0 w2 ]- v4 `( N2 S  r    MPU-6050集成了 3 轴陀螺仪、3 轴加速度计及温度传感器,并且预留一个IIC 接口,可用于连接外部磁力传感器,并利用自带的数字运动处理器(DMP: Digital Motion Processor)硬件加速引擎,通过主 IIC 接口,向应用端输出完整的 9 轴融合演算数据。  d% O2 U" w0 u+ D
4 O0 I2 k: i1 |& a1 m1 r6 q
    MPU-6050 对陀螺仪和加速度计分别用了三个16 位的ADC(0~65535),将其测量的模拟量转化为可输出的数字量。且传感器的测量范围都是可以根据需求设定的,陀螺仪可测范围为±250,±500,±1000,±2000°/秒(dps),加速度计可测范围为±2,±4,±8,±16g。
  M4 a* }) A$ E, |& ]/ m2 @: G# r4 A6 J7 U/ _

: F0 ^, _6 h2 y; Z; r& V" |0 C# } Q(D3Y}_OBQFJ0FA501Q1PWI.png
6 U* }/ r% q4 k. j8 w9 l3 C) ^. E
9 {! q2 Q4 _% h, i; w
MPU-6050姿态获取与处理(惭愧,我也一知半解)
/ w; p2 L1 I+ O9 a4 ?0 T0 s    理论上只需要对3轴陀螺仪的角度进行积分,就可以得到MPU-6050的姿态数据。实际上由于陀螺仪受噪声的影响,只对陀螺仪积分并不能得到完全准确的姿态,所以需要用加速度计进行辅助矫正,常用的方法有三种:互补滤波、卡尔曼滤波、硬件DMP解算四元数。6 S; d0 G- s+ }. h# T9 J
" t& p& j* E' W" S+ a, w
3M4ZAPN6Y61O]`Q(`F`%P%T.png . ~5 b0 j" Y* J5 g
互补滤波、卡尔曼滤波计算位姿示例0 r$ T. I' o# z3 q; k# z  Y8 a# T

( j, H0 D" j7 V9 ? LTC%Z6L{1J6%~U}3VE@]A$P.png   C6 n% n$ }* Y+ B; D8 ^
# \& D! G4 l% Y: V! k7 K! N
互补滤波、卡尔曼滤波计算位姿示例9 d+ d0 M- n" _7 t9 n  ?
) o& v4 f. h" }
    除了使用加速度计计算的噪声较大,卡尔曼滤波,一阶互补滤波,二阶互补滤波看着差不多,O(∩_∩)O哈哈~(四元数计算的示例就不演示了,有兴趣的可以自己研究一下。)3 v$ B( B" V( W! V* \
    1)一阶互补滤波:因为加速度计有高频噪声,陀螺仪有低频噪声,需要互补滤波融合得到较可靠的角度值。9 e- x' P" b& B4 K$ k  T  \$ i; ~
    2)卡尔曼滤波:利用线性系统状态方程,通过系统输入输出观测数据,对系统状态进行最优估计的算法。由于观测数据中包括系统中的噪声和干扰的影响,所以最优估计也可看作是滤波过程(来源于百度词条,我也看不懂这说的啥)。' [( B' n1 z1 e# o7 r/ J. N( T) [& N
    3)硬件DMP解算四元数:DMP将原始数据直接转换成四元数输出,运用欧拉角转换算法,从而得到Yaw、Roll和Pitch(使用四元数计算位姿,好像会将初始化的位姿设定为零位)。
" p5 a* K+ J7 ?+ t
2 M8 S# T0 f4 a' ~; a& @3 x- A7 O
~3FY21~}NEG2BK(M@DI]NBR.png * |' i, J: t" H
: n6 h  p  y7 t  {

# ~; E/ e8 ?% t一、一阶互补滤波算法

+ v) Z6 e' K- Y; n    MPU-6050 的加速度计和陀螺仪各有优缺点,三轴的加速度值没有累积误差,通过简单的计算即可得到倾角,但是它包含的噪声太多(因为待测物运动时会产生加速度,电机运行时振动会产生加速度等),不能直接使用;陀螺仪对外界振动影响小,精度高,通过对角速度积分可以得到倾角,但是会产生累积误差。所以不能单独使用MPU-6050的加速度计或陀螺仪来得到倾角,需要二者进行互补。一阶互补算法的思想就是给加速度和陀螺仪不同的权值,把它们结合到一起进行修正,通过加速度和角速度就可以计算 Pitch 和 Roll 角(单靠 MPU6050 无法准确得到 Yaw 角,需要和地磁传感器结合使用)。/ l, i( I' y; ]1 E; ~, F
' I2 O' h* q) S6 i
8 ~$ u- p4 Z; _- G# V! U/ Z+ i
一阶互补算法如下:
$ |; G- V; ?8 m( x% r' f
  1. //一阶互补滤波
    * V/ q2 j, x$ P
  2. float K1 =0.1;         // 对加速度计取值的权重' \1 ^4 V( ?: }9 Y+ n" T9 {
  3. float FirstOrder_Pitch;" [2 `6 x; M/ l% n
  4. 4 c, p; k% P% i; R, s2 N
  5. float FirstOrder(float newAngle, float newRate, float dt)//采集后计算的角度和角加速度
    . X1 _+ H( G9 z; G
  6. {5 Q/ n8 q+ w7 Q1 i0 c$ b' Z" q
  7.   FirstOrder_Pitch = K1 * newAngle + (1-K1) * (angle + newRate * dt);
    5 `+ F% O  z( w- h+ n
  8.   return FirstOrder_Pitch;
    * k" X+ d' o9 e+ ?: s3 B
  9. }
复制代码

) a: \* Q5 A1 ^) H" i2 b/ b二阶互补算法如下:
) j' V, g4 c) K, r& C
  1. //二阶互补滤波6 `. ?/ ]( x: I( K1 X* J$ r
  2. float K2 =0.2; // 对加速度计取值的权重0 I* W/ \' h9 K# c7 M# E
  3. float x1,x2,y1;% t* q  p; C6 A& C
  4. float SecondOrder_Pitch;
    9 D. H! X# K" e! f3 V6 ?2 G( H1 a

  5. 1 Z3 Q. L2 ~& {6 ^# k) A: Z
  6. float SecondOrder(float newAngle, float newRate, float dt)//采集后计算的角度和角加速度
    8 M2 y/ u9 f5 ]. g% U
  7. {1 {# w& |' j$ {. q( E9 T# Q
  8. x1=(newAngle-SecondOrder_Pitch)*(1-K2)*(1-K2);/ v9 [" H1 x/ u& E% Y  j5 D7 n
  9. y1=y1+x1*dt;) L/ x5 T9 K" Y" a2 V
  10. x2=y1+2*(1-K2)*(newAngle-SecondOrder_Pitch)+newRate;. Q: O5 h# g% R1 H
  11. SecondOrder_Pitch=SecondOrder_Pitch+ x2*dt;
      ]4 t  o, c$ p: u4 _2 u, _
  12. return SecondOrder_Pitch;
    ' R  j5 y5 b# p
  13. }
复制代码
/ m* m. y* O' o7 w7 Q9 G
二、卡尔曼滤波
- Q: F6 Y7 E! N/ g9 u- {4 l5 S
  1. /* Kalman filter variables */
    " X" [! ?0 @( Y1 L
  2.         float Q_angle = 0.001; // Process noise variance for the accelerometer
    8 x( d9 Z: }& K6 B$ x
  3.         float Q_bias = 0.003; // Process noise variance for the gyro bias  A6 i0 ^9 q6 E  E+ m+ e
  4.         float R_measure = 0.03; // Measurement noise variance - this is actually the variance of the measurement noise
    ) D& W2 X$ G# k' N9 E) w

  5. ( u! u) H- S  m  q, W. n
  6.         float angle = 0.0; // The angle calculated by the Kalman filter - part of the 2x1 state vector
    9 b. e: ^) K0 ^7 M! P! t; G4 Y
  7.         float bias = 0.0; // The gyro bias calculated by the Kalman filter - part of the 2x1 state vector
    : B/ ?0 P# A5 S; `% j  ~+ K
  8.         float rate; // Unbiased rate calculated from the rate and the calculated bias - you have to call getAngle to update the rate
    $ j1 O% f! ]  m0 W) z
  9. 5 T* M5 B4 q7 x% i+ ^
  10.         float P[2][2]={0.0,0.0,0.0,0.0}; // Error covariance matrix - This is a 2x2 matrix: ]0 x/ Q, S9 Q4 n
  11. ' n- m# K  m& d# |/ u

  12. + S5 v3 n9 n+ G, ~) @
  13. // The angle should be in degrees and the rate should be in degrees per second and the delta time in seconds
    4 [2 f' U# m6 _) n- p5 n/ l
  14. float Kalman_filter(float newAngle, float newRate, float dt) {
    % e) v* x4 B- ^" I: u0 W% d0 @
  15.     // KasBot V2  -  Kalman filter module - http://www.x-firm.com/?page_id=145% B! a' i) c- F3 p$ e: t. X6 T
  16.     // Modified by Kristian Lauszus
    ! n9 k8 y% F+ q3 T! N. }
  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! a9 A* {7 c9 [+ u9 L
  18. 6 j4 P0 ?& L1 b: k: R0 v
  19.     // Discrete Kalman filter time update equations - Time Update ("Predict")
    * g6 l8 Y( t+ ?" a
  20.     // Update xhat - Project the state ahead
    ' W# R. R* z3 f
  21.     /* Step 1 */+ _$ e8 s, o; m9 \9 o
  22.     rate = newRate - bias;( G% u3 e4 E( y" a* V( ]: C+ p
  23.     angle += dt * rate;
    2 x: S0 k9 x6 [, s0 L/ v5 o: H( m

  24. $ L1 e5 P* |, b* k+ _# I& V
  25.     // Update estimation error covariance - Project the error covariance ahead% T) z  L+ f4 k6 B7 @' C
  26.     /* Step 2 */3 _+ H/ v/ v! Z6 Q5 R8 u0 p
  27.     P[0][0] += dt * (dt*P[1][1] - P[0][1] - P[1][0] + Q_angle);
    # g  R  |* [$ s. ?. G/ I# Y- d
  28.     P[0][1] -= dt * P[1][1];8 }3 u* X) c7 Z
  29.     P[1][0] -= dt * P[1][1];( d4 B" M6 A) X0 c4 p! h, v
  30.     P[1][1] += Q_bias * dt;& B- m) s, Z8 x# H' u% Y

  31. : q0 y8 n* a: h# M! g9 s% g
  32.     // Discrete Kalman filter measurement update equations - Measurement Update ("Correct")
    ! H1 M0 ]# l) K* t- p: d  S
  33.     // Calculate Kalman gain - Compute the Kalman gain$ G8 B  l& X6 ~
  34.     /* Step 4 */' @  L9 [$ G- V, ~# Y7 _
  35.     float S = P[0][0] + R_measure; // Estimate error
    6 l7 V9 d1 T+ ~
  36.     /* Step 5 */! N' ]) K- [' q1 U
  37.     float K[2]; // Kalman gain - This is a 2x1 vector
    0 h, Y( j% i+ A
  38.     K[0] = P[0][0] / S;4 Q& L+ j! H/ r1 ~/ }8 W
  39.     K[1] = P[1][0] / S;% ~: e0 W1 ?6 I/ t" W5 Y  m! |8 b

  40. # V/ y2 [. {% C
  41.     // Calculate angle and bias - Update estimate with measurement zk (newAngle)' Z. n8 L7 ~# r" l2 G: \5 j
  42.     /* Step 3 */+ X! g/ o! S' O7 l( m" s
  43.     float y = newAngle - angle; // Angle difference
    $ s$ V2 f- u* C+ M# }& C5 J
  44.     /* Step 6 */% C8 F4 D" C* w. T- Y9 }
  45.     angle += K[0] * y;" M& P  r0 B  [/ x+ M, x
  46.     bias += K[1] * y;% [6 U0 q/ u* r6 i9 [5 l& G: Q2 k3 F

  47. ( H+ k$ n& ^% N) w: l, O; Q/ f
  48.     // Calculate estimation error covariance - Update the error covariance
    ( i$ T" F& ^7 u; |, V
  49.     /* Step 7 */, ^/ O6 j: h9 F% S) N# `; i
  50.     float P00_temp = P[0][0];
    * u/ x+ _9 r4 u" z2 F
  51.     float P01_temp = P[0][1];
    2 Q0 N& m) Q' O5 q+ o

  52. % `; f) r+ _) a& ]
  53.     P[0][0] -= K[0] * P00_temp;
    ( {+ l7 g, S& A) Z7 A
  54.     P[0][1] -= K[0] * P01_temp;
    : Z' ~  c  J+ q, n7 A; Z: W
  55.     P[1][0] -= K[1] * P00_temp;# i) p; m, c/ w/ C
  56.     P[1][1] -= K[1] * P01_temp;& u: I3 e7 ^5 j- Y

  57. 1 j& g% s: b# [( e
  58.     return angle;
    , c$ Y. o$ Y! i& U2 n" H$ e2 n$ ]
  59. };
复制代码
4 v/ Z- A6 |0 A) [2 |6 b
三、四元数法6 t3 x8 k, F8 X. e3 ~8 Y

0 V0 H% B! C, S, T     MPU-6050 自带了数字运动处理器,即 DMP,并且InvenSense公司 提供了一个 MPU-6050 的嵌入式运动驱动库,结合 MPU-6050 的 DMP,可以将我们的原始数据直接转换成四元数输出,而得到四元数之后,可以很方便的计算出欧拉角,从而得到 Yaw、Roll 和Pitch。
5 g4 v5 j$ I% }8 R0 j* H1 a. q( Y
/ |/ U' r, k2 _( s, e       使用内置的 DMP,大大简化了代码设计,且 MCU 不用进行姿态解算过程,大大降低了 MCU 的负担,从而有更多的时间去处理其他事件,提高系统实时性。
* h3 {$ ]6 V6 U* X3 Y3 L7 [7 K
  1. void Read_DMP(float *Pitch,float *Roll,float *Yaw)
    . x7 M) W& K4 t
  2. {2 }- c0 Y% {/ J/ _  s3 ?
  3.         unsigned long sensor_timestamp;
    6 q: H( `$ Y1 V6 l
  4.         unsigned char more;
    $ I- W2 O/ G+ t) |$ u
  5.         long quat[4];
    8 t% d" t. I0 i* ?4 M) t3 c. s
  6. ; K5 {# U7 A- y: z: f
  7.         dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors, &more);
    + }0 a5 f' C3 ?9 o5 c: U0 A4 k- {0 l
  8.         if (sensors & INV_WXYZ_QUAT )
    1 A6 X+ R( u6 p4 E/ q7 ^$ M/ j, z7 d% F
  9.         {
    1 j: z8 b# O( }% h
  10.                 q0=quat[0] / q30;$ @7 i: m+ _0 E  @# ^; l8 _- l0 a
  11.                 q1=quat[1] / q30;
      x2 S- j& m1 w" Q& V0 D( g/ F4 `
  12.                 q2=quat[2] / q30;# w8 x! i6 o. t" l+ u* A% |
  13.                 q3=quat[3] / q30;
      R# p& B6 d7 |* d- ~: _
  14.                 *Pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3;
    6 g' y: J8 R6 z8 J2 o
  15.                 *Roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll
    . `5 l: t! o& @2 s3 H4 a9 R
  16.                  *Yaw = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3;        //yaw
    7 f+ _( z6 g4 p( N
  17.         }; \/ t# s7 o' ~% @. R' B6 M
  18. ' C/ t' W6 X0 E5 U; K! a6 m0 a! W
  19. }
复制代码
8 Y6 _. D3 r9 k5 f" O8 j
    以下是我测试一阶互补滤波和卡尔曼滤波项目的搭建过程,老鸟请忽略。
$ Z* s' u$ X2 s& p3 g! Z3 [# D8 J* E9 }$ W8 n
    固件开发采用STM32CubeIDE开发,对于我这种菜鸡来说还是很友好的。' B3 B) c) W# g1 G' z

/ G, ^/ H9 l: R6 K9 M; }    我使用的是STM32CubeIDE1.9.0,打开软件后,依次点击 File-->New-->Stm32 Project,弹出如下界面,输入MCU型号-->选择封装形式-->Next
* ]1 M9 a' V8 X9 S$ O$ P( {3 i# k# n4 I0 c
6Z1)SWD0)NGR@]O%J3~_A`G.png
$ J1 c8 g' {' f( I8 _

" y$ O/ h; h( J6 g* w输入工程名字,点击Next/ Y  H% w% ?  K4 f, W
2 d) x) t- i, q, r
KU9167BQ{6[RN@@Z{Q}@K7B.png . l* E' V. Y: J$ ^2 ?) R

8 ^$ [/ \& T: x$ x! [% G! m设置完成后,点击Finish1 z4 Q8 C! b7 r- B

+ I4 e. ~% A* i9 \: y
)W%A{MBY6WZ9DC`3B@F1KM8.png , ]2 e/ X1 Y$ m. ]! r  D
! A6 |# M. U+ L, }# z9 z+ i3 Y
进入MCU配置界面2 z0 _& H7 A; Q% J! K
  C% ]: @! A! R9 h% |& k
LG2T6_XWK%N3B66H$BJS(D0.png 5 I4 v7 t% \$ l6 p- D
2 g9 v, f4 J! r8 |) V2 \
设置I2C与MPU-6050通讯。
3 t; T' x! |" e( j
  R2 D% K6 E8 c9 v) M6 k! g
CO(V62(7YX0CZ4}@DZ)$Q.png
) I+ l# S% X/ t5 h5 r

1 O! P! k7 O* N: g  S, `6 N: I0 N设置串口与上位机通讯,实时显示MPU-6050位姿。
- c& N" w" u' ^4 U6 J, I
( P4 N2 q3 h3 b7 w6 t  [, v. N
ZJQU$P3UR1R4)729_G][%{S.png
9 G) f( Q! U+ b. m

. ]% A$ O9 I* h: j+ h# B/ E5 U: M设置系统时钟及调试模式。, N% \0 q( W6 ?; M0 ?# U

, v' k9 C6 s! n  o
(~J`34%%TXA1IXKWT(T@WN7.png 0 e# i! r' l8 Z7 D9 z! Z
, Y8 \8 f. [* R) B; S6 u9 ?9 o- Z
设置时钟源。
$ i) w2 X0 \) Y
+ q' y. W! D& ?, [4 i
~VCI0F0AO}W_{IQM08)~J44.png
. e2 z1 G/ x% i1 D6 F, }. |+ J8 c9 b' x+ x8 w/ Q& W& F- \
全部设置好后,芯片引脚的分配情况。3 n( ~/ V) ^/ e9 a" O4 G1 B2 A( `% E
4 G7 m# B" N% }6 D
SL6GUUTZ2Q)$R5{_){T9V@T.png * c; D3 t  F! ~( x  v

* m4 ~, ]; u( O在时钟配置里设置时钟最大频率。
3 G7 S" [3 h5 p
9 A5 W3 ^* q1 M+ \+ U' ?, [
3_0F%FN3W27QQvC$O5S$I.png 5 t3 l% O( F- X

# ^+ I3 L) {5 u在工程管理——>代码生成器中进行设置
9 ]7 N% Q) P% |4 \4 m9 K. a  o% x5 L; Y' ^* f. ~4 I4 {9 L+ U5 X* S, E
2ET9LUB057L`M4NNOJ@@HRK.png 5 Q% X1 i% m9 e- T$ {6 p

& x9 c5 _/ G/ }& g点击此处,生成代码
6 i* ~. L. z& }% u# v$ x  p
+ \2 g7 J" ]! g$ P
NB$AP684`5LM@ZCOK85OHM6.png $ T; V, W# ]1 d' E: u( }5 u

: q3 `" V0 _) r" F8 R# G* O. E在工程文件夹中创建iCode文件。8 }% z* e+ v2 e. R, W2 e
5 R4 _  j( C! j) O" u& A5 T
~3XUPXUKJ3X311X9L%MRRSX.png
9 z9 A' A1 P; _
1 O$ ?3 S0 W' g将MPU-6050、卡尔曼滤波库文件复制到iCode文件中。( l$ n! O5 U, g1 [# n
$ S7 x& H( d" U5 g1 T/ g
XFBF%G7M)$$FIFDRGQDM6W1.png , _( b/ `8 O" }5 t
3 l9 p( W# x$ L0 X
点击Debug,刚刚添加的驱动文件全部在工程文件中显示出来。2 t' _) C: y  ^5 B
6 ]" c3 R. n& j  z' O  K5 v. F
Y%XW13SG7SJ%JBPZA1JIEAH.png
1 y+ F+ K8 d5 y& f; O& _
" ]8 x: G: e8 |% r4 y" u8 @按照下列方式设置MPU-6050、卡尔曼滤波驱动文件的编译路径。, u6 @! ?; D4 p0 f# P% R

+ ^2 J; s4 X! y6 i6 C% `
DJZL)$_$FQL20QWJM(8)A.png
+ A; b/ ~/ |; M3 ]* [# M( M# }9 o8 F# p7 s  C, Y5 h7 E
WQUT]3V991@5_$%1Q%8XS`S.png
" w# r5 D& K3 {& B
" \) ^9 M: h4 W+ {" E) z
然后再main.c文件中添加如下代码,用于重定义printf函数,用于串口输出;获取系统时间,用于滤波。
! q0 l+ x# P& W$ D( q/ l
  1. /* USER CODE END Header */
    ; }4 }7 ?. n& c1 G
  2. /* Includes ------------------------------------------------------------------*/
    & Z$ X  F( n2 Z1 E2 A& V
  3. #include "main.h"
    - H) S* Z/ C+ i0 x
  4. #include "i2c.h"& y+ R  q, J5 E
  5. #include "usart.h"8 |, C+ d% Y" o) ]8 O* c$ ^3 a
  6. #include "gpio.h"
    0 _7 T4 s$ ]2 ~7 S/ K
  7. & G) e9 x' `. i' ~
  8. /* Private includes ----------------------------------------------------------*/$ y5 ]" d3 g, \& r" c3 E
  9. /* USER CODE BEGIN Includes *// y: O2 w, Z) _$ A- d9 O
  10. #include "mpu6050.h"# a1 J" o( C1 A4 p. z, T4 i
  11. #include <stdio.h>; H6 m8 m7 b1 ^1 M; g, d
  12. /* USER CODE END Includes */
    - [1 [0 V5 o, D! b1 T/ u! l! U/ q
  13. - f9 m. G0 `; |1 F% r: R: x: b
  14. /* Private typedef -----------------------------------------------------------*/7 s8 [) e, X/ Q- I) T% a6 D( l
  15. /* USER CODE BEGIN PTD */4 `+ t% Y0 w5 r5 o
  16. $ |4 K) x6 Y/ B9 l( k5 a
  17. /* USER CODE END PTD */
    4 X; C: _1 Z+ i! k4 z2 P0 o
  18. 8 D: Q1 X- p$ z4 l" O
  19. /* Private define ------------------------------------------------------------*/
    & r3 G3 y4 O* W( e' K4 M9 D! `
  20. /* USER CODE BEGIN PD */9 ^, }4 }; \3 V# Z2 @1 W7 f
  21. /* USER CODE END PD */
    1 s- c8 U8 Q% V# b7 ], N) H5 e2 G

  22. " r1 s, o: d- J( Z
  23. /* Private macro -------------------------------------------------------------*/: H  k2 `- }! ~4 ^2 m# ^2 l
  24. /* USER CODE BEGIN PM */5 m: s) g! b+ A/ S7 H7 s9 x

  25. ! ]) m4 ]/ P$ L' P
  26. /* USER CODE END PM */
    ( \8 @- p6 y! l+ ^0 ?

  27. 8 g  V8 o( x6 V7 {: C  t7 e
  28. /* Private variables ---------------------------------------------------------*/% }2 X8 L6 P$ m/ D
  29. ' v9 T% Y; P# d& i* m
  30. /* USER CODE BEGIN PV */( [7 w9 X! F* `) ^* V  Z
  31.   x, x3 k+ t1 X6 U) q
  32. //重定义printf函数,用于串口输出
    3 S2 k8 N9 }- b  A( x' J. H5 v
  33. #ifdef __GNUC__
    ( y* `( e* _% E( D0 j( i9 V
  34. #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    # h+ \& O3 y9 U: ]+ U
  35. #else
    , P. }& |! S+ h% }0 Q$ v
  36. #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)/ f- i6 C& R' N7 h! i
  37. #endif
    * O6 h4 h& e" B
  38. ; X/ k; R/ C. C8 Y& G
  39. PUTCHAR_PROTOTYPE
    ! E' y5 C. X) g# t/ @8 S
  40. {
    - a% T; A) v9 X% V1 p; P  [/ _
  41.         HAL_UART_Transmit(&huart1, (uint8_t*)&ch,1,HAL_MAX_DELAY);
    0 I0 l/ i; u1 l/ Q& p. Y# i2 I/ K- v" _
  42.     return ch;& }! M0 K9 n% k  z" Q
  43. }5 y+ R* ~) S9 ^- ~+ C
  44. /* ---------------------------------------------------------------------------*/
    5 ]- q' x' ?0 ?4 @

  45. $ O. E2 @2 u" Z. \
  46. //获取系统时间,用于卡尔曼滤波- t3 X. K9 y+ y8 }: A/ z6 T
  47. __STATIC_INLINE uint32_t GXT_SYSTICK_IsActiveCounterFlag(void)* `" ~; q1 u( B' @
  48. {
    ; z0 M. ~$ L9 J: c3 k0 C
  49.   return ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == (SysTick_CTRL_COUNTFLAG_Msk));
      }6 M2 d+ G* T; y4 Y: k
  50. }4 H7 H+ S2 `) U( J2 f
  51. static uint32_t getCurrentMicros(void)
    / S) s$ D" d  r: W5 L  U: b
  52. {
    2 l" `& M# y& f; D" p
  53.   /* Ensure COUNTFLAG is reset by reading SysTick control and status register */' J1 u: ~! Q& w9 g3 o
  54.   GXT_SYSTICK_IsActiveCounterFlag();+ k: _9 c# L: {
  55.   uint32_t m = HAL_GetTick();. P$ G, l, |# F9 W) x/ ]
  56.   const uint32_t tms = SysTick->LOAD + 1;
    1 i3 Y0 R3 T4 Y+ V& C$ I: o
  57.   __IO uint32_t u = tms - SysTick->VAL;
    ; _7 {% S- \: P* G8 |2 h* c
  58.   if (GXT_SYSTICK_IsActiveCounterFlag()) {
    8 o) J4 {/ T/ s- A# p
  59.     m = HAL_GetTick();
    " y9 H) v# l5 R: B
  60.     u = tms - SysTick->VAL;
    ! `9 `6 N! B0 B. _% _% J! x
  61.   }
    / j! L: {. D# ^1 f. Z
  62.   return (m * 1000 + (u * 1000) / tms);
    ' ^/ P( F* R% F$ I: O6 w
  63. }" ?: M5 b  \& {7 ~3 R9 l9 _& \7 M
  64. //获取系统时间,单位us" J+ F$ ?+ `8 K4 P. T
  65. uint32_t micros(void)* ?- J4 o  z' ?  t8 O- f! u+ x
  66. {$ R/ Q+ J0 w  a" z; W+ [$ F
  67.   return getCurrentMicros();2 m$ S4 q" ~# ]) T' h' z
  68. }5 l( W, U/ t' d
  69. ! y% l0 v% Y/ K6 x/ A" N) `, L' k
  70. " M' |+ j' K/ i- R  K! z+ L
  71. /* USER CODE END PV */
复制代码

0 |2 X6 v+ O2 z  t# ~8 Z( T使用printf()函数,还需进行如下设置:点击菜单栏 Project-->properties,弹出如下界面5 `7 e# T$ }8 ]- J8 D, k% n6 n
+ O" I" j$ Z9 F4 H! |
[I](O`R0}YFTDA{2SX~2%)J.png 3 Q+ d8 F: z8 U. p! y
# A2 x4 u3 b- x3 h2 Z# V+ R. A
在main()函数中添加MPU6050初始化代码和发送位姿数据代码  ^9 t0 X( ^7 k& M' N/ S' S7 O6 V% t$ I
  1. int main(void)
    7 v1 ?& q6 c9 p$ s. w
  2. {
    4 H& C6 L" J3 A
  3.   /* USER CODE BEGIN 1 */; c& g/ y& o8 q) O
  4. 1 C% T0 j6 E9 _: `! a
  5.   /* USER CODE END 1 */
    * S( O  X6 i9 ~" ^; s
  6. : b7 ~+ t- v$ V
  7.   /* MCU Configuration--------------------------------------------------------*/
    / \1 Z! ?) E9 h$ u4 K. g" S

  8. - @/ ?" Y5 c" z
  9.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */5 P. d1 [6 R' M2 p" e2 J
  10.   HAL_Init();
    5 B. [$ w- a# U; a8 w1 ^. U
  11. - C( V5 _& P+ g
  12.   /* USER CODE BEGIN Init */5 w- g, w  v# y; x5 @- |

  13. , p) U5 M9 U! \0 J
  14.   /* USER CODE END Init */
    6 l; y$ ~% E8 Z) ]
  15. 6 C8 M0 ^# [  T& X+ o3 A
  16.   /* Configure the system clock */1 x% V" v0 }7 ]3 h& G6 {( p
  17.   SystemClock_Config();/ W1 S& W  q- H

  18. , a# W4 G. U7 R- u% V( h
  19.   /* USER CODE BEGIN SysInit */' v2 z. a8 V6 H

  20. 5 [/ v, d2 p% q3 v- l2 y
  21.   /* USER CODE END SysInit */
    % B4 L4 U% Y$ z! J
  22. 5 P- _$ d2 y8 D
  23.   /* Initialize all configured peripherals */
    9 v$ g( |/ k- {
  24.   MX_GPIO_Init();6 W: o" ?' m) Q
  25.   MX_I2C1_Init();
    ( x; @- {" ~) d5 n' S
  26.   MX_USART1_UART_Init();- {4 z; g, K% ?0 ], _
  27.   /* USER CODE BEGIN 2 */
    ' m+ W  ?8 z8 @2 {) v

  28. 3 ~2 V7 N; u4 F  c
  29.   while (MPU_Init()){                //如果初始化失败,继续初始化4 `. {  D9 [' [; `$ r& R5 ?
  30.           printf("MPU_Init_Fail...");
    7 c0 r/ ]; s0 m* @
  31.   }
    . m- \% S. \9 n: }9 f4 e, d
  32. $ m+ B. E  A5 K
  33.   /* USER CODE END 2 */2 L& N1 E- Y5 X7 b& {3 V2 N/ H, M' R
  34. 1 [6 u5 y8 U' T; Y! R
  35.   /* Infinite loop */1 r8 A- Q0 Q% Y+ ^' S% F
  36.   /* USER CODE BEGIN WHILE */8 d5 _" @: W% p
  37.   while (1)# j# T) Z9 ~- S0 i% M
  38.   {& H8 O7 M1 ~' F  R/ ]
  39.           GetAngle();% h2 p2 Q5 K! T# N0 ], S
  40.           GetPitch();+ x3 l9 N: N5 N/ M: B' B, D
  41.           HAL_Delay(10);( z4 P7 f, O4 A7 u0 q/ t
  42.     /* USER CODE END WHILE */& G' M( H+ d% k$ u  @6 {+ F2 A
  43. & X9 S! t( E1 x) B( |; B
  44.     /* USER CODE BEGIN 3 */7 S- o5 H& Z( |
  45.   }/ V( i. w' Y- Q2 p( q( H8 S! L
  46.   /* USER CODE END 3 */
    $ p% H9 v/ j% y$ L6 a) J
  47. }
复制代码
- L& ~% l) g: O& z4 G# J4 p' f
点击,下载程序3 C% j7 B, |% {2 G+ B& \2 ]2 R
* B; e6 K- C) n' y
{90_LT8S2ACLQ4(5~ZX943B.png
9 s+ i$ i" g  p9 ~
$ G. |8 k. f0 u3 W7 o
程序烧录好后就会通过串口通讯向上位机发送位姿数据(加速度计、卡尔曼、一阶二阶互补滤波计算的位姿),上位机我用的是Arduino编译器来接受并显示数据。设置好波特率后即可显示数据。
$ F1 W3 A1 S+ Q0 k# p3 D! s# o% m+ I5 T  l$ L& u. D+ v! M
H5PKUP6Y02TULI6(N)Y_NZ5.png
; O' A7 [' }% K7 \: h; r  P
0 w- y8 c1 ]! O% P* t4 j 作者:DIY攻城狮
# A& K/ z3 S, w2 J  t/ I3 [( T) q+ [8 B% N/ z: T

4 \& W7 ^: |" d( [3 K) p
收藏 评论1 发布时间:2023-2-9 17:00

举报

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

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

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版