Skocz do zawartości

Arduino - projekt Turbina, prędkość obrotowa


Sodar

Pomocna odpowiedź

Witam

Wykombinowałem sobie takie małe urządzonko. Jest to wentylator odśrodkowy z chłodzenia serwera, arduino zaś robi za kontroler obrotów poprzez generowanie sygnału PWM.

Kontrola PWM działa bez problemu. Co prawda turbiny nie da się spowolnić do zera bo ma własną elektronikę - układ soft-start.

PROBLEM: Niebieski przewód zdaje się że generuje sygnał obrotowy, podłączyłem go wg zaleceń w jednym z tutoriali. Teoretycznie działa ale im szybciej pracuje turbina tym bardziej niewiarygodna jest wartość, którą podaje. Przy maksymalnej prędkości w ogóle się coś zawiesza na stałej wartości, ok 1500 (ale czego to nie wiem).

Dla mnie w ogóle dziwne jest że przy oknie zliczania na ok 0,5s podaje prawie 5000 impulsów.. Jak to w ogóle możliwe? Coś mi się wydaje że czujnik prędkości w tej turbinie to coś innego niż spotykany na ogół sensor Halla. Oczywiście dokumentacji dokładnie do tej wersji turbiny nie znalazłem, więc trochę błądzę po omacku 😋.

Turbina: Delta Electronics BFB1012VH-5D84

Arduino Uno R3, dekoder I2C

Zasilacz od dekodera polsatu 12V 2,3A 😋

Zdjęcie jej i schematu w załącznikach. Niebieski obwód to właśnie ten kabel czujnika obrotów. Żółty zaś to PWM.

Kod:

#include <Wire.h>
#include <TimerOne.h>
#include <LiquidCrystal_I2C.h> // pobrany lib od I2C

#define btnUp 4
#define btnDown 5
#define ctrlPwm 3
#define sensePin 2

// LCD na 0x3F
LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6 ,7, 3, POSITIVE);

// speed
unsigned int ctrl;
unsigned int pwm;
unsigned int rpm;
unsigned int counts;

void licznik() {
 counts++;
}

void pomiarRPM() {
 Timer1.detachInterrupt();
 rpm = counts;
 counts=0;
 Timer1.attachInterrupt(pomiarRPM);
}

void bar() {
 switch (ctrl){
   case 1:
   lcd.setCursor(5,0);
   lcd.print("[         ]");
   break;
   case 2:
   lcd.print("[#        ]");
   break;
   case 3:
   lcd.print("[##       ]");
   break;
   case 4:
   lcd.print("[###      ]");
   break;
   case 5:
   lcd.print("[####     ]");
   break;
   case 6:
   lcd.print("[#####    ]");
   break;
   case 7:
   lcd.print("[######   ]");
   break;
   case 8:
   lcd.print("[#######  ]");
   break;
   case 9:
   lcd.print("[######## ]");
   break;
   case 10:
   lcd.print("[#########]");
   break;
 }
}

void sterowanie() {
 while(digitalRead(btnUp) == LOW){
 if(ctrl < 10){
 ctrl++;
 lcd.setCursor(5,0);
 bar();
 delay(200);
 } else {
   lcd.setCursor(5,0);
   lcd.print("[ - MAX - ]");
   delay(200);
 }
 } 
 while(digitalRead(btnDown) == LOW){
 if(ctrl > 1){
 --ctrl;
 lcd.setCursor(5,0);
 bar();
 delay(200);
 } else {
   lcd.setCursor(5,0);
   lcd.print("[ - MIN - ]");
   delay(200);
 }
 } 
}

void regulacja() {
 unsigned int spd;
 spd = ctrl*20+55; 
 // sprawdzenie czy za wolno
 if(pwm < spd){
   for (pwm; pwm < spd; pwm++) {
   analogWrite(ctrlPwm, pwm);
   delay(50);
   lcd.setCursor(3,1);
   lcd.print((pwm/10)*4);
   lcd.print("%");
   }
 } else {
 }
// sprawdzenie czy za szybko
 if(pwm > spd){
   for (pwm; pwm > spd; --pwm) {
   analogWrite(ctrlPwm, pwm);
   delay(50);
   lcd.setCursor(3,1);
   lcd.print((pwm/10)*4);
   lcd.print("%");
   }
 } else {
 }
 lcd.clear();
}

