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

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

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

/ T  u2 f! J) G7 ]. a; _

9 y* i' C- W- u N1UI`V7SWCGTLM$D1GQ54.png 3 q6 Y' Z5 m9 p4 N2 h

: V% W, a7 q; {

# z2 e2 d' c2 X背景
+ J0 ~; E' A8 ^, y- j2 j1 a
        本文用于记录平衡自行车的制作过程,及制作中遇到的问题;总体方案如下:采用STM32F103C8T6作为主控单元、MPU6050作为位姿采集单元、无刷电机带动动量轮调节小车平衡、1S锂电池配合5V和12V升压模块作为电源、蓝牙模块用于和微信小程序进行无线遥控及PID调试、舵机用于控制行驶方向和支撑小车站立。1 i3 `) F- y" ]. u( h

$ \& U% ~, l8 m1 a: zMPU-6050简介
2 m' M* V3 Q- f( n1 I3 C5 G" C, N    MPU-6050集成了 3 轴陀螺仪、3 轴加速度计及温度传感器,并且预留一个IIC 接口,可用于连接外部磁力传感器,并利用自带的数字运动处理器(DMP: Digital Motion Processor)硬件加速引擎,通过主 IIC 接口,向应用端输出完整的 9 轴融合演算数据。& s# W3 ]4 ~# Y, l/ t8 ~1 |
  I- M' F( E  P1 G8 g+ v1 c" Q) [
    MPU-6050 对陀螺仪和加速度计分别用了三个16 位的ADC(0~65535),将其测量的模拟量转化为可输出的数字量。且传感器的测量范围都是可以根据需求设定的,陀螺仪可测范围为±250,±500,±1000,±2000°/秒(dps),加速度计可测范围为±2,±4,±8,±16g。3 S# l3 r8 [% s1 y  N

: K8 e0 q% ~9 J/ ?3 T

! d  i& d/ s+ |) f4 g* n Q(D3Y}_OBQFJ0FA501Q1PWI.png ' S! L# R8 Z$ l/ ~- w; U" o/ o2 n8 ^
) q! i3 C# e9 D
MPU-6050姿态获取与处理(惭愧,我也一知半解)
' g5 c; p' |; L: k3 X    理论上只需要对3轴陀螺仪的角度进行积分,就可以得到MPU-6050的姿态数据。实际上由于陀螺仪受噪声的影响,只对陀螺仪积分并不能得到完全准确的姿态,所以需要用加速度计进行辅助矫正,常用的方法有三种:互补滤波、卡尔曼滤波、硬件DMP解算四元数。- h  N* O4 [9 c
9 n  Q4 z% j& L# u1 V
3M4ZAPN6Y61O]`Q(`F`%P%T.png
9 X9 a3 U8 E" X4 }' G互补滤波、卡尔曼滤波计算位姿示例& \+ U1 g) H$ j6 I4 O$ x2 M2 X* _
4 j7 b, B9 F, t& M
LTC%Z6L{1J6%~U}3VE@]A$P.png
. l( Q, |+ t, P
+ t4 ~" ^' a2 P$ ^" w( z! d4 A
互补滤波、卡尔曼滤波计算位姿示例
. b+ }& u$ y/ Z  m% z& t2 r) H- J+ e: \% |2 J* n
    除了使用加速度计计算的噪声较大,卡尔曼滤波,一阶互补滤波,二阶互补滤波看着差不多,O(∩_∩)O哈哈~(四元数计算的示例就不演示了,有兴趣的可以自己研究一下。)# W6 C/ `3 }# Y
    1)一阶互补滤波:因为加速度计有高频噪声,陀螺仪有低频噪声,需要互补滤波融合得到较可靠的角度值。
7 C2 e. l& l2 ^+ O- |0 A    2)卡尔曼滤波:利用线性系统状态方程,通过系统输入输出观测数据,对系统状态进行最优估计的算法。由于观测数据中包括系统中的噪声和干扰的影响,所以最优估计也可看作是滤波过程(来源于百度词条,我也看不懂这说的啥)。; p; ]0 Z, u* P0 C
    3)硬件DMP解算四元数:DMP将原始数据直接转换成四元数输出,运用欧拉角转换算法,从而得到Yaw、Roll和Pitch(使用四元数计算位姿,好像会将初始化的位姿设定为零位)。
