简介:
芯片的使用
本次平衡车开发基于STM32F103C8T6最小系统板
使用STM32标准库开发
用到的芯片有MPU6050 , STM32F103C8T6 , 0.96寸OLED显示屏 , TB6612电机驱动模块 , HC05蓝牙模块
片上外设的选择(可以在我的另外一个专栏STM32标准库常用库中找到对应的函数)
使用定时器一的通道一和通道四输出PWM波
使用定时器二与定时器四的编码器模式读取两个电机的速度
使用PB12,13,14,15控制电机正反转
MPU6050接PB8 , PB9号引脚 , 中断引脚接PB5号引脚
链接:https://pan.baidu.com/s/1AI-nY0aakWgEwcDUg1LDCA?pwd=jzjm
提取码:jzjm
--来自百度网盘超级会员V1的分享
开发里程
开发中常见的误区总结
1.直立环的作用不是让小车直立起来 , 只有直立环小车是很难真正达到平衡的
2.速度环的作用是负反馈的 , 能够增加小车的平衡性和抗干扰能力
3.PID的参数也不是有一个固定的范围的 , 参数范围有许多条件共同影响 , 参数范围都是通过计算得出 , 具体范围要根据实际的情况而定
4.不要纠结与参数范围 , 正常的调参 , 调了那个参数有什么现象 , 循序渐进的调参即可 , 不必过于拘束
5.调参中千万要注意的问题实电池组的供电状况 , 在满电和没电的时候的参数有很大的差别
尽量保持在电池有电的情况下进行参数的调节
6.调参过程中如果觉得小车的灵敏性不够 , 请立即检查代码问题 , 不要在灵敏程度不正常的情况下进行参数的调节
7.调参过程中注意两个编码器电机读取到的数据 , 可能是一正一反的 , 需要取反 , 具体还要看硬件是怎么连接的
8.最新总结:使用Vscode中的插件来烧录Keil5的程序和使用Keil5直接烧录程序是存在较大的差异,小车的平衡效果会有很大的差别,建议使用Vscode写代码,使用Keil5烧录程序
一次类推对于其他的控制类型的代码应该是一样的 , 望读者注意 , 原因还未探究
正式启航
先介绍PID部分的代码
#include "stm32f10x.h" // Device header
#include "inv_mpu.h"
#include "OLED.h"
#include "Encoder.h"
#include "Motor.h"
#include "Serial.h"
#include "MPU6050.h"
float Med =-2.5; //机械中值
int target=0; //期望速度
int turn_angle; //期望转向---可以放到蓝牙中断里面
float SKp =420 ,SKd=4000; //直立环的比例项与微分项参数
float VKp =-60 ,VKi=-0.30; //速度环的比例项与积分项参数
float Turn_Kp = 0.1; //转向环的比例系数
//直立环
float err , last_err; //本次误差---上次误差
float err_sum=0 , err_difference; //误差累加--误差的差值
//速度环
float filt_velocity; //滤波后的速度
float last_filt_velocity; //上一次的滤波后的速度
float velocity_sum=0; //速度的累加
//转向环
float pitch,roll,yaw; //定义的解算的参数
short gyroz; //定义Z轴上的角速度
int PWM_OUT; //PWM值的输出(PID的计算结果)
int S_output(float Med , float angle)
{
err = angle - Med; //误差
err_sum+=err; //误差的累加
err_difference = err - last_err; //误差的差值
last_err = err; //此次误差记录为“上次误差”
return SKp*err+ SKd*err_difference;
}
void I_xianfu(int max) //积分限幅函数
{
if(velocity_sum>max) velocity_sum=max;
if(velocity_sum<-max) velocity_sum=-max;
}
float Turn(short gyroz) // 简单的转向环控制 , 只使用了比例控制
{
return Turn_Kp*(gyroz - turn_angle);
}
float V_output(int velocity , int target)//速度环输出
{
float a=0.3; //滤波系数(反映滤波程度)
filt_velocity = a*(velocity - target) + (1-a)*last_filt_velocity; //一阶速度滤波
velocity_sum += filt_velocity; //速度的累加
I_xianfu(3000); //累加限幅
last_filt_velocity = filt_velocity; //此次速度记录为“上次速度”
return VKp*filt_velocity + VKi*velocity_sum;
}
/*
此处玄学出现 , 困扰我许久
读取函数MPU6050_GetData(&gyroz)若是放在中断最开始的时候就会出现小车灵敏度严重下降的问题
为此我改了又改这个函数硬是没成功解决
最后竟然是顺序出了问题
放在最后就完全没问题了
而且还不能乱放
必须MPU6050_DMP_Get_Data(&pitch,&roll,&yaw)前面
而MPU6050_GetData(&gyroz)在后面
因此各位朋友千万注意这个问题
玄学-------------------------------------
*/
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line5)!=0)
{
EXTI_ClearITPendingBit(EXTI_Line5);
MPU6050_DMP_Get_Data(&pitch,&roll,&yaw); //读取DMP库解算的数据
int Velocity = Read_Speed(2) + Read_Speed(4); //编辑器电机速度读取
PWM_OUT = S_output(Med , roll) + V_output(Velocity , target);
if(roll > 30 || roll <-30) //保护程序 , 如果翻滚角变化得过大 , 立马停止电机
{
PWM_OUT = 0;
}
MotorR_SetSpeed(PWM_OUT);
MotorL_SetSpeed(PWM_OUT); //PWM输出到电机
MPU6050_GetData(&gyroz); //读取Z轴角速度
}
}
void USART2_IRQHandler(void)//蓝牙串口的中断函数
{
if(USART_GetFlagStatus(USART2,USART_FLAG_RXNE) == SET)
{
if(USART_GetFlagStatus(USART2,USART_IT_RXNE) == SET)
{
Serial_RxData = USART_ReceiveData(USART2);//对数据进行一个简单的处理
if(Serial_RxData == '1')
{
SKp +=10;//这里可以写你想进行的操作
}
if(Serial_RxData == '2')//当时用蓝牙调参遗留下来的
{
SKp -=10;
}
if(Serial_RxData == '3')
{
SKd +=100;
}
if(Serial_RxData == '4')
{
SKd -=100;
}
if(Serial_RxData == '5')
{
VKp +=1;
}
if(Serial_RxData == '6')
{
VKp -=1;
}
if(Serial_RxData == '7')
{
VKi +=0.01;
}
if(Serial_RxData == '8')
{
VKi -=0.01;
}
if(Serial_RxData == '0') // 小车加速
{
target +=15;
}
if(Serial_RxData == '9')//小车减速
{
target -= 15;
}
USART_ClearITPendingBit(USART2,USART_IT_RXNE);
}
}
}
对PID调节参数的理解
直立环(正反馈的系统)中Kp是乘上前角度与期望角度的差值 , 增大Kp能够提高系统的反应速度 , Kp太大就会出现 过冲现象 , 因此我们引入Kd微分控制 , 当过冲现象出现的时候能够在一定的程度上抑制过冲现象
举个例子 , 假设机械中值是0 , 现在的角度为5 , 当前的差值就是5 , 下一时刻 , 角度为4, 当前差值就是4 , 上一时刻差值就是5 , 4-5就是-1 , 这个-1再乘上一个微分系数Kd就是对Kp的抑制
速度环是一个负反馈的系统
速度环的本意就是在小车向一方偏的时候能够实现更快的到达那个位置 , 有一点误差就不断的进行误差的累加进行控制
对于片上外设相关的函数 , 可以在另外一个专栏中STM32标准库开发中找到
如果本文存在任何问题 , 欢迎广大的读者指出纠正 , 有不懂的地方也可以留言进行评论 , 看到将会及时回复