将机器人整体开源,同时总结一下机器人搭建过程中遇到的坑和未来的改进方向。在分享的文件里包含了结构设计、程序控制、电路设计以及其他模块相关资料供大家参考。 文末扫码可以获取相关资料。 机器人原理分析 首先来看成品图:
$ t Y% U0 ? l5 X 如图所示,该机器人根据陀螺仪的位姿数据,通过三个全向轮驱动底部球体调整自己在球上的位置,保持动态平衡的同时实现全向移动。 保持动态平衡过程需要对机器人进行运动学分析,这里参考了平衡小车之家的运动学方程:( H9 W3 Q& G- L; Z4 a B" ~6 u# O
自平衡控制问题转化为三步:输入X、Y角度—控制器计算—输出A、B、C电机转速的控制模型。 控制器设计 首先考虑参考平衡车控制,球上自平衡机器人本质上依然是一个一阶倒立摆问题。 这里参考了飞思卡尔直立车的控制方法,采用串级PID控制器,外环PD角度环,内环速度PI环。相关文章:PID算法原理介绍。 由于我的驱动方案选择的是42步进电机,在速度闭环的时候有些问题。正常的直流电机+编码器的控制方案可以通过编码器将轮子的真实速度计算出来,从而和控制器的理想转速作差,实现速度控制。
4 b! v6 X, J' H 而我这里的速度闭环是通过计算上一个时钟周期时给步进电机的控制量,通过运动学方程分解,得到机器人的虚拟速度,与理想转速作差控制。我认为这种速度闭环方式还是存在一定缺陷的,但是在网上查看论文的时候我发现有很多自平衡机器人都是用42步进电机来实现速度闭环的,不知道是什么方法。
1 r/ ]+ s; v$ l& P 这里还可以好好思考一下为什么角度环要用PD控制,速度环要PI控制,角度环的P部分和D部分对机器人控制有什么影响?在很多CSDN调试平衡车的博客中都有解释,这里就留给大家思考了。 硬件及结构设计 自平衡机器人的硬件清单有: 56mm全向轮 45元/个 42步进电机 25/个 42步进闭环模块 59.8元/个 LM2596S降压模块 20元 STM32F103C8T6-4飞控板 59.8元 GY-521六轴陀螺仪 25元 用到的模块大致如上所示,C8T6的价格随着最近芯片涨价直线上升,我白嫖了实验室的两块板子,现在买一块实在太贵,可以等芯片价格稳定一些再买。其余开关排针等常见元件不再赘述。
^0 _1 o9 F! S/ F 电路原理图如下所示:4 _* Z X# X' ?7 q: h
机器人使用solidworks设计整体结构,底板可在某宝定制6050太空铝切割,蓝色件为正常3D打印件。 程序部分 在keil 5中开发STM32。 控制程序采用定时器0.5ms定时中断的方式进行计算,每触发两次中断计算对电机控制一次,这里还是推荐大家采用外部中断读取GY-521上的INT引脚的方式,控制计算周期。GY-521上的INT引脚每5ms触发一次跳变,采用外部中断的方式可以严格保证读取位姿数据与计算处理同步。 " O6 u# F+ ^/ s
- int TIM1_UP_IRQHandler (void)
0 f+ @6 M8 L! S R. c' c, M - {( O4 N6 S0 Y: Q% u, R
- u8 key_cal;0 J8 n! C6 E3 I8 A+ L4 G; A
- if(TIM_GetITStatus(TIM1,TIM_IT_Update) != RESET)
# U& U- |5 i' T4 t5 Q - {' a/ o, L, o6 |- U6 T" B
- TIM_ClearITPendingBit(TIM1,TIM_IT_Update); ( d+ b! B7 {. Y) ~4 S/ m
- flag_target=!flag_target;! Y5 `$ I- ]" J( B8 O f
- key_cal=KEY_Scan(0);
, {! J& f2 @+ l- L5 X - if(state_flag==1)//矫正结束" ~0 ~+ P+ P8 F7 F# @% f
- {
0 |5 ~# s0 M; P6 l7 |* x/ N - if(flag_target==1)//每读取两次陀螺仪控制一次
, H2 ?5 Y4 q( Y3 L! e - {, e& Z) V: |8 M
- Read_DMP(); //===读取倾角% {" J K, `* v0 s- S7 x g
- scope();
$ I- v+ ^0 V" g5 M - return 0;
9 A5 Y0 Q j) u- Q1 ]) @ - }
: Q4 q& i) A& H; Y - }% ^+ ~6 {9 c3 l0 E7 [1 G
- if(key_cal==1)//矫正按键. d( j8 g i0 N7 S6 G. Z
- {
$ X8 |& k( V0 a* n' N8 \. e: m1 X0 [ - Angle_Zero_X=Angle_Balance_X;/ _5 ]2 X6 M V! Q5 c9 y
- Angle_Zero_Y=Angle_Balance_Y;( f/ Z( \8 |9 I
- key_cal=0;4 R9 F) T e$ o2 _1 ~
- Flag_Stop=0;/ m; N: N( i" U; O+ N; ], X* b9 _
- }' y3 ~& `3 h5 t2 i+ z7 e) V+ v
- if(key_cal==2||key_cal==3)//矫正按键% O$ C3 d9 M0 ?6 |' H# h v6 X
- {: V& X" a: l& q
- Flag_Stop=1;//关闭速度环I积分# f1 |4 y% H, Y* D) _8 w5 X
- key_cal=0;
/ x! \1 W! ?" S, F7 y8 Q* C$ B' m - }
8 ] c7 ]8 q6 \! v4 S3 S0 ~" Y - Angle_Bias_X =Angle_Balance_X-Angle_Zero_X; //获取Y方向的偏差5 c' J" U- t$ R! d
- Angle_Bias_Y =Angle_Balance_Y-Angle_Zero_Y; //获取Y方向的偏差
; J/ X' x8 G0 C9 j, h9 ~% h4 ` - if(control_mode==0)//PID控制模式& @# @/ u$ ^' B9 X
- {
y0 H$ T7 N: Y, `, t& V4 } - Encoder_Analysis(Motor_A,Motor_B,Motor_C); //正运动学分析,得到X Y方向的速度
8 S8 w L9 g. Y8 j4 D9 { - Balance_Pwm_X= balance_X(Angle_Bias_X,Gyro_Balance_X);//X方向的倾角控制4 G& e, m ?/ l+ M
- Balance_Pwm_Y=-balance_Y(Angle_Bias_Y,Gyro_Balance_Y); //Y方向的倾角控制 s( d: k+ h: G N- s( @# h7 j! K
- // if(++flag_target_2==4)//速度环频率慢于加速度环 但是还没加速度环 ; b, l( g2 A. F
- // {
1 R1 L; @* _: S. N( @ - Velocity_Pwm_X=velocity_X(compute_X); //X方向的速度控制% Q; G5 u& T8 x, }0 {7 ^/ V$ p. m
- Velocity_Pwm_Y=velocity_Y(compute_Y); //Y方向的速度控制
& q$ _# d3 f. J5 W/ A* O2 f) V# D - // flag_target_2=0;
3 S) K: ^* k0 l; p) P7 I - // }' t: A1 D3 p1 J( p! \9 L& _
- Move_X =Balance_Pwm_X+Velocity_Pwm_X; //===X方向控制量累加 ) U9 Y4 e* w0 {! u) w$ @+ P
- Move_Y =Balance_Pwm_Y+Velocity_Pwm_Y; //===Y方向控制量累加 6 l+ A1 ^- }4 y8 c9 @+ ]/ N
- Move_Z=0; 0 {# q/ Y9 z% z2 E
- Kinematic_Analysis(Move_X,Move_Y,Move_Z);//逆运动学分析得到ABC电机控制量
3 @7 d+ ~: P4 e9 D2 M! g- W - }5 {# T6 C% M4 x' g& a- v3 f
- Motor_A=Target_A;//直接调节PWM频率 / n( E7 \1 o% `! q- F) T( C
- Motor_B=Target_B;//直接调节PWM
5 p z6 w1 ^6 f - Motor_C=Target_C;//直接调节PWM
& \% U$ N d! \7 b8 } - //以下都是为了速度连续化处理防止突变; E" K5 q& v. \
- if(Motor_A==0) Motor_A=motor_a_last;
p4 U4 X( o j, J8 D - if(Motor_B==0) Motor_B=motor_b_last;
5 M: h( j8 L& C# _# L - if(Motor_C==0) Motor_C=motor_c_last;
7 g( T. b! Y# \, N* @6 J; R g9 X7 l - Xianfu_Pwm(2000);4 i) {5 T# Q" c3 K* Q: ~) K! i4 W
- Set_Pwm(Motor_A,Motor_B,Motor_C);& S4 X5 C7 P, q, s) E$ n
- Gyro_Balance_X_last=Gyro_Balance_X;
4 g1 p" G& M- S# _& E5 b - Gyro_Balance_Y_last=Gyro_Balance_Y;
0 I* g4 o: s9 M; X- y& F4 s - Gyro_Balance_Z_last=Gyro_Balance_Z;- |0 D1 P6 G2 K! E: U
- Angle_Balance_X_last=Angle_Balance_X;/ [; L# G; ]9 I6 b
- Angle_Balance_Y_last=Angle_Balance_Y;
Y5 o9 U; s( G+ o - Angle_Balance_Z_last=Angle_Balance_Z;$ n2 O6 d/ Z/ c9 R5 T
- motor_a_last=Motor_A;
% e, r1 |# t/ K" K& G7 n# ` - motor_b_last=Motor_B;2 Q; A* x0 o% I. H/ }' @
- motor_c_last=Motor_C;( e, Y* \0 B5 \) g
- }9 s- k5 B) G8 G
- return 0;! V( S. N6 P$ ]& x- e
- }
复制代码
* d( u% v5 F, \5 T( c, h: ~1 e对于电机控制,由于采用的驱动方案是步进电机,调速的方式是改变驱动步进电机的脉冲频率。我这里选择了三个定时器,动态调节定时器的频率,具体方式是在初始化时设定好定时器的预分频系数psc的值,然后在程序里动态更改ARR寄存器的值,从而改变定时器的定时频率。3 n& L2 Y6 i2 t7 x' g; P/ l. J8 d
; Q/ x6 U& l0 \5 G/ M' i- //这里以A电机的速度控制为例 输入为 电机方向和电机速度5 v+ v' P# _3 X/ N8 r7 s: }8 ^2 s
- void set_motorA_speed(u8 dir,u16 speed), e5 d6 O3 ~* }% p/ g1 h) N8 k- M
- {
' d( `4 h, g7 W( }: k% a: w - u32 arr;
0 V3 h i: c7 {' N6 N - arr=speed;
3 s" Z9 a Q. @: q( f - TIM_ARRPreloadConfig(TIM3,DISABLE);
% [1 K* w& t) C3 o1 F! B3 y8 D - TIM3->ARR=arr;//计数到10000在归零重新计数
3 f4 ?& {+ Q& O9 W% [8 `4 { - TIM3->CCR4=arr/2;//保持占空比为50%/ x# q: _; i, {1 Q! l0 B+ m% }
- TIM_ARRPreloadConfig(TIM3,ENABLE);- X/ N' R2 w! h5 i7 z* \
- TIM_Cmd(TIM3,ENABLE);: E2 x8 V7 |3 C1 c' J: _+ i7 y
- if(dir==0)! W2 O" v) W/ f: }; J
- {5 ?! t* |: d' v, J
- GPIO_SetBits(GPIOA,GPIO_Pin_1);; F1 `' Z1 L$ v8 ]) ^7 u6 Y
- }8 F5 B) q1 U0 b- ?
- else* u' r1 D P9 Y$ _; L+ y4 a, W# {( N
- {
0 ?* r# P) q1 A/ ? - GPIO_ResetBits(GPIOA,GPIO_Pin_1);
; U/ C) L; K: H+ Y/ W- @ - }& R# m9 c4 L; }0 d2 P
- }
复制代码 $ i$ T& a( u/ I( r
小车的运动学分解代码实现如下,参考了平衡小车之家的代码: ) i8 |3 N& n. r$ M% N, ~& r
; f M2 v7 J* l# \; n: p! U9 @- i- /**********************************************************& A$ @. g. K! m3 }) z
- 函数功能:小车运动数学模型
6 t7 L; M' E" s0 y' F0 _ - 入口参数:X Y Z 三轴速度或者位置2 N2 h* A* E$ K
- 返回 值:无* o/ ~- k$ J/ d
- ***********************************************************/3 I- d* O/ F5 y1 W
- void Kinematic_Analysis(float Vx,float Vy,float Vz)
) p7 v+ D) Q9 k2 D+ z6 E6 ~: { - {4 @" |) e# V# M) E1 t6 z
- Target_A = Vx + L_PARAMETER*Vz;
+ y# ~$ \. k( N4 W( q! @ - Target_B = -X_PARAMETER*Vx + Y_PARAMETER*Vy + L_PARAMETER*Vz;6 Q9 \: z7 d* Y+ B6 g
- Target_C = -X_PARAMETER*Vx - Y_PARAMETER*Vy + L_PARAMETER*Vz;
; j% r/ v( k! e - }( _9 M6 d1 l v0 S8 @; @
- /*****************************************************************
9 A' a2 n# ~6 d$ k7 n - 函数功能:小车运动 正运动学分析 {3 _* E V1 y# i8 [ v# e
- 入口参数:A B C三个电机的速度
6 h# L+ V8 U0 r& P: O) U - 返回 值:无( P; L$ `+ X$ Q9 M" T; z j9 W
- ******************************************************************/
* r6 [6 q: Q. u: L0 \3 P - void Encoder_Analysis(float Va,float Vb,float Vc)
0 x, X) w" L& e$ u# Q% }7 d) x9 n - {$ L6 Q4 j8 q9 M. | v& Y: D2 ~: t: ?
- compute_X=(Va*2-Vb-Vc);5 ?& T4 D4 d3 V/ m# e" z+ }
- compute_Y=((Vb-Vc)*sqrt(3));$ W8 Q- }, |& D
- compute_Z=(Va+Vb+Vc);
2 ~0 m' k9 p+ ?) \9 c! | - }
复制代码 0 O. j' w, g; O
总结与展望 球上自平衡机器人可以作为算法试验平台, 输入输出固定,更换不同控制器,将数据导入MATLAB进行分析即可比较控制器性能。 个人认为结构有两个改进方向,一方面参考以下论文:余义. 单球驱动自平衡机器人位姿解算与控制系统研究[D].武汉科技大学,2019。论文中采用的四足式驱动结构更有利于机器人自平衡控制。3 E# l, M$ |$ W K4 H: b6 k
另一方面可以增加球体和机器人固定装置,利用机械结构将机器人与底部驱动球结合成一个整体防止机器人跳轮等问题。同时驱动球对于机器人平衡的影响较大,最好还是定制空心钢球,然后喷漆增大摩擦力,最有利于机器人自平衡控制。8 S! E7 A' ~( P% \) J& U+ _
控制部分的改进,首先是控制原理,本文是针对建立好的运动学方程进行分析,通过串级PID算法来实现自平衡运动。该机器人的控制问题本质上是一阶倒立摆问题,可以采用动力学建模的方式,通过动力学分析算出平衡需要的虚拟力矩,再对电机进行力矩控制。相关推荐:演示PID三个参数的控制作用。 其次是控制器,PID控制算法应用广泛但也有一定的缺点,可以考虑采用模糊PID,ADRC自抗扰控制器,强化学习等智能控制算法对机器人自平衡进行控制。 # |/ [+ H$ ?* _; V
. e5 D: U# C( }# u0 X9 h |