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

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

[复制链接]
STMCU小助手 发布时间:2023-2-9 17:00
/ T3 h* P- r3 _7 b8 l5 x
6 c. N6 L0 V8 z% L& M" `
N1UI`V7SWCGTLM$D1GQ54.png 5 Q0 ?$ Y( l- n; Y8 E8 B+ j
: E8 }+ ]8 m% P. v$ W- a
% S- D4 H; Z0 H" g; o  t8 `
背景
' p0 l5 T* w$ ?1 J) u2 S4 ^
        本文用于记录平衡自行车的制作过程,及制作中遇到的问题;总体方案如下:采用STM32F103C8T6作为主控单元、MPU6050作为位姿采集单元、无刷电机带动动量轮调节小车平衡、1S锂电池配合5V和12V升压模块作为电源、蓝牙模块用于和微信小程序进行无线遥控及PID调试、舵机用于控制行驶方向和支撑小车站立。
/ }# y- V" @6 W  g
2 b' p9 O" b5 p: d7 eMPU-6050简介
/ G  S8 H7 s- e2 _: q. a6 y5 e    MPU-6050集成了 3 轴陀螺仪、3 轴加速度计及温度传感器,并且预留一个IIC 接口,可用于连接外部磁力传感器,并利用自带的数字运动处理器(DMP: Digital Motion Processor)硬件加速引擎,通过主 IIC 接口,向应用端输出完整的 9 轴融合演算数据。4 y. c0 b0 K) p) |! Q

% i; Y) R' H# U6 Z0 E4 l0 U! s& z0 Z    MPU-6050 对陀螺仪和加速度计分别用了三个16 位的ADC(0~65535),将其测量的模拟量转化为可输出的数字量。且传感器的测量范围都是可以根据需求设定的,陀螺仪可测范围为±250,±500,±1000,±2000°/秒(dps),加速度计可测范围为±2,±4,±8,±16g。$ e5 s4 |+ g- x5 S: y# v; ~
  s& m% i, h% Y; T$ D: g
/ m  k- |5 H: @
Q(D3Y}_OBQFJ0FA501Q1PWI.png
; J: F9 M& X# s! Q' I& L( E
7 b7 G2 i& w7 Y! E" R2 U
MPU-6050姿态获取与处理(惭愧,我也一知半解)- z2 q- U& K$ P8 p/ U  _, v
    理论上只需要对3轴陀螺仪的角度进行积分,就可以得到MPU-6050的姿态数据。实际上由于陀螺仪受噪声的影响,只对陀螺仪积分并不能得到完全准确的姿态,所以需要用加速度计进行辅助矫正,常用的方法有三种:互补滤波、卡尔曼滤波、硬件DMP解算四元数。
