A felhasználó ennek a mérésnek a segítségével elsajátíthatja egy szervomotor és robot vezérlését számítógépen keresztül. Először a felhasználó megismerkedik a szervo és a számítógép közti kommunikációval, ami lényeges eleme a mobilrobotnak. Következőleg a robot belső rendszere kerül bemutatásra. (Egy külön használati útmutató áll a robot kísérletekhez rendelkezésre).
Egy DAC segítségével küldünk információt a gépről a szervonak, mivel a legtöbb szervo esetében a referenciajelek analóg feszültség jelek. A motor mozgását egy enkóder méri, amely impulzussort küld a számítógépnek. Ezeket egy számláló számolja és a számítógép olvassa ki, így szerezve információt a motor pozíciójáról. A sebességet az előző és pillanatnyi pozíciókból számítjuk. Ez egy nagyon zajos sebességjelet eredményez. A mérésben egy diszkrét idejű szűrőt használunk, hogy csökkentsük a mérés zaját. A felhasználó megtanulhatja, hogyan kell megírni és behangolni a PI szabályozóját egy szervo egységnek.
A felhasználónak nem kell kitűnő programozónak lennie, de az alapokkal tisztában kell lennie. A felhasználó néhány nagyon egyszerű programot ír C-ben, illetve Visual Basic-ben. Példák bemutatásával segítjük a sajár program megírását, és remélhetőleg a mérést bárki úgy is el tudja majd végezni, ha ez lesz majd az első olyan C vagy Visual Basic programja, pontosabban program részlete, melyet sajár maga írt.
A kísérletek a következő helyen érhetőek el: http://dind.mogi.bme.hu/experiment/
A megoldandó kód, és amit a kódmezőbe kell írni:
motorDA = 3; new_voltage = 5.0; //Első lépés: Eszköz megnyitása dwErrCde = DRV_DeviceOpen(lDevNumDA, &lDriverHandleDA); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); exit(1); } // Második lépés: Kimeneti érték a meghatározott csatornára tAOVoltageOut.chan = motorDA; tAOVoltageOut.OutputValue = new_voltage; dwErrCde = DRV_AOVoltageOut(lDriverHandleDA, &tAOVoltageOut); if (dwErrCde != SUCCESS) { ErrorStop(&lDriverHandleDA, dwErrCde); return; }
Ez a kód bekapcsolja a szervot. Ilyenkor egy zöld LED világít az egységen.
A LED lekapcsolásához a következő sort kell módosítani: new_voltage = 0.0;
//Harmadik lépés: eszköz megnyitása dwErrCde = DRV_DeviceOpen(lDevNum, &lDriverHandle); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); printf("Program terminated!\n"); printf("Press any key to exit...."); getch(); exit(1); } // Negyedik lépés: Kimeneti jelet meghatározott csatornára tAOVoltageOut.chan = usChan; tAOVoltageOut.OutputValue = fOutValue; dwErrCde = DRV_AOVoltageOut(lDriverHandle, &tAOVoltageOut); if (dwErrCde != SUCCESS) { ErrorStop(&lDriverHandle, dwErrCde); printf("Press any key to exit...."); getch(); return; }
//Harmadik lépés: Eszköz megynitása dwErrCde = DRV_DeviceOpen(lDevNum, &lDriverHandle); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); printf("Program terminated!\n"); printf("Press any key to exit...."); getch(); exit(1); } // Negyedik lépés: Kimeneti értéket a meghatározott csatornára tAOVoltageOut.chan = usChan; tAOVoltageOut.OutputValue = fOutValue; dwErrCde = DRV_AOVoltageOut(lDriverHandle, &tAOVoltageOut); if (dwErrCde != SUCCESS) { ErrorStop(&lDriverHandle, dwErrCde); printf("Press any key to exit...."); getch(); return; }
motorDA = 3; new_voltage = 5.0; //Első lépés: Eszköz megnyitása dwErrCde = DRV_DeviceOpen(lDevNumDA, &lDriverHandleDA); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); exit(1); } // Második lépés: Kimeneti érték a meghatározott csatornára tAOVoltageOut.chan = motorDA; tAOVoltageOut.OutputValue = new_voltage; dwErrCde = DRV_AOVoltageOut(lDriverHandleDA, &tAOVoltageOut); if (dwErrCde != SUCCESS) { ErrorStop(&lDriverHandleDA, dwErrCde); return; } Alternatív megoldás adható az előre definiált változók nélkül (motorDAésnew_votlage). Ebben az esetben a kód a következő: //Első lépés: Eszköz megnyitása dwErrCde = DRV_DeviceOpen(lDevNumDA, &lDriverHandleDA); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); exit(1); } // Második lépés: Kimeneti érték a meghatározott csatornára tAOVoltageOut.chan = 3; tAOVoltageOut.OutputValue = 5.0f; dwErrCde = DRV_AOVoltageOut(lDriverHandleDA, &tAOVoltageOut); if (dwErrCde != SUCCESS) { ErrorStop(&lDriverHandleDA, dwErrCde); return; }
A váltózó mezőbe írandó kód:
float sin_amp = 3.0;
float sin_ang_freq = 0.25;
A kódmezőbe írandó kód:
new_voltage = sin_amp * sin(sin_ang_freq * (time_array[tickCount] / 10000000.0));
A 10000-rel való osztás szükséges, hogy milliszekundumot és 1000-rel, hogy szekundumot kapjunk.
A sin()függvény benne van a C++-ban, ami a Math.h header fájlban található. Ez kiszámolja a megadott érték szinuszát.
Ha más változót használunk a new_voltage helyett, a program nem fog működni.
Az eredmény:
Közvetlenül lehet írni a változókba, változók létrehozása nélkül. Ebben az esetben a kód a következő:
new_voltage = 3.0 * sin(0.25 * (time_array[tickCount] / 10000000.0));
A kód ami az első szöveg mezőbe másolandó (inicializáció):
//Első lépés: Eszköz megnyitása dwErrCde = DRV_DeviceOpen(lDevNumCounter, &lDriverHandleCounter); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); exit(1); } // Második lépés: Számláló visszaállítása DRV_CounterReset dwErrCde = DRV_CounterReset(lDriverHandleCounter, wChannelCounter); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); exit(1); } // Harmadik lépés: Számlálás elindítása DRV_CounterEventStart tCounterEventStart.counter = wChannelCounter; dwErrCde = DRV_CounterEventStart(lDriverHandleCounter, &tCounterEventStart); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); exit(1); } // Negyedik lépés: Számláló érték kiolvasása DRV_CounterEventRead tCounterEventRead.counter = wChannelCounter; tCounterEventRead.overflow = &wOverflow; tCounterEventRead.count = &dwReading; dwErrCde = DRV_CounterEventRead(lDriverHandleCounter, &tCounterEventRead); if (dwErrCde != SUCCESS) { ErrorStop(&lDriverHandleCounter, dwErrCde); return; }
A kód, ami a második szövegmezőbe kerül(cikluson belül):
dwErrCde = DRV_CounterEventRead(lDriverHandleCounter, &tCounterEventRead); if (dwErrCde != SUCCESS) { ErrorStop(&lDriverHandleCounter, dwErrCde); return; }
Az eredmény a szinuszos nyomaték hatására kialakuló pozíció és sebesség grafikon.
//Harmadik lépés: Eszköz megnyitása dwErrCde = DRV_DeviceOpen(lDevNum, &lDriverHandle); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); printf("Program terminated!\n"); printf("Press any key to exit...."); getch(); exit(1); } // Negyedik lépés: Számláló visszaállítása DRV_CounterReset dwErrCde = DRV_CounterReset(lDriverHandle, wChannel); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); printf("Program terminated!\n"); printf("Press any key to exit...."); getch(); exit(1); } // Ötödik lépés: Számláló elindítása DRV_CounterEventStart tCounterEventStart.counter = wChannel; dwErrCde = DRV_CounterEventStart(lDriverHandle, &tCounterEventStart); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); printf("Program terminated!\n"); printf("Press any key to exit...."); getch(); exit(1); } // Hatodik lépés: Számláló kiolvasása DRV_CounterEventRead while ciklusban // and display counter value, exit when pressing any key tCounterEventRead.counter = wChannel; tCounterEventRead.overflow = &wOverflow; tCounterEventRead.count = &dwReading; while( !kbhit() ) { dwErrCde = DRV_CounterEventRead(lDriverHandle, &tCounterEventRead); if (dwErrCde != SUCCESS) { ErrorStop(&lDriverHandle, dwErrCde); return; } printf("\nCounter value = %lu", dwReading); Sleep(1000); }
//Harmadik lépés: Eszköz megnyitása dwErrCde = DRV_DeviceOpen(lDevNum, &lDriverHandle); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); printf("Program terminated!\n"); printf("Press any key to exit...."); getch(); exit(1); } // Negyedik lépés: Számláló visszaállítása DRV_CounterReset dwErrCde = DRV_CounterReset(lDriverHandle, wChannel); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); printf("Program terminated!\n"); printf("Press any key to exit...."); getch(); exit(1); } // Ötödik lépés: Számláló elindításaDRV_CounterEventStart tCounterEventStart.counter = wChannel; dwErrCde = DRV_CounterEventStart(lDriverHandle, &tCounterEventStart); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); printf("Program terminated!\n"); printf("Press any key to exit...."); getch(); exit(1); } // Hatodik lépés: Számláló kiolvasása DRV_CounterEventReadwhile ciklusban // and display counter value, exit when pressing any key tCounterEventRead.counter = wChannel; tCounterEventRead.overflow = &wOverflow; tCounterEventRead.count = &dwReading; while( !kbhit() ) { dwErrCde = DRV_CounterEventRead(lDriverHandle, &tCounterEventRead); if (dwErrCde != SUCCESS) { ErrorStop(&lDriverHandle, dwErrCde); return; } printf("\nCounter value = %lu", dwReading); Sleep(1000); }
//Első lépés: Eszköz megnyitása dwErrCde = DRV_DeviceOpen(lDevNumCounter, &lDriverHandleCounter); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); exit(1); } // Második lépés: Számláló visszaállítása DRV_CounterReset dwErrCde = DRV_CounterReset(lDriverHandleCounter, wChannelCounter); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); exit(1); } // Harmadik lépés: Számláló indítása DRV_CounterEventStart tCounterEventStart.counter = wChannelCounter; dwErrCde = DRV_CounterEventStart(lDriverHandleCounter, &tCounterEventStart); if (dwErrCde != SUCCESS) { ErrorHandler(dwErrCde); exit(1); } // Step 4: Számláló kiolvasása DRV_CounterEventRead tCounterEventRead.counter = wChannelCounter; tCounterEventRead.overflow = &wOverflow; tCounterEventRead.count = &dwReading; dwErrCde = DRV_CounterEventRead(lDriverHandleCounter, &tCounterEventRead); if (dwErrCde != SUCCESS) { ErrorStop(&lDriverHandleCounter, dwErrCde); return; }
Hurkon belül:
dwErrCde = DRV_CounterEventRead(lDriverHandleCounter, &tCounterEventRead); if (dwErrCde != SUCCESS) { ErrorStop(&lDriverHandleCounter, dwErrCde); return; }
A nyílt hurkú tesztre több lehetőségünk van. A kimeneti feszültség kiszámítható a motor paramétereiből. Mivel itt nincs visszacsatolás, nincs pontos ismeretünk a motor pontos tengelysebességéről és a zavaró hatások teljes mértékben hatnak.
A motort egy eltérő kimeneti nyomatékkal teszteltük. Először 0.1-t alkalmaztunk és másodszor 1-t. A kontrollernek deklarációk nélkül az alábbi értékei adottak:
A mérés hossza milliszekundumban : 1000
Az állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adott) - sebesség
torque (adott) - nyomaték
Deklaráció:
Szabályozó kód:
ResultData.Torque = 1;
vagy
ResultData.Torque = 0.1;
Az eredmények az alábbi ábrákon láthatók. Látható, hogy a tengelypozíció és sebesség diagramja állandó gyorsulással éri el a maximális sebességet. A nyílt hurkú rendszer nagyon lassú, mivel állandó feszültséget használ.
A motort különböző kimeneti nyomatékokkal teszteltük ebben az esetben. 0.1 az első és 1 a második esetben.
A mérés hossza milliszekundumban : 1000
Az állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adott) - sebesség
torque (adott) - nyomaték
Deklarációk:
/* velocity filter variables */ static float z_1=0.0, z_2=0.0, z_3=0.0; static float ztmp_1=0.0, ztmp_2=0.0; /* velocity filter parameters */ /* Tsample=1e-3 and Tc=0.007 */ float ad11= 0.9996, ad12= 9.9072e-004, ad13= 4.3344e-007; float ad21= -1.2637, ad22= 0.9730, ad23= 8.0496e-004; float ad31= -2.3468e+003, ad32= -50.5468, ad33= 0.6280; float bd1= 4.3671e-004, bd2= 1.2637, bd3= 2.3468e+003;
Szabályozó kód:
/* Velocity filter */ ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity; ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity; z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity; z_1 = ztmp_1; z_2 = ztmp_2; ResultData.Velocity =z_1; ResultData.Torque = 1;
A szervoerősítő offset feszültséggel rendelkezik. Ez akkor a legszembetűnőbb, ha szinuszos feszültséget alkalmazunk. Ebben az esetben azt várnánk, hogy a motor tengelyének sebessége és pozíciója is egyaránt szinuszosan változik.
A mérés hossza milliszekundumban : 4000
Az állapot változók nevei:
time (adott) - idő
position (adott)- pozíció
velocity (adott) - sebesség
torque (adott) - nyomaték
sin_torque (választott) – sin_nyomaték
Deklaráció:
doubleparam = 0; doublesinperiod = 1; doublesinamplitude = 2; doubleoffset = 0; // változtassuk az offset tartományát -0.3 és -0.2 közt
Szabályozó kód:
// Pillanatnyi idő millisekundumban param = CurrentTime; // Pillanatnyi idő másodpercben param /= 1000; // Eredmény param = param * 2 * PI / sinperiod; // // Szabályozó kezdete // // Sinuszos feszültség ResultData.Torque = sinamplitude * sin(param) + offset; ResultData.StateVariable_5 = sinamplitude * sin(param); // // Szabályozó vége
Az eredmények lentebb az ábrán láthatók. A pozíció diagram nem teljesen szinuszos, hanem van egy lineáris komponense is. Ez a szervoerősítő offsetjének hatása. Ezt ki lehet vonni az alkalmazott feszültségből és ezáltal a lineáris komponens megszüntethető, csak a deklarációt kell megváltoztatni a vezérlőben:
doubleoffset = -0.27; A Matlab program az eredmények megjelenítéséhez % módosítani a fájlneveknek megfelelően sv_1_14axc255hnyzli55axowlm55 sv_2_14axc255hnyzli55axowlm55 sv_3_14axc255hnyzli55axowlm55 sv_4_14axc255hnyzli55axowlm55 sv_5_14axc255hnyzli55axowlm55 time=time/1000; plot(time,position) set(gca, 'fontsize', [25]); xlabel('Time [sec]'); ylabel('Position [rad]'); title('Open loop'); % itt állítható a tengely axis([0 4 0 12]); grid pause; print -djpegopen_poz plot(time,velocity) set(gca, 'fontsize', [25]); xlabel('Time [sec]'); ylabel('Velocity [rad/s]'); title('Open loop'); % itt állítható a tengely axis([0 4 -15 15]); grid print -djpegopen_vel pause; plot(time,torque) set(gca, 'fontsize', [25]); xlabel('Time [sec]'); ylabel('Torque [mNm]'); title('Open loop'); % itt állítható a tengely axis([0 4 -2.5 2.5]); grid print -djpegopen_torque pause; plot(time,sin_torque) set(gca, 'fontsize', [25]); xlabel('Time [sec]'); ylabel('Torque without offset'); title('Open loop'); % itt állítható a tengely axis([0 4 -2.5 2.5]); grid print -djpegopen_sin
|
|
|
|
(a)
|
|
|
|
(b)
A mérés hossza milliszekundumban: 1000
Az állapotváltozók nevei:
time (adott)
position (adott)
velocity (adott)
torque (adott)
ref
filt
filtb
Deklarációk:
doubleparam = 0; doublesinperiod = 0.1; doublesinamplitude = 0.6; doubleoffset = -0.217; /* sebesseg szuro valtozoi */ static float z_1=0.0, z_2=0.0, z_3=0.0; static float ztmp_1=0.0, ztmp_2=0.0; /* Tsample=1e-3 Tc=0.0032*/ float ad11 = 0.99591; float ad12 = 0.00095987; float ad13 = 3.652e-007; float ad21 = -11.3235; float ad22 = 0.88778; float ad23 = 0.00061567; float ad31 = -19089.6748; float ad32 = -193.6165; float ad33 = 0.30752; float bd1 = 0.0040906; float bd2 = 11.3235; float bd3 = 19089.6748; /* Bessel sebesseg szuro valtozoi */ static float z_1b=0.0, z_2b=0.0, z_3b=0.0; static float ztmp_1b=0.0, ztmp_2b=0.0; /* Bessel Tsample=1e-3 Tc=0.0032*/ float ad11b = 0.95193; float ad12b = 0.00083371; float ad13b = 2.6009e-007; float ad21b = -120.9668; float ad22b = 0.56688; float ad23b = 0.00034345; float ad31b = -159737.83; float ad32b = -629.4281; float ad33b = -0.080513; float bd1b = 0.048071; float bd2b = 120.9668; float bd3b = 159737.83;
Szabályozó kód:
// Current time param = CurrentTime; // Only miliseconds param /= 1000; // Result param = param * 2 * PI / sinperiod; // // Controller part begin // // Sinusoidal voltage ResultData.Torque = sinamplitude * sin(param) + offset; ResultData.StateVariable_5 = sinamplitude * sin(param); /* Velocity filter */ ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity; ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity; z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity; z_1 = ztmp_1; z_2 = ztmp_2; ResultData.StateVariable_6=z_1; /* Bessel velocity filter */ ztmp_1b=ad11b* z_1b+ad12b* z_2b+ad13b* z_3b + bd1b* ResultData.Velocity; ztmp_2b=ad21b* z_1b+ad22b* z_2b+ad23b* z_3b + bd2b* ResultData.Velocity; z_3b=ad31b* z_1b+ad32b* z_2b+ad33b* z_3b + bd3b* ResultData.Velocity; z_1b = ztmp_1b; z_2b = ztmp_2b; ResultData.StateVariable_7=z_1b;
A mérést két különböző periódusidővel végeztük el
doublesinperiod = 0.1;
és
doublesinperiod = 0.5;
Az utóbbi esetben a mérés időtartamát 5000 ms hosszúságúra állítottuk, így mindkét esetben 10 periódust vizsgáltunk. A tranziensek lezajlása után az állandósultnak tekinthető utolsó két periódust rajzoltuk ki (ld. 6-8. és 6-9. ábra). Jól látható, hogy a gerjesztés frekvenciáját növelve a válasz amplitúdója csökken és a fáziskésése növekszik.
A 6-8. és 6-9. ábra elkésszítéséhez használt MatLab program:
sv_1_zlwzmf45pqyn1c45p31cvve4 sv_2_zlwzmf45pqyn1c45p31cvve4 sv_3_zlwzmf45pqyn1c45p31cvve4 sv_4_zlwzmf45pqyn1c45p31cvve4 sv_5_zlwzmf45pqyn1c45p31cvve4 sv_6_zlwzmf45pqyn1c45p31cvve4 sv_7_zlwzmf45pqyn1c45p31cvve4 plot(time,velocity,time,filt,time,filtb,time,ref) set(gca, 'fontsize', [25]); xlabel('Time [sec]'); ylabel('Velocity [rad/s]'); title('Open loop'); % you can adjust your axis % axis([0.8 1 -1 1]); axis([4 5 -3 3]); grid print -djpeg open_vel
Az első feladat a PID szabályozásnál a tagok hangolása. Ehhez a Ziegler-Nichols módszert használjuk, ami tapasztalati úton, könnyen alkalmazható a rendszerre még egy kezdő számára is. A PI szabályozó digitális megvalósításakor nem a TI paraméterre van szükség, hanem az I paraméterre. Ezért a Ziegler-Nichols hangolási táblázatot át kell írnunk.
AP |
I |
TD |
|
P szabályozó |
AU/2 |
||
PI szabályozó |
AU/2.2 |
1.2AP/Tu |
|
PID szabályozó |
AU/1.7 |
2AP/Tu |
AP Tu/8 |
A hangolás első lépésében szükségünk van egy P szabályozóra (I=0, D=0), a körerősítést növelve AU értét kell meghatározni. A kontroller alakja a következő:
A mérés hossza milliszekundumban: 1000
Az állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adott) - sebesség
torque (adott) - nyomaték
ref (választott) - ref
error (választott) - hiba
Deklarálás:
doubleP = 4.75;
doubleref_vel = 7;
doubleerror_vel= 0;
/* sebesség szűrő változók */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* sebesség szűrő paraméterei */
/* Tsample=1e-3 and Tc=0.005 */
float ad11= 0.9989, ad12= 9.8248e-004, ad13= 4.0937e-007;
float ad21= -3.2749, ad22= 0.9497, ad23= 7.3686e-004;
float ad31= -5.8949e+003, ad32= -91.6978, ad33= 0.5076;
float bd1= 0.0011, bd2= 3.2749, bd3= 5.8949e+003;
Szabályozó kód:
/* sebesség szűrő */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
//hibaszámítás a sebesség szabályozáshoz
error_vel=ref_vel- ResultData.Velocity;
ResultData.StateVariable_5 = ref_vel;
ResultData.StateVariable_6 = error_vel;
ResultData.Torque = P*error_vel;
if (ResultData.Torque > 5)
{
ResultData.Torque = 5;
}
if (ResultData.Torque < -5)
{
ResultData.Torque = -5;
}
A szabályozó beállítja a kimenő feszültség minimális és maximális értékeit -5 és 5 V közötti tartományban. Az eredmények az ábrákon láthatók:
Következtetés: AU = 4.75 és TU≈0.2/6. A táblázat szerint PPI=2.1 és IPI=75.
Megjegyzés: AU és TU értékei függenek a szűrő paramétereitől.
Ehhez a teszthez a referencia tengely szögsebességet 1 rad/s-ra állítjuk. A szabályozó az alábbi formájú:
A mérés hossza milliszekundumban : 1000
Az állapotváltozók nevei :
time (adott) - idő
position (adott) - pozíció
velocity (adot) -sebesség
torque (adott) - nyomaték
ref (választott) - ref
error (választott) - hiba
integral (választott) - integrál
Deklaráció:
doubleP_par = 2.3;
doubleI_par = 0.0;
doubleref_vel = 7;
doubleerror_vel;
static doubleerror_vel_int=0.0;
double load = 0;
/* sebesség szűrő változói */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* Tsample=1e-3 és Tc=0.0027 */
float ad11= 0.9936, ad12= 9.4621e-004, ad13= 3.4524e-007;
float ad21= - 17.5400, ad22= 0.8515, ad23= 5.6261e-004;
float ad31= -2.8584e+004, ad32= -249.0676, ad33= 0.2264;
float bd1= 0.0064, bd2= 17.5400, bd3= 2.8584e+004;
Szabályozó kód:
/* sebesség szűrő */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
//Sebesség szabályozó hibaszámítása
error_vel=ref_vel- ResultData.Velocity;
error_vel_int = error_vel_int + error_vel*(CurrentTime - OldTime)/1000;
ResultData.StateVariable_5 = ref_vel;
ResultData.StateVariable_6 = error_vel;
ResultData.StateVariable_7 = error_vel_int;
ResultData.Torque = P_par*error_vel + I_par*error_vel_int - load;
if (ResultData.Torque > 5) { ResultData.Torque = 5; }
if (ResultData.Torque < -5) { ResultData.Torque = -5; }
Az eredményeket az alábbi ábrák mutatják. Látható a P szabályozó állandósult hibája.
Ebben a tesztben a referencia szögsebességet két időpontban változtatjuk: t = 0.2 s és t = 0.4 s. A P szabályozó állandósult hibája itt is látható. A szabályozó a következő alakú:
A mérés ideje milliszekundumban : 1000
Állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adot) -sebesség
torque (adott) - nyomaték
ref (választott) - ref
error (választott) - hiba
integral (választott) - integrál
Deklarációk:
doubleP_par = 2.3;
doubleI_par = 0.0;
doubleref_vel = 7;
doubleerror_vel;
static doubleerror_vel_int=0.0;
double load = 0;
/* sebesség szűrő változói */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* Tsample=1e-3 és Tc=0.0027 */
float ad11= 0.9936, ad12= 9.4621e-004, ad13= 3.4524e-007;
float ad21= - 17.5400, ad22= 0.8515, ad23= 5.6261e-004;
float ad31= -2.8584e+004, ad32= -249.0676, ad33= 0.2264;
float bd1= 0.0064, bd2= 17.5400, bd3= 2.8584e+004;
Szabályozó kód:
//Ugrások a referencia sebességben
if (CurrentTime > 0*1e3 && CurrentTime < 0.2*1e3)
{
ref_vel = 4;
}
if (CurrentTime >= 0.2*1e3 && CurrentTime < 0.4*1e3)
{
ref_vel = 8;
}
if (CurrentTime >= 0.4*1e3 && CurrentTime < 0.6*1e3)
{
ref_vel = 2;
}
/* Velocity filter */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
//sebesség szabályozás hibaszámítása
error_vel=ref_vel- ResultData.Velocity;
error_vel_int = error_vel_int + error_vel*(CurrentTime - OldTime)/1000;
ResultData.StateVariable_5 = ref_vel;
ResultData.StateVariable_6 = error_vel;
ResultData.StateVariable_7 = error_vel_int;
ResultData.Torque = P_par*error_vel + I_par*error_vel_int - load;
if (ResultData.Torque > 5) { ResultData.Torque = 5; }
if (ResultData.Torque < -5) { ResultData.Torque = -5; }
Látható, hogy a P szabályozó legnagyobb hátránya az állandósult hibája és a zavarok bennhagyása.
A mérés hossza milliszekundumban : 1000
Az állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adot) -sebesség
torque (adott) - nyomaték
ref (választott) - ref
error (választott) - hiba
integral (választott) - integrál
Deklaráció:
doubleP_par = 2.3;
doubleI_par = 0.0;
doubleref_vel = 7;
doubleerror_vel;
static doubleerror_vel_int=0.0;
double load = 2;
/* sebesség szűrő változók */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* Tsample=1e-3 és Tc=0.0027 */
float ad11= 0.9936, ad12= 9.4621e-004, ad13= 3.4524e-007;
float ad21= - 17.5400, ad22= 0.8515, ad23= 5.6261e-004;
float ad31= -2.8584e+004, ad32= -249.0676, ad33= 0.2264;
float bd1= 0.0064, bd2= 17.5400, bd3= 2.8584e+004;
Szabályozó kód:
if (CurrentTime >= 0.4*1e3 && CurrentTime < 0.7*1e3)
{
load = 0.5;
}
/* Sebesség szűrő */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
//Sebesség szabályozás hibaszámítása
error_vel=ref_vel- ResultData.Velocity;
error_vel_int = error_vel_int + error_vel*(CurrentTime - OldTime)/1000;
ResultData.StateVariable_5 = ref_vel;
ResultData.StateVariable_6 = error_vel;
ResultData.StateVariable_7 = error_vel_int;
ResultData.Torque = P_par*error_vel + I_par*error_vel_int - load;
if (ResultData.Torque > 5) { ResultData.Torque = 5; }
if (ResultData.Torque < -5) { ResultData.Torque = -5; }
Ennél a tesztnél a referencia szögsebesség 1 rad/s. A szabályozó az alábbi alakú:
Mérés ideje milliszekundumokban : 1000
Állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adot) -sebesség
torque (adott) - nyomaték
ref (választott) - ref
error (választott) - hiba
integral (választott) - integrál
Deklaráció:
doubleP = 0.6;
doubleI = 7;
doubleref_vel = 8;
doubleerror_vel;
static doubleerror_vel_int=0.0;
double load = 0;
/* sebesség szűrő változói */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* sebesség szűrő paraméterei */
/* Tsample=1e-3 and Tc=0.007 */
float ad11= 0.9996, ad12= 9.9072e-004, ad13= 4.3344e-007;
float ad21= -1.2637, ad22= 0.9730, ad23= 8.0496e-004;
float ad31= -2.3468e+003, ad32= -50.5468, ad33= 0.6280;
float bd1= 4.3671e-004, bd2= 1.2637, bd3= 2.3468e+003;
Szabályozó kód:
/* Sebesség szűrő */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
//Sebesség szabályozó hibaszámítása
error_vel=ref_vel- ResultData.Velocity;
error_vel_int = error_vel_int + error_vel*(CurrentTime - OldTime)/1000.0;
ResultData.StateVariable_5 = ref_vel;
ResultData.StateVariable_6 = error_vel;
ResultData.StateVariable_7 = error_vel_int;
ResultData.StateVariable_8 = CurrentTime;
ResultData.Torque = P*error_vel + I*error_vel_int - load;
if (ResultData.Torque > 5)
{
ResultData.Torque = 5;
}
if (ResultData.Torque < -5)
{
ResultData.Torque = -5;
}
Alapvető különbség P és PI vezérkő között, hogy utóbbinak nincs állandósult hibája. Ez az integráló tag hatása, amely összegzi a hibát. Az integrálási hiba is megjelenik, ami kezdetben túllövést eredményez, majd később lecseng. Ugyanakkor a PI szabályozó gyorsabb is. Hátránya, hogy túl nagy túllövést vagy instabilitást okozhat. A jelentős túllövés oka a szabályozó telítődése (ld. 5-14. ábra). Ez egy hozzáadott D taggal csökkenthető, de lásd még a 5.23 mérési feladatot.
Ebben a tesztben a referencia tengely szögsebességét két időpillanatban változtatjuk: t = 0.3 s és t = 0.6 s. A PI szabályozó gyorsabb a P-hez képest és a motor állandósult hiba nélkül működik. A szabályozó az alábbi alakú:
A mérés hossza milliszekundumban : 1000
Állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adot) -sebesség
torque (adott) - nyomaték
ref (választott) - ref
error (választott) - hiba
integral (választott) - integrál
Deklaráció:
doubleP_par = 2;
doubleI_par = 50.0;
doubleref_vel = 7;
doubleerror_vel;
static doubleerror_vel_int=0.0;
double load = 0;
/* sebesség szűrő változók */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* Tsample=1e-3 és Tc=0.0027 */
float ad11= 0.9936, ad12= 9.4621e-004, ad13= 3.4524e-007;
float ad21= - 17.5400, ad22= 0.8515, ad23= 5.6261e-004;
float ad31= -2.8584e+004, ad32= -249.0676, ad33= 0.2264;
float bd1= 0.0064, bd2= 17.5400, bd3= 2.8584e+004;
Szabályozó kód:
//Ugrás változások referencia sebességben
if (CurrentTime > 0*1e3 && CurrentTime < 0.3*1e3)
{
ref_vel = 4;
}
if (CurrentTime >= 0.3*1e3 && CurrentTime < 0.6*1e3)
{
ref_vel = 8;
}
if (CurrentTime >= 0.6*1e3 && CurrentTime < 1.6*1e3)
{
ref_vel = 2;
}
/* Sebességszűrő */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
//Sebesség szabályozás hbaszámítása
error_vel=ref_vel- ResultData.Velocity;
error_vel_int = error_vel_int + error_vel*(CurrentTime - OldTime)/1000;
ResultData.StateVariable_5 = ref_vel;
ResultData.StateVariable_6 = error_vel;
ResultData.StateVariable_7 = error_vel_int;
ResultData.Torque = P_par*error_vel + I_par*error_vel_int - load;
if (ResultData.Torque > 5) { ResultData.Torque = 5; }
if (ResultData.Torque < -5) { ResultData.Torque = -5; }
Ebben a tesztben virtuális terhelést alkalmazunk. Ha t< 0.3 s, akkor load=2.0 load, ha 0.3 s<t< 0.7 s, akkor load=0.5, végül, ha t>0.7 s, akkor load=2.0 ismét.
Mérés hossza milliszekundumban : 1000
Állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adot) -sebesség
torque (adott) - nyomaték
ref (választott) - ref
error (választott) - hiba
integral (választott) - integrál
Deklarálás:
doubleP_par = 2.3;
doubleI_par = 50;
doubleref_vel = 7;
doubleerror_vel;
static doubleerror_vel_int=0.0;
double load = 2;
/* sebesség szűrő változói */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* Tsample=1e-3 és Tc=0.0027 */
float ad11= 0.9936, ad12= 9.4621e-004, ad13= 3.4524e-007;
float ad21= - 17.5400, ad22= 0.8515, ad23= 5.6261e-004;
float ad31= -2.8584e+004, ad32= -249.0676, ad33= 0.2264;
float bd1= 0.0064, bd2= 17.5400, bd3= 2.8584e+004;
Szabályozó kód:
/* Sebesség szűrő */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
if (CurrentTime >= 0.4*1e3 && CurrentTime < 0.7*1e3)
{
load = 0.5;
}
//Sebesség szabályozó hibaszámítása
error_vel=ref_vel- ResultData.Velocity;
error_vel_int = error_vel_int + error_vel*(CurrentTime - OldTime)/1000.0;
ResultData.StateVariable_5 = ref_vel;
ResultData.StateVariable_6 = error_vel;
ResultData.StateVariable_7 = error_vel_int;
ResultData.Torque = P_par*error_vel + I_par*error_vel_int - load;
if (ResultData.Torque > 5)
{
ResultData.Torque = 5;
}
if (ResultData.Torque < -5)
{
ResultData.Torque = -5;
}
Észrevehető a PI szabályozó jó zavarszűrése a P vezérlőjével szemben.
Először az integráló tag értékét közvetlenül korlátozzuk.
A mérés hossza mil l iszekundumban: 12000
Az állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adot) -sebesség
torque (adott) - nyomaték
ref (választott) - ref
error (választott) - hiba
integral (választott) - integrál
Deklarációk:
doubleP_par = 2;
doubleI_par = 50;
doubleref_vel = 7;
doubleerror_vel;
static doubleerror_vel_int=0.0;
double load = 0;
/*
// a esetben
doubleint_lim = 100;
doubleTf = -0.3*1e3;
// besetben
doubleint_lim = 100;
doubleTf = 0.3*1e3;
// c esetben
doubleint_lim = 0.9;
doubleTf = 0.3*1e3;
// d esetben
doubleint_lim = 0.15;
doubleTf = 0.3*1e3;
*/
// kérem másolja be az egyik pár paramétert
doubleint_lim = 0.15;
doubleTf = 0.3*1e3;
/* sebesség szűrő változói */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* Tsample=1e-3 és Tc=0.0027 */
float ad11= 0.9936, ad12= 9.4621e-004, ad13= 3.4524e-007;
float ad21= - 17.5400, ad22= 0.8515, ad23= 5.6261e-004;
float ad31= -2.8584e+004, ad32= -249.0676, ad33= 0.2264;
float bd1= 0.0064, bd2= 17.5400, bd3= 2.8584e+004;
Szabályozó kód:
/* Sebesség szűrő */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
//Sebesség szabályozó hibaszámítása
error_vel=ref_vel- ResultData.Velocity;
error_vel_int = error_vel_int + error_vel*(CurrentTime - OldTime)/1000.0;
ResultData.StateVariable_5 = ref_vel;
ResultData.StateVariable_6 = error_vel;
ResultData.StateVariable_7 = error_vel_int;
if (error_vel_int > int_lim)
{
error_vel_int = int_lim;
}
ResultData.Torque = P_par*error_vel + I_par*error_vel_int - load;
if ( CurrentTime < Tf)
{
ResultData.Torque = 0.0;
}
if (ResultData.Torque > 5)
{
ResultData.Torque = 5;
}
if (ResultData.Torque < -5)
{
ResultData.Torque = -5;
}
Hasonló eredményt érhetünk el, ha a szabályozó telítődésénél az integrálótagot kikapcsoljuk.
A mérés hossza mil l iszekundumban: 1000
Az állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adot) -sebesség
torque (adott) - nyomaték
ref (választott) - ref
error (választott) - hiba
integral (választott) - integrál
Deklarációk:
doubleP_par = 2.1;
doubleI_par = 75.0;
doubleref_vel = 7;
doubleerror_vel;
static doubleerror_vel_int=0.0;
double load = 2;
double delta = 0;
/* velocity filter variables */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* Tsample=1e-3 and Tc=0.0027 */
float ad11= 0.9936, ad12= 9.4621e-004, ad13= 3.4524e-007;
float ad21= - 17.5400, ad22= 0.8515, ad23= 5.6261e-004;
float ad31= -2.8584e+004, ad32= -249.0676, ad33= 0.2264;
float bd1= 0.0064, bd2= 17.5400, bd3= 2.8584e+004;
Szabályozó kód:
/* Velocity filter */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
//Error calculation for velocity control
error_vel=ref_vel- ResultData.Velocity;
delta=error_vel*(CurrentTime - OldTime)/1000;
error_vel_int = error_vel_int + delta;
ResultData.StateVariable_5 = ref_vel;
ResultData.StateVariable_6 = error_vel;
ResultData.Torque = P_par*error_vel + I_par*error_vel_int - load;
if (ResultData.Torque > 5) { ResultData.Torque = 5; error_vel_int = error_vel_int - delta*0; }
if (ResultData.Torque < -5) { ResultData.Torque = -5; error_vel_int = error_vel_int - delta*0;}
ResultData.StateVariable_7 = error_vel_int;
A 6-14. ábrán látható túllövést csökkenthetjük azzal, ha az integrátort kikapcsoljuk a telítődés időtartamára.
Hasonlítsuk össze a két esetben az integráló tag értékét:
A mérés hossza mil l iszekundumban: 1000
Az állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adot) -sebesség
torque (adott) - nyomaték
ref (választott) - ref
error (választott) - hiba
integral (választott) - integrál
Deklarációk:
doubleP_par = 0.85;
doubleI_par = 12.0;
doubleref_vel = 7;
doubleerror_vel;
static doubleerror_vel_int=0.0;
double load = 0;
/* Time delay pipe */
static float T_1=0.0, T_2=0.0, T_3=0.0;
static float T_4=0.0, T_5=0.0, T_6=0.0;
static float T_7=0.0, T_8=0.0, T_9=0.0;
/* velocity filter variables */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* Tsample=1e-3 and Tc=0.0032 */
float ad11 = 0.99591, ad12 = 0.00095987, ad13 = 3.652e-007;
float ad21 = -11.3235, ad22 = 0.88778, ad23 = 0.00061567;
float ad31 = -19089.6748, ad32 = -193.6165, ad33 = 0.30752;
float bd1 = 0.0040906, bd2 = 11.3235, bd3 = 19089.6748;
Szabályozó kód:
//Step changes in reference speed
/* Velocity filter */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
//Error calculation for velocity control
error_vel=ref_vel- ResultData.Velocity;
error_vel_int = error_vel_int + error_vel*(CurrentTime - OldTime)/1000;
ResultData.StateVariable_5 = ref_vel;
ResultData.StateVariable_6 = error_vel;
ResultData.StateVariable_7 = error_vel_int;
ResultData.Torque = T_1;
T_1=T_2;
T_2=T_3;
T_3=T_4;
T_4=T_5;
T_5=T_6;
T_6=T_7;
T_7=T_8;
T_8=T_9;
T_9= P_par*error_vel + I_par*error_vel_int - load;
if (T_9 > 5) { T_9 = 5;
error_vel_int = error_vel_int - error_vel*(CurrentTime - OldTime)/1000;}
if (T_9 < -5) { T_9 = -5;
error_vel_int = error_vel_int - error_vel*(CurrentTime - OldTime)/1000;}
Az eredményeket megjelenítő MatLab program:
% please, modify it according to you file names
sv_1_hwztupv131qjbi55l3k5oi45
sv_2_hwztupv131qjbi55l3k5oi45
sv_3_hwztupv131qjbi55l3k5oi45
sv_4_hwztupv131qjbi55l3k5oi45
sv_5_hwztupv131qjbi55l3k5oi45
sv_6_hwztupv131qjbi55l3k5oi45
sv_7_hwztupv131qjbi55l3k5oi45
time=time/1000;
plot(time,position)
set(gca, 'fontsize', [25]);
xlabel('Time [sec]');
ylabel('Position [rad]');
title('Delyed system with PI controller');
% you can adjust your axis
axis([0 1 0 12]);
grid
pause;
print -djpeg Delay_poz
plot(time,velocity)
set(gca, 'fontsize', [25]);
xlabel('Time [sec]');
ylabel('Velocity [rad/s]');
title('Delyed system with PI controller');
% you can adjust your axis
axis([0 1 0 12]);
grid
print -djpeg Delay_vel
pause;
plot(time,torque)
set(gca, 'fontsize', [25]);
xlabel('Time [sec]');
ylabel('Torque [mNm]');
title('Delyed system with PI controller');
% you can adjust your axis
%axis([0 1 -1 1]);
grid
print -djpeg Delay_torque
pause;
plot(time,int)
set(gca, 'fontsize', [25]);
xlabel('Time [sec]');
ylabel('Int');
title('Delyed system with PI controller');
% you can adjust your axis
%axis([0 1 -1 1]);
grid
print -djpeg Delay_int
Következtetés: AU = 1.7 és TU≈0.08. A táblázat szerint PPI=0.85 és IPI=12. E paraméterekkel behangolt PI szabályozóval végzett mérés eredménye a 6-22. ábrán látható.
Ehhez a teszthez a referencia tengely-pozíciót 10 rad-ra állítjuk. A szabályozó a következő alakú:
A mérés hossza milliszekundumban : 1000
Az állapot változók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adot) -sebesség
torque (adott) - nyomaték
ref (választott) - ref
error (választott) - hiba
integral (választott) - integrál
Deklarálás:
doubleP_par = 1;
doubleI_par = 0;
doubleref_pos = 10;
doubleerror_pos;
static doubleerror_pos_int=0.0;
double load = 0;
/* sebesség szűrő változói */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* Tsample=1e-3 és Tc=0.0027 */
float ad11= 0.9936, ad12= 9.4621e-004, ad13= 3.4524e-007;
float ad21= - 17.5400, ad22= 0.8515, ad23= 5.6261e-004;
float ad31= -2.8584e+004, ad32= -249.0676, ad33= 0.2264;
float bd1= 0.0064, bd2= 17.5400, bd3= 2.8584e+004;
static double ini_0 = 0;
static double ini_1 = -10;
Szabályozó kód:
if (ini_1 < 0)
{
ini_0 = ResultData.Position;
}
ini_1 = 5;
/* Velocity filter */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
//Sebesség szabályozó hibaszámítása
error_pos=ref_pos- ResultData.Position + ini_0;
error_pos_int = error_pos_int + error_pos*(CurrentTime - OldTime)/1000.0;
ResultData.StateVariable_5 = ref_pos;
ResultData.StateVariable_6 = error_pos;
ResultData.StateVariable_7 = error_pos_int;
ResultData.StateVariable_8 = ResultData.Position - ini_0;
ResultData.Torque = P_par*error_pos + I_par*error_pos_int - load;
if (ResultData.Torque > 5)
{
ResultData.Torque = 5;
}
if (ResultData.Torque < -5)
{
ResultData.Torque = -5;
}
Látható a nagy túllövés a vezérlőben, és az állandósult hiba is jelen van. Viszont most a pozíciót mértük, és nem deriválásból számoltuk, a pozíció görbe sima az előző esetekhez képest.
Mérés ideje milliszekundumban : 10000
Az állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adot) -sebesség
torque (adott) - nyomaték
ref (választott) - ref
error (választott) - hiba
integral (választott) - integrál
Deklarációk:
doubleP_par = 0.1;
doubleI_par = 0.1;
doubleref_pos = 5;
doubleerror_pos;
static doubleerror_pos_int=0.0;
double load = 0;
/* sebesség szűrő változói */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* Tsample=1e-3 és Tc=0.0027 */
float ad11= 0.9936, ad12= 9.4621e-004, ad13= 3.4524e-007;
float ad21= - 17.5400, ad22= 0.8515, ad23= 5.6261e-004;
float ad31= -2.8584e+004, ad32= -249.0676, ad33= 0.2264;
float bd1= 0.0064, bd2= 17.5400, bd3= 2.8584e+004;
static double ini_0 = 0;
static double ini_1 = -10;
Szabályozó kód:
if (ini_1 < 0)
{
ini_0 = ResultData.Position;
}
ini_1 = 5;
/* Sebesség szűrő */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
//Sebesség szabályozó hibaszámítása
error_pos=ref_pos- CurrentPosition;
error_pos_int = error_pos_int + error_pos*(CurrentTime - OldTime)/1000.0;
ResultData.StateVariable_5 = ref_pos;
ResultData.StateVariable_6 = error_pos;
ResultData.StateVariable_7 = error_pos_int;
ResultData.Torque = P_par*error_pos + I_par*error_pos_int - load;
if (ResultData.Torque > 5)
{
ResultData.Torque = 5;
}
if (ResultData.Torque < -5)
{
ResultData.Torque = -5;
}
A szabályozó kódja a következő:
Az állapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adott) - sebesség
torque (adott) - nyomaték
ref_poz (választott)
ref_vel (választott)
err_pos (választott)
err_vel (választott)
int_vel (választott)
poz (választott)
Deklaráció:
doubleP_pos = 3;
doubleP_vel = 3;
doubleI_vel = 30;
doubleref_pos = 10;
double ref_vel = 0;
doubleerror_pos = 0;
double error_vel = 0;
static double error_vel_int = 0;
static double ini_0 = 0;
static double ini_1 = -10;
/* sebesség szabályozó változói */
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/* Tsample=1e-3 és Tc=0.0027 */
float ad11= 0.9936, ad12= 9.4621e-004, ad13= 3.4524e-007;
float ad21= - 17.5400, ad22= 0.8515, ad23= 5.6261e-004;
float ad31= -2.8584e+004, ad32= -249.0676, ad33= 0.2264;
float bd1= 0.0064, bd2= 17.5400, bd3= 2.8584e+004;
Szabályozó kód:
if (ini_1 < 0)
{
ini_0 = ResultData.Position;
}
ini_1 = 5;
/* Sebesség szűrő */
ztmp_1=ad11* z_1+ad12* z_2+ad13* z_3 + bd1* ResultData.Velocity;
ztmp_2=ad21* z_1+ad22* z_2+ad23* z_3 + bd2* ResultData.Velocity;
z_3=ad31* z_1+ad32* z_2+ad33* z_3 + bd3* ResultData.Velocity;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
//Pozíció szabályozás hibaszámítása
error_pos = ref_pos- ResultData.Position + ini_0;
// Sebesség szabályozás hibaszámítása
ref_vel = error_pos*P_pos;
error_vel = ref_vel - ResultData.Velocity;
error_vel_int = error_vel_int + error_vel*(CurrentTime - OldTime)/1000;
ResultData.StateVariable_5 = ref_pos;
ResultData.StateVariable_6 = ref_vel;
ResultData.StateVariable_7 = error_pos;
ResultData.StateVariable_8 = error_vel;
ResultData.StateVariable_9 = error_vel_int;
ResultData.StateVariable_10 = ResultData.Position - ini_0;
//Szabályozó
ResultData.Torque = P_vel*error_vel + I_vel*error_vel_int;
if (ResultData.Torque > 5)
{
ResultData.Torque = 5;
}
if (ResultData.Torque < -5)
{
ResultData.Torque = -5;
}
Látható, hogy ez esetben nincs túllövés, mert a tengely sebessége idővel csökkeni kezd. Megállapíthatjuk, hogy a pozíció szabályozó belső sebességszabályozóval sokkal jobb eredményeket ad, mint a sima P szabályozó.
Csúszómód szabályozót is lehet használni. Ekkor két különböző mérést vizsgálunk, amelyek szabályozó részeik megegyeznek, de mivel a sebességet a motor pozíciójából számoljuk, így az zajossá teszi a sebesség diagramot. A sebesség értékeket szűrve a zajok kiküszöbölhetőek.
Mérés hossza milliszekundumban : 12000
Az á llapotváltozók nevei:
time (adott) - idő
position (adott) - pozíció
velocity (adott) - sebesség
torque (adott) - nyomaték
sigma (választott) - szigma
A szabályozó kódja a következő:
Deklarációk:
float sigma;
float error;
float error_dot;
float ref=5.0;
float lambda=2;
static double ini_0 = 0;
static double ini_1 = -10;
Szabályozó kód:
if (ini_1 < 0)
{
ini_0 = ResultData.Position;
}
ini_1 = 5;
error=ref-ResultData.Position+ ini_0;
error_dot=- ResultData.Velocity;
sigma= error+ lambda*error_dot;
ResultData.StateVariable_5 = sigma;
if (sigma>0)
{ ResultData.Torque=0.1;
}
if (sigma<0)
{ ResultData.Torque =-0.1;
}
if (sigma=0)
{ ResultData.Torque=0;
}
Válassza kia a letöltendő elemet: Minden fájl
Kérem, töltse le a DOWNLOAD gombra kattintva
Az eredmények a következő MATLAB fájlba plottolódnak:
% módosítani a fájlnevek szerint
sv_1_n0ktqaui51tw5hrtvpypxu45
sv_2_n0ktqaui51tw5hrtvpypxu45
sv_3_n0ktqaui51tw5hrtvpypxu45
sv_4_n0ktqaui51tw5hrtvpypxu45
sv_5_n0ktqaui51tw5hrtvpypxu45
time=time/1000;
plot(time,position)
set(gca, 'fontsize', [25]);
xlabel('Time [sec]');
ylabel('Position [rad]');
title('Sliding mode controller');
% tengely beállítása
axis([0 12 0 6]);
grid
pause;
print -djpegsmc_poz
plot(time,velocity)
set(gca, 'fontsize', [25]);
xlabel('Time [sec]');
ylabel('Velocity [rad/s]');
title('Sliding mode controller');
% tengely beállítása
axis([0 12 0 3]);
grid
print -djpegsmc_vel
pause;
plot(5-position,-velocity)
set(gca, 'fontsize', [25]);
xlabel('Position error [rad]');
ylabel('Velocity error [rad/s]');
title('Sliding mode controller');
% tengely beállítása
axis([0 5 -3 0]);
grid
print -djpegsmc_traj
pause;
plot(time,torque)
set(gca, 'fontsize', [25]);
xlabel('Time [sec]');
ylabel('Torque [mNm]');
title('Sliding mode controller');
% tengely beállítása
axis([0 12 -0.15 0.15]);
grid
print -djpegsmc_torque
pause;
plot(time,sigma)
set(gca, 'fontsize', [25]);
xlabel('Time [sec]');
ylabel('Sigma');
title('Sliding mode controller');
% tengely beállítása
axis([0 12 -2.5 2.5]);
grid
print -djpegsmc_sigm
|
|
|
|
|
6-28. ábra: A pozíció szabályozó eredményei csúszómódban
Csúszómód szabályozó sebesség szűrővel a következő alakú:
Deklaráció:
float sigma;
float error;
float error_dot;
float ref=5.0;
float lambda=2;
/* filter variables*/
static float z_1=0.0, z_2=0.0, z_3=0.0;
static float ztmp_1=0.0, ztmp_2=0.0;
/*omega_c=10 a modellezetlen dinamika nagy kotyogást okoz */
float Azd11= 1, Azd12= 0.0010, Azd13= 0.0000;
float Azd21= -0.0005, Azd22= 0.9999, Azd23= 0.0010;
float Azd31= -0.9851, Azd32= -0.2960, Azd33= 0.9703;
float Bzd1= 0.0000, Bzd2= 0.0005, Bzd3= 0.9851;
/* omega_c=1/0.007 a modellezetlen dinamika nem okoz nagy kotyogást
float Azd11= 0.9996, Azd12= 9.9072e-004, Azd13= 4.3344e-007;
float Azd21= -1.2637, Azd22= 0.9730, Azd23= 8.0496e-004;
float Azd31= -2.3468e+003, Azd32= -50.5468, Azd33= 0.6280;
float Bzd1= 4.3671e-004, Bzd2= 1.2637, Bzd3= 2.3468e+003;
*/
static double ini_0 = 0;
static double ini_1 = -10;
Szabályozó kód:
if (ini_1 < 0)
{
ini_0 = ResultData.Position;
}
ini_1 = 5;
error=ref-ResultData.Position+ ini_0;
/* filter */
ztmp_1 = Azd11* z_1 +Azd12* z_2 +Azd13* z_3 + Bzd1*ResultData.Velocity ;
ztmp_2 = Azd21* z_1 +Azd22* z_2 +Azd23* z_3 + Bzd2*ResultData.Velocity ;
z_3 = Azd31*z_1 +Azd32*z_2 +Azd33* z_3 + Bzd3*ResultData.Velocity ;
z_1 = ztmp_1;
z_2 = ztmp_2;
ResultData.Velocity =z_1;
error_dot=- z_1;
sigma=error+ lambda*error_dot;
ResultData.StateVariable_5 = sigma;
if (sigma>0)
{ ResultData.Torque=0.1;}
if (sigma<0)
{ ResultData.Torque =-0.1;
}
if (sigma=0)
{ ResultData.Torque=0;}
Az eredmények az alábbi ábrákon láthatók:
|
|
|
|
|
6-29. ábra: Csúszómód szabályozó sebesség szűrővel
|
|
|
|
|
6-30. ábra: A csúszómód sebesség szabályozóval ellátott pozíció szabályozó eredményei
Ebben a fejezetben áttekintjük a DC motor szabályozásánál alkalmazott állapot-visszacsatolásos szabályozás elméleti alapjait. Mivel a motor szabályozását egy számítógép segítségével végezzük el, így diszkrét idejű modellt fogunk használni, hiszen a szabályozás implementációja is ezt kívánja. Az itt bemutatott módszerek gyakorlati megvalósítását majd később MatLab segítségével el is végezzük. Az elméleti áttekintés során csak azokat a dolgokat emeljük ki, amelyek a később elvégzett motorszabályozás során hasznosításra is kerülnek, például nem lesz szó a folytonos idejű megvalósításról, illetve nem kerül minden felhasznált matematikai eljárás részletes bemutatásra, bizonyításra, ezekhez a leírás végén az ajánlott irodalom részben helyeztünk el forrásokat.
Tekintsük az alábbi időinvariáns diszkrét idejű állapottér modellt:
|
( 6.1 ) |
Ahol
: az állapotváltozók oszlop vektora -adik időpontban;
: a bemeneti jel oszlopvektora -adik időpontban;
: a kimeneti jel vektora -adik időpontban;
: a rendszer/állapot mátrix;
: a bemeneti mátrix;
: a kimeneti mátrix;
: a segédmátrix (ez általában 0).
Az állapot-visszacsatolás célja az, hogy a rendszermátrixot olyan módon változtassuk meg, hogy a rendszer viselkedése számunkra kedvező legyen. Mivel sajátértékei a rendszer pólusai, így jól látható, hogy ezek megváltoztatásával a rendszer viselkedését – bizonyos korlátok mellett – szabadon választhatjuk meg. Az állapotegyenletekre ránézve észrevehetjük, hogy – -t a következőképpen választva – értéke megváltoztatható:
|
( 6.2 ) |
ahol az a rendszer referenciajele. Így a következőt kapjuk:
|
( 6.3 ) |
ahol:
: az állapot-visszacsatolást definiáló mátrix;
: a rendszer új bemeneti jele.
Az új rendszermátrix lesz, amelynek értékét a mátrixszal állíthatjuk be. Mivel nem közvetlenül hat a rendszermátrixra, hanem csak -vel való szorzás után, így tetszőleges eredményt nem érhetünk el.
Ezért az állapot-visszacsatolás tervezésének, megfelelő működésének feltétele a rendszer állapot irányíthatósága, azaz, hogy a rendszer tetszőleges állapotából véges idő alatt eljuthatunk egy megfelelő bemeneti jel segítségével bármely másik állapotba. Diszkrét idejű rendszer esetén a teljes állapotirányíthatóság feltétele, hogy a rendszer irányíthatósági mátrixa () maximális rangú legyen, azaz rangja megegyezzen az állapotváltozók számával ():
|
( 6.4 ) |
Az állapot-visszacsatolás tervezése azon alapul, hogy felírjuk, milyen karakterisztikus egyenletet szeretnénk elérni (), azaz hova kerüljenek a rendszer pólusai (természetesen a pólusok száma nem változhat az eredeti rendszerhez képest). Ez a felírás általában két domináns pólus megválasztására épül, amelyekkel definiáljuk, hogyan is viselkedjen az adott rendszer, és további segédpólusokat is megadunk, amelyek az előbbieknél nagyobbak (ezekre azért van szükség, hogy az eredeti rendszerrel egyező fokszámot nyerjünk).
A pólusok megadása diszkrét szabályozó esetén is folytonos időben történik, hiszen a szabályozott szakasz általában folytonos idejű. Stabil rendszert kívánva az összes pólus negatív valós résszel kell rendelkezzen. A domináns póluspárt a csillapítás () és a szabályozási idő () segítségével is megadhatjuk. Tekintsük a következő kéttárolós lengőtagot:
|
( 6.5 ) |
Ha , akkor az átviteli függvény pólusai:
|
( 6.6 ) |
ahol:
|
( 6.7 ) |
|
( 6.8 ) |
Az ugrásválasz burkológörbéjéből levezethető, hogy az százalékos szabályozási idő:
|
( 6.9 ) |
Ha tehát megadjuk és értékét, akkor:
|
( 6.10 ) |
Innen pedig a domináns póluspár számítható. A további pólusokat, hogy hatásuk ne legyen mérvadó, sokkal nagyobbra választjuk. Ha megvannak a folytonos idejű pólusok (), azok diszkrét idejű megfelelői () a következőképpen számíthatóak:
|
( 6.11 ) |
ahol az alkalmazott mintavételezési idő.
Ezután a feladat az, hogy kiszámítsuk azt a értéket, amivel ez a cél elérhető. Egybemenetű, egykimenetű (SISO) rendszerek esetében ez könnyen megoldható: felírjuk rendszermátrix pólusait paraméterrel, majd ezeket összevetve a kitűzött pólusokkal értéke meghatározható. Ezt a módszert foglalja össze az úgynevezett Ackermann formula:
|
( 6.12 ) |
ahol a cél karakterisztikus egyenletbe behelyettesített eredeti rendszermátrixot jelöli. Több bemenetű, több kimenetű (MIMO) rendszerek esetében meghatározása összetettebb probléma, és általában csak közelítő megoldások érhetőek el, azaz a pólusok pozíciója eltérhet a tervezettől.
Az előbbi eljárással egy olyan rendszert nyertünk, amely az általunk választott pólusokkal rendelkezik. Szabályozó tervezés esetén azonban azt is elvárjuk, hogy a teljes rendszer kimenete olyan értéket vegyen fel állandósult állapotban, amelyet annak alapjelként megadunk. Ehhez szükségünk van az úgynevezett alapjel korrekcióra. Az alapjel korrekcióval ellátott állapot-visszacsatolás a következő felépítésű:
Az az állapotvektor állandósult értéke, az pedig a beavatkozó jel értéke állandósult állapotban. Az -t tartalmazó előrecsatoló ág azért szükséges, mert nélküle nem lenne beavatkozó jel állandósult állapotban (tulajdonképpen egy integrátort helyettesít). Az ág kiszámítja az állapotváltozók célértékeit a kimenet célértékének megfelelően, így biztosítja, hogy bemenetére a hibajel érkezzen. Érdemes megjegyezni, hogy ha csak az előrecsatolást alkalmaznánk visszacsatolás nélkül, akkor is a véghelyzetbe kerülne egy idő után a rendszer (csak nem az általunk tervezett módon).
A cél tehát:
|
( 6.13 ) |
Ehhez:
|
( 6.14 ) |
meghatározása:
|
( 6.15 ) |
meghatározása:
|
( 6.16 ) |
Összefoglalva:
|
( 6.17 ) |
ahol egy n x m méretű csupa nullából álló mátrix, míg egy m méretű egységmátrix.
A valóságban sok esetben nem oldható meg, hogy az összes állapotváltozót megmérjük. Ennek oka lehet az, hogy az adott állapotváltozó mérésére nincs technikai lehetőség vagy nem mérhető elég pontosan, a mérés nem gazdaságos, vagy az állapotváltozó fizikai jelentése ismeretlen (például mesterségesen létrehozott, identifikációból kapott állapottér modellek esetén). Ilyenkor szükségünk lesz egy állapot-megfigyelőre, melynek feladata nevéből is jól érzékelhetően a rendszer állapotának meghatározása a kimenő- és bemenőjelek ismeretében, melyek mérése a legtöbb esetben jól megoldható. Az állapot-megfigyelőnek tehát két bemenete lesz: a szakasz be- () és kimenete (), a kimenete pedig a megfigyelt állapotváltozók vektora lesz ().
Állapot-megfigyelő csak akkor alkalmazható, ha a vizsgált rendszer megfigyelhető. Megfigyelhetőség alatt azt értjük, hogy a rendszer véges időintervallumbeli bemenetének és kimenetének ismeretében meghatározható a rendszer kezdő időpontbeli állapota. Diszkrét időinvariáns rendszerek esetén ez akkor teljesül, ha a rendszer megfigyelhetőségi mátrixa maximális rangú, azaz rangja :
|
( 6.18 ) |
Az állapot-megfigyelő alakja diszkrét időben:
|
( 6.19 ) |
A becslési hiba:
|
( 6.20 ) |
Célunk az, hogy a becslési hiba nullához tartson (). Helyettesítsük be az állapot-megfigyelő egyenletébe a becslési hibát és fejezzük azt ki:
|
( 6.21 ) |
Mivel:
|
( 6.22 ) |
Így:
|
( 6.23 ) |
Ebből:
|
( 6.24 ) |
A második két tag mindig nulla, ha:
|
( 6.25 ) |
Célunk továbbra is az, hogy minél gyorsabban, ez akkor teljesül, ha a következő rendszer stabil és gyors:
|
( 6.26 ) |
Ezt megfelelő választásával biztosíthatjuk. Ehhez helyettesítsük be a becslési hiba egyenletébe -t és -t:
|
( 6.27 ) |
Transzponáljuk az egyenletet:
|
( 6.28 ) |
Vessük ezt össze az állapot-visszacsatolás egyenletével:
|
( 6.29 ) |
Láthatjuk, hogy a két egyenlet igen hasonló szerkezetű, és ezt felhasználhatjuk ahhoz, hogy értékét meghatározzuk az állapot-visszacsatolás esetén alkalmazott módszerekkel. Tekintsük a következő rendszert:
|
( 6.30 ) |
Erre a rendszerre állapot-visszacsatolást tervezve értéke kiadódik:
|
( 6.31 ) |
Még az a fontos kérdés marad, hogy hova helyezzük a pólusokat. Ennek eldöntéséhez induljunk ki az eredeti állapot-visszacsatolás tervezése során megadott legnagyobb abszolút értékű folytonos idejű pólusból, és annál válasszunk több nagyságrenddel nagyobbat. Ez utóbbinak különösen nagy szerepe lesz akkor, ha a szabályozott rendszerünk eltér a tervezéskor használt modellünktől, ugyanis ilyenkor a lassú megfigyelő instabil rendszert eredményez. Tehát alapvető követelmény, hogy a megfigyelő gyorsabb legyen, mint a megfigyelt rendszer. Ezzel az alábbi szabályozót nyerjük:
Az eddig tervezett szabályozónk nagyon jól működik elméletben: mi határozzuk meg a rendszer pólusait, megadjuk hogyan, milyen gyorsan álljon be a rendszer és nincs maradó hiba sem. Igen ám, de mi történik akkor, ha a szakasz eltér a tervezésnél használttól? Ebben az esetben egyrészt az állapot-visszacsatolásunk már nem fogja tökéletesen mozgatni a pólusokat, hiszen azok máshol vannak, mint vártuk őket, ami akár instabil rendszerhez is vezethet, és biztosan maradó hibához, hiszen az alapjel korrekció az állapot-egyenleteken alapul. Ráadásul állapot-megfigyelőnk se a valós állapotokat fogja figyelni, továbbá a zavarójel elnyomása sem megfelelő.
A maradó hibára megoldást jelent, ha a rendszerbe egy integrátort helyezünk, ezzel egyben kiválthatjuk az alapjel korrekció részét is, hiszen az integrátor nulla hiba esetén is tud kimenetet biztosítani. Az integrátor elhelyezéséhez egy új állapotváltozó bevezetése válik szükségessé:
|
( 6.32 ) |
Itt a bal oldali téglalapszabály alapján valósult meg a diszkrét idejű integrátor. Ezzel bővítsük ki a szakasz állapot-egyenletét:
|
( 6.33 ) |
Ilyenkor az állapot-visszacsatolás alakja a következő lesz:
|
( 6.34 ) |
Ebben az esetben tehát az állapot-visszacsatolást ehhez a bővített rendszerhez tervezzük meg, majd az így kapott és mátrixokat a következőképpen alkalmazzuk:
Amint látható, az előrecsatoló -t tartalmazó ág helyét az integrátor vette át, hiszen így az integrátor biztosítja a nulla maradó hibát. Az integráló tag elhelyezése sem az alapjel-korrekció, sem az állapot-megfigyelő tervezését nem érinti.
A szabályozó megtervezéséhez először meg kell határoznunk az irányítandó szakasz modelljét. Ennek előállítását ebben az esetben a MATLAB System Identification Toolbox segítségével végezzük el. Ez lehetőséget ad rendszerek modelljeinek mérési eredményekből való meghatározására, amelyet felhasználhatunk például szabályozó tervezéséhez is. Ennek fontos feltétele, hogy készítsünk valamilyen, a rendszert jól jellemző mérést, hiszen a program a be- és kimeneti jelek értékeiből fogja előállítani a modellt, így ha valamilyen speciális jelenség abban nem jelentkezik, az valószínűleg a modellben sem fog.
A mérés megkezdése előtt célszerű kiválasztani, hogy milyen modellt fogunk használni. Ebben az esetben a szabályozó megtervezéséhez egy diszkrét idejű állapottér modellre lenne szükség. Ez könnyen előállítható egy átviteli függvényből is. Erre a MATLAB több lehetőséget is ad, mi az úgynevezett ARMAX modellt fogjuk használni. A betűszó az „autoregressive moving-average model with exogeneous inputs” kifejezésre utal, aminek magyar jelentése: autoregresszív mozgóátlag modell külső bemenetekkel. Felmerülhet a kérdés, ha hangsúlyt kap, hogy külső bemenetek is vannak, akkor mire szolgálható anélkül a modell. Az ARMAX modell bemutatása előtt tekintsük meg két komponensét, az AR és MA modelleket. Az autoregresszív (AR) modell:
|
( 6.35 ) |
ahol:
a kimenet értéke -adik időlépésben
a belső bemenet, fehér zaj értéke -adik időlépésben
Ahogy az látható, egy sztochasztikus folyamat kerül leírásra. Hasonló szerkezetű a mozgóátlag (MA) modell is:
|
( 6.36 ) |
ahol. Mivel ezek a modellek nem rendelkeznek külső bemenettel, így nem alkalmasak a motor leírására, azonban a kettőt összerakva és külső bemenetekkel kibővített ARMAX modell már igen. Ennek szerkezete:
|
( 6.37 ) |
ahol:
a külső bemenet
a rendszer holtideje
, itt az első együttható nem 1, erre azért van szükség, hogy a rendszer erősítése tetszőleges lehessen
A modellben lévő fehér zaj bemenetet tartalmazó tag célja a mérési hiba leképezése, ez tulajdonképpen az egyenlethiba:
|
( 6.38 ) |
Ha nincs mérési hiba, akkor :
|
( 6.39 ) |
Innen az impulzusátviteli függvény:
|
( 6.40 ) |
Az átviteli függvény felírását a MatLab elvégzi nekünk, ugyanakkor hasznos lehet a működési módjának alapszintű ismerete. Az ARMAX egyenletbe behelyettesítve az egyes értékeket paraméteresen adódik a hiba nagysága a polinomok együtthatóinak függvényében. A polinomok értéke és így az átviteli függvény úgy határozható meg, hogy a hiba négyzetét minimalizáljuk. A MatLab a holtidő értékét nem határozza meg, hanem azt nekünk kell megadni.
Az átviteli függvényből több fajta módon is létrehozhatunk állapottér modellt, például a megfigyelhetőségi vagy az irányíthatósági alakban való felírással. Ezeknél a módszereknél természetesen az állapotváltozók fizikai jelentése nem ismert, ez azonban most nem jelent hátrányt, hiszen az identifikáció miatt ez egyébként sem lenne elvárható. Alapesetben az állapottér modell felírását az alábbi átviteli függvényből kell elvégezni:
|
( 6.41 ) |
Legyen:
|
( 6.42 ) |
Ilyenkor:
|
( 6.43 ) |
Legyenek az állapotváltozók a következők:
|
( 6.44 ) |
Így:
|
( 6.45 ) |
Tehát az állapottér modell:
|
( 6.46 ) |
Az állapottér modell birtokában a szabályozótervezés elvégezhető.
Az elméleti alapok áttekintése után sor kerülhet a szabályozó megtervezésre. Ez három részre bontható fel:
Rendszer identifikációja
a szabályozó tervezés és szimuláció;
a szabályozó implementálása és tesztelése;
a motor identifikációja.
Ezek bemutatására kerül sor a következő fejezetben.
Mielőtt az identifikációt elkezdenénk, először vizsgáljuk meg magát a szabályozandó rendszert. Az általunk alkalmazott DC motort az interneten keresztül tudjuk elérni. A rendszer segítségével C nyelven elkészíthetjük a motor bemeneti jeleit megadó szabályozót, majd ennek kódját a távoli mérőállomásra elküldve, az lefuttatja a programot, és visszaküldi a mérési eredményeket. A motor bemenete ebben az esetben az elérni kívánt nyomaték, amelyből ezután egy áramszabályozó kör számítja ki a kiadott feszültséget, így a motorra nem adhatunk a motort károsítóan nagy bemenőjelet. Az általunk szabályozott kimenőjel pedig a sebesség lesz.
Nézzük először, hogy reagál a motor konstans bemenőjel esetén, amelyet most szándékosan nagyra választva vizsgáljuk meg a maximális elérhető forgási sebességet. Legyen tehát az előírt nyomaték 100mNm:
Megjegyezzük, hogy a sebesség számítása a pozícióból numerikus deriválással és szűréssel történik. Ezért marad néhány tüske a mérési eredményben. A szűrő tervezésére majd a későbbiekben visszatérünk.
Ugyanez 3mNm esetén:
Több méréssel megállapítható, hogy kb. 14 rad/s a szögsebesség felső maximuma. Jól láthatjuk, hogy kisebb nyomaték esetén a rendszer nagyobb választ adott, ami arra utal, hogy nagy nyomatékok esetén a nyomatékszabályozás helytelenül működik.
Most nézzük ugyanezt a másik forgási irányba is (-3mNm nyomaték esetén):
Jól látható, hogy a szögsebesség végértéke közel azonos. Hogy a rendszerről több információt nyerjünk, elvégeztünk egy kvázistatikus mérést, hogy megvizsgáljuk a nyomaték és a kimeneti sebesség közti összefüggést. A mérés során lassan, először -3mNm nyomatékot adtunk ki, és megvártuk, míg a kimenet ennek megfelelően beállt, ezután lassan növeltük a nyomatékot +3mNm eléréséig:
Amint látható, rendszerünk igen távol áll a lineáristól:
Holtsáv: a tapadási súrlódás miatt.
Több töréspont is látható.
Hiszterézis: az is befolyásolja az eredményt, hogy milyen irányban kezdjük a mérést.
Ahhoz, hogy szabályozónk működni tudjon, minél lineárisabb rendszerre kell törekednünk. Ennek eléréséhez használjuk fel azt, hogy a nemlineáris rendszerek adott munkapont körül általában jól linearizálhatóak. Így a szabályozó működésének bemutatása során egy precíziós sebességszabályozót készítünk. Először egy egyszerű szabályozóval a munkapont közelébe visszük a rendszert, majd onnan átadjuk azt a precíziós szabályozónknak.
Hogy a rendszer identifikációját elvégezzük, a rendszert először a kívánt munkapont közelébe visszük, majd ott adunk rá négyszögjelet, amelyre adott válasz lesz az identifikáció alapja. A kiválasztott lineáris szakasz a 0.2..0.5mNm-es lineáris szakasz. Ennek identifikációjához egy 4s periódus idejű, 0.35mNm középnyomatékú, 0.15mNm amplitúdójú négyszögjelet használtunk. A munkapontba való beállásra 2s időt hagytunk.
Amint látható, ez a kimenet már lineáris rendszer eredményének mutatkozik. A méréshez szükséges vezérlés C kódja:
typedef struct { float Position; float Velocity; float Torque; float StateVariable_5; float StateVariable_6; float StateVariable_7; float StateVariable_8; float StateVariable_9; float StateVariable_10; } NewControllerData; NewControllerData CalculateController( const float CurrentPosition, const float OldPosition, const float OldVelocity, const float CurrentTime, const float OldTime, const float Old_StateVariable_5, const float Old_StateVariable_6, const float Old_StateVariable_7, const float Old_StateVariable_8, const float Old_StateVariable_9, const float Old_StateVariable_10 ) { NewControllerData ResultData; // result //Szűrőegyütthatók static float filB0 = 0, filB1 = 0.00013337, filB2 = 0.0012028, filB3 = 0.0009847099, filB4 = 7.3193E-05; static float filA0 = 1, filA1 = -3.1152, filA2 = 3.6392, filA3 = -1.8895, filA4 = 0.36788; //Bemeneti értékek static float input0 = 0, input1 = 0, input2 = 0, input3 = 0, input4 = 0; //Szűrt értékek static float filter0 = 0, filter1 = 0, filter2 = 0, filter3 = 0, filter4 = 0; float u; //Nyomaték középérték float bias = 0.35; //Várakozás a munkapontba beállásra int start = 2000; //Periódusidő int period = 4000; //Amplitúdó float amplitude = 0.15; // Position is saved automatically ResultData.Position = CurrentPosition; // Angular velocity is saved automatically if (CurrentTime != 0.0f){ ResultData.Velocity = (1000.0f * (float)(CurrentPosition - OldPosition)/(float)(CurrentTime - OldTime)); } else { ResultData.Velocity = 0.0f; }
//Sebesség szűrése input0 = ResultData.Velocity; filter0 = (filB0 * input0 + filB1 * input1 + filB2 * input2 + filB3 * input3 + filB4 * input4 - (filA1 * filter1 + filA2 * filter2 + filA3 * filter3 + filA4 * filter4)) / filA0; input4 = input3; input3 = input2; input2 = input1; input1 = input0; filter4 = filter3; filter3 = filter2; filter2 = filter1; filter1 = filter0; ResultData.StateVariable_5 = filter0; //Nyomaték megadása if(CurrentTime < start) { u = 0; } else { u = (((int)CurrentTime - start) % period - period / 2 > 0 ? amplitude : -amplitude); } ResultData.Torque = u + bias; ResultData.StateVariable_6 = u; return ResultData; }
A szűrő tervezési módját a későbbiekben tárgyaljuk.
Először be kell töltenünk a mérési eredményeket a MATLAB környezetbe. A beállási szakaszt levágjuk, hiszen arra nem lesz szükség. Az identifikáció során a valódi sebesség és nyomaték helyett azok munkaponttól való különbségét használjuk, így ezek kezdő időpontban – a munkapontban – mért értékét a mérési eredményekből levonjuk. Az identifikációhoz meg kell adnunk a mintavételezési időt, ami ebben az esetben 1ms. Ezután létrehozzuk a mérést leíró adatszerkezetet a System Identification Toolbox számára az iddata parancs segítségével, amelynek meg kell adni a bemeneti és kimeneti jelek mérésének eredményét és a mintavételezési időt. Ezután következhet a polinomok fokszámának megadása az ARMAX modellhez, ezt egy sorvektorban kell megtennünk, amelynek elemei sorban: . Az első elem adja meg az polinom fokszámát, azaz az állapotváltozók számát, a második a fokszámánál eggyel nagyobb, a harmadik elem fokszáma, míg a negyedik a holtidő. A megadás során ügyeljünk arra, hogy és . Bár az identifikáció igen sok polinomfokszám variáció mellett lefut, legyünk figyelemmel arra is, hogy az identifikáció bizonytalansága nő a fokszámmal, ilyenkor a MATLAB a „Warning: Pole locations are more than 10% in error.” üzenetet jeleníti meg. Ez azért fontos, mert később ezeket a pólusokat át akarjuk helyezni, ehhez pedig ki kellene ejteni őket, ami nem fog sikerülni, ha nem ott vannak, mint azt terveztük. Mivel az identifikáció csak pár másodpercig tart, így számos beállítást kipróbálhatunk. Az identifikációt az armax parancs segítségével, a mérési adatok és a polinomfokszámok megadása után végezhetjük el. A függvény eredménye az identifikált rendszert leíró struktúra, amelyből a th2poly függvénnyel megkaphatjuk a polinomokat leíró sorvektorokat. Ezekben az együtthatók csökkenő kitevőjű tagok mellett értendőek, az utolsó elem így taghoz tartozik, és az eredményben lehetnek 0 együtthatók is. Mivel ezzel tulajdonképpen megkapjuk a diszkrét átviteli függvény számlálóját és nevezőjét, így az a tf függvénnyel elő is állítható. Az identifikációt elvégző MATLAB kód így:
start=1800; %kezdőidőpont y=velocity2(start:end)-velocity2(start); %szögsebesség-mérések vektora u=torque2(start:end)-torque2(start); %nyomatékmérések vektora n=3; %állapotváltozók száma Ts=0.001; %mintavételezési idő megadása z=iddata(y,u,Ts); %mérési adatok megadása nn=[n,1,1,1]; %fokszámok megadása tharmax=armax(z,nn); %identifikáció [A,B,C]=th2poly(tharmax); %polinomok kiolvasása sys=tf(B,A,Ts); %átviteli függvény előállítása
A diszkrét idejű átviteli függvény így:
|
( 6.47 ) |
Ez persze így nem sokat mond számunkra arról, hogy ez tényleg jól leírja-e a rendszert, ezért számoljuk ki az identifikált rendszer válaszát az általunk a valós rendszernek megadott bemenetre (a nyomaték már a MATLAB importálás előtt a munkapont körül volt):
yid=idsim(tharmax,u,zeros(n,1)); %identifikált rendszer válasza
t=0:Ts:(size(y)-1)*Ts; %idővektor az ábrázoláshoz
figure(1); %új grafikon
plot(t,y,t,yid); %grafikon megjelenítése
legend('mért','identifikált'); %jelmagyarázat
xlabel('idő [s]'); %vízszintes tengelycím
ylabel('szögsebesség [rad/s]'); %függőleges tengelycím
Az identifikált rendszer működését az idsim függvény segítségével ellenőrizzük, amely meghatározza a bemenetek sorvektorának és a kezdeti állapotnak megfelelő kimeneti értékeket. Ezután ezt grafikonon is könnyen ábrázolhatjuk, de ehhez szükség van egy idő sorvektorra is, amelyet a szokásos módon előállíthatunk:
Amint az látható, az identifikált rendszer és eredeti viselkedése jól közelíti egymást. Fontos figyelembe venni, hogy az identifikációhoz használt gerjesztés ugyan tetszőleges lehet, de fontos, hogy olyan legyen, amelynek kimenete alkalmas a rendszerjellemzők megjelenítésére. Az identifikációs algoritmus a legtöbb esetben adni fog eredményt, azonban azt semmi sem garantálja, hogy ez az eredmény jól írja le a kívánt rendszert, ez ugyanis csak az adott gerjesztés esetén jelenthető ki nagyobb biztonsággal.
A szabályozó megtervezéséhez szükségünk van a rendszer diszkrét idejű állapottér modelljére, ezt az ss parancs segítségével könnyen megkaphatjuk:
sysd=ss(sys); %állapottér modell [Phi Gamma C D]=ssdata(sysd); %mátrixok kiolvasása
Ezzel készen állunk a szabályozó megtervezésére.
A szabályozó megtervezéséhez először az elméleti bevezetőnek megfelelően definiáljuk a célrendszer paramétereit, és ezekből meghatározzuk a folytonos idejű pólusokat. A jobb érthetőség kedvéért meg is jeleníthetjük a célrendszer ugrásválaszát:
a=2; %hány százalékra álljunk be Tap=0.2; %szabályozási idő xi=0.8; %csillapítás w0=log(100/a)/Tap/xi; %sajátfrekvencia sdom1=-w0*xi+1i*w0*sqrt(1-xi^2); %domináns pólusok sdom2=conj(sdom1); scinf=-4*w0; %segédpólusok a szabályozóhoz soinf=scinf*5; %segédpólusok a megfigyelőhöz if(-soinf>0.5/Ts) %Shannon-elv ellenőrzése display('Túl kicsi a mintavételezési idő!'); end; %Célrendszer modellje egységnyi erősítéssel tcsys=zpk([],[sdom1, sdom2, scinf*ones(1,n-2)],-w0^2*scinf^(n-2)); figure(2); %új grafikon step(tcsys); %ugrásválasz számítása
Az ugrásválasz:
Ezután átszámítjuk a pólusokat diszkrét időre:
%Pólusok átszámítása diszkrét időre zdom1=exp(sdom1*Ts); zdom2=exp(sdom2*Ts); zcinf=exp(scinf*Ts); zoinf=exp(soinf*Ts);
Ezt követően az állapot-visszacsatolás, az alapjel-korrekció és az állapot-megfigyelő az elméleti bevezető alapján már gyorsan megtervezhető:
%Az irányíthatóság ellenőrzése
Mc=ctrb(Phi,Gamma);
if rank(Mc)~=n
disp('A rendszer nem irányítható!');
end
%Állapot-visszacsatolás tervezése
phic=[zdom1 zdom2 zcinf*ones(1,n-2)]; %pólusok új helye
K=acker(Phi,Gamma,phic); %pólusáthelyezés
%Alapjel-korrekció számítása
NxNu=inv([Phi-eye(n) Gamma;C 0])*[zeros(n,1);1];
Nx=NxNu(1:n);
Nu=NxNu(n+1);
%A megfigyelhetőség ellenőrzése
Mo=obsv(Phi,C);
if rank(Mo)~=n
disp('A rendszer nem megfigyelhető!');
end
%Állapot-megfigyelő tervezése
phio=ones(1,n)*zoinf; %megfigyelő pólusai
G=acker(Phi',Phi'*C',phio)'; %pólusáthelyezés duális rsz.-en
F=Phi-G*C*Phi;
H=Gamma-G*C*Gamma;
Ezzel meg is terveztük a szabályozót. A tervezés ellenőrzéséhez végezzünk szimulációt Simulink segítségével. Építsük fel a következő modellt az elméleti bevezető alapján:
Amint látható, a szakaszt két részre bontottuk, a diszkrét idejű állapottér modell minden állapotváltozót kivezet, ami felhasználható az állapotbecslő működésének ellenőrzésére, míg a C-t tartalmazó erősítés adja a kimeneti jelet. Ügyeljünk arra, hogy minden elem esetében megadjuk a mintavételezési idő értékét! Ezután lássuk milyen eredmények jelentek meg:
Amint látjuk, a szabályozónk megfelelően működik. A beavatkozójel nem fut ki a lineáris tartományból.
Az integrátorral ellátott szabályozó megtervezése csak minimális módosításokat igényel. Először elő kell állítanunk az integrátorral bővített állapotegyenletet:
iPhi=[Phi, zeros(n,1); Ts*C, 1]; iGamma=[Gamma; 0]; iC=[C 0];
Ezenkívül szükségünk lesz még egy pólusra az állapot-visszacsatolás tervezéséhez:
siinf=scinf; %segédpólus integrátorhoz ziinf=exp(siinf*Ts); if(-min([siinf soinf])>0.5/Ts) %Shannon-elv ellenőrzése display('Túl kicsi a mintavételezési idő!'); end;
Így már meg is tudjuk tervezni az állapotvisszacsatolást:
iPhic=[zdom1 zdom2 zcinf*ones(1,n-2) ziinf];%pólusok új helye iK=acker(iPhi,iGamma,iPhic); %pólusáthelyezés K=iK(1:n); %állapot-visszacsatolás Ki=iK(n+1); %integrátor visszacsatolása
Ezután módosítanunk kell a Simulink modellt is:
Ebben az esetben a szimuláció eredményeit változókban tároltuk el, hogy az eredményeket kódból ábrázolhassuk. Tekintsük meg ezeket:
Látható, hogy a rendszer válasza nem igazán változott meg. A különbség akkor jelenik meg, ha pl. feltesszük, hogy 0.01mNm-al elmértük a munkapont helyét, ilyenkor az integrátor nélküli változat válasza:
Míg az integrátoros verzióé:
Látható, hogy az integrátorral már nem marad maradó hiba, a rendszer válasza közel van az ideálishoz. Ez egyben azt is mutatja, hogy az integrátor nélküli változat csak ideális esetben alkalmazható.
Mivel az általunk vizsgált rendszernél a szögsebesség a szöghelyzetből kerül meghatározásra numerikus deriválással, ezért ez mint bemenet nagyon zajos lesz. Így célszerű ezen egy szűrést is elvégezni mielőtt az állapot-megfigyelő kezébe adjuk. A szűrő megtervezése egymással ellentmondó követelmények kielégítését igényli: egyrészt minél gyorsabb szűrőre van szükség, hiszen a szűrővel bevitt késleltetés a rendszer stabilitását csökkenti, másrészt minél jobb zajszűrés kell, hiszen a zaj torzítja az állapot-megfigyelőnk működését. A legegyszerűbb szűrőt talán úgy kapjuk, hogyha sorba kapcsolunk néhány egytárolós tagot, hiszen mint ismeretes a tag pólusa után -20dB/dekád erősítésváltozással rendelkezik. Egy ilyen szűrő megtervezése egyszerűen megoldható MATLAB segítségével: mindössze a fokszámra és a pólus helyére van szükségünk. Ebből a folytonos és diszkrét átviteli függvények már könnyen előállíthatóak:
filT=0.001; %Szűrő időállandója filN=3; %Szűrő fokszáma %Folytonos idejű szűrő filsys=tf(zpk([],-ones(1,filN)/filT,(1/filT)^filN)); dfilsys=c2d(filsys,Ts,'zoh'); %Diszkrét idejű verzió
A diszkrét átviteli függvény birtokában már implementálhatjuk is a szűrőt, de előtte érdemes annak működését megvizsgálni. Erre mi a szűrő Bode-diagramját, ugrásválaszát és a méréssel nyert zajos bemenetre adott válaszát használtuk:
figure(1); dbode(dfilsys.num{1},dfilsys.den{1},Ts) %Dode diagram figure(2); step(dfilsys); %Ugrásválasz figure(3); v=velocity(start:end)-velocity(start); %Zajos jel lsim(dfilsys,v,t); %Zajos jel szűrése
Az eredmények:
A szabályozó integrálása C nyelven nem különösebben nehéz feladat, az egyetlen komolyabb probléma, hogy az internetes tesztkörnyezet számos korlátozást alkalmaz. Többek között nincs lehetőségünk tömbök használatára, nem definiálhatunk saját függvényeket és globális változókat sem. Ezért erősen javasolt egy program elkészítése, amely a MATLAB kódokat átalakítja C kóddá, így könnyebben tesztelhetjük munkánkat. Ezt kívánja bemutatni a függelékben bemutatott segédprogram.
Az implementáció módját különösebben nem részletezzük, amennyiben az olvasó rendelkezik alapvető C programozási tudással, úgy a szabályozók implementálása nem okozhat gondot a Simulink modellek alapján.
#include <windows.h> #include <stdio.h> #include <rtapi.h> #include <math.h> #include <string.h> #define PI 3.14159265358979323846 typedef struct { float Position; float Velocity; float Torque; float StateVariable_5; float StateVariable_6; float StateVariable_7; float StateVariable_8; float StateVariable_9; float StateVariable_10; } NewControllerData; NewControllerData CalculateController( const float CurrentPosition, const float OldPosition, const float OldVelocity, const float CurrentTime, const float OldTime, const float Old_StateVariable_5, const float Old_StateVariable_6, const float Old_StateVariable_7, const float Old_StateVariable_8, const float Old_StateVariable_9, const float Old_StateVariable_10 ) { NewControllerData ResultData; // result static float filB0 = 0, filB1 = 0.080301, filB2 = 0.1544, filB3 = 0.017881; static float filA0 = 1, filA1 = -1.1036, filA2 = 0.40601, filA3 = -0.049787; static float input0 = 0, input1 = 0, input2 = 0, input3 = 0; static float filter0 = 0, filter1 = 0, filter2 = 0, filter3 = 0; static float Nu0_0 = 0.14662; static float Nx0_0 = 23.677, Nx1_0 = 47.353, Nx2_0 = 23.677; static float K0_0 = -1.2522, K0_1 = 0.81068, K0_2 = -0.37451; static float F0_0 = 2.7896, F0_1 = -1.889, F0_2 = 0.84863, F1_0 = 2, F1_1 = -0.94991, F1_2 = 0, F2_0 = 0, F2_1 = 0.13587, F2_2 = 0; static float G0_0 = 26.977, G1_0 = 44.981, G2_0 = 17.242; static float H0_0 = 0.0625, H1_0 = 0, H2_0 = 0; static float Gamma0_0 = 0.0625, Gamma1_0 = 0, Gamma2_0 = 0; static float Phi0_0 = 2.7896, Phi0_1 = -1.3193, Phi0_2 = 0.84863, Phi1_0 = 2, Phi1_1 = 0, Phi1_2 = 0, Phi2_0 = 0, Phi2_1 = 0.5, Phi2_2 = 0; static float C0_0 = 0, C0_1 = 0, C0_2 = 0.042236; static float r = 0; static float xu = 0; static float xuL = 0; static float xk0 = 0, xk1 = 0, xk2 = 0; static float xkL0 = 0, xkL1 = 0, xkL2 = 0; static float y = 0; // Position is saved automatically ResultData.Position = CurrentPosition; // Angular velocity is saved automatically if (CurrentTime != 0.0f){ ResultData.Velocity = (1000.0f * (float)(CurrentPosition - OldPosition)/(float)(CurrentTime - OldTime)); } else { ResultData.Velocity = 0.0f; } r=sin(CurrentTime/1000)/2-1; input0 = ResultData.Velocity; filter0 = (filB0 * input0 + filB1 * input1 + filB2 * input2 + filB3 * input3 - (filA1 * filter1 + filA2 * filter2 + filA3 * filter3)) / filA0; input3 = input2; input2 = input1; input1 = input0; filter3 = filter2; filter2 = filter1; filter1 = filter0; if(CurrentTime < 2000) ResultData.Torque = 3.5000e-001; else { y = filter0 + -9.5218e+000; xk0 = F0_0 * xkL0 + F0_1 * xkL1 + F0_2 * xkL2 + H0_0 * xuL + G0_0 * y; xk1 = F1_0 * xkL0 + F1_1 * xkL1 + F1_2 * xkL2 + H1_0 * xuL + G1_0 * y; xk2 = F2_0 * xkL0 + F2_1 * xkL1 + F2_2 * xkL2 + H2_0 * xuL + G2_0 * y; xu = Nu0_0 * r + K0_0 * (Nx0_0 * r - xk0) + K0_1 * (Nx1_0 * r - xk1) + K0_2 * (Nx2_0 * r - xk2); if(xu > 1) xu = 1; if(xu < -1) xu = -1; ResultData.Torque = xu + 3.5000e-001; xuL = xu; xkL0 = xk0; xkL1 = xk1; xkL2 = xk2; } ResultData.StateVariable_5 = r; ResultData.StateVariable_6 = y; return ResultData; }
Amint az látható, a szabályozó jól követi a referenciajelet annak ellenére is, hogy nincs integrátor a körben, és a valós rendszer is eltér a tervezettől. Az is észlelhető, hogy távolodva a munkaponttól az alapjel és a kimenet eltérése nő.
#include <windows.h> #include <stdio.h> #include <rtapi.h> #include <math.h> #include <string.h> #define PI 3.14159265358979323846 typedef struct { float Position; float Velocity; float Torque; float StateVariable_5; float StateVariable_6; float StateVariable_7; float StateVariable_8; float StateVariable_9; float StateVariable_10; } NewControllerData; NewControllerData CalculateController( const float CurrentPosition, const float OldPosition, const float OldVelocity, const float CurrentTime, const float OldTime, const float Old_StateVariable_5, const float Old_StateVariable_6, const float Old_StateVariable_7, const float Old_StateVariable_8, const float Old_StateVariable_9, const float Old_StateVariable_10 ) { NewControllerData ResultData; // result static float filB0 = 0, filB1 = 0.080301, filB2 = 0.1544, filB3 = 0.017881; static float filA0 = 1, filA1 = -1.1036, filA2 = 0.40601, filA3 = -0.049787; static float input0 = 0, input1 = 0, input2 = 0, input3 = 0; static float filter0 = 0, filter1 = 0, filter2 = 0, filter3 = 0; static float Nu0_0 = 0.14662; static float Nx0_0 = 23.677, Nx1_0 = 47.353, Nx2_0 = 23.677; static float K0_0 = 0.2386, K0_1 = -0.5816, K0_2 = 0.9255, K0_3 = 1.9278; static float F0_0 = 2.7896, F0_1 = -1.889, F0_2 = 0.84863, F1_0 = 2, F1_1 = -0.94991, F1_2 = 0, F2_0 = 0, F2_1 = 0.13587, F2_2 = 0; static float G0_0 = 26.977, G1_0 = 44.981, G2_0 = 17.242; static float H0_0 = 0.0625, H1_0 = 0, H2_0 = 0; static float Gamma0_0 = 0.0625, Gamma1_0 = 0, Gamma2_0 = 0; static float Phi0_0 = 2.7896, Phi0_1 = -1.3193, Phi0_2 = 0.84863, Phi1_0 = 2, Phi1_1 = 0, Phi1_2 = 0, Phi2_0 = 0, Phi2_1 = 0.5, Phi2_2 = 0; static float C0_0 = 0, C0_1 = 0, C0_2 = 0.042236; static float r = 0; static float xu = 0; static float xuL = 0; static float xk0 = 0, xk1 = 0, xk2 = 0; static float xkL0 = 0, xkL1 = 0, xkL2 = 0; static float y = 0; static float i = 0; // Position is saved automatically ResultData.Position = CurrentPosition; // Angular velocity is saved automatically if (CurrentTime != 0.0f){ ResultData.Velocity = (1000.0f * (float)(CurrentPosition - OldPosition)/(float)(CurrentTime - OldTime)); } else { ResultData.Velocity = 0.0f; } r=sin(CurrentTime/1000)/2-1; input0 = ResultData.Velocity; filter0 = (filB0 * input0 + filB1 * input1 + filB2 * input2 + filB3 * input3 - (filA1 * filter1 + filA2 * filter2 + filA3 * filter3)) / filA0; input3 = input2; input2 = input1; input1 = input0; filter3 = filter2; filter2 = filter1; filter1 = filter0; if(CurrentTime < 2000) ResultData.Torque = 3.5000e-001; else { y = filter0 + -9.5218e+000; xk0 = F0_0 * xkL0 + F0_1 * xkL1 + F0_2 * xkL2 + H0_0 * xuL + G0_0 * y; xk1 = F1_0 * xkL0 + F1_1 * xkL1 + F1_2 * xkL2 + H1_0 * xuL + G1_0 * y; xk2 = F2_0 * xkL0 + F2_1 * xkL1 + F2_2 * xkL2 + H2_0 * xuL + G2_0 * y; i += 0.0019278 * (r - y); xu = i + K0_0 * (Nx0_0 * r - xk0) + K0_1 * (Nx1_0 * r - xk1) + K0_2 * (Nx2_0 * r - xk2); if(xu > 1) xu = 1; if(xu < -1) xu = -1; ResultData.Torque = xu + 3.5000e-001; xuL = xu; xkL0 = xk0; xkL1 = xk1; xkL2 = xk2; } ResultData.StateVariable_5 = r; ResultData.StateVariable_6 = y; return ResultData; }
Ez a szabályozó is helyesen működik, az alapjelet jól követi a kimeneti jel. Az ábrán jól látható, hogy az integrátor hatására megszűnt az eltérés az alapjeltől.
%% Beállítások integrator=true; idplots=true; stateplots=true; outplots=true; filterdesign=true; %% Identifikáció start=1800; %kezdőidőpont y=velocity2(start:end)-velocity2(start); %szögsebesség-mérések vektora u=torque2(start:end)-torque2(start); %nyomatékmérések vektora n=3; %állapotváltozók száma Ts=0.001; %mintavételezési idő z=iddata(y,u,Ts); %mérési adatok megadása nn=[n,1,1,1]; %fokszámok megadása tharmax=armax(z,nn); %identifikáció [A,B,C]=th2poly(tharmax); %polinomok kiolvasása sys=tf(B,A,Ts); %átviteli függvény előállítása yid=idsim(tharmax,u,zeros(n,1)); %identifikált rendszer válasza t=0:Ts:(size(y)-1)*Ts; %idővektor az ábrázoláshoz if(idplots) figure(1); %új grafikon plot(t,y,t,yid); %grafikon megjelenítése legend('mért','identifikált'); %jelmagyarázat xlabel('idő [s]'); %vízszintes tengelycím ylabel('szögsebesség [rad/s]'); %függőleges tengelycím end; sys %% Szűrő tervezése if(filterdesign) filT=0.001; %Szűrő időállandója filN=3; %Szűrő fokszáma %Folytonos idejű szűrő filsys=tf(zpk([],-ones(1,filN)/filT,(1/filT)^filN)); dfilsys=c2d(filsys,Ts,'zoh'); %Diszkrét idejű verzió figure(1); dbode(dfilsys.num{1},dfilsys.den{1},Ts) %Bode-diagram figure(2); step(dfilsys); %Ugrásválasz figure(3); v=velocity(start:end)-velocity(start); %Zajos jel lsim(dfilsys,v,t); %Zajos jel szűrése end; %% Szabályozó tervezés sysd=ss(sys); %állapottér modell [Phi Gamma C D]=ssdata(sysd); %mátrixok kiolvasása a=2; %hány százalékra álljunk be Tap=0.2; %szabályozási idő xi=0.8; %csillapítás w0=log(100/a)/Tap/xi; %sajátfrekvencia sdom1=-w0*xi+1i*w0*sqrt(1-xi^2); %domináns pólusok sdom2=conj(sdom1); scinf=-4*w0; %segédpólusok a szabályozóhoz soinf=scinf*5; %segédpólusok a megfigyelőhöz siinf=scinf; %segédpólus integrátorhoz %Shannon-elv ellenőrzése if(-soinf>0.5/Ts || (integrator && -min([siinf soinf])>0.5/Ts)) display('Túl kicsi a mintavételezési idő!'); end; %Célrendszer modellje egységnyi erősítéssel if(idplots) tcsys=zpk([],[sdom1, sdom2, scinf*ones(1,n-2)],-w0^2*scinf^(n-2)); figure(2); %új grafikon step(tcsys); %ugrásválasz számítása end; %Pólusok átszámítása diszkrét időre zdom1=exp(sdom1*Ts); zdom2=exp(sdom2*Ts); zcinf=exp(scinf*Ts); zoinf=exp(soinf*Ts); ziinf=exp(siinf*Ts); %Az irányíthatóság ellenőrzése Mc=ctrb(Phi,Gamma); if rank(Mc)~=n disp('A rendszer nem irányítható!'); end %Állapot-visszacsatolás tervezése if(integrator) iPhi=[Phi, zeros(n,1); Ts*C, 1]; iGamma=[Gamma; 0]; iC=[C 0]; iPhic=[zdom1 zdom2 zcinf*ones(1,n-2) ziinf];%pólusok új helye iK=acker(iPhi,iGamma,iPhic); %pólusáthelyezés K=iK(1:n); %állapot-visszacsatolás Ki=iK(n+1); %integrátor visszacsatolása else phic=[zdom1 zdom2 zcinf*ones(1,n-2)]; %pólusok új helye K=acker(Phi,Gamma,phic); %pólusáthelyezés end %Alapjel-korrekció számítása NxNu=inv([Phi-eye(n) Gamma;C 0])*[zeros(n,1);1]; Nx=NxNu(1:n); Nu=NxNu(n+1); %A megfigyelhetőség ellenőrzése Mo=obsv(Phi,C); if rank(Mo)~=n disp('A rendszer nem megfigyelhető!'); end %Állapot-megfigyelő tervezése phio=ones(1,n)*zoinf; %megfigyelő pólusai G=acker(Phi',Phi'*C',phio)'; %pólusáthelyezés duális rsz.-en F=Phi-G*C*Phi; H=Gamma-G*C*Gamma; %% Szimuláció if(integrator) open('szabint'); sim('szabint'); else open('szab'); sim('szab'); end; rout=simruy(:,1); uout=simruy(:,2); yout=simruy(:,3); tout=[0; tout]; if(outplots) figure(3); plot(tout,rout,tout,yout); xlabel('idő [s]'); ylabel('szögsebesség [rad/s]'); legend('r','y'); figure(4); plot(tout,uout); xlabel('idő [s]'); ylabel('nyomaték [mNm]'); end; if(stateplots) figure(5); plot(tout,simxo(:,1:n)); xlabel('idő [s]'); ylabel(''); legend('x1','x2','x3'); figure(6); plot(tout,simxo(:,n+1:2*n)); xlabel('idő [s]'); ylabel(''); legend('x1','x2','x3'); end;