/ T u2 f! J) G7 ]. a; _
9 y* i' C- W- u
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
' 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
9 X9 a3 U8 E" X4 }' G互补滤波、卡尔曼滤波计算位姿示例& \+ U1 g) H$ j6 I4 O$ x2 M2 X* _
4 j7 b, B9 F, t& M
. 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
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 b. Z# S8 e+ {/ H0 Q
- float K1 =0.1; // 对加速度计取值的权重 C; t4 ~7 [/ f! V* {: ]
- float FirstOrder_Pitch;* u# w5 C, a) n8 Q4 o2 a. A
6 z+ J: r* u6 W6 S8 g; ]- float FirstOrder(float newAngle, float newRate, float dt)//采集后计算的角度和角加速度
4 |- B1 C! n3 ~ - {
2 B c2 p# c0 S - FirstOrder_Pitch = K1 * newAngle + (1-K1) * (angle + newRate * dt);
* r7 o0 q8 S; G( O% @ - return FirstOrder_Pitch;8 ]5 g6 t% m1 d/ h# X0 \' |2 P: x
- }
复制代码 w" `* n- f% s7 a
二阶互补算法如下:/ C& v: {, j3 [9 i( o
- //二阶互补滤波; `6 ~' _# I) G6 j4 P$ q9 V( f7 s G# l
- float K2 =0.2; // 对加速度计取值的权重
: s8 K, _- Y6 q" W( |# J! ? - float x1,x2,y1;7 _0 u* W& d2 ~" I; M
- float SecondOrder_Pitch;
+ Y! w0 R" m- @ - 8 e4 Q3 D- h1 n" z, c% O- }
- float SecondOrder(float newAngle, float newRate, float dt)//采集后计算的角度和角加速度
?' g" F# `& d2 u+ Q7 V - {
/ H, Y/ y4 |) k: o. L0 k/ V - x1=(newAngle-SecondOrder_Pitch)*(1-K2)*(1-K2);' c) `6 q% z8 j& a$ p2 x
- y1=y1+x1*dt;
/ U) E% o+ U$ I0 L# x3 f3 w - x2=y1+2*(1-K2)*(newAngle-SecondOrder_Pitch)+newRate;
( |8 ^% n! L* N% j8 Y4 }9 W( V% O - SecondOrder_Pitch=SecondOrder_Pitch+ x2*dt;
* [% G r, N; ?# M% k - return SecondOrder_Pitch;) w: J: I8 ^+ R, e3 \
- }
复制代码
; ?0 k1 y% `0 z$ o# x; n ~2 E二、卡尔曼滤波
9 N9 A0 o5 |4 l5 V) S+ y* u- /* Kalman filter variables */
* ]/ _: `& S3 L; o$ L3 s8 K - float Q_angle = 0.001; // Process noise variance for the accelerometer
9 Q& S6 K8 q6 ]4 Z( ]8 j! k - float Q_bias = 0.003; // Process noise variance for the gyro bias+ P: A, n0 K" C% P: p* h
- float R_measure = 0.03; // Measurement noise variance - this is actually the variance of the measurement noise8 T5 [0 ?8 s8 \+ D& [4 L
% L# g7 q ~9 l4 N, n K- 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
- 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
- float rate; // Unbiased rate calculated from the rate and the calculated bias - you have to call getAngle to update the rate
' ? ^( _! o; q- \* D - ( u8 Q) W3 r- \4 @
- 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
- 2 j! |+ [- S+ A h3 @0 a
) d* v1 M$ i" N$ C: y$ m5 S- // 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 - float Kalman_filter(float newAngle, float newRate, float dt) {) M% f* Q- Y6 @, h* }% L4 }
- // KasBot V2 - Kalman filter module - http://www.x-firm.com/?page_id=1456 Z2 K z; O! y( \
- // Modified by Kristian Lauszus
% l/ C+ q8 W3 k. L* g - // 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
6 S. e' g$ a4 M& x8 t& q7 H- // Discrete Kalman filter time update equations - Time Update ("Predict")" k" l+ d5 W5 e- q
- // Update xhat - Project the state ahead: t# {4 y2 ~# J! Q
- /* Step 1 */
5 B* X& F1 u y l4 u$ n; d - rate = newRate - bias;" G1 M5 I I4 ^9 G9 v8 ^! f0 v8 m
- angle += dt * rate;
1 P9 e% `# M: `7 D& _5 A
3 a7 ~8 d6 m- p3 _3 v- // Update estimation error covariance - Project the error covariance ahead1 L$ R' g. o+ p" R
- /* Step 2 */1 S: O1 E# `* O1 k- p
- P[0][0] += dt * (dt*P[1][1] - P[0][1] - P[1][0] + Q_angle);
$ e; z" T" X2 t0 N) X& @, F9 [ - P[0][1] -= dt * P[1][1];2 V; l0 K5 t, O' k7 ^
- P[1][0] -= dt * P[1][1];) s& t2 o1 W, i7 n' ~
- P[1][1] += Q_bias * dt;7 D1 A6 `) X3 u+ b
2 m8 h8 V0 K8 w' H- // Discrete Kalman filter measurement update equations - Measurement Update ("Correct")
3 W l! p1 S7 g4 f - // Calculate Kalman gain - Compute the Kalman gain. |7 k n8 y8 e
- /* Step 4 */
9 @( D' G# T/ u, T - float S = P[0][0] + R_measure; // Estimate error5 v3 @# O" N+ \0 ~6 z
- /* Step 5 */! w t: i( j% \7 i! J7 _
- float K[2]; // Kalman gain - This is a 2x1 vector3 u: }" g! X% {& `
- K[0] = P[0][0] / S;" [) ^$ u2 \; A: S: F& i
- K[1] = P[1][0] / S;
: z% ^5 C9 m: `; |8 {( k! f" ?7 H
: ]7 ?- s/ F1 v- // Calculate angle and bias - Update estimate with measurement zk (newAngle)
' ?/ ^; l' D# X! G4 D- E - /* Step 3 */
; A8 L2 |0 s: o6 ~/ y - float y = newAngle - angle; // Angle difference8 m# _' @/ N7 s+ Z- t7 d
- /* Step 6 */
" y# \4 z; l: _% L* A: ? - angle += K[0] * y;3 H( R3 `4 I8 E
- bias += K[1] * y;
0 o- T' I; Y- c6 E/ M
& k' Q# K' g6 M4 {- // Calculate estimation error covariance - Update the error covariance7 G+ e. W6 w( H) o. P
- /* Step 7 */+ B* F1 N% u- {4 s+ C; n5 G
- float P00_temp = P[0][0];# P, k$ P6 H1 k" |3 |; ?
- float P01_temp = P[0][1];
% h+ b9 K' ?; s# @0 W
% q3 b, X6 m3 _- P[0][0] -= K[0] * P00_temp;6 t7 `: ^- r( p9 C
- P[0][1] -= K[0] * P01_temp;1 ?/ k/ ^/ p) y. M( C0 S) s4 { Q; u
- P[1][0] -= K[1] * P00_temp;) O' c' ]' H& `+ p7 Y) A: e
- P[1][1] -= K[1] * P01_temp;' ?. c9 f- B& T( {- e
- 6 ?) A: R+ ^. s) F `4 V. C
- return angle;$ U- D) C* v4 |6 J2 G Q
- };
复制代码
- @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
- void Read_DMP(float *Pitch,float *Roll,float *Yaw)
4 z9 B6 q; O7 f8 J - {
" x5 \( m! P$ L - unsigned long sensor_timestamp;+ Z7 ^( D9 z% ?1 G) K. A5 E
- unsigned char more;
3 Y" d, R+ B! a+ l+ o - long quat[4];
( r( M" H, w" W - ' i* j# O( {9 }; Z- e$ o8 e
- dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors, &more);
: w/ u; T8 ^" b- s& k - if (sensors & INV_WXYZ_QUAT )% G @2 X4 e# Z! h% u
- {3 O4 _4 o I* N/ |+ I+ F
- q0=quat[0] / q30;2 [: ~' m7 ~, ?( u6 {, j% I& X- K
- q1=quat[1] / q30;
, Z! n6 ]$ w; x5 `0 X - q2=quat[2] / q30;8 e0 `- M! l* ?+ _* \ l
- q3=quat[3] / q30;
# P% W: K9 X% y3 V6 w4 @0 W- U - *Pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3;
4 Q3 F# p a6 \: y" Z! W+ ^2 X' D - *Roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll# @; O" x# \! w8 Z
- *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 - }' y% m2 w# @: ]0 D: d1 W. i
( t3 G9 {* |( J" \) z& K- }
复制代码 % {/ 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
: O* O8 m" O6 ?+ u+ r1 n3 y$ V# D8 k- X5 @2 T- o
输入工程名字,点击Next
; A6 h$ Q6 u) x9 B* t& g, A1 i& z* e# K% L5 y. ~; U, ^6 D8 \
" @: 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
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/ }
' 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
+ 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+ ~
; 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
) ~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
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
; 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
: 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
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
( 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 ]
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 {
* 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
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
1 _' m8 a( C- e+ G2 u
8 r+ a, Y# ?# p, M5 @3 U5 C
8 Y$ T0 E, j' p+ _2 P: _, J+ j5 I2 }3 P
然后再main.c文件中添加如下代码,用于重定义printf函数,用于串口输出;获取系统时间,用于滤波。
( J% V; f4 `! Y" [, J' Y" e- /* USER CODE END Header */3 Y# m0 t }7 d, c& `
- /* Includes ------------------------------------------------------------------*/
, k4 `. H: t2 A( y' E/ F( U& n - #include "main.h"
- Z, x0 }: _- C/ Z7 `8 A - #include "i2c.h"3 k5 _+ B+ ? a+ `7 e( `, W
- #include "usart.h"+ \* F) @! n. ?/ B1 e6 v% {
- #include "gpio.h": Q: ]8 l8 D+ X, q2 D+ `: Q C& ^
s" `% \6 O. t# B- /* Private includes ----------------------------------------------------------*/) T( E& q3 W5 D5 Y! N$ q% c0 K- M
- /* USER CODE BEGIN Includes */6 v' ~% O: N' D. N: Z0 i; ]* V
- #include "mpu6050.h"2 ]$ @, n8 Y2 a) \# V6 r
- #include <stdio.h>
8 s" j' T5 E9 d+ |; z - /* USER CODE END Includes */# _/ d- {! N8 N* u8 N
3 G( P1 T; w. D! V# F( J$ |- /* Private typedef -----------------------------------------------------------*/; Y9 m7 g+ A9 Z$ J. J. _
- /* USER CODE BEGIN PTD */
: ?" ]5 Q; I7 E9 V
) N5 ?- F" K5 o3 T7 V2 _* R' T$ X5 X- /* USER CODE END PTD */3 K7 q* W+ [( h7 c% Z$ K$ o
0 k w' T k9 @8 x9 V- /* Private define ------------------------------------------------------------*/ @5 ]8 @2 J7 F6 ^6 J6 V
- /* USER CODE BEGIN PD */
* S/ V% j. V# U, w6 T% p" \, q4 L" T - /* USER CODE END PD */
j& N$ }2 Y3 h
- J8 |; G) I, k" X- Q- /* Private macro -------------------------------------------------------------*/
1 h4 s' Q$ R6 a. _ - /* USER CODE BEGIN PM */6 x& t5 L6 R$ F9 s
1 }; F( X0 r9 F- /* USER CODE END PM */3 J; ^" u8 ~8 g/ x+ g8 r" Z
' J V5 @8 q% n0 P' K- /* Private variables ---------------------------------------------------------*/, u' \4 {4 G6 x/ a
& u0 I! g, ^9 ?! @3 `- /* USER CODE BEGIN PV */" g h' ]0 Y, w/ e( a- Z& n, P
- 8 Z5 ?+ x- r' B' ]# j
- //重定义printf函数,用于串口输出
8 \" [. ~ q' w0 W3 `' \ - #ifdef __GNUC__* |1 |' U) O0 r4 n/ P0 b" c9 X& P" ]
- #define PUTCHAR_PROTOTYPE int __io_putchar(int ch), W4 o1 L. g4 O
- #else
+ S8 s& S+ \* v$ g+ V" W - #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
$ ^, M. `+ i2 F7 S - #endif4 W, O6 u- |0 \2 C' c- c
- * |1 `6 y" ^" o7 W# n
- PUTCHAR_PROTOTYPE, c' w& A G9 x
- {0 n6 K' h" O9 i& r' M
- HAL_UART_Transmit(&huart1, (uint8_t*)&ch,1,HAL_MAX_DELAY);
) H. D5 V9 [; d; P2 J( K( | - return ch;
I- K! v; V" }; z- S; | - }- y4 Q5 z; Z* L5 @6 B
- /* ---------------------------------------------------------------------------*/( K8 g$ R3 b0 G0 P* V6 K
- 1 b( o x$ [0 l5 O% L% r# j) C
- //获取系统时间,用于卡尔曼滤波3 h8 ~3 L1 I; O0 z
- __STATIC_INLINE uint32_t GXT_SYSTICK_IsActiveCounterFlag(void), a7 q; W: Z5 \5 A; |5 x
- {/ G+ ]; u* w! Y6 k6 W" x
- return ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == (SysTick_CTRL_COUNTFLAG_Msk));, b: Q. q4 Z. u
- }5 i' }! W% N( G4 @( ]
- static uint32_t getCurrentMicros(void)
5 E. U& d1 D0 J: ^3 J - {6 N6 g& l# S! J$ w/ H
- /* Ensure COUNTFLAG is reset by reading SysTick control and status register */
1 \3 n" ^6 o: B - GXT_SYSTICK_IsActiveCounterFlag();
) l1 G6 E- t- Y# N/ z - uint32_t m = HAL_GetTick();& m1 g1 J/ { n0 F$ X' J) d5 w1 }( ?
- const uint32_t tms = SysTick->LOAD + 1;
5 V6 w+ o D: U. B3 ` - __IO uint32_t u = tms - SysTick->VAL; U \. n6 i! ^/ B3 I
- if (GXT_SYSTICK_IsActiveCounterFlag()) {
2 n6 {% B6 R# k3 F - m = HAL_GetTick();
! h. \% D# y% j4 G/ E# s - u = tms - SysTick->VAL;
' p+ S6 e1 h1 X6 l; H. K+ a8 { - }
5 M; L5 }$ j3 z - return (m * 1000 + (u * 1000) / tms);9 _; ~7 H) x0 l% T9 c7 g
- }, O$ C0 A) B' K" E
- //获取系统时间,单位us) D- c M6 [% D
- uint32_t micros(void)0 ~' k T; ~7 j0 ?3 s$ ]$ U
- {
8 I0 c" j, m2 V3 E! f - return getCurrentMicros();
. e% o3 W6 z, ]+ ] - }
9 `8 ^% Q) I3 o! B$ a, ^
) h# G- I( Z# ^+ H: _9 Z- 7 V, C4 ^6 S$ @! ^
- /* 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
$ g! y$ ^4 M/ H( i+ O' ~
0 F! W& ~" R7 T: }* r X$ k! O
在main()函数中添加MPU6050初始化代码和发送位姿数据代码
: M4 T- q& v. P: Z- int main(void)
7 b/ ^0 q, I9 m8 ]- s7 P1 b - {
4 K& p1 I+ ]4 x J6 u+ P7 T/ i - /* USER CODE BEGIN 1 */
& [6 Z1 z6 U7 ]% \6 u6 h
/ E8 u2 n$ o( O, U- F: z- /* USER CODE END 1 */, ?$ W, j0 l( s& L% Y0 @7 y
- ) ~6 r. D. f: ]* t
- /* MCU Configuration--------------------------------------------------------*/3 P# k! z; ?/ l/ M. \# G% l3 g
- \$ }, V0 j( I& S- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
" |8 U2 |) }' b5 _ - HAL_Init();
* `. o. ~- H; S; a - 5 ?8 t" A4 z1 c8 _% c' j3 p& G7 ]
- /* USER CODE BEGIN Init */4 d+ z( p" O, _; p9 ?1 G8 L" y
" d3 y6 {& ?1 `1 C6 e- /* USER CODE END Init */; Q! W, }" S: C4 ^* z0 b
- 9 I9 M6 |8 Y* P! N8 | ~
- /* Configure the system clock */3 M% ~$ `' r5 i4 w$ {/ A) }0 r( D
- SystemClock_Config();: L# ~. t0 m* L' M
: d, V6 ~$ q V+ H. n7 O- /* USER CODE BEGIN SysInit */
3 I/ [8 N& M# X. F% V; c& s
O: y1 A1 i4 i2 ~- /* USER CODE END SysInit */
& t8 `4 f$ o& E0 o4 m- W - ! Z3 W- k6 n4 E. q) g2 {8 p5 v& }
- /* Initialize all configured peripherals */
% O- y' p. b! E C - MX_GPIO_Init();9 H; }- ]3 m' B5 A( z% V% d7 |( P
- MX_I2C1_Init();
/ g" V% w7 C- k. @( e! N - MX_USART1_UART_Init();+ s, L2 M M6 }
- /* USER CODE BEGIN 2 */
) `$ d' M" M: N; C - # w v) q; S7 {- j
- while (MPU_Init()){ //如果初始化失败,继续初始化2 }; o8 S9 M7 m2 \4 s, n
- printf("MPU_Init_Fail...");: C3 _5 Z; B: j; q9 c7 [/ J
- }6 R+ _* m* m( s, w b! ]% ^
- : M- F/ _; ?1 M. ?2 O4 v; f
- /* USER CODE END 2 */
' Y- C6 b9 a7 O
& |# \. q7 T) v3 t' i- /* Infinite loop */$ T9 J& E' y- C5 x
- /* USER CODE BEGIN WHILE */; p! ~1 j& k# @/ z
- while (1)
$ ?, y% q% D. l* C - {2 B, o( a. D( q: Z6 b
- GetAngle();. J2 R5 D }) m+ L8 d9 E4 _; ^
- GetPitch();
0 {7 S( U; h* Z' ^$ A. f* ?4 f, b% ` - HAL_Delay(10);
, U% @$ M( Y7 w$ O: U& ] - /* USER CODE END WHILE */& a6 Y5 p5 I+ f2 j+ m( b
- - x3 G7 B' ?( H& F7 A
- /* USER CODE BEGIN 3 */0 N- R% H% l6 ~1 R& z0 J. M+ ]
- }
! f4 S) t5 D" p* w6 m8 e% E- v - /* USER CODE END 3 */6 @. o) Y3 p1 E. u+ j
- }
复制代码
. G1 {8 W8 l Q点击,下载程序: c: e& T. W6 q$ L
/ D% l/ d2 P! w3 M4 K8 G0 c
; 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
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 |
MPU-6050、卡尔曼滤波库在哪里呀