使用VOFA+ 進(jìn)行PID調(diào)試
1. VOFA+是啥
? 簡(jiǎn)單地來說,VOFA+是一個(gè)超級(jí)串口助手,除了可以實(shí)現(xiàn)一般串口助手的串口數(shù)據(jù)收發(fā),它還可以實(shí)現(xiàn)數(shù)據(jù)繪圖(包括直方圖、FFT圖),控件編輯,圖像顯示等功能。使用VOFA+,可以給我們平常的PID調(diào)參等調(diào)試帶來方便,還可以自己制作符合自己要求的上位機(jī),為嵌入式開發(fā)帶來方便。
? 這個(gè)是VOFA+的官網(wǎng)VOFA+ | VOFA+。
2. 如何使用VOFA+調(diào)試PID
2.1 VOFA+部分
? 在正式開始使用VOFA+之前,最好先花十幾分鐘把官網(wǎng)的文檔看一遍,熟悉一下基本操作。
? 如果只是想要用VOFA+來進(jìn)行數(shù)據(jù)繪圖,直接使用一個(gè)波形圖控件就行,但是如果想要把VOFA+當(dāng)作一個(gè)長(zhǎng)期使用的調(diào)參助手,我們最好設(shè)置一下控件。下面是我為調(diào)試直流電機(jī)的速度環(huán)和位置環(huán)設(shè)置的控件,包含一個(gè)波形圖,六個(gè)參數(shù)調(diào)節(jié)框,兩個(gè)目標(biāo)值調(diào)節(jié)框。
在設(shè)置好控件后我們需要對(duì)參數(shù)調(diào)節(jié)控件編寫命令,這里用速度環(huán)的P進(jìn)行舉例。來到命令界面,添加一個(gè)新命令并重命名為Spe_P,發(fā)送內(nèi)容為P2=%.2f!,這里的%.2f在實(shí)際發(fā)送中將被控件中的數(shù)值替代(詳見官網(wǎng)文檔),而感嘆號(hào)是我自定義的命令結(jié)束符。
然后在控件上綁定命令即可
這樣每次移動(dòng)控件就可以發(fā)送相應(yīng)的命令了
重復(fù)以上步驟設(shè)置完所有命令后VOFA+的部分就完成了。我這里還調(diào)整了參數(shù)控件的最大最小值,小數(shù)點(diǎn)位數(shù),步進(jìn)值和鼠標(biāo)彈起后發(fā)送而不是邊移動(dòng)邊發(fā)送等,各位可以按自己的需求調(diào)整
編輯完成后記得保存控件和命令,否則軟件卡死這些東西就都沒了
2.2 STM32部分
在STM32這邊,我們要解析VOFA+上位機(jī)發(fā)過來的數(shù)據(jù),并將其數(shù)據(jù)賦值到相應(yīng)的變量。
我這里使用的是串口中斷進(jìn)行接收,當(dāng)然也可以使用DMA進(jìn)行接收。
首先我們要在CubeMax上設(shè)置好串口并開啟串口中斷,然后在main的while前開啟串口中斷
uint8_t RxBuffer[1];//串口接收緩沖
uint16_t RxLine = 0;//指令長(zhǎng)度
uint8_t DataBuff[200];//指令內(nèi)容
HAL_UART_Receive_IT(&huart2,(uint8_t *)RxBuffer,1);//開啟串口中斷,我用的是串口2
下一步要編寫串口中斷函數(shù)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
if(UartHandle->Instance==USART2)//如果是串口2
{
RxLine++; //每接收到一個(gè)數(shù)據(jù),進(jìn)入回調(diào)數(shù)據(jù)長(zhǎng)度加1
DataBuff[RxLine-1]=RxBuffer[0]; //把每次接收到的數(shù)據(jù)保存到緩存數(shù)組
if(RxBuffer[0]==0x21) //接收結(jié)束標(biāo)志位,這個(gè)數(shù)據(jù)可以自定義,根據(jù)實(shí)際需求,這里只做示例使用,不一定是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);//數(shù)據(jù)解析和參數(shù)賦值函數(shù)
memset(DataBuff,0,sizeof(DataBuff)); //清空緩存數(shù)組
RxLine=0; //清空接收長(zhǎng)度
}
RxBuffer[0]=0;
HAL_UART_Receive_IT(&huart2, (uint8_t *)RxBuffer, 1); //每接收一個(gè)數(shù)據(jù),就打開一次串口中斷接收,否則只會(huì)接收一個(gè)數(shù)據(jù)就停止接收
}
}
這里有個(gè)注意點(diǎn),我們?cè)赩OFA+中設(shè)置的是以ASCII碼的形式發(fā)送,所以如果發(fā)送了一個(gè)感嘆號(hào)“!”,此時(shí)STM32接收到的將會(huì)是感嘆號(hào)的ASCII碼,十六進(jìn)制下是0x21。如果發(fā)送數(shù)字0,那么接收到的數(shù)據(jù)用十進(jìn)制表示是48,用十六進(jìn)制表示是0x72。
接下來,我們要將指令中的數(shù)據(jù)提取出來
/*
* 解析出DataBuff中的數(shù)據(jù)
* 返回解析得到的數(shù)據(jù)
*/
float Get_Data(void)
{
uint8_t data_Start_Num = 0; // 記錄數(shù)據(jù)位開始的地方
uint8_t data_End_Num = 0; // 記錄數(shù)據(jù)位結(jié)束的地方
uint8_t data_Num = 0; // 記錄數(shù)據(jù)位數(shù)
uint8_t minus_Flag = 0; // 判斷是不是負(fù)數(shù)
float data_return = 0; // 解析得到的數(shù)據(jù)
for(uint8_t i=0;i<200;i++) // 查找等號(hào)和感嘆號(hào)的位置
{
if(DataBuff[i] == '=') data_Start_Num = i + 1; // +1是直接定位到數(shù)據(jù)起始位
if(DataBuff[i] == '!')
{
data_End_Num = i - 1;
break;
}
}
if(DataBuff[data_Start_Num] == '-') // 如果是負(fù)數(shù)
{
data_Start_Num += 1; // 后移一位到數(shù)據(jù)位
minus_Flag = 1; // 負(fù)數(shù)flag
}
data_Num = data_End_Num - data_Start_Num + 1;
if(data_Num == 4) // 數(shù)據(jù)共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) // 數(shù)據(jù)共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) // 數(shù)據(jù)共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;
}
最后,將解析得到的數(shù)值賦值到PID參數(shù)變量中就行啦。
/*
* 根據(jù)串口信息進(jìn)行PID調(diào)參
*/
void USART_PID_Adjust(uint8_t Motor_n)
{
float data_Get = Get_Data(); // 存放接收到的數(shù)據(jù)
// printf("data=%.2f\r\n",data_Get);
if(Motor_n == 1)//左邊電機(jī)
{
if(DataBuff[0]=='P' && DataBuff[1]=='1') // 位置環(huán)P
pid_l_position.kp = data_Get;
else if(DataBuff[0]=='I' && DataBuff[1]=='1') // 位置環(huán)I
pid_l_position.ki = data_Get;
else if(DataBuff[0]=='D' && DataBuff[1]=='1') // 位置環(huán)D
pid_l_position.kd = data_Get;
else if(DataBuff[0]=='P' && DataBuff[1]=='2') // 速度環(huán)P
pid_l_speed.kp = data_Get;
else if(DataBuff[0]=='I' && DataBuff[1]=='2') // 速度環(huán)I
pid_l_speed.ki = data_Get;
else if(DataBuff[0]=='D' && DataBuff[1]=='2') // 速度環(huán)D
pid_l_speed.kd = data_Get;
else if((DataBuff[0]=='S' && DataBuff[1]=='p') && DataBuff[2]=='e') //目標(biāo)速度
L_Target_Speed = data_Get;
else if((DataBuff[0]=='P' && DataBuff[1]=='o') && DataBuff[2]=='s') //目標(biāo)位置
L_Target_Position = data_Get;
}
else if(Motor_n == 0) // 右邊電機(jī)
{
if(DataBuff[0]=='P' && DataBuff[1]=='1') // 位置環(huán)P
pid_r_position.kp = data_Get;
else if(DataBuff[0]=='I' && DataBuff[1]=='1') // 位置環(huán)I
pid_r_position.ki = data_Get;
else if(DataBuff[0]=='D' && DataBuff[1]=='1') // 位置環(huán)D
pid_r_position.kd = data_Get;
else if(DataBuff[0]=='P' && DataBuff[1]=='2') // 速度環(huán)P
pid_r_speed.kp = data_Get;
else if(DataBuff[0]=='I' && DataBuff[1]=='2') // 速度環(huán)I
pid_r_speed.ki = data_Get;
else if(DataBuff[0]=='D' && DataBuff[1]=='2') // 速度環(huán)D
pid_r_speed.kd = data_Get;
else if((DataBuff[0]=='S' && DataBuff[1]=='p') && DataBuff[2]=='e') //目標(biāo)速度
R_Target_Speed = data_Get;
else if((DataBuff[0]=='P' && DataBuff[1]=='o') && DataBuff[2]=='s') //目標(biāo)位置
R_Target_Position = data_Get;
}
}
到這里,串口調(diào)試的部分就全部完成了,PID部分和定時(shí)器部分就不贅述了。
2.2 開始調(diào)參
代碼寫完后,開啟串口,就可以實(shí)時(shí)調(diào)整PID各個(gè)參數(shù)和電機(jī)的目標(biāo)速度了,當(dāng)PID調(diào)節(jié)完成后再將參數(shù)寫進(jìn)代碼中。
這里要注意數(shù)據(jù)發(fā)送時(shí)必須嚴(yán)格按照數(shù)據(jù)引擎的格式,否則軟件將不能解析數(shù)據(jù)進(jìn)行畫圖。想要查看格式的花把鼠標(biāo)移動(dòng)到數(shù)據(jù)引擎那里的黑色問號(hào)上就行了
這不比原來那種看波形→改參數(shù)→燒代碼→看波形的流程爽多了
如果在小車和電腦分別插一個(gè)藍(lán)牙模塊,就能實(shí)現(xiàn)小車邊跑邊調(diào),直接化身遠(yuǎn)程遙控車,很好玩。文章來源:http://www.zghlxwxcb.cn/news/detail-427909.html
3. 其他
VOFA+簡(jiǎn)潔好用,但是又有點(diǎn)太簡(jiǎn)潔了,部分體驗(yàn)并不是很好(比如不能調(diào)整控件大小,保存文件不太方便等),所以如果學(xué)有余力,推薦自己用QT/PyQT寫上位機(jī)。文章來源地址http://www.zghlxwxcb.cn/news/detail-427909.html
到了這里,關(guān)于【重要】【程序】 使用VOFA+進(jìn)行PID調(diào)試的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!