[调参神器]使用VOFA+上位机进行PID调参(附下位机代码)

02-27 1929阅读

1. VOFA+是啥

​ 简单地来说,VOFA+是一个超级串口助手,除了可以实现一般串口助手的串口数据收发,它还可以实现数据绘图(包括直方图、FFT图),控件编辑,图像显示等功能。使用VOFA+,可以给我们平常的PID调参等调试带来方便,还可以自己制作符合自己要求的上位机,为嵌入式开发带来方便。

​ 这个是VOFA+的官网VOFA+ | VOFA+。

2. 如何使用VOFA+调试PID

2.1 VOFA+部分

​ 在正式开始使用VOFA+之前,最好先花十几分钟把官网的文档看一遍,熟悉一下基本操作。

​ 如果只是想要用VOFA+来进行数据绘图,直接使用一个波形图控件就行,但是如果想要把VOFA+当作一个长期使用的调参助手,我们最好设置一下控件。下面是我为调试直流电机的速度环和位置环设置的控件,包含一个波形图,六个参数调节框,两个目标值调节框。

[调参神器]使用VOFA+上位机进行PID调参(附下位机代码)

在设置好控件后我们需要对参数调节控件编写命令,这里用速度环的P进行举例。来到命令界面,添加一个新命令并重命名为Spe_P,发送内容为P2=%.2f!,这里的%.2f在实际发送中将被控件中的数值替代(详见官网文档),而感叹号是我自定义的命令结束符。

[调参神器]使用VOFA+上位机进行PID调参(附下位机代码)

然后在控件上绑定命令即可

[调参神器]使用VOFA+上位机进行PID调参(附下位机代码)

这样每次移动控件就可以发送相应的命令了

[调参神器]使用VOFA+上位机进行PID调参(附下位机代码)

重复以上步骤设置完所有命令后VOFA+的部分就完成了。我这里还调整了参数控件的最大最小值,小数点位数,步进值和鼠标弹起后发送而不是边移动边发送等,各位可以按自己的需求调整

[调参神器]使用VOFA+上位机进行PID调参(附下位机代码)

编辑完成后记得保存控件和命令,否则软件卡死这些东西就都没了

[调参神器]使用VOFA+上位机进行PID调参(附下位机代码)

2.2 STM32部分

在STM32这边,我们要解析VOFA+上位机发过来的数据,并将其数据赋值到相应的变量。

我这里使用的是串口中断进行接收,当然也可以使用DMA进行接收。

首先我们要在CubeMax上设置好串口并开启串口中断,然后在main的while前开启串口中断

uint8_t RxBuffer[1];//串口接收缓冲
uint16_t RxLine = 0;//指令长度
uint8_t DataBuff[200];//指令内容
HAL_UART_Receive_IT(&huart2,(uint8_t *)RxBuffer,1);//开启串口中断,我用的是串口2