void setup() {
  // sterowanie (przyciski)
 pinMode(btnUp, INPUT_PULLUP);
 pinMode(btnDown, INPUT_PULLUP);
 pinMode(ctrlPwm, OUTPUT);
 // pomiar
 pinMode(sensePin, INPUT);
 Timer1.initialize(500000);  // 0,5 sekund
 attachInterrupt(digitalPinToInterrupt(sensePin), licznik, RISING);
 Timer1.attachInterrupt(pomiarRPM);
 // reset zmiennych
 ctrl = 1;
 pwm = 0;
 // wyswietlacz
 lcd.begin(16,2);
 lcd.clear();
 lcd.setCursor(0,0);
 lcd.print("REG: ");
 lcd.setCursor(0,1);
 lcd.print("P: ");
 lcd.setCursor(8,1);
 lcd.print("R: ");
}

void loop() {
 unsigned int spd;
 spd = ctrl*20+55;
 // czytanie przyciskow
   sterowanie();
 // uruchomienie
 if(pwm == 0){
   analogWrite(ctrlPwm, 75);
   pwm = 75;
 } else {
 }
 // sprawdzanie
 if(pwm != spd){
   regulacja();
 } else {
   lcd.setCursor(0,0);
   lcd.print("REG: ");
   bar();
   lcd.setCursor(0,1);
   lcd.print("P: ");
   lcd.print((pwm/10)*4);
   lcd.print("%");
 }
 lcd.setCursor(8,1);
 lcd.print("R: ");
 lcd.print(rpm);
 delay(25);
//koniec
}
Link do komentarza
Share on other sites

Nie wnikałem w szczegóły starając się raczej zrozumieć ogólne założenia programu. Mam kilka uwag:

1. W funkcji pomiarRPM() nie powinieneś ruszać niczego związanego z Timerem1. Przecież został on już raz odpalony z okresem 0.5s i co tyle czasu możesz spodziewać się wykonania tej funkcji. Niech sobie spokojnie cyka, nie przeszkadzaj mu. Co najwyżej jedynym niebezpieczeństwem może być asynchroniczna inkrementacja zmiennej counts i to właśnie wykonanie funkcji licznik() powinieneś w tym momencie blokować przez odpięcie sensePin od funkcji licznik() - jeżeli w ogóle coś. Jeśli jednak (na tym najniższym poziomie) funkcja pomiarRPM() wykonywana jest w obsłudze przerwania sprzętowego od Timera1, to z powodu jednopoziomowego systemu przerwań AVR nie musisz się obawiać nawet i tego - w czasie pracy pomiarRPM() nic innego nie będzie się wykonywało na pewno.

2. Natomiast istnieje poważna obawa, że program główny czyli loop() będzie odczytywał zmienną rpm asynchronicznie do jej uaktualniania w pomiarRPM(). Ponieważ rpm jest dłuższa niż 8 bitów, procesor AVR nie inkrementuje jej ani nie odczytuje "atomowo" tj. niepodzielnie. Może się więc zdarzyć, że zostanie odczytana w loop() tylko część zmiennej, w tej chwili zostanie ona zmieniona w pomiarRPM() a następnie doczytana druga część już z nowej wartości i po złożeniu w bzdurną całość - wypisana. Musisz tu zrobić prostą synchronizację, wiesz jak?

3. Turbina nie musi oddawać jednego impulsu na obrót. Mogą to być 2, 3 lub 4 impulsy w zależności od konstrukcji silnika i elektroniki. To w sumie nie ma znaczenia dla regulacji, choć jeśli chcesz mieć miernik obrotów w rpm, to prawdziwą prędkość musisz zmierzyć czymś innym.

4. Powinieneś jednak obejrzeć sygnały na oscyloskopie. Jeżeli zbocza są koślawe lub niemonotoniczne, to wyniki pomiarów będą mocno losowe, bo jedno zaszumione zbocze może wygenerować kilka przerwań. Jeśli podczas stabilnej pracy (np. turbina zasilana przez jakiś opornik zamiast Twojego sterowania PWM) widzisz w sumie małe wahania (np. <10%) i chcesz je jeszcze zmniejszyć, możesz użyć jakiegoś prostego filtrowania np. typu IIR pierwszego rzędu:

float wsp = 0.8;

float obroty = (zmierzone_rpm * (1.0 - wsp)) + (obroty * wsp);

