基于 STM32F103C8T6 的 PID 平衡车项目,含直立环、速度环、转向环的详细调参与代码实现。
STM32 PID 平衡车详细教学
本文是基于 STM32F103C8T6 单片机开发的平衡小车项目。这个项目常用于广大学子的 PID 算法的入门项目,我认为 PID 应用并非很难,重要的是调参。所以调参的过程很重要。而且平衡车也相对简单,也以选择它作为学完 32 后的第一个项目(本人就是)。本人的小车添加了直立环,速度环和转向环。最后有源码和调好的视频。
一、下面介绍平衡车的硬件
- 一个开发板或者用面包板也可以,开发板可以自己嘉立创打板,也可以选择淘宝的亚博智能科技的开发板。
- 两个编码电机(520、370 都可以,只要有编码器就行),或者买直流电机再买编码器,也是可以的。
PS:如果买亚博智能科技的板子,需要买他家的电机,因为别的商家的电机接口不一样!
- TB6612 电机驱动不多说,买个 10 多块的,不要图便宜买几块的,到时候小车抽搐就难受了。
- HC-06 蓝牙通信模块,用于手机控制小车,也可用上位机调参。
- MPU6050 陀螺仪模块,这个模块用于测量小车的三维的各种参数。
- 12V 可充电的锂电池,开发板应有降压模块。
- OLED 0.96 屏幕,可有可无。
- 车模(轮胎等等)。
- ST-Link 下载器(也有可用 J-Link 等下载器)。
- 如果要加避障功能,可以购买超声波避障模块。
- 适当的杜邦线,螺丝刀,电池充电器等等基础器件。
PS:可能说的不全,如果有遗漏根据自己的需求自行购买。
贴一张组装好的照片:

二、介绍用到的单片机软件资源
- 3 个通用定时器分别是:TIM2、TIM3、TIM4。
- 1 个 USART3 串口。
- OLED 和 MPU6050 的软件模拟 IIC 通信。
PS: 如果添加避障模块,应该还要一个定时器用来计时超声波模块。其他功能如果需要,自行分配单片机资源。
三、介绍主要硬件模块的使用方式
TB6612: 下面是 TB6612 的引脚图

PWMA 和 PWMB 是要从单片机输入的两个 PWM 波接收口,AIN1、AIN2 为 PWMA 的 IO 控制,对应的为右边的 A01、A02,通过控制两个 IO 口的高低电平来控制电机的正反转。同理 PWMB 也是如此。至于 VCC 和 GND 是用来上电的(要共地)。STBY 接 5V/3.3V,VM 接 12V 即可。
电机编码器:

