起因
因?yàn)樵趯W(xué)習(xí)PID算法,程序里并不能很好的展示調(diào)參效果,于是使用VOFA+,伏特加
上位機(jī)軟件來調(diào)試PID,可以很好的展示各個(gè)數(shù)據(jù)的直觀曲線形式,特別適合數(shù)據(jù)變化較大的數(shù)據(jù)進(jìn)行直觀顯示。
我們?cè)谙挛粰C(jī)(單片機(jī)里進(jìn)行調(diào)節(jié)參數(shù)),在上位機(jī)里查看數(shù)據(jù)變化的曲線,根據(jù)曲線進(jìn)行動(dòng)態(tài)調(diào)節(jié)參數(shù),從而達(dá)到我們的目的
本文以調(diào)節(jié)PID位置式,以PID速度環(huán)閉環(huán)調(diào)試參數(shù)為例,來給大家?guī)砣绾问褂肰OFA+軟件,來達(dá)到我們PID速度環(huán)閉環(huán)
一、VOFA+支持的三種數(shù)據(jù)傳輸協(xié)議
Vofa支持3種數(shù)據(jù)流方式:分別為:RawData、firewater、justfloat
。
-
RawData
:RawData協(xié)議適用于不需要解析數(shù)據(jù),僅僅查看字節(jié)流的需求。RawData不做采樣數(shù)據(jù)解析。RawData不做采樣數(shù)據(jù)解析。就相當(dāng)于普通的串口助手。 -
firewater
:本協(xié)議是CSV風(fēng)格的字符串流,直觀簡潔,編程像printf簡單。但由于字符串解析消耗更多的運(yùn)算資源(無論在上位機(jī)還是下位機(jī)),建議僅在通道數(shù)量不多、發(fā)送頻率不高的時(shí)候使用。 -
justfloat
:本協(xié)議是小端浮點(diǎn)數(shù)組形式的字節(jié)流協(xié)議,純十六進(jìn)制浮點(diǎn)傳輸,節(jié)省帶寬。此協(xié)議非常適合用在通道數(shù)量多、發(fā)送頻率高的時(shí)候。
RawData協(xié)議格式
RawData:協(xié)議適用于不需要解析數(shù)據(jù),僅僅查看字節(jié)流的需求。【可以直接當(dāng)串口助手使用】
如果您只是想將本軟件當(dāng)成串口助手使用,選擇RawData協(xié)議,可以保證接收到什么打印什么。
1、使用方法
意思是發(fā)什么,顯示什么,可以字符串顯示,可以Hex顯示
選擇RawData數(shù)據(jù)引擎
2、示例
stm32下位機(jī)程序示例
void RawData_Test(void) //RawData數(shù)據(jù)協(xié)議 直接當(dāng)串口助手使用 測(cè)試是否可行
{
u1_SendByte(0x40);
u1_SendByte(0x41);
u1_SendByte(0x42);
u1_SendByte(0x43);
u1_SendByte(0x0d);
u1_SendByte(0x0a);
}
3、測(cè)試
測(cè)試成功,單片機(jī)發(fā)什么,就接收什么,就相當(dāng)于一個(gè)串口助手
firewater協(xié)議格式
firewater:本協(xié)議是CSV風(fēng)格的字符串流,直觀簡潔,編程像printf簡單。但由于字符串解析消耗更多的運(yùn)算資源(無論在上位機(jī)還是下位機(jī)),建議僅在通道數(shù)量不多、發(fā)送頻率不高的時(shí)候使用。
1、使用方法
FireWater
遇到換行才會(huì)打印數(shù)據(jù)
,而且數(shù)據(jù)之間用逗號(hào)隔開,換行打印數(shù)據(jù),并且顯示數(shù)據(jù)曲線
2、數(shù)據(jù)格式
"<any>:ch0,ch1,ch2,...,chN\n"
● any和冒號(hào)可以為空,但換行(\n)不可省略;
● any不可以為"image",這個(gè)前綴用于解析圖片數(shù)據(jù);
● 此處\n為換行,并非指字符斜杠+字符n;
● \n也可以為\n\r,或\r\n。
3、示例
說白了,就是/n就會(huì)打印數(shù)據(jù),逗號(hào)隔開通道
一個(gè)通道就
數(shù)據(jù) \n
這就會(huì)顯示這個(gè)通道的曲線,并且動(dòng)態(tài)顯示
兩個(gè)通道
數(shù)據(jù)1 ,數(shù)據(jù)2 \n
代表兩個(gè)通道,ch1和ch2,并將數(shù)據(jù)變化曲線動(dòng)態(tài)顯示
//FireWater數(shù)據(jù)協(xié)議 換行結(jié)尾 /n或/r/n 逗號(hào)分隔通道
//指定三個(gè)通道
float a=5,b=10,c=20;
void FireWater_Test(void)
{
a+=100;
b+=50;
c+=10;
u1_printf("%.2f,%.2f,%.2f\n",a,b,c);
}
4、測(cè)試
測(cè)試成功 三條通道都成功輸出波形
justfloat協(xié)議格式
justfloat:本協(xié)議是小端浮點(diǎn)數(shù)組形式
的字節(jié)流協(xié)議,純十六進(jìn)制浮點(diǎn)傳輸,節(jié)省帶寬
。此協(xié)議非常適合用在通道數(shù)量多、發(fā)送頻率高
的時(shí)候。
1、使用方法
小端浮點(diǎn)數(shù)組
的形式傳輸數(shù)據(jù),適用于通道數(shù)量多,且發(fā)送頻率高的場(chǎng)景
2、數(shù)據(jù)格式
#define CH_COUNT <N>
struct Frame {
float ch_data[CH_COUNT];
unsigned char tail[4]{0x00, 0x00, 0x80, 0x7f};
};
● ch_data為小端浮點(diǎn)數(shù)組,里面放著需要發(fā)送的CH_COUNT個(gè)通道。
● tail為幀尾。
說白了就是,數(shù)據(jù)格式就是
浮點(diǎn)數(shù)據(jù)1,浮點(diǎn)數(shù)據(jù)2…幀尾
這里的幀尾是 0x00, 0x00, 0x80, 0x7f
3、示例
大家可以看示例,發(fā)送4個(gè)數(shù)據(jù),把浮點(diǎn)數(shù)轉(zhuǎn)換成了4個(gè)字節(jié)數(shù)據(jù),然后加上幀尾。
我們?nèi)绾伟迅↑c(diǎn)數(shù)據(jù)與4個(gè)單字節(jié)轉(zhuǎn)換?
大家可以看下面這篇鏈接:
浮點(diǎn)數(shù)據(jù)與4個(gè)單字節(jié)的轉(zhuǎn)換
有了上面的認(rèn)識(shí)之后,我們已經(jīng)知道了如何將浮點(diǎn)數(shù)據(jù)轉(zhuǎn)換為4個(gè)單字節(jié)的數(shù)據(jù)
所以我們?cè)谑褂胘ustfloat協(xié)議,把要傳入的浮點(diǎn)數(shù),轉(zhuǎn)換為4個(gè)字節(jié),然后幀尾結(jié)束就可以了
,這樣一個(gè)數(shù)據(jù)就發(fā)送到上位機(jī)了,上位機(jī)根據(jù)協(xié)議進(jìn)行解析,將數(shù)據(jù)動(dòng)態(tài)顯示
/*
要點(diǎn)提示:
1. float和unsigned long具有相同的數(shù)據(jù)結(jié)構(gòu)長度
2. union據(jù)類型里的數(shù)據(jù)存放在相同的物理空間
*/
typedef union
{
float fdata;
unsigned long ldata;
} FloatLongType;
/*
將浮點(diǎn)數(shù)f轉(zhuǎn)化為4個(gè)字節(jié)數(shù)據(jù)存放在byte[4]中
*/
void Float_to_Byte(float f,unsigned char byte[])
{
FloatLongType fl;
fl.fdata=f;
byte[0]=(unsigned char)fl.ldata;
byte[1]=(unsigned char)(fl.ldata>>8);
byte[2]=(unsigned char)(fl.ldata>>16);
byte[3]=(unsigned char)(fl.ldata>>24);
}
void JustFloat_Test(void) //justfloat 數(shù)據(jù)協(xié)議測(cè)試
{
float a=1,b=2; //發(fā)送的數(shù)據(jù) 兩個(gè)通道
u8 byte[4]={0}; //float轉(zhuǎn)化為4個(gè)字節(jié)數(shù)據(jù)
u8 tail[4]={0x00, 0x00, 0x80, 0x7f}; //幀尾
//向上位機(jī)發(fā)送兩個(gè)通道數(shù)據(jù)
Float_to_Byte(a,byte);
//u1_printf("%f\r\n",a);
u1_SendArray(byte,4); //1轉(zhuǎn)化為4字節(jié)數(shù)據(jù) 就是 0x00 0x00 0x80 0x3F
Float_to_Byte(b,byte);
u1_SendArray(byte,4); //2轉(zhuǎn)換為4字節(jié)數(shù)據(jù) 就是 0x00 0x00 0x00 0x40
//發(fā)送幀尾
u1_SendArray(tail,4); //幀尾為 0x00 0x00 0x80 0x7f
}
4、測(cè)試
測(cè)試結(jié)果如下,上位機(jī)解析成功
我發(fā)送的1和2數(shù)據(jù),加上幀尾,這就是一個(gè)完整的數(shù)據(jù)幀
完整的數(shù)據(jù)幀是這樣子
00 00 80 3F 00 00 00 40 00 00 80 7F
分別表示 1 2 幀尾
可以看到兩條數(shù)據(jù)曲線
三種協(xié)議使用總結(jié)
-
大家如果只是想把VOFA+當(dāng)普通的串口助手,可以
選擇RawData協(xié)議
-
大家如果對(duì)于數(shù)據(jù)傳輸通道數(shù)量不多、發(fā)送頻率不高,而又想動(dòng)態(tài)顯示數(shù)據(jù)曲線的時(shí)候,可以
選擇firewater協(xié)議
-
而
justfloat協(xié)議
適用于通道數(shù)量多、發(fā)送頻率高的時(shí)候,這是和firewater協(xié)議的最本質(zhì)區(qū)別。
二、PID調(diào)參
由于首先學(xué)習(xí)的是PID的位置式,所以本文以PID位置式來調(diào)節(jié)速度環(huán),大家可以看看這個(gè)VOFA+上位機(jī)軟件,直觀感受一下
PID位置式
計(jì)算目標(biāo)和實(shí)際的誤差,經(jīng)過pid計(jì)算后得到輸出,返回
float PID_realize(float temp_val) //tem_val為實(shí)際速度
{
//計(jì)算目標(biāo)值與實(shí)際值的誤差
pid.err=pid.target_val-temp_val;
//誤差累積
pid.integral+=pid.err;
//PID算法實(shí)現(xiàn)
pid.actual_val=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
//誤差傳遞
pid.err_last=pid.err;
//返回當(dāng)前實(shí)際值
return pid.actual_val;
}
調(diào)參記錄
周期讀取脈沖數(shù) 這里定的是10ms
設(shè)置目標(biāo)速度30(這里脈沖數(shù)近似速度)讀取到的脈沖數(shù)作為pid輸入
經(jīng)過pid計(jì)算后的out傳入pwm設(shè)置占空比
kp
- 1、判斷kp極性
kp= -10
綠線
表示pid運(yùn)算得出的結(jié)果值紅線
表示實(shí)際速度藍(lán)線
表示目標(biāo)速度
使用上位機(jī)查看曲線,可以看到,并沒有很快的接近目標(biāo)值,并且輸出以最大轉(zhuǎn)速,說明kp極性反了
- 調(diào)節(jié)kp
kp=10
可以看到這次的極性正確了,曲線迅速接近目標(biāo)速度,達(dá)到25
再次調(diào)節(jié)kp
kp=20
可以看到,這一次實(shí)際速度更加接近目標(biāo)值了,但是實(shí)際速度曲線有些抖動(dòng),這說明我們應(yīng)該是到kp的極限了,所以我們往下調(diào)一下
再次調(diào)節(jié)kp
kp=15
這次看到kp=15的時(shí)候,和20的時(shí)候,實(shí)際速度差不多,而且pid輸出的曲線也更加平滑了,所以我們就取kp=15
但是無論怎么調(diào),我們都無法達(dá)到目標(biāo)值,所以這個(gè)時(shí)候就需要積分,即ki
ki
ki=0.5
可以看到實(shí)際值迅速到達(dá)目標(biāo)值,且調(diào)參效果較好,我們?cè)偌蛹觡i看看
調(diào)節(jié)ki
ki=1.0
可以看到,這與我們期望的速度相同,大體實(shí)現(xiàn)速度環(huán)要求,上下誤差2,速度較為平穩(wěn)
kd
試試kd
kd=1.0
調(diào)節(jié)kd后,曲線震蕩的周期變快了,我們可以和上一個(gè)曲線對(duì)比
看,曲線的震蕩頻率加快
雖然目前還沒有完全理解這幾個(gè)參數(shù),但大體上了解這個(gè)參數(shù)應(yīng)該如何調(diào)節(jié),這個(gè)參數(shù)實(shí)際上使用VOFA+可視化曲線一邊調(diào)一遍看,調(diào)節(jié)就很好調(diào)了
注意:我這里只是以VOFA+調(diào)節(jié)PID參數(shù)直觀顯示為例,如果調(diào)節(jié)錯(cuò)誤,還請(qǐng)大家多多包含。
三、總結(jié)
本文總結(jié)了VOFA+的使用,以及三種數(shù)據(jù)傳輸協(xié)議的使用方法和示例。
大家可以直接使用我的示例,稍微修改一下就可以直接使用VOFA+上位機(jī)軟件了
然后展示了如何使用VOFA+軟件調(diào)節(jié)PID參數(shù)
,很明顯這里的PID參數(shù)調(diào)節(jié)還存在很多問題
目前還在學(xué)習(xí)中
如果錯(cuò)誤,歡迎指出
VOFA+官方手冊(cè)
VOFA+官方手冊(cè)
解答疑問
文章自發(fā)出,有很多同學(xué)對(duì)這三種協(xié)議有疑問,現(xiàn)在對(duì)評(píng)論區(qū)中各位同學(xué)的疑問做一個(gè)解答
1、三種協(xié)議的區(qū)別
大家可以往上翻
這里簡單說下
- RawData 相當(dāng)于串口助手
- firewater 可以顯示波形,但是printf后要加\n
- justfloat 也可以顯示波形,適用于通道數(shù)量多、發(fā)送頻率高的情況
2、printf函數(shù)如何重定向
這里我就不說如何重定向了吧,無論是正點(diǎn)原子還是各類課程,都有這方面的介紹
這里推薦江科大,Stm32教程里有詳細(xì)介紹
3、如果我要顯示波形,應(yīng)該是選擇firewater還是justfloat呢?
其實(shí)兩種協(xié)議,都可以
不過根據(jù)評(píng)論區(qū)的同學(xué)來看,justfloat協(xié)議貌似有點(diǎn)太過于復(fù)雜
這里建議使用firewater協(xié)議,同樣可以顯示數(shù)據(jù)哇
還不用移植我的文件,多方便啊
直接
printf("%d,%d\n",data1,data2);
通過串口打印出data1和data2的數(shù)據(jù),經(jīng)過VOFA+解析后,就可以在VOFA+上顯示波形
大家如果只是想通過VOFA+來調(diào)PID,firewater協(xié)議完全夠用了
而調(diào)PID時(shí)
只需要看期望值和真實(shí)值就行了,根據(jù)這兩個(gè)值來調(diào)PID
4、如果想使用firewater協(xié)議,應(yīng)該如何移植呢
首先查看這篇文章
我的PID學(xué)習(xí)歷程—PID位置式和增量式
在文末找到工程下載鏈接,進(jìn)入gitee,下載工程
然后在我的代碼/任意一個(gè)工程SYSTEM/Vofa
,直接把這個(gè)文件夾復(fù)制到自己的工程中,添加到自己的工程中
然后將這個(gè)函數(shù)復(fù)制下來,到自己的文件當(dāng)中
// 向vofa發(fā)送數(shù)據(jù) 三個(gè)數(shù)據(jù) 三個(gè)通道 可視化顯示 幀尾
void vofa_sendData(float a, float b, float c)
{
// float a=1,b=2; //發(fā)送的數(shù)據(jù) 兩個(gè)通道
u8 byte[4] = {0}; // float轉(zhuǎn)化為4個(gè)字節(jié)數(shù)據(jù)
u8 tail[4] = {0x00, 0x00, 0x80, 0x7f}; // 幀尾
// 向上位機(jī)發(fā)送兩個(gè)通道數(shù)據(jù)
Float_to_Byte(a, byte);
// u1_printf("%f\r\n",a);
u1_SendArray(byte, 4); // 1轉(zhuǎn)化為4字節(jié)數(shù)據(jù) 就是 0x00 0x00 0x80 0x3F
Float_to_Byte(b, byte);
u1_SendArray(byte, 4); // 2轉(zhuǎn)換為4字節(jié)數(shù)據(jù) 就是 0x00 0x00 0x00 0x40
Float_to_Byte(c, byte);
u1_SendArray(byte, 4);
// 發(fā)送幀尾
u1_SendArray(tail, 4); // 幀尾為 0x00 0x00 0x80 0x7f
}
調(diào)用此函數(shù),即可實(shí)現(xiàn)justFloat協(xié)議的顯示波形數(shù)據(jù),我在這里封裝了三個(gè)通道數(shù)據(jù),大家也可以自行添加
其實(shí)這個(gè)一看我的工程就明白了,大家可以自行參考
5、如何測(cè)試
我在vofa.c
文件中已經(jīng)給出三種協(xié)議的測(cè)試代碼,大家只要調(diào)用相應(yīng)的函數(shù),并且在上位機(jī)中選擇相同的協(xié)議,即可測(cè)試文章來源:http://www.zghlxwxcb.cn/news/detail-426242.html
//RawData數(shù)據(jù)協(xié)議 測(cè)試
void RawData_Test(void) // 直接當(dāng)串口助手使用 測(cè)試是否可行
{
u1_SendByte(0x40);
u1_SendByte(0x41);
u1_SendByte(0x42);
u1_SendByte(0x43);
u1_SendByte(0x0d);
u1_SendByte(0x0a);
}
//FireWater數(shù)據(jù)協(xié)議 測(cè)試
float a=5,b=10,c=20;
void FireWater_Test(void)
{
a+=100;
b+=50;
c+=10;
u1_printf("%.2f,%.2f,%.2f\n",a,b,c);
}
void JustFloat_Test(void) //justfloat 數(shù)據(jù)協(xié)議測(cè)試
{
float a=1,b=2; //發(fā)送的數(shù)據(jù) 兩個(gè)通道
u8 byte[4]={0}; //float轉(zhuǎn)化為4個(gè)字節(jié)數(shù)據(jù)
u8 tail[4]={0x00, 0x00, 0x80, 0x7f}; //幀尾
//向上位機(jī)發(fā)送兩個(gè)通道數(shù)據(jù)
Float_to_Byte(a,byte);
//u1_printf("%f\r\n",a);
u1_SendArray(byte,4); //1轉(zhuǎn)化為4字節(jié)數(shù)據(jù) 就是 0x00 0x00 0x80 0x3F
Float_to_Byte(b,byte);
u1_SendArray(byte,4); //2轉(zhuǎn)換為4字節(jié)數(shù)據(jù) 就是 0x00 0x00 0x00 0x40
//發(fā)送幀尾
u1_SendArray(tail,4); //幀尾為 0x00 0x00 0x80 0x7f
}
這個(gè)測(cè)試的前提是,要移植我的Vofa文件夾
到自己的工程中去文章來源地址http://www.zghlxwxcb.cn/news/detail-426242.html
到了這里,關(guān)于如何使用VOFA+?一款好用的上位機(jī)軟件(VOFA+的三種數(shù)據(jù)傳輸協(xié)議)——以PID調(diào)參為例的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!