下一步要编写串口中断函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
    if(UartHandle->Instance==USART2)//如果是串口2
    {
        RxLine++;                      //每接收到一个数据,进入回调数据长度加1
        DataBuff[RxLine-1]=RxBuffer[0];  //把每次接收到的数据保存到缓存数组
        if(RxBuffer[0]==0x21)            //接收结束标志位,这个数据可以自定义,根据实际需求,这里只做示例使用,不一定是0x21
        {
            printf("RXLen=%d\r\n",RxLine);
            for(int i=0;i
    uint8_t data_Start_Num = 0; // 记录数据位开始的地方
    uint8_t data_End_Num = 0; // 记录数据位结束的地方
    uint8_t data_Num = 0; // 记录数据位数
    uint8_t minus_Flag = 0; // 判断是不是负数
    float data_return = 0; // 解析得到的数据
    for(uint8_t i=0;i
        if(DataBuff[i] == '=') data_Start_Num = i + 1; // +1是直接定位到数据起始位
        if(DataBuff[i] == '!')
        {
            data_End_Num = i - 1;
            break;
        }
    }
    if(DataBuff[data_Start_Num] == '-') // 如果是负数
    {
        data_Start_Num += 1; // 后移一位到数据位
        minus_Flag = 1; // 负数flag
    }
    data_Num = data_End_Num - data_Start_Num + 1;
    if(data_Num == 4) // 数据共4位
    {
        data_return = (DataBuff[data_Start_Num]-48)  + (DataBuff[data_Start_Num+2]-48)*0.1f +
                (DataBuff[data_Start_Num+3]-48)*0.01f;
    }
    else if(data_Num == 5) // 数据共5位
    {
        data_return = (DataBuff[data_Start_Num]-48)*10 + (DataBuff[data_Start_Num+1]-48) + (DataBuff[data_Start_Num+3]-48)*0.1f +
                (DataBuff[data_Start_Num+4]-48)*0.01f;
    }
    else if(data_Num == 6) // 数据共6位
    {
        data_return = (DataBuff[data_Start_Num]-48)*100 + (DataBuff[data_Start_Num+1]-48)*10 + (DataBuff[data_Start_Num+2]-48) +
                (DataBuff[data_Start_Num+4]-48)*0.1f + (DataBuff[data_Start_Num+5]-48)*0.01f;
    }
    if(minus_Flag == 1)  data_return = -data_return;
//    printf("data=%.2f\r\n",data_return);
    return data_return;
}

    float data_Get = Get_Data(); // 存放接收到的数据
//    printf("data=%.2f\r\n",data_Get);
    if(Motor_n == 1)//左边电机
    {
        if(DataBuff[0]=='P' && DataBuff[1]=='1') // 位置环P
            pid_l_position.kp = data_Get;
        else if(DataBuff[0]=='I' && DataBuff[1]=='1') // 位置环I
            pid_l_position.ki = data_Get;
        else if(DataBuff[0]=='D' && DataBuff[1]=='1') // 位置环D
            pid_l_position.kd = data_Get;
        else if(DataBuff[0]=='P' && DataBuff[1]=='2') // 速度环P
            pid_l_speed.kp = data_Get;
        else if(DataBuff[0]=='I' && DataBuff[1]=='2') // 速度环I
            pid_l_speed.ki = data_Get;
        else if(DataBuff[0]=='D' && DataBuff[1]=='2') // 速度环D
            pid_l_speed.kd = data_Get;
        else if((DataBuff[0]=='S' && DataBuff[1]=='p') && DataBuff[2]=='e') //目标速度
            L_Target_Speed = data_Get;
        else if((DataBuff[0]=='P' && DataBuff[1]=='o') && DataBuff[2]=='s') //目标位置
            L_Target_Position = data_Get;
    }
    else if(Motor_n == 0) // 右边电机
    {
        if(DataBuff[0]=='P' && DataBuff[1]=='1') // 位置环P
            pid_r_position.kp = data_Get;
        else if(DataBuff[0]=='I' && DataBuff[1]=='1') // 位置环I
            pid_r_position.ki = data_Get;
        else if(DataBuff[0]=='D' && DataBuff[1]=='1') // 位置环D
            pid_r_position.kd = data_Get;
        else if(DataBuff[0]=='P' && DataBuff[1]=='2') // 速度环P
            pid_r_speed.kp = data_Get;
        else if(DataBuff[0]=='I' && DataBuff[1]=='2') // 速度环I
            pid_r_speed.ki = data_Get;
        else if(DataBuff[0]=='D' && DataBuff[1]=='2') // 速度环D
            pid_r_speed.kd = data_Get;
        else if((DataBuff[0]=='S' && DataBuff[1]=='p') && DataBuff[2]=='e') //目标速度
            R_Target_Speed = data_Get;
        else if((DataBuff[0]=='P' && DataBuff[1]=='o') && DataBuff[2]=='s') //目标位置
            R_Target_Position = data_Get;
    }
}
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]