当前位置: 代码网 > it编程>编程语言>C/C++ > [调参神器]使用VOFA+上位机进行PID调参(附下位机代码)

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

2024年08月02日 C/C++ 我要评论
​简单地来说,VOFA+是一个超级串口助手,除了可以实现一般串口助手的串口数据收发,它还可以实现数据绘图(包括直方图、FFT图),控件编辑,图像显示等功能。​如果只是想要用VOFA+来进行数据绘图,直接使用一个波形图控件就行,但是如果想要把VOFA+当作一个长期使用的调参助手,我们最好设置一下控件。VOFA+简洁好用,但是又有点太简洁了,部分体验并不是很好(比如不能调整控件大小,保存文件不太方便等),所以如果学有余力,推荐自己用QT/PyQT写上位机。接下来,我们要将指令中的数据提取出来。

1. vofa+是啥

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

​ 这个是vofa+的官网vofa+ | vofa+

2. 如何使用vofa+调试pid

2.1 vofa+部分

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

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

image-20230111003022681

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

image-20230112132644320

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

image-20230112132856421

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

image-20230112133002948

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

image-20230112133112739

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

image-20230112134543188

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<rxline;i++)
                printf("uart databuff[%d] = %c\r\n",i,databuff[i]);
            usart_pid_adjust(1);//数据解析和参数赋值函数
            memset(databuff,0,sizeof(databuff));  //清空缓存数组
            rxline=0;  //清空接收长度
        }
        rxbuffer[0]=0;
        hal_uart_receive_it(&huart2, (uint8_t *)rxbuffer, 1); //每接收一个数据,就打开一次串口中断接收,否则只会接收一个数据就停止接收
    }
}

这里有个注意点,我们在vofa+中设置的是以ascii码的形式发送,所以如果发送了一个感叹号“!”,此时stm32接收到的将会是感叹号的ascii码,十六进制下是0x21。如果发送数字0,那么接收到的数据用十进制表示是48,用十六进制表示是0x72。

接下来,我们要将指令中的数据提取出来

/*
 * 解析出databuff中的数据
 * 返回解析得到的数据
 */
float get_data(void)
{
    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<200;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;
}

最后,将解析得到的数值赋值到pid参数变量中就行啦。

/*
 * 根据串口信息进行pid调参
 */
void usart_pid_adjust(uint8_t motor_n)
{
    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;
    }
}

到这里,串口调试的部分就全部完成了,pid部分和定时器部分就不赘述了。

2.2 开始调参

代码写完后,开启串口,就可以实时调整pid各个参数和电机的目标速度了,当pid调节完成后再将参数写进代码中。

这里要注意数据发送时必须严格按照数据引擎的格式,否则软件将不能解析数据进行画图。想要查看格式的花把鼠标移动到数据引擎那里的黑色问号上就行了

image-20230112134843798

这不比原来那种看波形→改参数→烧代码→看波形的流程爽多了

如果在小车和电脑分别插一个蓝牙模块,就能实现小车边跑边调,直接化身远程遥控车,很好玩。

3. 其他

vofa+简洁好用,但是又有点太简洁了,部分体验并不是很好(比如不能调整控件大小,保存文件不太方便等),所以如果学有余力,推荐自己用qt/pyqt写上位机。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com