【STM32】VOFA+上位机 PID调参
【STM32】VOFA+上位机 PID调参
- 前言
- VOFA+
- 简介
- VOFA+配置
- VOFA+动态调节PID参数
- VOFA+控件
- STM32解析数据包
- PID参数整定
- 一般调节法
- 临界比例法
- 试凑法
前言
参考文章如下:VOFA+使用说明
使用VOFA+上位机进行PID调参
PID参数整定
VOFA+
VOFA+是一款简单易上手的上位机调试软件(超级串口助手),可以动态调节PID参数,直观的显示数据的变化曲线
建议先把官方文档看一遍VOFA+官方文档
简介
VOFA+支持3种数据流方式:FireWater、JustFloat、RawData
FireWater:编程类似 printf,但字符串解析消耗资源,适用于通道少、发送频率低的场景。
JustFloat:以小端浮点数组形式传输,适合通道多、频率高的场景。
RawData:不进行采样数据解析,类似普通串口助手。
VOFA+配置
使用FireWater字节流协议
FireWater字节流协议
使用说明
FireWater遇到换行才会打印数据,可自行选择加或不加标签 < any >
以打印电机转速为例
PRINTF_PC("speed_r:%f,%f\n",wheel_rpm_Ref_R,wheel_rpm_Fdb_R);
标签:speed_r
ch0数据:wheel_rpm_Ref_R
ch1数据:wheel_rpm_Fdb_R
打开波形图
** 填充 **
VOFA+动态调节PID参数
VOFA+控件
拖拽出来
添加命令
命令和命令组的区别是,命令组是一系列连续的命令,命令是单步命令。我们目前使用命令就行
编辑命令
一会儿着重讲一下如何解析VOFA+发送的数据包
根据所需,设置控件指令的最值、步进、发送模式等等
STM32解析数据包
数据包是以什么形式发送的?
以 VOFA+ 发送 “PR=2.32!” 为例,每个字符会被转换为对应的 ASCII 码并以二进制数的形式发送。例如,“PR=2.32!” 被解析为 ASCII 码:80 82 61 50 46 51 50 33,再以二进制数值发送
解析思路
- 找到有效数据的开始(=)和结束(!)索引
- 提取等号与感叹号之间的字符,将其存入 valueStr
- 将 valueStr中的字符串转换为浮点数
- 根据头两个字符(如 PR),将浮点数赋值给相应的 PID 参数
代码示例
//入参:字符串数组(存储ascii码数组)的地址 ; 字符串数组的长度
void get_PIDdata(uint8_t *data, uint16_t size)
{
int startIdx,endIdx; //定义有效数据的起始索引和结束索引
char valueStr[10] = {0}; //定义有效数据对应的字符串
float PIDpara; //
if(data[size-1] == '!') //当最后一位为字符'!'(说明下,==进行判断时,两端都必须是数值,也即左侧会解析为数值(uint8_t数组的值),右侧也会解析为数值(字符'!'对应的ascii值)
{
//找到 '=' 的索引
for(int i=0;i<size;i++)
{
if(data[i] == '=')
{
startIdx = i + 1; //找到有效数据起始索引
break;
}
}
//找到 '!' 的索引
for (int i = startIdx; i < size; i++)
{
if (data[i] == '!')
{
endIdx = i; //找到有效数据结束索引
break;
}
}
//提取 '='与'!'之间的数值
if (startIdx > 0 && endIdx > startIdx)
{
strncpy(valueStr, (char*)&data[startIdx], endIdx - startIdx); //将有效数据长度的字符从data源字符串中拷贝到valueStr字符串中
valueStr[endIdx - startIdx] = '\0'; //将valueStr字符串尾部补上'\0',作为字符串结束标志
PIDpara = atof(valueStr); //将字符串转换为浮点数("2.32"-->2.32)
}
// 设置左右电机的PID参数
if (data[0] == 'P' && data[1] == 'L')
{
speed_pid_L.kp = PIDpara;
PRINTF_PC("L_KP = %.3f\n", speed_pid_L.kp);
}
else if (data[0] == 'I' && data[1] == 'L')
{
speed_pid_L.ki = PIDpara;
PRINTF_PC("L_KI = %.3f\n", speed_pid_L.ki);
}
else if (data[0] == 'D' && data[1] == 'L')
{
speed_pid_L.kd = PIDpara;
PRINTF_PC("L_KD = %.3f\n", speed_pid_L.kd);
}
else if (data[0] == 'M' && data[1] == 'L')
{
speed_pid_L.maxIntegral = PIDpara;
PRINTF_PC("L_MaxIntegral = %.3f\n", speed_pid_L.maxIntegral);
}
else if (data[0] == 'P' && data[1] == 'R')
{
speed_pid_R.kp = PIDpara;
PRINTF_PC("R_KP = %.3f\n", speed_pid_R.kp);
}
else if (data[0] == 'I' && data[1] == 'R')
{
speed_pid_R.ki = PIDpara;
PRINTF_PC("R_KI = %.3f\n", speed_pid_R.ki);
}
else if (data[0] == 'D' && data[1] == 'R')
{
speed_pid_R.kd = PIDpara;
PRINTF_PC("R_KD = %.3f\n", speed_pid_R.kd);
}
else if (data[0] == 'M' && data[1] == 'R')
{
speed_pid_R.maxIntegral = PIDpara;
PRINTF_PC("R_MaxIntegral = %.3f\n", speed_pid_R.maxIntegral);
}
}
}
PID参数整定
注意,在一般的控制系统中,只用pi或pd就能满足需求
一般调节法
① 设定比例系数(P):将积分和微分系数置零,系统设为比例控制,将输出设为最大值的 60%-70%,逐步增大比例系数至系统振荡,再减小至振荡消失,设定为此时比例系数的 60%-70%。
② 设定积分系数(I):在确定比例系数后,设定较小的积分系数,逐步增大至系统振荡,再减小至振荡消失,设为此时积分系数的 55%-65%。
③ 微分系数(D):通常为 0。如果系统有小幅振荡,通过 P、I 无法优化,可按相同方法调整 D,设为系统不振荡时的 30%。
联调:系统空载、带载联调,并对 PID 参数进行微调。
建议:PID 的 P 是必须的,通常只使用两个参数组合(如 PI 用于稳定系统,PD 用于快速响应),三个参数更难调,通常前两个参数已足够。
临界比例法
4.1 内容
将系统设置为纯比例控制,逐渐增大比例系数,直至系统曲线出现等幅振荡,然后根据公式计算 PID 参数
4.2 调节思路
- 将积分、微分系数置零,系统按比例控制运行一段时间。
- 逐渐增大比例系数,观察曲线变化,若振荡减小则继续增大,振荡变大则减小,直到出现等幅振荡,记录临界比例系数和振荡周期。
- 根据经验公式计算 PID 参数
控制环节 | Kp | Ki | Kd |
---|---|---|---|
P | δK/2 | 0 | 0 |
PI | δK/2.2 | Kp / (0.833 * Tk) | 0 |
PID | δK/1.7 | Kp / (0.5 * Tk) | 0.125 * Tk * Kp |
试凑法
结合系统的具体情况以及经验,先试凑几组合理的 PID 系数,同时需要观察系统的曲线变化,确定每一个系数对于整个系统曲线的大致影响,然后再根据具体的曲线进行调整。
调节思路
① 先是比例(P),再积分(I),最后是微分(D)
② 按纯比例系统整定比例系数,使其得到比较理想的调节过程曲线,然后再把比例系数缩小 1.2 倍左右,将积分系数从小到大改变,使其得到较好的调节过程曲线
③ 在这个积分系数下重新改变比例系数,再看调节过程曲线有无改善
④ 如果有改善,可将原整定的比例系数减少,改变积分系数,这样多次的反复,就可得到合适的比例系数和积分系数
⑤ 如果存在外界的干扰,系统的稳定性不好,可把比例、积分系数适当减小,使系统足够稳定
⑥ 如果系统存在小幅度超调,可以将整定好的比例系数和积分系数适当减小,增大微分系数,以得到超调量最小、调节作用时间最短的系统曲线。