8 h/ y! [2 X9 C
' g2 W5 J+ J; q6 T& e" ^0 c7 `
3M4ZAPN6Y61O]`Q(`F`%P%T.png 9 [3 }9 S/ `: e6 o8 Q# @' @% D
互补滤波、卡尔曼滤波计算位姿示例
( ?# Y1 x- s" y/ N
" c$ h+ L/ B4 S( F5 H
LTC%Z6L{1J6%~U}3VE@]A$P.png 2 q- {) @1 @( N9 n  M, G
3 |. b  Y4 N. g. d, x- o
互补滤波、卡尔曼滤波计算位姿示例& x6 Q% N9 A% Z4 y+ _! _
* |! s+ N0 \8 N/ V0 E
    除了使用加速度计计算的噪声较大,卡尔曼滤波,一阶互补滤波,二阶互补滤波看着差不多,O(∩_∩)O哈哈~(四元数计算的示例就不演示了,有兴趣的可以自己研究一下。)
" n; a. z5 I8 Q9 J; Z    1)一阶互补滤波:因为加速度计有高频噪声,陀螺仪有低频噪声,需要互补滤波融合得到较可靠的角度值。
& o1 @# d# V3 n0 V* f; Z5 p    2)卡尔曼滤波:利用线性系统状态方程,通过系统输入输出观测数据,对系统状态进行最优估计的算法。由于观测数据中包括系统中的噪声和干扰的影响,所以最优估计也可看作是滤波过程(来源于百度词条,我也看不懂这说的啥)。
, D; J% U2 v* S9 z- K$ `    3)硬件DMP解算四元数:DMP将原始数据直接转换成四元数输出,运用欧拉角转换算法,从而得到Yaw、Roll和Pitch(使用四元数计算位姿,好像会将初始化的位姿设定为零位)。
& v: H2 V- G. c+ Z1 L; T2 q$ I3 Z$ n: {0 S4 Q# N
~3FY21~}NEG2BK(M@DI]NBR.png
; n' ]- Y9 _$ W, G  y( r# x, ]1 d% {4 e, l. T
6 _1 n. e1 J* B7 j
一、一阶互补滤波算法

6 H$ P4 A* a$ @7 F, A. \1 K    MPU-6050 的加速度计和陀螺仪各有优缺点,三轴的加速度值没有累积误差,通过简单的计算即可得到倾角,但是它包含的噪声太多(因为待测物运动时会产生加速度,电机运行时振动会产生加速度等),不能直接使用;陀螺仪对外界振动影响小,精度高,通过对角速度积分可以得到倾角,但是会产生累积误差。所以不能单独使用MPU-6050的加速度计或陀螺仪来得到倾角,需要二者进行互补。一阶互补算法的思想就是给加速度和陀螺仪不同的权值,把它们结合到一起进行修正,通过加速度和角速度就可以计算 Pitch 和 Roll 角(单靠 MPU6050 无法准确得到 Yaw 角,需要和地磁传感器结合使用)。( }4 _5 u5 J% P* M

7 l0 }( ?3 W4 ~5 U: a
5 ^" {/ p( u$ p0 b" |* v
一阶互补算法如下:
. ^5 |1 t9 j# r3 C
  1. //一阶互补滤波6 n; H6 m5 @8 m2 f
  2. float K1 =0.1;         // 对加速度计取值的权重$ k& H1 B) V% y- J8 Z# Z9 q/ p
  3. float FirstOrder_Pitch;8 s. I) f5 k9 ~& _. d

  4. 7 {1 c+ e1 ?0 W* ?% E
  5. float FirstOrder(float newAngle, float newRate, float dt)//采集后计算的角度和角加速度, [& N/ ]+ |- z; O! V/ R
  6. {: O: h- u9 @: Y8 q
  7.   FirstOrder_Pitch = K1 * newAngle + (1-K1) * (angle + newRate * dt);& Z1 c4 N; d. ^
  8.   return FirstOrder_Pitch;
    * @" h- _- ^; C1 k0 k% Y1 ~
  9. }
复制代码

9 w6 U" e8 }% I% c二阶互补算法如下:/ s( b  [0 L# V; }
  1. //二阶互补滤波/ U* n; t) k: g
  2. float K2 =0.2; // 对加速度计取值的权重5 V! Z2 @6 t) O. f; k' o
  3. float x1,x2,y1;
    " \3 v2 n' }, W. B1 _& |& n' y- `
  4. float SecondOrder_Pitch;' T* H+ T2 }5 A5 t
  5. 8 ?: h; G& e& @5 Z$ ?- m3 s: M
  6. float SecondOrder(float newAngle, float newRate, float dt)//采集后计算的角度和角加速度
    2 J, B# J6 }  z6 {
  7. {+ B# N3 M$ H: b" A
  8. x1=(newAngle-SecondOrder_Pitch)*(1-K2)*(1-K2);
    & i6 f# {6 \- B: g4 r
  9. y1=y1+x1*dt;
    * O3 U- }# Q) y9 s! t2 ?% G3 Q# g
  10. x2=y1+2*(1-K2)*(newAngle-SecondOrder_Pitch)+newRate;' r# G9 [, n8 A7 R( \
  11. SecondOrder_Pitch=SecondOrder_Pitch+ x2*dt;0 v2 Q8 r+ O2 [) \7 [9 V) R0 u7 h
  12. return SecondOrder_Pitch;) n7 g9 X. [$ [) t7 V
  13. }
