# P) z1 T, g+ ?7 {
' A9 n7 @1 H3 Y6 b D0 ~
- ~2 x: Z2 w& E! H+ T/ a6 X
/ Q+ m# D$ x8 C5 {# v; r. q5 k) v% u' ^+ _ t
背景6 j- Y3 G# |6 l9 \# }7 [/ K
本文用于记录平衡自行车的制作过程,及制作中遇到的问题;总体方案如下:采用STM32F103C8T6作为主控单元、MPU6050作为位姿采集单元、无刷电机带动动量轮调节小车平衡、1S锂电池配合5V和12V升压模块作为电源、蓝牙模块用于和微信小程序进行无线遥控及PID调试、舵机用于控制行驶方向和支撑小车站立。
' U+ e6 }* n- N, R
6 [! |- G L- s9 yMPU-6050简介! j* w4 Y# J* k4 o9 e& z
MPU-6050集成了 3 轴陀螺仪、3 轴加速度计及温度传感器,并且预留一个IIC 接口,可用于连接外部磁力传感器,并利用自带的数字运动处理器(DMP: Digital Motion Processor)硬件加速引擎,通过主 IIC 接口,向应用端输出完整的 9 轴融合演算数据。; K9 U( y! H2 M5 Z3 Y" w
) _9 H* S+ K' ~3 R) l
MPU-6050 对陀螺仪和加速度计分别用了三个16 位的ADC(0~65535),将其测量的模拟量转化为可输出的数字量。且传感器的测量范围都是可以根据需求设定的,陀螺仪可测范围为±250,±500,±1000,±2000°/秒(dps),加速度计可测范围为±2,±4,±8,±16g。
9 E) _6 l7 u* K# `( A1 ~
: K. c- e" H4 d7 s3 u# ]& p2 ^) M
! ?: e5 ~% t) L- |: D5 R1 W
0 D0 {" H$ \" s& V& L7 p; f. o. ^
MPU-6050姿态获取与处理(惭愧,我也一知半解)' h" C9 M0 z3 Z, `! w+ l+ e
理论上只需要对3轴陀螺仪的角度进行积分,就可以得到MPU-6050的姿态数据。实际上由于陀螺仪受噪声的影响,只对陀螺仪积分并不能得到完全准确的姿态,所以需要用加速度计进行辅助矫正,常用的方法有三种:互补滤波、卡尔曼滤波、硬件DMP解算四元数。# o9 W7 E% ~( h3 ~
+ g: p2 T: t- |$ \0 Y2 a
1 [8 Y; Q4 i) ]* _7 o o0 c3 [互补滤波、卡尔曼滤波计算位姿示例
; u/ {2 k2 A" V6 B4 G1 P. y0 _" W) G0 t8 K* t
: Q( I- @( u+ I t
: \" R& P" M; v' _6 s互补滤波、卡尔曼滤波计算位姿示例
7 n F" T4 i! C1 V3 Y4 A) Y
4 ]1 R+ v* @2 m* K8 J- x) z, Y6 I, P 除了使用加速度计计算的噪声较大,卡尔曼滤波,一阶互补滤波,二阶互补滤波看着差不多,O(∩_∩)O哈哈~(四元数计算的示例就不演示了,有兴趣的可以自己研究一下。)' p# d8 c, M& E, ?( [
1)一阶互补滤波:因为加速度计有高频噪声,陀螺仪有低频噪声,需要互补滤波融合得到较可靠的角度值。* y8 d6 W8 T% h4 f
2)卡尔曼滤波:利用线性系统状态方程,通过系统输入输出观测数据,对系统状态进行最优估计的算法。由于观测数据中包括系统中的噪声和干扰的影响,所以最优估计也可看作是滤波过程(来源于百度词条,我也看不懂这说的啥)。3 u2 I; p% H2 X# J4 A' `, ?
3)硬件DMP解算四元数:DMP将原始数据直接转换成四元数输出,运用欧拉角转换算法,从而得到Yaw、Roll和Pitch(使用四元数计算位姿,好像会将初始化的位姿设定为零位)。
- |9 ^# N* S7 W' p N( C) l3 E: U; Z$ A8 B C/ U4 I' _
$ L' [; v5 q! h0 V7 y
- B1 s5 Y2 _6 O3 V% S
: C ?; h! x& e( k$ O
一、一阶互补滤波算法
3 q* K# Z! I( {; ` MPU-6050 的加速度计和陀螺仪各有优缺点,三轴的加速度值没有累积误差,通过简单的计算即可得到倾角,但是它包含的噪声太多(因为待测物运动时会产生加速度,电机运行时振动会产生加速度等),不能直接使用;陀螺仪对外界振动影响小,精度高,通过对角速度积分可以得到倾角,但是会产生累积误差。所以不能单独使用MPU-6050的加速度计或陀螺仪来得到倾角,需要二者进行互补。一阶互补算法的思想就是给加速度和陀螺仪不同的权值,把它们结合到一起进行修正,通过加速度和角速度就可以计算 Pitch 和 Roll 角(单靠 MPU6050 无法准确得到 Yaw 角,需要和地磁传感器结合使用)。& P" i" [8 m1 y! a$ b( A8 V+ p
* A, e. u+ @! ]3 K- w2 j I/ ~- l3 c
一阶互补算法如下:- [' D9 z w2 y
- //一阶互补滤波" A. x, t4 c/ H% I! k$ `5 l6 q
- float K1 =0.1; // 对加速度计取值的权重
* c8 v8 y- M8 Z2 ^# J+ T - float FirstOrder_Pitch;
# y' E% b; D( `- a- z& u* \1 G - $ a `* [- B& V! G" W1 _2 ~) n) u
- float FirstOrder(float newAngle, float newRate, float dt)//采集后计算的角度和角加速度& E; A' N7 F$ n) p* G+ V
- {' [' w2 \: @4 G3 Z
- FirstOrder_Pitch = K1 * newAngle + (1-K1) * (angle + newRate * dt);+ H! Z! b. ~1 s
- return FirstOrder_Pitch;. I; w K$ R. Y; Q9 F0 ?
- }
复制代码 0 R: D# s, {3 b f' h
二阶互补算法如下:7 ^, _8 V% r. C) X# j4 E9 s
- //二阶互补滤波
0 B8 ^* d$ s( W% u - float K2 =0.2; // 对加速度计取值的权重9 a( s) L; }: C& k
- float x1,x2,y1;
# s3 }4 T# U) E6 c& L; J - float SecondOrder_Pitch;
! W% W! W2 u( o7 c( j
7 b9 J: [. R2 @* b+ b% l- float SecondOrder(float newAngle, float newRate, float dt)//采集后计算的角度和角加速度1 Q4 @6 l, b" y
- {
+ L" A4 ~; w2 M - x1=(newAngle-SecondOrder_Pitch)*(1-K2)*(1-K2);
6 g9 u5 x" R$ m k - y1=y1+x1*dt;
1 F x- A4 A: c+ n& y - x2=y1+2*(1-K2)*(newAngle-SecondOrder_Pitch)+newRate;6 F7 H5 Z. _1 d6 {2 T
- SecondOrder_Pitch=SecondOrder_Pitch+ x2*dt;+ }5 y8 l5 f+ B3 d2 h8 O
- return SecondOrder_Pitch;
8 {+ g6 H$ ?5 e9 k6 [ - }
复制代码 & r1 R' }0 w8 a) ^1 K5 V; i& U
二、卡尔曼滤波
! `) o* A4 s5 \7 q- L6 F, n1 i- /* Kalman filter variables */* m+ r+ N, R7 ~6 i. ]7 p
- float Q_angle = 0.001; // Process noise variance for the accelerometer' G' F. b8 J" {) k }2 K% {' U
- float Q_bias = 0.003; // Process noise variance for the gyro bias0 d4 p" ?; g+ R* W
- float R_measure = 0.03; // Measurement noise variance - this is actually the variance of the measurement noise
( ^1 u2 z P2 G - * B" p0 y8 ^2 |! `/ p
- float angle = 0.0; // The angle calculated by the Kalman filter - part of the 2x1 state vector
1 c+ q+ v) O( S2 z3 ^ r, I - float bias = 0.0; // The gyro bias calculated by the Kalman filter - part of the 2x1 state vector
8 [* @' J1 P4 N/ b; j# u% _ - float rate; // Unbiased rate calculated from the rate and the calculated bias - you have to call getAngle to update the rate+ J$ T# x2 f# d9 z; t
$ Z! g. S' x2 r- float P[2][2]={0.0,0.0,0.0,0.0}; // Error covariance matrix - This is a 2x2 matrix. i$ f* F" o5 i$ K% h
; c. R1 x, l6 Z; Z* x7 j- 6 p' Q" Q% {& N. Q5 ]# \+ O
- // The angle should be in degrees and the rate should be in degrees per second and the delta time in seconds8 t7 O. Y8 W/ Y" X+ W; {4 }' J
- float Kalman_filter(float newAngle, float newRate, float dt) {
, e* y/ O2 `: K) C; p' f! O - // KasBot V2 - Kalman filter module - http://www.x-firm.com/?page_id=1451 [% i' n3 r0 @* d4 U6 s' h$ n
- // Modified by Kristian Lauszus2 E, s' d* H0 W) [( i
- // See my blog post for more information: http://blog.tkjelectronics.dk/2012/09/a-practical-approach-to-kalman-filter-and-how-to-implement-it1 y# _: p" {( z V/ E
- 1 Y8 T, ]4 B% `) d
- // Discrete Kalman filter time update equations - Time Update ("Predict")
: C% @' d7 V" ^( o* d! ? - // Update xhat - Project the state ahead
& E/ v. }. [; { - /* Step 1 */5 \* d4 M3 W9 A
- rate = newRate - bias;: d8 m D. d+ \( ]+ l: E+ d* l
- angle += dt * rate;
4 F) F* e7 l' e5 C& J. H - 2 ^) k4 U& a+ l |1 L/ t4 l m; Z
- // Update estimation error covariance - Project the error covariance ahead+ B: l2 K* ~5 _8 \& @" m& W
- /* Step 2 */+ \$ ]5 e8 p! N L V
- P[0][0] += dt * (dt*P[1][1] - P[0][1] - P[1][0] + Q_angle);# d0 Z0 H' H8 Z# C$ U! U# m; X7 J
- P[0][1] -= dt * P[1][1];
. `4 {; g" Y+ R$ V- U0 w5 m - P[1][0] -= dt * P[1][1];& _+ f+ i) y) }# y
- P[1][1] += Q_bias * dt;* i9 K$ G" A0 ?) Y! q5 E
- 2 r4 d3 P$ s3 [+ r
- // Discrete Kalman filter measurement update equations - Measurement Update ("Correct")
" a8 a& x B9 n; X* w - // Calculate Kalman gain - Compute the Kalman gain. ^" f2 b5 Z: x9 Y: C- l6 c
- /* Step 4 */: C$ j' u- a- b
- float S = P[0][0] + R_measure; // Estimate error% L* J- e# e0 A4 U1 |: ]/ ?
- /* Step 5 */
! ^* Z7 ^+ o1 U% W, x! G8 h, j - float K[2]; // Kalman gain - This is a 2x1 vector2 S; e$ a M5 j5 g5 e+ f% `% x- L" \3 i
- K[0] = P[0][0] / S;
8 P. Y7 a' R7 m5 c6 u' Z1 P5 L - K[1] = P[1][0] / S;* e$ u3 S% L. j0 B( ]3 S
9 C. N+ ?4 e! i8 S; h- B& r- // Calculate angle and bias - Update estimate with measurement zk (newAngle), B# W- {- }1 d. f
- /* Step 3 */) U- j0 N- B! u) q/ r6 M
- float y = newAngle - angle; // Angle difference
/ H$ R' H( a3 z, X# v4 L - /* Step 6 */
5 s1 Q+ J& o7 Z4 Y% w* F - angle += K[0] * y;
# a p$ h! ?3 X/ G7 D$ S" ^ - bias += K[1] * y;0 ~9 ?% K( U, G- n1 s# I0 }8 d, o
- 7 [8 l: K7 k/ b! @0 Q$ R
- // Calculate estimation error covariance - Update the error covariance7 W/ w! s+ t- `) e3 i% S2 R. {4 s% i$ O3 t
- /* Step 7 */
* r2 r( `$ l5 d2 u# h f" f - float P00_temp = P[0][0];
5 o1 m3 D/ w* u$ B3 g d - float P01_temp = P[0][1];
9 s, w) A% B$ W5 R) z2 N
9 M% U1 w9 v" h$ f- P[0][0] -= K[0] * P00_temp;
$ J2 H* n- h' y2 Z- l' k - P[0][1] -= K[0] * P01_temp;
7 A- S7 z9 ]) Y7 ]- @ - P[1][0] -= K[1] * P00_temp;& Q7 N8 O0 b) q) _' ]
- P[1][1] -= K[1] * P01_temp;
' a) A2 i3 f0 t6 f5 d; o( l& L3 j - 1 o/ ?# D- j6 c
- return angle;
- t5 f0 z, ?" f% d# C$ p5 x& R6 P - };
复制代码
$ t1 Z1 D3 x) U4 C, q- s! ]三、四元数法! ~3 d4 W( W& e/ q5 J
$ Y7 A* W2 ?: f* r+ ~4 a MPU-6050 自带了数字运动处理器,即 DMP,并且InvenSense公司 提供了一个 MPU-6050 的嵌入式运动驱动库,结合 MPU-6050 的 DMP,可以将我们的原始数据直接转换成四元数输出,而得到四元数之后,可以很方便的计算出欧拉角,从而得到 Yaw、Roll 和Pitch。$ `" R9 f* \/ k$ m Z6 ^' t6 n& P, U s
; T: e6 ^1 @1 J# N+ O 使用内置的 DMP,大大简化了代码设计,且 MCU 不用进行姿态解算过程,大大降低了 MCU 的负担,从而有更多的时间去处理其他事件,提高系统实时性。. K k) e; J7 v. o7 r
- void Read_DMP(float *Pitch,float *Roll,float *Yaw)2 [: g8 S7 E' D/ w* j/ _
- {
8 L" f4 I) \2 q C - unsigned long sensor_timestamp;9 f4 q; @$ n( i7 ^) d0 i* N
- unsigned char more;2 ~6 i i4 Z N2 q; m
- long quat[4];4 E* F' x0 a& a- s1 \
) z. y/ p; W) Z4 Q- dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors, &more);
" N5 \9 {7 A# T. `0 P - if (sensors & INV_WXYZ_QUAT )
- c7 T7 a6 i3 h0 F( T - {4 I+ U+ b& O: O, {( n
- q0=quat[0] / q30;/ \) s1 f0 d ^, t) w: Y* i
- q1=quat[1] / q30;
6 x/ ?' {, z$ m5 P0 R - q2=quat[2] / q30;7 s3 b6 v0 e- h3 w
- q3=quat[3] / q30;
. m8 Z8 t, h1 ~+ m$ |; Y2 b - *Pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3;
, {( T3 ?5 E" b& g! r7 W u - *Roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll" c+ F' a( p1 ]' `
- *Yaw = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3; //yaw0 p& n$ n7 D. O& C! s3 A
- }
7 p7 m. U. u. k' s, \6 V% c - & p' X1 E+ w$ X. Z( ~! Q
- }
复制代码 / {( A/ E6 d& A9 B$ @, ?
以下是我测试一阶互补滤波和卡尔曼滤波项目的搭建过程,老鸟请忽略。
2 p0 J2 u/ ^/ S1 C* b) `9 e& q$ U r4 l2 v
固件开发采用STM32CubeIDE开发,对于我这种菜鸡来说还是很友好的。) O8 A$ c5 [ q
G1 d& a' R2 n: z. v9 A 我使用的是STM32CubeIDE1.9.0,打开软件后,依次点击 File-->New-->Stm32 Project,弹出如下界面,输入MCU型号-->选择封装形式-->Next
9 S" l" Y% j( B; T9 j$ W* ]: E9 w/ ^: v) u) L7 X/ H2 s
6 k* i8 O8 E& c7 z0 c' _8 A
! r: m, |, Q# p& i+ E0 B: P# l2 X# G
输入工程名字,点击Next
. X; s+ l1 {& M/ E! c0 [
, g1 E: r* [2 P& ?& A N! \
9 ]8 w2 Z" g: P% f) I; W+ ]
+ _/ D$ s/ t; D5 u- [( k* R; h3 U设置完成后,点击Finish
. d% C4 C" U' g) S! j) k+ T" s( t, s1 L
: X" \4 Y, c; N6 I
6 h, i, {! i5 U# j& B) S8 V! A" }
进入MCU配置界面
0 \$ @" ^& `' D# a
8 p1 z$ [' o0 n9 Z$ Y+ X5 I$ s
7 C8 j3 L6 @ R4 A3 F
; `5 \0 Q' y* x2 z3 ]& ]设置I2C与MPU-6050通讯。5 E# Q7 U/ _4 s: C3 q
) f4 _ b. S% v/ T
4 |. W7 O! p3 L4 S% `5 G
; z- N9 b5 c5 u" F6 p设置串口与上位机通讯,实时显示MPU-6050位姿。
+ a6 {. J7 J5 O: _- {4 S: ~3 b7 R* ^3 S# h s- N5 Y4 O& q! N
: w. Q6 l1 V/ W( X6 F) O% N) W9 u. X' W6 X# V) o/ V9 \9 K
设置系统时钟及调试模式。) h4 i2 t- w, ?5 h: j
& U5 p6 c# E( ~; D
' c/ R5 t" C u0 r1 l( ?" W* x- N$ Z( t2 `/ D6 @
设置时钟源。
% k; P! f8 [; k- g8 ?4 n7 L1 }4 s' \1 J$ R
, s2 @# ?+ N. y2 \% i! o3 Q3 t0 J) `# x; R# H+ M
全部设置好后,芯片引脚的分配情况。* A# c% J/ O' ?* G% r" ~ _
" M% d0 n: G; e% i# A
) Z( S3 F3 J q9 F0 e
5 |) {1 t8 P/ I( y0 p: \. z$ |9 z在时钟配置里设置时钟最大频率。' }; k1 L0 J; X% m
5 U1 v- p3 s3 b( s5 [7 R
5 t$ ^+ q4 ^0 o: O0 A: ^( Q5 i" G, G6 p9 t3 x
在工程管理——>代码生成器中进行设置+ q6 }- N4 g8 c" c ]
/ x( C s; c6 \7 k; A0 o A K
5 Q* ?* |) e O' c( _6 J) u
$ ]& t) b$ Q& t, R3 s点击此处,生成代码* _* U7 k% L& ~2 w2 R( X3 y
2 _! r7 y! w, z
$ D2 d. n* |+ @' @/ T9 C
# E4 E, O. m; f$ \在工程文件夹中创建iCode文件。7 }$ N5 Z+ ?+ z* x8 H: P
/ O! d. ^& v& K1 t0 J6 P/ ]
( u) V# p4 y( \8 k
+ h( b/ z" j+ x+ B$ s; C* \6 s( G将MPU-6050、卡尔曼滤波库文件复制到iCode文件中。5 ]8 u5 h9 M( @' ^7 O
) ^$ }; B) j( `, E
4 }+ |2 p5 V8 P% V
+ Y) P% C, _5 I. @
点击Debug,刚刚添加的驱动文件全部在工程文件中显示出来。# y! M6 q( a A* O8 q7 M, r8 }
& v: ?2 }- M+ k( Q0 X7 a
7 W+ P* i- h, J+ {" ~! w- h; c* j
按照下列方式设置MPU-6050、卡尔曼滤波驱动文件的编译路径。- J4 @2 o: H, k% V0 }
& z. d" u- t5 ^) c5 V) {
2 o# E- ^/ V# V
# V" l+ _! K3 U3 F) _0 U
) J* X: o% t% W2 c0 m" L
9 n! g. N- O3 W. H; a然后再main.c文件中添加如下代码,用于重定义printf函数,用于串口输出;获取系统时间,用于滤波。
' r7 t$ q7 x$ @. F- /* USER CODE END Header */- b3 J7 u1 Z4 B, z# P
- /* Includes ------------------------------------------------------------------*/
+ S# X! G9 @7 d3 r - #include "main.h"
* ~; g& z2 M' n7 D - #include "i2c.h"
/ ^8 C u8 G( e5 n" O& P4 N - #include "usart.h"; K/ L* T' C7 {' d
- #include "gpio.h"6 a" O0 k' [8 I- p2 S0 X1 ~
- B3 x- b% ^& U1 b+ X) q- /* Private includes ----------------------------------------------------------*/
3 v8 U6 z( \- ~8 l* U$ |4 m+ d - /* USER CODE BEGIN Includes */+ E$ \/ b) C, c
- #include "mpu6050.h"
: _! u' Z/ q ]" Q' `$ o+ k- \7 e - #include <stdio.h>+ G, x' j/ z* M4 N* {4 ?. S
- /* USER CODE END Includes */
, j- n+ j- N4 i% N
! W0 `: r$ W6 D( m/ E4 m9 \, q! L- /* Private typedef -----------------------------------------------------------*/
- W9 E" ]! @( G( Z y - /* USER CODE BEGIN PTD */* ]! j, T# c, P& z. A- O- k; x
- 7 o$ T j: C* r4 F
- /* USER CODE END PTD */! Z$ l* b. f v$ K3 i) A3 e
/ R2 ~+ I3 b) C* U! R- /* Private define ------------------------------------------------------------*/
: _* M2 N! B/ a - /* USER CODE BEGIN PD */
Y6 C# I" F( e( O - /* USER CODE END PD */
# E4 N9 w+ a1 e - 3 n* a7 t+ c& }3 C: ]7 a
- /* Private macro -------------------------------------------------------------*/
! h, y( Z; E. b" C - /* USER CODE BEGIN PM */' T1 w% w& G+ P0 h! n: \
- a& {& y/ L9 r' C
- /* USER CODE END PM */5 d5 W4 j2 h6 }# w. M* m4 Y9 l
- 6 y0 }5 J% m1 [( D2 h% w* C, `9 D
- /* Private variables ---------------------------------------------------------*/
/ S# C6 ?7 u3 ~( m1 z' g, \
; {8 J5 p3 L+ m" C) |( N) O- /* USER CODE BEGIN PV */# Z0 f4 r. W% a- ~# S
- " _' j' s* p2 t; l
- //重定义printf函数,用于串口输出: s, u0 `2 `0 v; }
- #ifdef __GNUC__
$ }' h" I( x+ h# a3 L0 l; j - #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
+ m w0 e1 ^3 `, g" b - #else. b7 f( v- Z5 ]# r8 d0 t
- #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
' y L4 j6 E1 d# V0 [. Q1 C' P* ~ - #endif% V9 t5 \. }# \
- 9 t0 w4 r, n; V: m2 F
- PUTCHAR_PROTOTYPE
3 H4 [. B3 M% Q. V# C - {- I& }1 _7 ?: k/ y D2 W5 ~
- HAL_UART_Transmit(&huart1, (uint8_t*)&ch,1,HAL_MAX_DELAY);$ I* v7 J$ P( N: B* ?
- return ch;7 l* V5 ]- | D+ l% c
- }
3 C, r+ I A$ a! V - /* ---------------------------------------------------------------------------*/- x! p7 U' e; R# M* D7 d. `
: t1 @9 L8 |) s2 l; {, X4 |- //获取系统时间,用于卡尔曼滤波
' n' R O4 D& {1 F - __STATIC_INLINE uint32_t GXT_SYSTICK_IsActiveCounterFlag(void)7 z! b" o, M! S/ C$ l+ r. m: Y
- {6 N0 H& N' v. K8 J% I
- return ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == (SysTick_CTRL_COUNTFLAG_Msk));, @! b X3 `9 T: r" H7 j
- }
" m/ V* \/ K+ M p" U - static uint32_t getCurrentMicros(void)$ t0 X# u0 E/ P" |3 q ^
- {- g$ d5 P8 D3 }! j
- /* Ensure COUNTFLAG is reset by reading SysTick control and status register */ C3 K, } O' P2 n- h
- GXT_SYSTICK_IsActiveCounterFlag();" q& y. S2 N, i, y% {5 A( A' |$ v
- uint32_t m = HAL_GetTick();
# t3 x) z; [. d' l - const uint32_t tms = SysTick->LOAD + 1; D1 Q7 l3 _4 h' `% U$ ~/ \% S
- __IO uint32_t u = tms - SysTick->VAL;
: ]6 t5 @' ?) [7 k/ q - if (GXT_SYSTICK_IsActiveCounterFlag()) {
# {6 y3 h. X* J: ~" j* f - m = HAL_GetTick();
' Z: V) N- Y8 O9 k3 _# j2 T& R5 @ - u = tms - SysTick->VAL;
, @9 S2 U( b( P, ]& l6 s9 @ - }
7 L7 H/ r% ^3 Q6 H - return (m * 1000 + (u * 1000) / tms);/ [2 c; ~* F0 x
- }
6 q$ k7 @) }, V7 U7 ]; z - //获取系统时间,单位us
( n/ R# [3 k4 F% e7 |! | - uint32_t micros(void); L1 E W' _& o; s9 @
- {
- K: `- ^) o% l7 p; G9 L% w' j0 n: @# l - return getCurrentMicros();$ P) J/ |/ G5 }9 K0 x
- }+ s' O6 P# O5 i
- u& J- q6 j8 y
- " U, {8 ?6 k' \; t6 A( L
- /* USER CODE END PV */
复制代码
. n& O) J6 d0 U& Y$ Q% M0 G使用printf()函数,还需进行如下设置:点击菜单栏 Project-->properties,弹出如下界面
7 w& e/ R6 U/ Y! X7 ?" y: P8 Z3 Y {% f; j$ D m$ c
" D2 ~, q5 S0 J9 \0 ?! x- R4 m$ i; b2 L% r! W8 o! G9 {
在main()函数中添加MPU6050初始化代码和发送位姿数据代码
: }0 p. G5 l# \- int main(void)6 p6 M, [. \3 t) D
- { s5 u$ W* w- v% x7 M
- /* USER CODE BEGIN 1 */2 ^: F. V! l5 ^5 o4 \* J" ~: w3 @
: L+ R, M1 b5 g/ ]- /* USER CODE END 1 */
& n# X8 _8 z' J0 a
@, k+ g" }9 [5 v- /* MCU Configuration--------------------------------------------------------*// e) s9 x# W& Z4 [
- 9 u1 Y5 \3 v9 g3 j
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
! t: M# `' U. m+ }! b6 I - HAL_Init();8 {5 B) w2 q: i4 F1 F6 H, L4 h+ {2 {
- 6 v8 t( }3 d; S; ?8 I/ f7 m0 @
- /* USER CODE BEGIN Init */2 ^) Z0 y) Y. ~
- 6 r' k6 Y/ h3 d2 i2 c
- /* USER CODE END Init */
1 z( M2 ?& P! O5 p) z' F5 e
! ]0 O0 Z8 K& ]) u5 J1 T- /* Configure the system clock */, N( j) F0 V1 g$ O* m
- SystemClock_Config();
s3 j1 D7 j" P* r0 v8 i$ Z0 y - 0 A; n i$ w# K
- /* USER CODE BEGIN SysInit */# s$ C! S- T4 T9 \9 S$ R( f
+ {0 }" b6 P& R6 e& w0 _- /* USER CODE END SysInit */2 @# D2 K; _& d. c; H9 {2 b8 O, q
2 p& U3 ^" v* c6 }2 Y- /* Initialize all configured peripherals */$ I2 n' c% @" i2 l0 l
- MX_GPIO_Init();
- f- k% [7 c; K2 L% L% M4 C& c - MX_I2C1_Init();
& ?/ Q! ^" a* u" i. D7 k - MX_USART1_UART_Init();
) r2 b5 L5 q) l7 |. r - /* USER CODE BEGIN 2 */ M( x k* d( P( b) P
; V- h( e1 M3 v% U @; S, g" \- while (MPU_Init()){ //如果初始化失败,继续初始化7 N1 Z( ]! w: G3 y T0 }
- printf("MPU_Init_Fail...");
7 S5 }+ ]& I& [( C8 ^1 }6 ~; n8 m Y - }, I7 E& R6 g+ p+ M# M( D% Q0 _
% R% V2 E0 N w+ i* d, D- /* USER CODE END 2 */% m3 S1 ?' r7 F e; n
- . p1 T8 E2 Q% L4 t0 }( k
- /* Infinite loop */: C8 R; h! ]+ x
- /* USER CODE BEGIN WHILE */' ]4 W N6 v- v+ w
- while (1)
7 @, }% l) {2 \$ ^ - {
' d/ G+ X( j( a - GetAngle();
2 |% _, w; I- R3 p) d$ x, R2 ` - GetPitch();
! z" C% x( N C8 U - HAL_Delay(10);
5 L k, w4 y* W% f" \) P0 j - /* USER CODE END WHILE */
P1 M8 I6 g' O7 v - 2 h4 d8 ~' v* k9 Y
- /* USER CODE BEGIN 3 */
8 Q+ t6 M# |& D* m3 E - }" c/ f2 m' p3 Y
- /* USER CODE END 3 */
+ v" W; [- Y; l - }
复制代码
+ |- A1 E9 G/ ~4 |, t2 L点击,下载程序7 M- ^9 f j: V( v! Q1 ]; z5 H
; y ~) n) U& Y# v
0 F/ H/ ~3 {/ G, J+ \! }1 ]: `$ Z, w! u2 C
程序烧录好后就会通过串口通讯向上位机发送位姿数据(加速度计、卡尔曼、一阶二阶互补滤波计算的位姿),上位机我用的是Arduino编译器来接受并显示数据。设置好波特率后即可显示数据。
! @1 D4 a$ G; P1 Q- M/ f6 N: X; B% h5 w7 |
( q$ W5 Z$ F4 n( K/ A1 L
: U. _3 a: a3 w3 |: W6 i; L# D' s
作者:DIY攻城狮
' u& `( M6 S" V# ~! J! R
$ k' p7 `' Q5 T' |8 S. l [4 m
* _( A4 d$ `# I2 g5 V8 {! a |
MPU-6050、卡尔曼滤波库在哪里呀