这个图片已经介绍的很清楚了,有的电机接口不一样,要注意。
MPU6050: 这个模块涉及太多,简单应用可以去看 B 站江科大的 MPU6050 教程,这是更加详细的介绍:
MPU6050 详细教程
,有需要可以去看看。
四、下面是各个软件资源的分配
- TIM2: 输出两路 PWM 波,开启定时中断,在中断中存放着最主要的电机控制程序,周期应该最好不要超过 10μs。
- TIM3 和 TIM4: 为读取两个电机的编码值。
- USART3: 用于与手机相连接。
五、接着介绍最主要的 PID 部分
六、PID 代码
(PS:直立环需 P、D,速度环需 P、I,转向环需 P、D)
1. 三环变量声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| extern float Kp,Ki,Kd; //直立环参数
float err; //此次误差
float last_err; //上次误差
float err_sum=0; //误差累加
float err_difference; //误差的差值
extern float VKp,VKi; //速度环参数
float err2; //此次误差
float filt_velocity; //滤波后的速度
float last_filt_velocity;//上一次的滤波后的速度
float velocity_sum;
extern float tkp,tkd;//转向环
float yaw_err,yaw_err_differnence,last_yaw_err;
|
2. 直立环
1
2
3
4
5
6
7
8
9
10
| //直立环:
int vertical_PID_value(float measure,float calcu)
{
err = measure - calcu; //误差
err_sum+=err; //误差的累加
err_difference = err - last_err; //误差的差值
last_err = err; //此次误差记录为"上次误差"
return Kp*err + Ki*err_sum + Kd*err_difference;
}
|
3. 速度环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| //速度环:
int velocity_PID_value(int velocity_measure,int velocity_calcu)
{
float a=0.3; //滤波系数(反映滤波程度)
err2=velocity_measure-velocity_calcu;
filt_velocity = a*err2 + (1-a)*last_filt_velocity; //一阶速度滤波
velocity_sum+= filt_velocity; //速度的累加
I_xianfu(&velocity_sum,3000); //累加限幅,调参时可以去掉
last_filt_velocity = filt_velocity; //此次速度记录为"上次速度"
return VKp*filt_velocity + VKi*velocity_sum;
}
//I限幅:
void I_xianfu(float *velocity_sum,int max)
{
if(*velocity_sum>max) *velocity_sum=max;
if(*velocity_sum<-max) *velocity_sum=-max;
}
|
4. 转向环
1
2
3
4
5
6
7
8
9
10
11
12
| //转向环
int yaw_pid_value(float yaw_measure,float yaw_calcu){
yaw_err=yaw_measure-yaw_calcu;
//防止临界突然转动,改变系统稳定
if(yaw_err>=180){yaw_err-=360;}
if(yaw_err<-180){yaw_err+=360;}
yaw_err_differnence=yaw_err-last_yaw_err;
last_yaw_err=yaw_err;
return tkp*yaw_err+tkd*yaw_err_differnence;
}
|
七、PID 调参
PS:极性就是参数符号的正负。(所有调参时初始参数为 0,一点一点加,调极性时所有环的 PID 参数初始为 0)
1. 直立环
- 确立极性: 给 P 一定的值,如果发现小车有缓慢摔倒而不是促进摔倒的话,证明极性正确。
- P 值: P 值不断加大直到出现低频抖动。
- D 值: D 值增加后发现有一定的平衡性,加到小车可以平稳的直立,但注意此时是假平衡,他只可以站立一会,所以速度环弥补,为了引用速度环还要增加 D 值,直到出现低频的快速抖动,注意要及时关闭电机,防止电机电流过大损坏电机或其他器件。
2. 速度环(正反馈)
- 确定极性: 给一定 P 值,用手转动轮子,轮子很快的转动起来,证明极性正确,否则你会发现有一股力阻止你转动轮子。
- 工程经验: 多年来总结的经验,加入速度环之前应将直立环的参数调成原来的 0.6 倍。
- 增大 PI 值: 速度环 P = 200 × I,根据此关系可以一起调两个值,逐渐增大,直到出现平稳状态,如果出现一些抖动,可适当增大直立环的 D 值来抑制系统。
3. 转向环
- 还是要确定极性:和速度环差不多。
- 调参: 首先转向环可以有三种组合方式:纯 P 控制,P + D 控制,P + D + I 控制。如果只有 P,调法很简单,极性确认完之后,P 从 0 往上加,直到你满意的效果为止。如果 P 大了,就会出现过冲现象,即给小车一个角度理论值后(或者用手拨弄一下小车)车头会左右摆动几下才稳定,此时就可以加入 D 来抑制过冲,也是从 0 开始往上加,观察到过冲越来越小,直到你满意为止,如果 D 太大了,小车会抽搐(高频率小幅度震荡),要避免这种现象发生,这就是最常用的 P + D 控制。如果你想追求更高的精度,消除稳态误差,那么加一点点 I 就可以了,即 P + D + I 控制,不过这个稳态误差一般可以忽略不计,如果你稳态误差很大,考虑是不是 P 太小了。
PS:调参视频可观看:
平衡车调参教程
或
更加详细的调参教程
八、控制代码部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){
if(MPU6050_DMP_Get_Data(&pitch,&roll,&yaw)==0)
{
measure = roll; //roll测量值
calcu = zhongzhi; //roll理论值
measure_yaw=yaw;
velocity = ( read_encoder2() + read_encoder1() )/2; //速度测量值
//PID计算:直立环+速度环
PWM = vertical_PID_value(measure, calcu) + 1.88555*velocity_PID_value(velocity,ad_v);
//1.88555这个系数可以有也可没有,用pid参数可以弥补,1.88555就是我大概试出来的一个范围然后瞎打的
PWM_yaw=yaw_pid_value(measure_yaw,calcu_yaw);
//转向环计算
pwm_left=PWM-PWM_yaw;
pwm_right=PWM+PWM_yaw;
PWM_Xianfu(5000,&PWM); //PWM限幅,速度环调参时可以先去掉
SETPWM_left(pwm_left);SETPWM_right(pwm_right);
if(motor_flag) {SETPWM_left(pwm_left);SETPWM_right(pwm_right);} //给电机PWM
else {SETPWM_left(0);SETPWM_right(0);} //关闭电机
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
|
(直立环和速度环为串级 PID,要保证定时中断周期不超过 10μs)
九、成功视频
演示视频:
STM32 平衡车演示
视频中的蓝牙调试助手随便一找就有,作者在这里就不贴链接了(因为作者也找不着了)。
点此获取源码:
源码下载
有问题主页联系作者或者邮箱:
1039214848@qq.com