复制代码
; }! {" J) F6 e3 W
二、卡尔曼滤波
: @% ~  E. o* ~
  1. /* Kalman filter variables */1 x+ Q: V! _: E8 D
  2.         float Q_angle = 0.001; // Process noise variance for the accelerometer
    6 N6 T1 N& ^8 o" P
  3.         float Q_bias = 0.003; // Process noise variance for the gyro bias
    " x3 J- j! e- g7 K
  4.         float R_measure = 0.03; // Measurement noise variance - this is actually the variance of the measurement noise
    ' L3 a' U+ \7 Y" U
  5.   u" t& Y: i5 T0 d8 ]0 u
  6.         float angle = 0.0; // The angle calculated by the Kalman filter - part of the 2x1 state vector0 _8 K: k$ w' X$ ^$ C$ b" {- S
  7.         float bias = 0.0; // The gyro bias calculated by the Kalman filter - part of the 2x1 state vector0 {) Q$ ^  L# E0 m5 O6 ^
  8.         float rate; // Unbiased rate calculated from the rate and the calculated bias - you have to call getAngle to update the rate7 n! x2 D9 @9 C1 n' m" o5 d

  9. ) b" f( V3 g& E; t" L* Q) R
  10.         float P[2][2]={0.0,0.0,0.0,0.0}; // Error covariance matrix - This is a 2x2 matrix, v. s7 m; o1 e" z. ~* d( K. G! @
  11.   I1 o9 r! ?, T& f8 V: L. ^
  12. 4 ]$ F, x6 I9 }+ j- j# }% d5 w8 _% {
  13. // The angle should be in degrees and the rate should be in degrees per second and the delta time in seconds
    4 Y- Q+ H+ S2 z1 C
  14. float Kalman_filter(float newAngle, float newRate, float dt) {" k- C- C5 o2 R  R% X9 E
  15.     // KasBot V2  -  Kalman filter module - http://www.x-firm.com/?page_id=145
    7 T; N  T: x1 n3 N( C
  16.     // Modified by Kristian Lauszus; Q7 v& t, k7 A8 \8 m6 P8 ?; k
  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
    2 I) H: p' h, j* a& T/ z! m
  18. $ R& c$ k8 I% i: _
  19.     // Discrete Kalman filter time update equations - Time Update ("Predict")
    * v, H* j- m- l" t$ Q: {$ W! q6 @6 ]
  20.     // Update xhat - Project the state ahead# g/ a8 W5 L! j2 x4 L- ~' _; D
  21.     /* Step 1 *// I1 z  g) n. q4 H& t
  22.     rate = newRate - bias;
    ( P  L& f) ~  v/ V) S
  23.     angle += dt * rate;
    / v5 ^2 P4 }1 V" [( J
  24. 1 v% \: n& m& m
  25.     // Update estimation error covariance - Project the error covariance ahead
    8 K7 S1 z, h, O7 I6 }
  26.     /* Step 2 */$ |, H! q% Y" R
  27.     P[0][0] += dt * (dt*P[1][1] - P[0][1] - P[1][0] + Q_angle);
    0 p6 l/ q1 F- B% G7 m( M
  28.     P[0][1] -= dt * P[1][1];
    3 }6 S& D2 p7 q" ^& F
  29.     P[1][0] -= dt * P[1][1];
    0 k# u% }5 q3 R/ f
  30.     P[1][1] += Q_bias * dt;
    . ?3 \% x7 N0 o6 M% p5 x: x( j' q/ Z
  31. + u2 v2 c" ^9 u# d/ N! c" ]
  32.     // Discrete Kalman filter measurement update equations - Measurement Update ("Correct")
    8 m% o! v4 @! Q! T3 S7 u6 J
  33.     // Calculate Kalman gain - Compute the Kalman gain7 E' o8 ]. V6 [: q' ?( Y
  34.     /* Step 4 */
    - A8 }8 ]- ?5 X9 m# o4 {
  35.     float S = P[0][0] + R_measure; // Estimate error
    6 _4 i- k  a2 A# e8 A! @: y2 W5 Q
  36.     /* Step 5 */- `6 |& u- v# L- C
  37.     float K[2]; // Kalman gain - This is a 2x1 vector; t9 Q  \- J: y" Q7 P& k& \# G
  38.     K[0] = P[0][0] / S;- Y. g; n( g; S7 e3 }0 K
  39.     K[1] = P[1][0] / S;
    9 I& i& p( f2 f. ^& ?" d2 J
  40. 0 {; M) W& ^+ c9 T
  41.     // Calculate angle and bias - Update estimate with measurement zk (newAngle)% ?3 `; k2 `% h; y
  42.     /* Step 3 */
    - f  t: z$ n& q: Q
  43.     float y = newAngle - angle; // Angle difference' N: r0 Q0 i: P% z6 H$ q# E  {$ |
  44.     /* Step 6 */
    ; \* h, J' U8 e! |3 p: T* H$ i
  45.     angle += K[0] * y;( `+ a- K* e5 P& [, q
  46.     bias += K[1] * y;* ?2 A' {4 T) v) A' Q2 s1 s& \
  47. ) i2 P) E( g- W" R+ U
  48.     // Calculate estimation error covariance - Update the error covariance: X7 q# l# D& w% v3 a( k
  49.     /* Step 7 */. L6 j( O; t3 }. e
  50.     float P00_temp = P[0][0];
    ; w+ C$ M; o2 @5 U" ?0 I
  51.     float P01_temp = P[0][1];
    7 s9 _/ v' h+ y: @; f3 B+ I* o

  52. 5 r2 ~4 u1 k5 A% i& X1 k# ]
  53.     P[0][0] -= K[0] * P00_temp;* G+ {: B) }) M! }" J
  54.     P[0][1] -= K[0] * P01_temp;
    * |- s$ o* _' }6 \5 o, c% L
  55.     P[1][0] -= K[1] * P00_temp;5 U. ~8 n' q4 B3 a
  56.     P[1][1] -= K[1] * P01_temp;4 r& A8 Y1 V$ T( n4 d

  57. # o4 f5 ~) [3 q8 ]5 M
  58.     return angle;4 P/ Z8 a' i& W
  59. };