Teraz obroty będą tylko w 20% zależały od najnowszego wyniku pomiaru więc będą tłumione szybkie zmiany powodowane naturalnymi wahaniami prędkości turbiny. Jeśli wsp dasz jeszcze większy (ale zawsze < 1.0), filtr będzie działał jeszcze mocniej. Oczywiście to samo możesz zapisać w szybkich int-ach, to tylko poglądowy przykład na zmiennym przecinku.

5. Inkrementacja 16-bitowej zmiennej jest szybka. AVR powinien umieć to zrobić nawet 50000/s więc nie widzę tutaj istotnych ograniczeń. Natomiast zmiany prędkości obrotowej względem Twojego wysterowania PWM mogą być mocno nieliniowe - czy możesz pokazać jakiś wykres?

6. Nie widzę tutaj zamkniętej pętli regulacji tylko osobny pomiar do zmiennej rpm i osobne sterowanie wg zmiennych spd i ctrl. Czy tak miało być?

Link do komentarza
Share on other sites

No właśnie nie bardzo wiem jak synchronizować inkrementacje zmiennej rpm z wykonywaniem programu. Może zmienię counts na byte a czas zliczania ograniczę na tyle aby nie "wychodziło" ponad 255?

Co to pracy w zamkniętej pętli - tak ma być, wypisanie prędkości obrotowej to tylko bajer. Choć planuję zrobić jakieś zabezpieczenie np. spowolnienie turbiny po przekroczeniu pewnej prędkości lub ostrzeżenie (np dioda) przy zbyt małej prędkości (zatkanie wylotu).

Turbina nie jest sterowana bezpośrednio poprzez PWM ale sygnał podawany jest do elektroniki silnika i to on steruje.

Link do komentarza
Share on other sites

Gdy wyświetlanie robisz w głównej pętli i nie chcesz już jego zmieniać np. przez rozbicie programu na osobne (quasi)procesy, to synchronizację możesz zrobić w taki przykładowy sposób:

1. Tworzysz 16-bitową (a więc ułomną z punktu widzenia kilku konkurencyjnych procesów zapisu/odczytu) zmienną statyczną służącą do przekazywania danych, powiedzmy data_pipe. To będzie zasób, o którego dostęp ubiega się dwóch chętnych: "producent" danych czyli asynchronicznie wykonywana funkcja pomiarRPM() oraz "konsument" danych czyli wykonywana w tle pętla główna.

2. Tworzysz atomowo dostępny semafor 1-bitowy (lub po prostu 1-bajtowy), np. sync_flag. To on posłuży do synchronizacji dostępu obu chętnych do wrażliwego zasobu data_pipe. Proces "producenta" będzie zapisywał do data_pipe tylko wtedy gdy sync_flag będzie wyzerowany a zaraz po zapisie nowej wartości będzie ustawiał znacznik na 1. "Konsument" będzie czytał wartość z data_pipe tylko wtedy, gdy sync_flag będzie ustawiony i zaraz po odczycie będzie go kasował na 0.

W skrócie to mniej więcej coś takiego:

volatile uint16_t data_pipe;
volatile uint8_t sync_flag;

void pomiarRPM() { 
 if (sync_flag == 0) {
   data_pipe = counts;
   sync_flag = 1;
 }
 counts=0; 
}

void loop() {
 static uint16_t rpm;
 if (sync_flag) {
   rpm = data_pipe;
   sync_flag = 0;
 }
// Tu są obliczenia rpm -> spd i wyswietlanie na LCD
}

Dzięki takiemu prostemu mechanizmowi masz pewność, że żaden z konkurencyjnych procesów nie popsuje drugiemu będącej w toku, wielobajtowej operacji zapisu lub odczytu. Co najwyżej, gdy "producent" nie zdąży wygenerować nowych danych, pętla główna wypisze poprzednio otrzymaną wartość po prostu nie uaktualniając w tym swoim przebiegu wartości rpm, bazując obliczenia na poprzedniej. A jeśli wypisywanie będzie tak wolne, że nie będzie nadążać za nowymi pomiarami, pomiarRPM() nie będzie ładował nowych wyników do data_pipe dopóki "konsument" nie upora się z przełknięciem poprzednio wysłanych danych i nie potwierdzi tego skasowaniem semafora.

Tak, wiem, że Twoja elektronika generuje tylko sterowanie PWM a nie moc (w postaci PWM) dla turbiny, ale to nie zmienia faktu, że zależność rmp = f(pwm) może być nieliniowa tak bardzo jak nieliniowe są opory śmigła wraz ze wzrostem dostarczanej do silnika mocy i rosnącymi obrotami. Przecież za pomocą PWM nie dostarczasz informacji "takie mają być obroty" tylko "taka ma być moc silnika" a to nie to samo.