0 H/ [. ]+ u. U& h) P2 `& L$ z/ ~2 ~( C. [0 d; e
~3FY21~}NEG2BK(M@DI]NBR.png
8 {5 }( g* }8 v  Y- Z
* t' d$ f! x9 n. @4 i4 |  a1 M' x# m# }' Y. ]2 r2 V( x
一、一阶互补滤波算法
3 e3 Y1 M8 R4 D- \8 e5 B4 R
    MPU-6050 的加速度计和陀螺仪各有优缺点,三轴的加速度值没有累积误差,通过简单的计算即可得到倾角,但是它包含的噪声太多(因为待测物运动时会产生加速度,电机运行时振动会产生加速度等),不能直接使用;陀螺仪对外界振动影响小,精度高,通过对角速度积分可以得到倾角,但是会产生累积误差。所以不能单独使用MPU-6050的加速度计或陀螺仪来得到倾角,需要二者进行互补。一阶互补算法的思想就是给加速度和陀螺仪不同的权值,把它们结合到一起进行修正,通过加速度和角速度就可以计算 Pitch 和 Roll 角(单靠 MPU6050 无法准确得到 Yaw 角,需要和地磁传感器结合使用)。
9 b  Q* X# m- T" {# J* i; d! e; e' }

" H: e  Q! B: M, S# _2 O& j3 J一阶互补算法如下:
( N' L( {( p) v. e1 u
  1. //一阶互补滤波1 b. Z# S8 e+ {/ H0 Q
  2. float K1 =0.1;         // 对加速度计取值的权重  C; t4 ~7 [/ f! V* {: ]
  3. float FirstOrder_Pitch;* u# w5 C, a) n8 Q4 o2 a. A

  4. 6 z+ J: r* u6 W6 S8 g; ]
  5. float FirstOrder(float newAngle, float newRate, float dt)//采集后计算的角度和角加速度
    4 |- B1 C! n3 ~
  6. {
    2 B  c2 p# c0 S
  7.   FirstOrder_Pitch = K1 * newAngle + (1-K1) * (angle + newRate * dt);
    * r7 o0 q8 S; G( O% @
  8.   return FirstOrder_Pitch;8 ]5 g6 t% m1 d/ h# X0 \' |2 P: x
  9. }
复制代码
  w" `* n- f% s7 a