复制代码
/ z$ a7 R& {4 K" }3 h1 ?/ d" n. J) w
三、四元数法/ Q6 `( b$ E( g
. i$ I( G$ I3 D# P! J( k( E
     MPU-6050 自带了数字运动处理器,即 DMP,并且InvenSense公司 提供了一个 MPU-6050 的嵌入式运动驱动库,结合 MPU-6050 的 DMP,可以将我们的原始数据直接转换成四元数输出,而得到四元数之后,可以很方便的计算出欧拉角,从而得到 Yaw、Roll 和Pitch。
) R* E- Q- L, ?- t2 G# j0 W9 F; u) C+ X  }6 K8 Y6 _; K
       使用内置的 DMP,大大简化了代码设计,且 MCU 不用进行姿态解算过程,大大降低了 MCU 的负担,从而有更多的时间去处理其他事件,提高系统实时性。
$ J* z+ P. D; D! \: o/ K/ K
  1. void Read_DMP(float *Pitch,float *Roll,float *Yaw)
    , G8 B9 {5 U$ k/ ?7 u( S% ^
  2. {0 K* s2 l0 q! N
  3.         unsigned long sensor_timestamp;
    1 I5 P) U6 U2 ]  D+ S' G1 _0 E* q
  4.         unsigned char more;  s- i. h: d, L8 }& x5 C2 w
  5.         long quat[4];
    2 Q3 E# f( f. b2 @/ i) @

  6. ; O) x1 f, H0 ^4 |
  7.         dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors, &more);& u- o# {5 \  X$ o
  8.         if (sensors & INV_WXYZ_QUAT )
    : u" X! q4 [1 P. i
  9.         {
    - @1 w8 Q( p. M/ D, c+ }
  10.                 q0=quat[0] / q30;
    # p, T. i* }( t- n8 n
  11.                 q1=quat[1] / q30;
    7 u" w! P' ^/ a! Z2 ~
  12.                 q2=quat[2] / q30;0 c6 V1 u7 @( H! `) n. W
  13.                 q3=quat[3] / q30;
    : n% Y# r' h9 \9 D' \
  14.                 *Pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3;# f' S6 E, G) x) b  P/ {
  15.                 *Roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll
    9 k* R6 {7 I; c
  16.                  *Yaw = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3;        //yaw% [3 z' e5 i* a# v
  17.         }
    . u3 y2 i& I( {  I" I

  18. $ |' m* T# P- f+ \# T- A" D! _
  19. }
复制代码
( m' N9 O- a4 G2 G+ v
    以下是我测试一阶互补滤波和卡尔曼滤波项目的搭建过程,老鸟请忽略。
7 j- d( V/ t* g$ T3 D' Q0 S* X
% {+ c$ y$ J/ c5 t- E1 [    固件开发采用STM32CubeIDE开发,对于我这种菜鸡来说还是很友好的。/ w7 w" S' W# ]: t6 k; F9 X
" d$ V/ @8 J0 [, r$ L. G( \
    我使用的是STM32CubeIDE1.9.0,打开软件后,依次点击 File-->New-->Stm32 Project,弹出如下界面,输入MCU型号-->选择封装形式-->Next2 a( d) a2 {5 J/ ~2 [  [
' `+ n4 M1 i! s6 ]% J2 @) O
6Z1)SWD0)NGR@]O%J3~_A`G.png 8 E; q5 H. _- W3 V  z. J5 Y1 N

, z3 E6 ~. P8 F/ G1 {+ m) Y4 C# M3 o输入工程名字,点击Next! h; i3 k9 |% r' ]

- ]6 Q7 H3 Y$ h2 [
KU9167BQ{6[RN@@Z{Q}@K7B.png
+ ?" c+ W; _- z7 r8 U

5 c7 s) P" n% Z, T! O# J设置完成后,点击Finish# i' t3 j" I2 \2 ^
* ]0 H6 m: }) _4 r
)W%A{MBY6WZ9DC`3B@F1KM8.png
7 \" v# u5 V) W
6 H, ^  B. D' p! z  |. N进入MCU配置界面
" F" K: @/ h0 D! f# t7 v- T
, C) P6 G2 a" F
LG2T6_XWK%N3B66H$BJS(D0.png : G3 f/ w6 F$ H/ b% ~# a$ _. @

4 ~2 {: `/ w& \7 _- x' x设置I2C与MPU-6050通讯。' u+ h9 _# ]" c- D2 w( S7 l, x

* c7 C/ Q8 m1 N( V  r  M
CO(V62(7YX0CZ4}@DZ)$Q.png 8 ?9 j  {! m; x% E) R  i
5 ^; \* L& o" f/ g; h1 q
设置串口与上位机通讯,实时显示MPU-6050位姿。
+ \; }+ b1 J* w6 t
" N; G3 C$ o2 x0 J. o
ZJQU$P3UR1R4)729_G][%{S.png 7 p. W. z' \( K; V4 p2 ~- O

5 w: z" ~( Z' W5 V! Z2 T5 A, j设置系统时钟及调试模式。2 m! D: q: z* b
/ Y, _$ b8 V0 M: P' ]+ H
(~J`34%%TXA1IXKWT(T@WN7.png
/ [+ y0 u& w6 F$ x
6 @, d) M) ~4 v: ^6 W设置时钟源。; \) h# S9 t- I* q5 T( A
5 H- u5 H6 o: h( s' ?, _
~VCI0F0AO}W_{IQM08)~J44.png
" B. Y, R" l& M- M' h* N
# _+ e& }$ c1 ?" m8 P6 K. A$ ?3 ~全部设置好后,芯片引脚的分配情况。. s( G6 @( ]# y# P1 E
1 S! L% t5 J* Y& C
SL6GUUTZ2Q)$R5{_){T9V@T.png
+ j3 t0 y+ [. i7 R1 _
$ X' I& x6 z9 u1 P+ w9 s在时钟配置里设置时钟最大频率。
$ t* E3 C( G5 x! ]2 h) D0 ^! P; S' K
3_0F%FN3W27QQvC$O5S$I.png + t0 B7 S6 t* I7 w) _( z7 u
) o4 S6 ^: y3 `
在工程管理——>代码生成器中进行设置
$ T, v; @! x9 F$ m2 R* U: T) d# d* \" Y7 n/ u* V8 u: v
2ET9LUB057L`M4NNOJ@@HRK.png 5 L! O2 Y& C; {0 @2 v& _; S
8 y: a+ r8 P. A! U
点击此处,生成代码
' j- V4 C9 U8 t) l. r0 L
2 x5 f2 j. q! _, H* ]* }
NB$AP684`5LM@ZCOK85OHM6.png + V! v, p: ?% E( ?7 u6 Z" y, N
- z8 Y! W) d1 w6 K& t
在工程文件夹中创建iCode文件。- w9 K' q. `* s4 o' ^
$ M  y& h" R% Z/ f3 ~
~3XUPXUKJ3X311X9L%MRRSX.png ; V. c' S4 b, V2 h( R4 \. w; Y  Z! ?