Z tego powodu zamknięta pętla regulacji była by intuicyjnie lepsza, bo obroty wzrastałyby liniowo wraz z podkręcaniem suwaczka na LCD. Obecnie pod koniec może już się nic nie zmieniać, bo przy dużej prędkości trzeba dużego wzrostu mocy, by jeszcze trochę zwiększyć obroty śmigła.

Kod pisałem z głowy więc mogą być w nim jakieś literówki itp. Mam nadzieję, że idea jest jasna.

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Dziękuję za pomoc. Coś to pomogło ale nadal wyniki są bardzo niestabilne. Pewnie masz racje że sygnał ma poszarpane zbocza i łapie na zakłóceniach przerwania. Niestety oscyloskopu nie mam ale tak to wygląda.

Link do komentarza
Share on other sites

Może i na to jest sposób. Wejścia przerwań są wobec takich "śmieciowych" sygnałów bezbronne, bo przecież ich zadaniem jest właśnie szybkie wykrywanie każdego zbocza. Dlatego też nigdy nie spotkasz w poważniejszych projektach takiego podłączenia pinów INT jak u Ciebie. Zawsze są po drodze jakieś filtry (gdy sygnał jest zewnętrzny, np. z jakiegoś czujnika) albo źródłem przerwań są układy inteligentne, które nie robią takiej kaszany, np. przetworniki ADC, ekspandery portów, UARTy, moduły radiowe itp peryferia.

Na szczęście masz jeszcze kilka sposobów do wypróbowania, choć aby się w to bawić mocno zalecałbym jednak obejrzenie sygnału na oscyloskopie. Nie masz nikogo znajomego z takim sprzętem? Być może zbocza są OK a rzeczywiście obroty tak się wahają i wtedy trzeba po prostu zrobić filtrowanie/uśrednianie przed wyświetlaniem, jak opisałem wcześniej. No to tak:

1. Prosty filtr RC. Szeregowo z sygnałem z turbiny dajesz opornik np 1k a za nim, przy nóżce procesora wstawiasz kondensator np. 22...100nF do masy. To powinno odfiltrować szybsze szpile, ale za to sygnał będzie bardzo "mułowaty" w sensie szybkości zboczy. Tego wejścia cyfrowe nie lubią więc podłącz to do wejścia wewnętrznego komparatora. Masz takie coś w procesorze, korzystałeś kiedyś z tego? Odpowiednio ustawiony komparator także może zgłaszać przerwania więc dalej już tak samo jak teraz.

2. Zaprząc do roboty procesor. Możesz wykorzystać jego całą wolną moc do analizy sygnału z turbiny. To oznacza, że przez czas analizy program będzie się zajmował tylko tym, więc czas ten trzeba ograniczyć np. do 0.1 lub 0.2s. Zliczysz wtedy mniej impulsów, ale to przecież nie ma znaczenia bo i tak przeliczasz je na rpm. Trochę spadnie rozdzielczość, ale to nie super-miernik tylko raczej wskaźnik. Niech procesor robi coś takiego:

a. Start liczenia czasu pomiaru i wyzerowanie licznika impulsów.

b. Oczekiwanie na stabilny stan 1 - kilkukrotne sprawdzenie czy na pewno jest taki stan na wejściu.

c. Oczekiwanie na stabilny stan 0 - kilkukrotne sprawdzenie czy na pewno jest taki stan na wejściu.

d. Zliczenie impulsu.

e. Sprawdzenie czy upłynął czas pomiaru. Jeśli nie, powrót do pkt. a.

To w sumie oznacza cyfrową filtrację zakłóceń. Przez dobieranie liczby powtórzeń sprawdzenia stabilności stanów 0 i 1 mógłbyś zrobić tak dobry filtr jak tylko można, ale nie na tyle mocny by wycinał impulsy najszybszych obrotów.

3. Skorzystać z wbudowanego w procesor podobnego filtra (sprzętowego) na wejściu IC1 timera 1 i ten timer wykorzystać do "łapania" impulsów przez pomiar okresu a nie częstotliwości.

Mam nadzieję, że jeszcze się nie zniechęciłeś, bo projekt - choć niewielki - daje pole do popisu w zwalczaniu choćby takich problemów jakie napotkałeś. Tylko na schematach i papierowych wykresach rzeczy są proste 🙂

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.