二阶互补算法如下:/ C& v: {, j3 [9 i( o
  1. //二阶互补滤波; `6 ~' _# I) G6 j4 P$ q9 V( f7 s  G# l
  2. float K2 =0.2; // 对加速度计取值的权重
    : s8 K, _- Y6 q" W( |# J! ?
  3. float x1,x2,y1;7 _0 u* W& d2 ~" I; M
  4. float SecondOrder_Pitch;
    + Y! w0 R" m- @
  5. 8 e4 Q3 D- h1 n" z, c% O- }
  6. float SecondOrder(float newAngle, float newRate, float dt)//采集后计算的角度和角加速度
      ?' g" F# `& d2 u+ Q7 V
  7. {
    / H, Y/ y4 |) k: o. L0 k/ V
  8. x1=(newAngle-SecondOrder_Pitch)*(1-K2)*(1-K2);' c) `6 q% z8 j& a$ p2 x
  9. y1=y1+x1*dt;
    / U) E% o+ U$ I0 L# x3 f3 w
  10. x2=y1+2*(1-K2)*(newAngle-SecondOrder_Pitch)+newRate;
    ( |8 ^% n! L* N% j8 Y4 }9 W( V% O
  11. SecondOrder_Pitch=SecondOrder_Pitch+ x2*dt;
    * [% G  r, N; ?# M% k
  12. return SecondOrder_Pitch;) w: J: I8 ^+ R, e3 \
  13. }
复制代码

; ?0 k1 y% `0 z$ o# x; n  ~2 E二、卡尔曼滤波
9 N9 A0 o5 |4 l5 V) S+ y* u
  1. /* Kalman filter variables */
    * ]/ _: `& S3 L; o$ L3 s8 K
  2.         float Q_angle = 0.001; // Process noise variance for the accelerometer
    9 Q& S6 K8 q6 ]4 Z( ]8 j! k
  3.         float Q_bias = 0.003; // Process noise variance for the gyro bias+ P: A, n0 K" C% P: p* h
  4.         float R_measure = 0.03; // Measurement noise variance - this is actually the variance of the measurement noise8 T5 [0 ?8 s8 \+ D& [4 L

  5. % L# g7 q  ~9 l4 N, n  K
  6.         float angle = 0.0; // The angle calculated by the Kalman filter - part of the 2x1 state vector# C/ M2 a+ G" n- k2 o# ?3 C0 v1 U
  7.         float bias = 0.0; // The gyro bias calculated by the Kalman filter - part of the 2x1 state vector( @  X6 N, _1 F* t( f- g
  8.         float rate; // Unbiased rate calculated from the rate and the calculated bias - you have to call getAngle to update the rate
    ' ?  ^( _! o; q- \* D
  9. ( u8 Q) W3 r- \4 @
  10.         float P[2][2]={0.0,0.0,0.0,0.0}; // Error covariance matrix - This is a 2x2 matrix$ Q) @, b4 ~9 x; Q. w: \% l( S
  11. 2 j! |+ [- S+ A  h3 @0 a

  12. ) d* v1 M$ i" N$ C: y$ m5 S
  13. // The angle should be in degrees and the rate should be in degrees per second and the delta time in seconds
    * Z/ ~3 Z+ U' z3 i- m* _; _- `5 T$ z7 I
  14. float Kalman_filter(float newAngle, float newRate, float dt) {) M% f* Q- Y6 @, h* }% L4 }
  15.     // KasBot V2  -  Kalman filter module - http://www.x-firm.com/?page_id=1456 Z2 K  z; O! y( \
  16.     // Modified by Kristian Lauszus
    % l/ C+ q8 W3 k. L* g
  17.     // See my blog post for more information: http://blog.tkjelectronics.dk/2012/09/a-practical-approach-to-kalman-filter-and-how-to-implement-it2 }! g* Y) F- G3 |  C" j

  18. 6 S. e' g$ a4 M& x8 t& q7 H
  19.     // Discrete Kalman filter time update equations - Time Update ("Predict")" k" l+ d5 W5 e- q
  20.     // Update xhat - Project the state ahead: t# {4 y2 ~# J! Q
  21.     /* Step 1 */
    5 B* X& F1 u  y  l4 u$ n; d
  22.     rate = newRate - bias;" G1 M5 I  I4 ^9 G9 v8 ^! f0 v8 m
  23.     angle += dt * rate;
    1 P9 e% `# M: `7 D& _5 A

  24. 3 a7 ~8 d6 m- p3 _3 v
  25.     // Update estimation error covariance - Project the error covariance ahead1 L$ R' g. o+ p" R
  26.     /* Step 2 */1 S: O1 E# `* O1 k- p
  27.     P[0][0] += dt * (dt*P[1][1] - P[0][1] - P[1][0] + Q_angle);
    $ e; z" T" X2 t0 N) X& @, F9 [
  28.     P[0][1] -= dt * P[1][1];2 V; l0 K5 t, O' k7 ^
  29.     P[1][0] -= dt * P[1][1];) s& t2 o1 W, i7 n' ~
  30.     P[1][1] += Q_bias * dt;7 D1 A6 `) X3 u+ b

  31. 2 m8 h8 V0 K8 w' H
  32.     // Discrete Kalman filter measurement update equations - Measurement Update ("Correct")
    3 W  l! p1 S7 g4 f
  33.     // Calculate Kalman gain - Compute the Kalman gain. |7 k  n8 y8 e
  34.     /* Step 4 */
    9 @( D' G# T/ u, T
  35.     float S = P[0][0] + R_measure; // Estimate error5 v3 @# O" N+ \0 ~6 z
  36.     /* Step 5 */! w  t: i( j% \7 i! J7 _
  37.     float K[2]; // Kalman gain - This is a 2x1 vector3 u: }" g! X% {& `
  38.     K[0] = P[0][0] / S;" [) ^$ u2 \; A: S: F& i
  39.     K[1] = P[1][0] / S;
    : z% ^5 C9 m: `; |8 {( k! f" ?7 H

  40. : ]7 ?- s/ F1 v
  41.     // Calculate angle and bias - Update estimate with measurement zk (newAngle)
    ' ?/ ^; l' D# X! G4 D- E
  42.     /* Step 3 */
    ; A8 L2 |0 s: o6 ~/ y
  43.     float y = newAngle - angle; // Angle difference8 m# _' @/ N7 s+ Z- t7 d
  44.     /* Step 6 */
    " y# \4 z; l: _% L* A: ?
  45.     angle += K[0] * y;3 H( R3 `4 I8 E
  46.     bias += K[1] * y;
    0 o- T' I; Y- c6 E/ M

  47. & k' Q# K' g6 M4 {
  48.     // Calculate estimation error covariance - Update the error covariance7 G+ e. W6 w( H) o. P
  49.     /* Step 7 */+ B* F1 N% u- {4 s+ C; n5 G
  50.     float P00_temp = P[0][0];# P, k$ P6 H1 k" |3 |; ?
  51.     float P01_temp = P[0][1];
    % h+ b9 K' ?; s# @0 W

  52. % q3 b, X6 m3 _
  53.     P[0][0] -= K[0] * P00_temp;6 t7 `: ^- r( p9 C
  54.     P[0][1] -= K[0] * P01_temp;1 ?/ k/ ^/ p) y. M( C0 S) s4 {  Q; u
  55.     P[1][0] -= K[1] * P00_temp;) O' c' ]' H& `+ p7 Y) A: e
  56.     P[1][1] -= K[1] * P01_temp;' ?. c9 f- B& T( {- e
  57. 6 ?) A: R+ ^. s) F  `4 V. C
  58.     return angle;$ U- D) C* v4 |6 J2 G  Q
  59. };
复制代码

- @6 A+ P" ^$ Y5 U8 v7 @0 E三、四元数法3 b' V6 f9 a8 f  v/ m* A  V% D0 N3 F
" I/ F( L) M" P
     MPU-6050 自带了数字运动处理器,即 DMP,并且InvenSense公司 提供了一个 MPU-6050 的嵌入式运动驱动库,结合 MPU-6050 的 DMP,可以将我们的原始数据直接转换成四元数输出,而得到四元数之后,可以很方便的计算出欧拉角,从而得到 Yaw、Roll 和Pitch。
+ p2 ?' H# }) g9 @" d% X" |) x5 R
6 W/ R4 }; j2 W7 f$ p       使用内置的 DMP,大大简化了代码设计,且 MCU 不用进行姿态解算过程,大大降低了 MCU 的负担,从而有更多的时间去处理其他事件,提高系统实时性。  ^# T1 ]& y& M0 M- w2 |# a& k
  1. void Read_DMP(float *Pitch,float *Roll,float *Yaw)
    4 z9 B6 q; O7 f8 J
  2. {
    " x5 \( m! P$ L
  3.         unsigned long sensor_timestamp;+ Z7 ^( D9 z% ?1 G) K. A5 E
  4.         unsigned char more;
    3 Y" d, R+ B! a+ l+ o
  5.         long quat[4];
    ( r( M" H, w" W
  6. ' i* j# O( {9 }; Z- e$ o8 e
  7.         dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors, &more);
    : w/ u; T8 ^" b- s& k
  8.         if (sensors & INV_WXYZ_QUAT )% G  @2 X4 e# Z! h% u
  9.         {3 O4 _4 o  I* N/ |+ I+ F
  10.                 q0=quat[0] / q30;2 [: ~' m7 ~, ?( u6 {, j% I& X- K
  11.                 q1=quat[1] / q30;
    , Z! n6 ]$ w; x5 `0 X
  12.                 q2=quat[2] / q30;8 e0 `- M! l* ?+ _* \  l
  13.                 q3=quat[3] / q30;
    # P% W: K9 X% y3 V6 w4 @0 W- U
  14.                 *Pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3;
    4 Q3 F# p  a6 \: y" Z! W+ ^2 X' D
  15.                 *Roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll# @; O" x# \! w8 Z
  16.                  *Yaw = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3;        //yaw
    $ S1 }' P6 M2 b9 u2 }; @9 J- p
  17.         }' y% m2 w# @: ]0 D: d1 W. i

  18. ( t3 G9 {* |( J" \) z& K
  19. }
复制代码
% {/ l/ g4 r* }5 t
    以下是我测试一阶互补滤波和卡尔曼滤波项目的搭建过程,老鸟请忽略。' N- o( C/ I$ \' v

; X( @/ K  D5 Z! d7 X* L8 }- \    固件开发采用STM32CubeIDE开发,对于我这种菜鸡来说还是很友好的。- _8 ]/ `% j5 P, e  x" w

