Politechnika Gdańska Wydział Elektrotechniki i Automatyki Katedra Inżynierii Systemów Sterowania Przemysłowe układy sterowania PID Układy regulacji PID w strukturze sprzętowej Pytania i zadania do zajęć laboratoryjnych 5 Opracowanie: Tomasz Rutkowski, dr inż. Jarosław Tarnawski, dr inż.
Cel laboratorium Celem laboratorium jest: - nabycie umiejętności wykorzystywania kart akwizycji w zastosowaniach akwizycji danych oraz budowaniu układów sterowania, - zilustrowanie struktury pętli sprzętowej i weryfikacji metody sterowania w czasie rzeczywistym - realizacji programowej regulatora PID w językach C/C++, - wykorzystania NAVI SDK i budowy aplikacji dla OS Windows realizującej regulator PID z wykorzystaniem karty akwizycji. Plan przebiegu laboratorium - Zapoznanie się ze sposobem połączenia obiektu sterowania z kartą akwizycji - Konfiguracja Matlab/Simulink/Real-Time Windows Target (RTWT) do realizacji symulacji w czasie rzeczywistym - Identyfikacja obiektów sterowania - Budowa regulatora PID w środowisku RTWT - budowa programu/aplikacji (niezależnej od M/S) dla Windows z wykorzystaniem karty akwizycji: - odczytujących stan wejść analogowych - zapisujących (ustawiających) stan wyjść analogowych - budowa aplikacji dla Windows realizującej zadania regulatora PID 2
Karta Advantech PCI-1711 Przyłącze PCLD-8712 Obiekt 3
Instrukcja budowy aplikacji dla Windows z wykorzystaniem kart Advantech PCI-1711 i pakietu SDK Instrukcję sporządzono w oparciu o wersje oprogramowania: DAQNavi_SDK_3.2.5.0, SDK 7.1 dla Windows 7 1. Konfiguracja win7 SDK 1.1. Uruchom linię poleceń Windows SDK v7.1 Start -> Wszystkie Programy -> Microsoft Windows SDK v7.1 -> Windows SDK v7.1 -> Command Prompt. Rekomendacja: zamień czcionkę w konsoli na rozmiar 12x16. 1.2. Dokonaj konfiguracji ustawiając domyślnie 32-bitowy kompilator i środowisko docelowe Windows 7 wpisując polecenie: SetEnv /win7 /x86 1.3. Konwersja projektów Visual C++ do formatu Visual C++ 2010 (niezbędne do zastosowania SDK) vcupgrade.exe Hello.vcproj, gdzie Hello to przykładowa nazwa uaktualnianego projektu Powinno powstać kilka nowych plików w katalogu źródłowym, a pośród nich plik Hello.vcxproj 2. Przygotowanie struktury plików i katalogów Załóż katalog z nazwiskami członków grupy w katalogu Moje Dokumenty Pobierz dołączony do instrukcji plik Źródła.zip wypakuj do trzech różnych katalogów w założonym folderze opatrzonym nazwą członków grupy: do obsługi analogowych wejść: InstantAI do obsługi analogowych wyjść: StaticAO obsługa we/wy na potrzeby budowy regulatora: PID Odznacz właściwości katalogów: tylko do odczytu Dokonaj konwersji projektów opisanej w pkt. 1.3 dla obu katalogów Zapoznaj się z plikami projektów, a w szczególności z plikami programów *.cpp Poszukaj plików bibliotecznych Advantech NAVI (przeważnie c:\advantech\) i ustal ścieżkę dostępu Dokonaj zmiany odnośników do plików LIB ze względnych na bezwzględne uwzględniając położenie plików. Przykłady w tabeli Adres względny (do Adres bezwzględny (zastępujący) zastąpienia) #include "../inc/compatibility.h" #include "C:\Advantech\DAQNavi\Examples\C++_Console\inc\ compatibility.h" #include "../../../inc/bdaqctrl.h" #include "C:\Advantech\DAQNavi\Inc\bdaqctrl.h" 3. Kompilacja plików Examples Usuń poniższe linie z pliku *.vcxproj co spowoduje wyłączenie przyrostowego sposobu działania Linkera (niezbędne dla popranej budowy aplikacji przez SDK) <LinkIncremental Condition="'$(Configuration) $(Platform)'=='Debug Win32'">true</LinkIncremental> <LinkIncremental Condition="'$(Configuration) $(Platform)'=='Release Win32'">false</LinkIncremental> Dokonaj kompilacji za pomocą polecenia MSBuild Hello.vcxproj Uruchom aplikację, którą odnajdziesz w katalogu bin/debug 4
Źródła Konfiguracja SDK https://msdn.microsoft.com/en-us/library/ff660764%28v=vs.100%29.aspx (01.06.2015) 5
KOD OBSŁUGI WEJŚĆ ANALOGOWYCH ******************************************************************************/ #include <stdlib.h> #include <stdio.h> //#include "../inc/compatibility.h" #include "C:\Advantech\DAQNavi\Examples\C++_Console\inc\compatibility.h //#include "../../../inc/bdaqctrl.h" #include "C:\Advantech\DAQNavi\Inc\bdaqctrl.h using namespace Automation::BDaq; //----------------------------------------------------------------------------------- // Configure the following three parameters before running the example //----------------------------------------------------------------------------------- #define devicedescription L"DemoDevice,BID#0" int32 startchannel = 0; const int32 channelcount = 3; inline void waitanykey() dosleep(1); while(!kbhit()); int main(int argc, char* argv[]) ErrorCode ret = Success; // Step 1: Create a 'instantaictrl' for InstantAI function. InstantAiCtrl * instantaictrl = AdxInstantAiCtrlCreate(); do mode. // Step 2: Select a device by device number or device description and specify the access // in this example we use AccessWriteWithReset(default) mode so that we can // fully control the device, including configuring, sampling, etc. DeviceInformation devinfo(devicedescription); ret = instantaictrl->setselecteddevice(devinfo); // Step 3: Read samples and do post-process, we show data here. printf("acquisition is in progress, any key to quit!\n\n"); double scaleddata[channelcount] = 0;//the count of elements in this array should not be less than the value of the variable channelcount int32 channelcountmax = instantaictrl->getfeatures()->getchannelcountmax(); do //read samples and save to buffer 'scaleddata'. ret = instantaictrl->read(startchannel,channelcount,scaleddata); // process the acquired data. only show data here. for (int32 i = startchannel; i< startchannel+channelcount;++i) printf("channel %d data: %10.6f\n", i % channelcountmax, scaleddata[istartchannel]); printf("\n"); SLEEP(1); while(!kbhit()); while(false); // Step 4 : Close device and release any allocated resource. instantaictrl->dispose(); // If something wrong in this execution, print the error code on screen for tracking. if(biofailed(ret)) printf("some error occurred. And the last error code is Ox%X.\n", ret); waitanykey();// wait any key to quit! return 0; 6
KOD OBSŁUGI WYJŚĆ ANALOGOWYCH ******************************************************************************/ #include <stdlib.h> #include <stdio.h> #include <math.h> //#include "../inc/compatibility.h" #include "C:\Advantech\DAQNavi\Examples\C++_Console\inc\compatibility.h" //#include "../../../inc/bdaqctrl.h" #include "C:\Advantech\DAQNavi\Inc\bdaqctrl.h" using namespace Automation::BDaq; //----------------------------------------------------------------------------------- // Configure the following three parameters before running the demo //----------------------------------------------------------------------------------- #define ONE_WAVE_POINT_COUNT 512 //define how many data to makeup a waveform period. #define devicedescription L"DemoDevice,BID#0" int32 channelstart = 0; int32 channelcount = 1; enum WaveStyle Sine, Sawtooth, Square ; //function GenerateWaveform: generate one waveform for each selected analog data output channel ErrorCode GenerateWaveform( InstantAoCtrl * instantaoctrl,int32 channelstart,int32 channelcount, double * wavebuffer, int32 SamplesCount,WaveStyle style); inline void waitanykey() dosleep(1); while(!kbhit()); int main(int argc, char* argv[]) ErrorCode ret = Success; // Step 1: Create a 'InstantAoCtrl' for Static AO function. InstantAoCtrl * instantaoctrl = AdxInstantAoCtrlCreate(); do // Step 2: Select a device by device number or device description and specify the access mode. // in this example we use AccessWriteWithReset(default) mode so that we can // fully control the device, including configuring, sampling, etc. DeviceInformation devinfo(devicedescription); ret = instantaoctrl->setselecteddevice(devinfo); // Step 3: Output data // Generate waveform data double *waveform = (double*)malloc( channelcount*one_wave_point_count*sizeof(double)); if( NULL == waveform ) printf( "Insufficient memory available\n" ); ret = GenerateWaveform( instantaoctrl,channelstart,channelcount, waveform,channelcount*one_wave_point_count,sine); printf("\n Outputting data... any key to quit!\n"); bool enforced = false; do for( int32 i = 0; i < ONE_WAVE_POINT_COUNT; i++ ) ret = instantaoctrl->write( channelstart,channelcount,&waveform[channelcount*i] ); SLEEP(1); if(kbhit()) printf("\n Static AO is over compulsorily"); enforced = true; while (false); free(waveform); if (!enforced) 7
printf("\n Static AO is over, press any key to quit!\n"); while(false); // Step 4: Close device and release any allocated resource. instantaoctrl->dispose(); // If something wrong in this execution, print the error code on screen for tracking. if(biofailed(ret)) printf("some error occurred. And the last error code is Ox%X.\n", ret); waitanykey();// Wait any key to quit!. waitanykey();// Wait any key to quit! return 0; ErrorCode GenerateWaveform( InstantAoCtrl * instantaoctrl, int32 channelstart,int32 channelcount,double * wavebuffer,int32 SamplesCount,WaveStyle style) ErrorCode ret = Success; int32 channel = 0; int32 channelcountmax = 0; int32 onewavesamplescount = SamplesCount/channelCount; int32 i = 0; MathInterval ranges[64] ; ValueRange valrange; channelcountmax = instantaoctrl->getfeatures()->getchannelcountmax(); for(i = 0;i < channelcountmax ;i++ ) valrange = instantaoctrl->getchannels()->getitem(i).getvaluerange(); if ( V_ExternalRefBipolar == valrange valrange == V_ExternalRefUnipolar ) if (instantaoctrl->getfeatures()->getexternalrefantipolar()) double referencevalue; if (valrange == V_ExternalRefBipolar) referencevalue = instantaoctrl->getextrefvalueforbipolar(); if (referencevalue >= 0) ranges[i].max = referencevalue; ranges[i].min = 0 - referencevalue; ranges[i].max = 0 - referencevalue; ranges[i].min = referencevalue; referencevalue = instantaoctrl->getextrefvalueforunipolar(); if (referencevalue >= 0) ranges[i].max = 0; ranges[i].min = 0 - referencevalue; ranges[i].max = 0 - referencevalue; ranges[i].min = 0; double referencevalue; if (valrange == V_ExternalRefBipolar) referencevalue = instantaoctrl->getextrefvalueforbipolar(); if (referencevalue >= 0) ranges[i].max = referencevalue; ranges[i].min = 0 - referencevalue; ranges[i].max = 0 - referencevalue; ranges[i].min = referencevalue; 8
referencevalue = instantaoctrl->getextrefvalueforunipolar(); if (referencevalue >= 0) ranges[i].max = referencevalue; ranges[i].min = 0; ranges[i].max = 0; ranges[i].min = referencevalue; ret = AdxGetValueRangeInformation( valrange,0,null,&ranges[i],null); if(biofailed(ret)) return ret; //generate waveform data and put them into the buffer which the parameter 'wavebuffer' give in, the Amplitude these waveform for(i = 0; i < onewavesamplescount; i++ ) for( int32 j = channelstart; j < channelstart+channelcount; j++ ) //pay attention to channel rollback(when startchannel+channelcount>chnumbermax+1) channel = j%channelcountmax; double amplitude = (ranges[channel].max - ranges[channel].min) / 2; double offset = (ranges[channel].max + ranges[channel].min) / 2; switch ( style) case Sine: *wavebuffer++ = amplitude*(sin((double)i*2.0*( 3.14159 )/onewavesamplescount )) + offset; case Sawtooth: if ((i >= 0) && (i < (onewavesamplescount / 4.0))) *wavebuffer++ = amplitude*( i/(onewavesamplescount/4.0)) + offset; if ((i >= (onewavesamplescount / 4.0)) && (i < 3 * (onewavesamplescount/4.0))) *wavebuffer++ = amplitude* ((2.0*(oneWaveSamplesCount/4.0)- i)/(onewavesamplescount/4.0)) + offset; *wavebuffer++ = amplitude* ((ionewavesamplescount)/(onewavesamplescount/4.0)) + offset; case Square: if ((i >= 0) && (i < (onewavesamplescount / 2))) *wavebuffer++ = amplitude * 1 + offset; *wavebuffer++ = amplitude * (-1) + offset; default: printf("invalid wave style,generate waveform error!"); ret = ErrorUndefined; return ret; ; 9