0 f. o6 @! @/ p6 }将MPU-6050、卡尔曼滤波库文件复制到iCode文件中。7 J# ^, G( y% h; v1 Y
% P/ J8 G4 |2 n4 e0 t0 g
XFBF%G7M)$$FIFDRGQDM6W1.png
) k9 N; `5 w/ B' Q' X
, _; y7 F2 E! `7 k! L点击Debug,刚刚添加的驱动文件全部在工程文件中显示出来。
' @% \! H( g7 r( U' K' \# e/ T4 [, x# g' i9 G- U1 f  }
Y%XW13SG7SJ%JBPZA1JIEAH.png
+ W4 [0 B+ H) k1 \$ e
. \' k7 u4 [$ u按照下列方式设置MPU-6050、卡尔曼滤波驱动文件的编译路径。+ n& s+ A7 k, U3 c9 U# l/ m5 Y* m: ?

; {+ B' B& @2 d
DJZL)$_$FQL20QWJM(8)A.png
( C/ \' J1 x, y
' s; J; Z2 m& P+ x% g& f% j
WQUT]3V991@5_$%1Q%8XS`S.png 6 p9 T+ j; n: ?7 D3 k3 F
4 u6 a2 c7 T$ ?- ^' ?  c
然后再main.c文件中添加如下代码,用于重定义printf函数,用于串口输出;获取系统时间,用于滤波。" a3 z6 l) {9 W, Y  {
  1. /* USER CODE END Header */
    ' G# u8 W- V4 V& O
  2. /* Includes ------------------------------------------------------------------*/. _- d7 y; a$ y6 {  W3 f: e3 w
  3. #include "main.h"
    ; Q3 u: q/ l) \1 V4 J7 b) J0 b
  4. #include "i2c.h"
    , n# ~) S, v) h, w& ]
  5. #include "usart.h"
    8 @1 V6 a, `+ l. I% T1 _( e6 v
  6. #include "gpio.h"& ^, H; f0 P- f  p
  7. 5 U4 I) O/ R  G- I
  8. /* Private includes ----------------------------------------------------------*/
    % E. _" ^2 j# w+ g& w  Y6 H: }
  9. /* USER CODE BEGIN Includes */
    5 Z4 X* G. n: e4 D/ C( S
  10. #include "mpu6050.h"5 Z# Q3 z$ @& z/ W: x
  11. #include <stdio.h>
    2 v, j" o# ~$ {
  12. /* USER CODE END Includes */# N: k5 \( Y9 A3 Z; @9 B0 a

  13. 0 W# X' V( P  X( G5 \! D- z, b) D- b2 E
  14. /* Private typedef -----------------------------------------------------------*/8 l1 E' D' s' L. _# k  k: D
  15. /* USER CODE BEGIN PTD */, i4 G* }% S  f  I2 O
  16. ( ?5 ]. U- `: X# z4 D8 [' ]) r$ u
  17. /* USER CODE END PTD */8 v9 e  t% y/ P) b$ @* @  i

  18. " B5 n0 n3 I. D8 E( L" O
  19. /* Private define ------------------------------------------------------------*/, t  H# I# d% x+ W
  20. /* USER CODE BEGIN PD */6 G# U6 w* ^* Y. M9 G
  21. /* USER CODE END PD */4 ]& {  ?. _7 e8 u
  22. ) H" X7 M9 X% [$ E9 j( u
  23. /* Private macro -------------------------------------------------------------*/
    ' H0 C+ ]: Z- t; X) G+ p$ L
  24. /* USER CODE BEGIN PM */
    ; d6 L6 K0 d+ b$ {1 l7 J$ ]% X+ L

  25. " w2 V1 e6 w0 y2 ?# h
  26. /* USER CODE END PM */0 q" d7 ^  \9 o; P( S1 H

  27. $ n2 w' Q0 E: b! E0 ^
  28. /* Private variables ---------------------------------------------------------*/7 N9 z3 h! n/ H+ K1 z& e0 u

  29. & ?' o( J: ~% U4 j
  30. /* USER CODE BEGIN PV */
    ! {  _6 S" a' P; f2 \( t
  31. % a  h% x/ `3 E7 ]1 w+ {
  32. //重定义printf函数,用于串口输出9 J' @8 S: @# [* D8 U) _5 I
  33. #ifdef __GNUC__; p& J5 U3 W) Y) l7 c( g
  34. #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    6 \: ?* ?7 o8 t- W" s( _4 {9 J
  35. #else; [" X8 T: ?' m4 l) ]
  36. #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    + _0 W) J0 ?& j2 j' V
  37. #endif
    / @$ R9 |1 `& w4 x/ g- S5 h

  38. ; q& M3 m: _; W2 @1 j/ e" C; a+ c
  39. PUTCHAR_PROTOTYPE; o* T$ E" n- P" Q8 G  B
  40. {* L: m# [( K! I; R% v4 e2 r
  41.         HAL_UART_Transmit(&huart1, (uint8_t*)&ch,1,HAL_MAX_DELAY);
    $ H9 k2 i. o- }0 Y
  42.     return ch;
    ( Q, ]8 M) l: s+ x/ ?5 d" U
  43. }
    , Q" d6 r' Q. B* C' |8 t
  44. /* ---------------------------------------------------------------------------*/
    4 a* G$ }! L3 ^4 ?2 @. r9 Z
  45. 1 u8 g. ~$ W, Q; b) S: s
  46. //获取系统时间,用于卡尔曼滤波
    # \2 l3 ~- L' d, }  T  E: Y. M
  47. __STATIC_INLINE uint32_t GXT_SYSTICK_IsActiveCounterFlag(void)
    6 A- m8 h7 C+ g3 {. j: Y
  48. {. h' W: n# B9 l
  49.   return ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == (SysTick_CTRL_COUNTFLAG_Msk));; R; x/ o5 y9 s( C
  50. }
    % q1 t4 r2 z; m$ ]7 e
  51. static uint32_t getCurrentMicros(void)
    " h& X0 `, b1 T% Z1 g
  52. {5 w/ r; _6 C) n# z7 f8 j3 l
  53.   /* Ensure COUNTFLAG is reset by reading SysTick control and status register */! S! R) ]5 S# \: r, r* O
  54.   GXT_SYSTICK_IsActiveCounterFlag();, f; l# A' C, ^. c
  55.   uint32_t m = HAL_GetTick();: i* S: u8 a: [4 Z1 z
  56.   const uint32_t tms = SysTick->LOAD + 1;
    : [; T: m4 {4 p" G1 @# F' ~* O
  57.   __IO uint32_t u = tms - SysTick->VAL;
    % k: `. T& S. \7 I: D% T
  58.   if (GXT_SYSTICK_IsActiveCounterFlag()) {- ]$ d; u8 M+ T* q; G6 u5 N
  59.     m = HAL_GetTick();2 I% b! }8 \7 `
  60.     u = tms - SysTick->VAL;
    . |; W+ B& K) K( P4 _: _% D( ]
  61.   }
    ' V# i9 e( Z& i$ o4 j$ j9 \
  62.   return (m * 1000 + (u * 1000) / tms);
    ) B. v% E4 q' L4 M7 a( D
  63. }
    + y% ~1 W, V1 }! Y
  64. //获取系统时间,单位us7 a  i/ z+ q7 O# K( ]4 l' U$ o/ I
  65. uint32_t micros(void)
    ; F$ L6 U+ G# h. Y0 V. d
  66. {
    4 ]8 `( V( m) o
  67.   return getCurrentMicros();, v, H8 y+ o* J3 U" q% k
  68. }
    , A2 s" Q' X2 \2 J
  69. . X6 z/ _) ?% e

  70. ! K" W. o6 a" M, y- E: d$ f% T
  71. /* USER CODE END PV */
复制代码

5 G+ W8 k0 [/ J7 h- i# h* D使用printf()函数,还需进行如下设置:点击菜单栏 Project-->properties,弹出如下界面
; ]% ?3 R" F. w4 g2 a  m0 U# t2 v. X+ t% `
[I](O`R0}YFTDA{2SX~2%)J.png
. O* }/ g5 d8 w* `% G% V
! V: ?4 S. q1 r1 j. ^$ {* d在main()函数中添加MPU6050初始化代码和发送位姿数据代码
8 x4 s1 @: a) v. |! {3 P
  1. int main(void)
    - Y  Z* o* T3 A5 ^) ?& K$ k4 ?; d) w
  2. {
    ; }5 O. M4 w6 y2 U! M( \1 \* r
  3.   /* USER CODE BEGIN 1 */( e6 q* e8 i6 ?% Z' G9 E3 I

  4. 2 |% @; m8 _& J% ^0 V8 I% K
  5.   /* USER CODE END 1 */6 g6 d) ]  Q0 d/ V9 d1 ?
  6. 4 D0 c: H5 E; [' l6 x, [
  7.   /* MCU Configuration--------------------------------------------------------*/
    ( Q. {& B- d1 @$ I

  8. 6 @* J' `  c. S/ L* u
  9.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    : u, u$ H! ^8 f3 o% L% Q
  10.   HAL_Init();
    2 p' l+ e; O4 a, \0 R5 T; k# m
  11. $ C* L. \) G3 j$ C+ L
  12.   /* USER CODE BEGIN Init */
    - r8 [" ?. k2 ?( Z% `

  13. 0 ]* h) ]9 e9 }8 k- B+ G' B+ |
  14.   /* USER CODE END Init */( H4 E0 M) d5 L( U
  15. 9 P5 b6 w4 q" t8 C+ |2 i- e
  16.   /* Configure the system clock */- |0 u8 ^/ h$ i
  17.   SystemClock_Config();5 z1 q- C# j9 e1 b  H
  18. : h5 j0 V1 N& H
  19.   /* USER CODE BEGIN SysInit */- E# u& S$ c7 ]# y4 R; `

  20. " B$ S1 B1 }& n
  21.   /* USER CODE END SysInit */) U0 D3 b. [" R& I
  22. ) `9 \) |6 ~7 X9 U3 e0 n$ q! n
  23.   /* Initialize all configured peripherals */
    + \: l& w$ K' s. R
  24.   MX_GPIO_Init();
    2 G( Z  R( p6 [' g& u9 U
  25.   MX_I2C1_Init();, x8 T2 u) n5 a- J
  26.   MX_USART1_UART_Init();9 m" I+ ]! U3 D! l  k5 H
  27.   /* USER CODE BEGIN 2 */- c8 o% W3 r" z" K! @
  28. & O6 G. r" @0 S
  29.   while (MPU_Init()){                //如果初始化失败,继续初始化
    : r8 m2 c  P7 s# r) V, P
  30.           printf("MPU_Init_Fail...");
    & G; S, N4 E, m) l; ?4 Y9 o% G' T3 |9 [
  31.   }
    1 e$ Q/ j$ Y( Q! I6 o# }+ d
  32. / r' k" k- @8 V- N- f5 L$ |
  33.   /* USER CODE END 2 */
    1 B+ g, h5 k: E9 j0 v  Q
  34. ; A# m& \+ U# |0 ~2 @" x5 @/ W
  35.   /* Infinite loop */! q- M5 F' b$ T& @2 K0 p: J
  36.   /* USER CODE BEGIN WHILE */
    6 t8 X% ^5 X( k4 n1 E# q/ T
  37.   while (1)
    & R4 J3 X" O. @
  38.   {
      u7 U) Z+ B: K9 F' H
  39.           GetAngle();( I9 K; H: ^& Z8 x! v0 x
  40.           GetPitch();; A4 ]% S0 G" Z6 I* w
  41.           HAL_Delay(10);. `0 E7 z* K& s0 [: S
  42.     /* USER CODE END WHILE */
    $ N: G# y4 d+ k; t0 p: n! F$ C

  43. 9 u; D# X& Y* o! h: b  l4 H; o! c
  44.     /* USER CODE BEGIN 3 */
    8 O) m1 q& F/ Q9 X1 m
  45.   }/ d6 d; S: u6 e
  46.   /* USER CODE END 3 */
    + m5 N5 V' w- k
  47. }
复制代码
9 S( t# |2 i  Z+ W( e! B, a  U+ i/ F
点击,下载程序
! _% j7 d! }7 {1 S1 ^
; w5 @$ C4 E9 ]% x# v1 I
{90_LT8S2ACLQ4(5~ZX943B.png 3 C3 P( ?( R  ~4 Y; ?2 i

: ^5 n( \7 C0 c2 K  m  x程序烧录好后就会通过串口通讯向上位机发送位姿数据(加速度计、卡尔曼、一阶二阶互补滤波计算的位姿),上位机我用的是Arduino编译器来接受并显示数据。设置好波特率后即可显示数据。/ ]9 ]+ d: _/ l8 |1 `5 ]

' C5 C4 t0 m9 Q9 T' j H5PKUP6Y02TULI6(N)Y_NZ5.png
; H! K* C# g" |4 T- {9 J  y& a; S- C$ Q
作者:DIY攻城狮
. ]$ {( @* E- G( s! X8 Q% K* n+ M9 b: c8 W

; O5 }* M, j/ _" ^5 G4 e' A
赞 收藏 评论1 发布时间:2023-2-9 17:00

举报

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

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

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版