7 H7 i$ t" Y' [4 q3 I7 x0 o    我使用的是STM32CubeIDE1.9.0,打开软件后,依次点击 File-->New-->Stm32 Project,弹出如下界面,输入MCU型号-->选择封装形式-->Next
2 L. {  D; b& s2 [& F; W6 i# F
3 r- ?/ \5 S) p6 M! B+ M
6Z1)SWD0)NGR@]O%J3~_A`G.png
: O* O8 m" O6 ?+ u+ r1 n3 y$ V# D
8 k- X5 @2 T- o
输入工程名字,点击Next
; A6 h$ Q6 u) x9 B* t& g, A1 i& z* e# K% L5 y. ~; U, ^6 D8 \
KU9167BQ{6[RN@@Z{Q}@K7B.png
" @: M6 G6 u0 u: b" i1 z. i
* w3 ]0 Y+ C- X- I+ ?( T
设置完成后,点击Finish
. I$ h  e. X" k1 N  K; E1 ~- S/ R2 b* O7 V/ @" N4 [2 Q
)W%A{MBY6WZ9DC`3B@F1KM8.png
6 J. |" o% B* ?0 v6 F4 c+ }/ i0 I/ l& C9 Q0 ^3 |  C. t% t- t
进入MCU配置界面* g) v% H; @0 f( i& {( F. n5 M
1 U7 L) v$ w" {8 d$ \# C$ i/ }
LG2T6_XWK%N3B66H$BJS(D0.png
' b3 M% G! g& s
- l8 l/ `# \: V& i0 ]
设置I2C与MPU-6050通讯。
. ^# r0 [" p% [+ D6 F; N; k# O# Q
3 Q) j( k. _1 L7 S2 e7 G! s
CO(V62(7YX0CZ4}@DZ)$Q.png + Y5 {  `+ v: f# s
+ E6 x$ x0 Z" j; S: }/ v4 F6 K( c& I
设置串口与上位机通讯,实时显示MPU-6050位姿。
, `/ V8 I1 P5 R' h7 N
% W5 O, [2 _! r# L; d$ z+ ~
ZJQU$P3UR1R4)729_G][%{S.png
; A- ?& h- U4 D. ^$ e/ u  J1 ]; O

0 t6 s% T3 `2 z3 [4 L+ ?  Z设置系统时钟及调试模式。* K1 @8 g. @  E: W5 \
# b# g( d: \( V1 o; P3 ^0 Q2 b
(~J`34%%TXA1IXKWT(T@WN7.png ) ~5 g4 D7 R, e2 _$ M  [1 ]2 C( S

  m4 }. b+ k* c4 C/ T) u: E, P设置时钟源。: t) \" m4 O4 j1 o: u
: D2 {, J& a' L, F
~VCI0F0AO}W_{IQM08)~J44.png 9 C& L$ p% Z" a3 `; A, ]0 X" E
1 C% r% d- R+ e  P$ k  j! O
全部设置好后,芯片引脚的分配情况。% b5 u# V8 h! W# S7 m

" G9 O3 |" c2 `- z+ K
SL6GUUTZ2Q)$R5{_){T9V@T.png ; n* m0 v" u7 o3 \' u

; |8 ^7 v% p8 s# {/ Y4 a+ g3 w在时钟配置里设置时钟最大频率。
+ _- u/ Z8 P/ N9 W6 k* w/ ~& V
( _; z' r! z, F( n+ p% n. c4 h
3_0F%FN3W27QQvC$O5S$I.png
: Z; x  F, I& q' @$ p
" p+ \) W- l& ^" v2 w3 _在工程管理——>代码生成器中进行设置
8 \5 ?/ T' e2 d* \
# z$ e# N  r1 m2 Z- q6 `% U' j
2ET9LUB057L`M4NNOJ@@HRK.png 9 T0 H7 \3 y* p

( X$ N8 a) Y- q5 y! F. W点击此处,生成代码
( ?7 w% W# I9 v" |! U) v$ y- `' `) j. J* e$ V7 g
NB$AP684`5LM@ZCOK85OHM6.png
( l4 C5 Q7 t7 n' `" w0 C$ Q
* L- l2 A$ `, r) M
在工程文件夹中创建iCode文件。
- O' G7 ?6 _# F8 w2 l$ r% f. t
5 m' U) T# r' ^, C5 m4 _3 m1 ]
~3XUPXUKJ3X311X9L%MRRSX.png 3 o% M1 m/ R6 {. ~' o4 ~

8 Q4 g# x3 v7 q) z9 M% G1 u- ]将MPU-6050、卡尔曼滤波库文件复制到iCode文件中。
0 ^: C7 s: e- |* k; l8 v1 X. k- U! c- I" }/ m8 {
XFBF%G7M)$$FIFDRGQDM6W1.png
* k7 j' \/ X9 o- K" a# ~# A
/ M! j6 e  T. V) J" L点击Debug,刚刚添加的驱动文件全部在工程文件中显示出来。& o. w% W" ~. A' y8 S
$ ^9 v7 u& l1 E% m( t
Y%XW13SG7SJ%JBPZA1JIEAH.png 9 ^& X# G- }% R" R# z

: w; e2 f8 w4 m) E4 p( P按照下列方式设置MPU-6050、卡尔曼滤波驱动文件的编译路径。
3 `7 }( m8 X# x2 Z8 v7 H/ I" k) L2 S5 ^/ {: |, [2 b0 B" k
DJZL)$_$FQL20QWJM(8)A.png 1 _' m8 a( C- e+ G2 u

8 r+ a, Y# ?# p, M5 @3 U5 C
WQUT]3V991@5_$%1Q%8XS`S.png
8 Y$ T0 E, j' p+ _
2 P: _, J+ j5 I2 }3 P
然后再main.c文件中添加如下代码,用于重定义printf函数,用于串口输出;获取系统时间,用于滤波。
( J% V; f4 `! Y" [, J' Y" e
  1. /* USER CODE END Header */3 Y# m0 t  }7 d, c& `
  2. /* Includes ------------------------------------------------------------------*/
    , k4 `. H: t2 A( y' E/ F( U& n
  3. #include "main.h"
    - Z, x0 }: _- C/ Z7 `8 A
  4. #include "i2c.h"3 k5 _+ B+ ?  a+ `7 e( `, W
  5. #include "usart.h"+ \* F) @! n. ?/ B1 e6 v% {
  6. #include "gpio.h": Q: ]8 l8 D+ X, q2 D+ `: Q  C& ^

  7.   s" `% \6 O. t# B
  8. /* Private includes ----------------------------------------------------------*/) T( E& q3 W5 D5 Y! N$ q% c0 K- M
  9. /* USER CODE BEGIN Includes */6 v' ~% O: N' D. N: Z0 i; ]* V
  10. #include "mpu6050.h"2 ]$ @, n8 Y2 a) \# V6 r
  11. #include <stdio.h>
    8 s" j' T5 E9 d+ |; z
  12. /* USER CODE END Includes */# _/ d- {! N8 N* u8 N

  13. 3 G( P1 T; w. D! V# F( J$ |
  14. /* Private typedef -----------------------------------------------------------*/; Y9 m7 g+ A9 Z$ J. J. _
  15. /* USER CODE BEGIN PTD */
    : ?" ]5 Q; I7 E9 V

  16. ) N5 ?- F" K5 o3 T7 V2 _* R' T$ X5 X
  17. /* USER CODE END PTD */3 K7 q* W+ [( h7 c% Z$ K$ o

  18. 0 k  w' T  k9 @8 x9 V
  19. /* Private define ------------------------------------------------------------*/  @5 ]8 @2 J7 F6 ^6 J6 V
  20. /* USER CODE BEGIN PD */
    * S/ V% j. V# U, w6 T% p" \, q4 L" T
  21. /* USER CODE END PD */
      j& N$ }2 Y3 h

  22. - J8 |; G) I, k" X- Q
  23. /* Private macro -------------------------------------------------------------*/
    1 h4 s' Q$ R6 a. _
  24. /* USER CODE BEGIN PM */6 x& t5 L6 R$ F9 s

  25. 1 }; F( X0 r9 F
  26. /* USER CODE END PM */3 J; ^" u8 ~8 g/ x+ g8 r" Z

  27. ' J  V5 @8 q% n0 P' K
  28. /* Private variables ---------------------------------------------------------*/, u' \4 {4 G6 x/ a

  29. & u0 I! g, ^9 ?! @3 `
  30. /* USER CODE BEGIN PV */" g  h' ]0 Y, w/ e( a- Z& n, P
  31. 8 Z5 ?+ x- r' B' ]# j
  32. //重定义printf函数,用于串口输出
    8 \" [. ~  q' w0 W3 `' \
  33. #ifdef __GNUC__* |1 |' U) O0 r4 n/ P0 b" c9 X& P" ]
  34. #define PUTCHAR_PROTOTYPE int __io_putchar(int ch), W4 o1 L. g4 O
  35. #else
    + S8 s& S+ \* v$ g+ V" W
  36. #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    $ ^, M. `+ i2 F7 S
  37. #endif4 W, O6 u- |0 \2 C' c- c
  38. * |1 `6 y" ^" o7 W# n
  39. PUTCHAR_PROTOTYPE, c' w& A  G9 x
  40. {0 n6 K' h" O9 i& r' M
  41.         HAL_UART_Transmit(&huart1, (uint8_t*)&ch,1,HAL_MAX_DELAY);
    ) H. D5 V9 [; d; P2 J( K( |
  42.     return ch;
      I- K! v; V" }; z- S; |
  43. }- y4 Q5 z; Z* L5 @6 B
  44. /* ---------------------------------------------------------------------------*/( K8 g$ R3 b0 G0 P* V6 K
  45. 1 b( o  x$ [0 l5 O% L% r# j) C
  46. //获取系统时间,用于卡尔曼滤波3 h8 ~3 L1 I; O0 z
  47. __STATIC_INLINE uint32_t GXT_SYSTICK_IsActiveCounterFlag(void), a7 q; W: Z5 \5 A; |5 x
  48. {/ G+ ]; u* w! Y6 k6 W" x
  49.   return ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == (SysTick_CTRL_COUNTFLAG_Msk));, b: Q. q4 Z. u
  50. }5 i' }! W% N( G4 @( ]
  51. static uint32_t getCurrentMicros(void)
    5 E. U& d1 D0 J: ^3 J
  52. {6 N6 g& l# S! J$ w/ H
  53.   /* Ensure COUNTFLAG is reset by reading SysTick control and status register */
    1 \3 n" ^6 o: B
  54.   GXT_SYSTICK_IsActiveCounterFlag();
    ) l1 G6 E- t- Y# N/ z
  55.   uint32_t m = HAL_GetTick();& m1 g1 J/ {  n0 F$ X' J) d5 w1 }( ?
  56.   const uint32_t tms = SysTick->LOAD + 1;
    5 V6 w+ o  D: U. B3 `
  57.   __IO uint32_t u = tms - SysTick->VAL;  U  \. n6 i! ^/ B3 I
  58.   if (GXT_SYSTICK_IsActiveCounterFlag()) {
    2 n6 {% B6 R# k3 F
  59.     m = HAL_GetTick();
    ! h. \% D# y% j4 G/ E# s
  60.     u = tms - SysTick->VAL;
    ' p+ S6 e1 h1 X6 l; H. K+ a8 {
  61.   }
    5 M; L5 }$ j3 z
  62.   return (m * 1000 + (u * 1000) / tms);9 _; ~7 H) x0 l% T9 c7 g
  63. }, O$ C0 A) B' K" E
  64. //获取系统时间,单位us) D- c  M6 [% D
  65. uint32_t micros(void)0 ~' k  T; ~7 j0 ?3 s$ ]$ U
  66. {
    8 I0 c" j, m2 V3 E! f
  67.   return getCurrentMicros();
    . e% o3 W6 z, ]+ ]
  68. }
    9 `8 ^% Q) I3 o! B$ a, ^

  69. ) h# G- I( Z# ^+ H: _9 Z
  70. 7 V, C4 ^6 S$ @! ^
  71. /* USER CODE END PV */
复制代码
. K+ W! Q7 q# z+ {
使用printf()函数,还需进行如下设置:点击菜单栏 Project-->properties,弹出如下界面
, W6 G3 \4 ~- e# K% x8 D' }9 ?5 c5 _2 @( w, j1 f! n
[I](O`R0}YFTDA{2SX~2%)J.png $ g! y$ ^4 M/ H( i+ O' ~
0 F! W& ~" R7 T: }* r  X$ k! O
在main()函数中添加MPU6050初始化代码和发送位姿数据代码
: M4 T- q& v. P: Z
  1. int main(void)
    7 b/ ^0 q, I9 m8 ]- s7 P1 b
  2. {
    4 K& p1 I+ ]4 x  J6 u+ P7 T/ i
  3.   /* USER CODE BEGIN 1 */
    & [6 Z1 z6 U7 ]% \6 u6 h

  4. / E8 u2 n$ o( O, U- F: z
  5.   /* USER CODE END 1 */, ?$ W, j0 l( s& L% Y0 @7 y
  6. ) ~6 r. D. f: ]* t
  7.   /* MCU Configuration--------------------------------------------------------*/3 P# k! z; ?/ l/ M. \# G% l3 g

  8. - \$ }, V0 j( I& S
  9.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    " |8 U2 |) }' b5 _
  10.   HAL_Init();
    * `. o. ~- H; S; a
  11. 5 ?8 t" A4 z1 c8 _% c' j3 p& G7 ]
  12.   /* USER CODE BEGIN Init */4 d+ z( p" O, _; p9 ?1 G8 L" y

  13. " d3 y6 {& ?1 `1 C6 e
  14.   /* USER CODE END Init */; Q! W, }" S: C4 ^* z0 b
  15. 9 I9 M6 |8 Y* P! N8 |  ~
  16.   /* Configure the system clock */3 M% ~$ `' r5 i4 w$ {/ A) }0 r( D
  17.   SystemClock_Config();: L# ~. t0 m* L' M

  18. : d, V6 ~$ q  V+ H. n7 O
  19.   /* USER CODE BEGIN SysInit */
    3 I/ [8 N& M# X. F% V; c& s

  20.   O: y1 A1 i4 i2 ~
  21.   /* USER CODE END SysInit */
    & t8 `4 f$ o& E0 o4 m- W
  22. ! Z3 W- k6 n4 E. q) g2 {8 p5 v& }
  23.   /* Initialize all configured peripherals */
    % O- y' p. b! E  C
  24.   MX_GPIO_Init();9 H; }- ]3 m' B5 A( z% V% d7 |( P
  25.   MX_I2C1_Init();
    / g" V% w7 C- k. @( e! N
  26.   MX_USART1_UART_Init();+ s, L2 M  M6 }
  27.   /* USER CODE BEGIN 2 */
    ) `$ d' M" M: N; C
  28. # w  v) q; S7 {- j
  29.   while (MPU_Init()){                //如果初始化失败,继续初始化2 }; o8 S9 M7 m2 \4 s, n
  30.           printf("MPU_Init_Fail...");: C3 _5 Z; B: j; q9 c7 [/ J
  31.   }6 R+ _* m* m( s, w  b! ]% ^
  32. : M- F/ _; ?1 M. ?2 O4 v; f
  33.   /* USER CODE END 2 */
    ' Y- C6 b9 a7 O

  34. & |# \. q7 T) v3 t' i
  35.   /* Infinite loop */$ T9 J& E' y- C5 x
  36.   /* USER CODE BEGIN WHILE */; p! ~1 j& k# @/ z
  37.   while (1)
    $ ?, y% q% D. l* C
  38.   {2 B, o( a. D( q: Z6 b
  39.           GetAngle();. J2 R5 D  }) m+ L8 d9 E4 _; ^
  40.           GetPitch();
    0 {7 S( U; h* Z' ^$ A. f* ?4 f, b% `
  41.           HAL_Delay(10);
    , U% @$ M( Y7 w$ O: U& ]
  42.     /* USER CODE END WHILE */& a6 Y5 p5 I+ f2 j+ m( b
  43. - x3 G7 B' ?( H& F7 A
  44.     /* USER CODE BEGIN 3 */0 N- R% H% l6 ~1 R& z0 J. M+ ]
  45.   }
    ! f4 S) t5 D" p* w6 m8 e% E- v
  46.   /* USER CODE END 3 */6 @. o) Y3 p1 E. u+ j
  47. }
复制代码

. G1 {8 W8 l  Q点击,下载程序: c: e& T. W6 q$ L
/ D% l/ d2 P! w3 M4 K8 G0 c
{90_LT8S2ACLQ4(5~ZX943B.png ; b; {& h  v# O5 H! k
( v: K. Y5 O8 y& e
程序烧录好后就会通过串口通讯向上位机发送位姿数据(加速度计、卡尔曼、一阶二阶互补滤波计算的位姿),上位机我用的是Arduino编译器来接受并显示数据。设置好波特率后即可显示数据。- Z7 Y5 h0 F, F* k, e

( O( K  G& k  L, g H5PKUP6Y02TULI6(N)Y_NZ5.png 0 `# e; h% b. J* p
0 ~* y8 u3 ~5 P! a8 P2 g( P) X
作者:DIY攻城狮 6 g6 L9 t5 G7 d+ x
6 ?" ?" _+ z$ |. G* `' r

3 J; ?: \: Z( _( W
收藏 评论1 发布时间:2023-2-9 17:00

举报

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

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

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版