Elektroda.pl
Elektroda.pl
X
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

Niedokładność pomiaru czasu pomiędzy zboczami narastającymi - RaspberryPi 4

ks_robak 31 Jan 2021 19:03 513 18
  • #1
    ks_robak
    Level 15  
    Na jednym GPIO nadaję impulsy, na drugim mierzę - oba są połączone najkrócej jak się dało. Nadaję takim programikiem C:
    Code: c
    Log in, to see the code

    Od zbocza narastającego do kolejnego narastającego powinno być 0.05 s

    Mierzę takim programem Python:
    Code: python
    Log in, to see the code


    Otrzymuję takie wyniki:
    0.050196170806884766
    0.05017828941345215
    0.05023026466369629
    0.050199270248413086
    0.050180912017822266

    Rozumiem, że nie uda się otrzymać 0.050000000000, no ale ciut dokładniej by się przydało.
    A może pomiar jest precyzyjny, a nadawanie nie...
  • Helpful post
    #2
    khoam
    Level 42  
    Sądzę, że sam pomiar z użyciem skryptu pythona wprowadza dość spory błąd w pomiarze czasu. Co do "nadawania", rozważyłbym użycie funkcji nanosleep() albo nawet clock_nanosleep(), zamiast delay(). Link
  • #3
    yego666
    Level 33  
    Dokładność masz rzędu jednego promila mierzonej wartości a więc nieźle.
    Możesz sobie programowo zaokrąglać wyniki to będzie trochę ładniej wyglądać :)

    Myślę, że bez użycia przerwań lepiej nie będzie.
  • #4
    ks_robak
    Level 15  
    yego666 wrote:
    Myślę, że bez użycia przerwań lepiej nie będzie.


    Ba... Próbowałem użyć (w C) funkcji oczekiwania na zbocze z bibliotek wiringPi lub pigpio, niestety poległem w obu przypadkach. Na trzeźwo nie poradzę, a tak się składa, że nie piję... ;-)

    Dodano po 1 [minuty]:

    khoam wrote:
    Co do "nadawania", rozważyłbym użycie funkcji nanosleep() albo nawet clock_nanosleep(), zamiast delay().


    Nie pomogło. Pewnie pomiar kiszka.
  • #5
    _jta_
    Electronics specialist
    Wahania czasu są na poziomie kilkudziesięciu mikrosekund - na moim Raspberry Pi wywołanie time.time() zajmuje około 20 mikrosekund. Ale z np. time.sleep() mam większe wahania:
    Code: python
    Log in, to see the code
    Code: less
    Log in, to see the code

    Tyle, że ja mam starszy model Pi, z wolniejszym procesorem.

    W systemie operacyjnym są jakieś funkcje do szybkiego i dokładnego odczytywania i odmierzania czasu, ale ich użycie z Pythona to trochę zachodu. Pakiet ctypes powinien umożliwić dostęp do nich. I pewnie warto skompilować kod w Pythonie (wystarczy napisać go jako moduł i dołączyć przez import).
  • #6
    ks_robak
    Level 15  
    Może powiem o co chodzi w projekcie... Program ma alarmować o spadku prędkości obrotowej poniżej zadanej normy. Czujnik hall'a będzie produkował 1 pik na jeden obrót wału. Na podstawie okresu czasu, pomiędzy zboczem rosnącym i następującym po nim zboczem rosnącym, program wylicza prędkość obrotową. Jeśli mieści się w normie, nie robi nic i przeskakuje na kolejny czujnik... - ale to inna kwestia i nie to jest problemem.

    Uruchomiłem w C funkcję z pigpio - gpioSetISRFunc. Ładnie działa, nie obciąża procesora jak np. szybkie pętle, których próbowałem wcześniej, ale ciągle wynik jest mało precyzyjny.

    Program daje takie wyniki: (nadajnik od zbocza do zbocza 50ms - powinno dać 1200 obr/min)
    Obr/min - 1196.9
    Obr/min - 1196.6
    Obr/min - 1196.7
    Obr/min - 1197.3
    Obr/min - 1196.8
    Obr/min - 1196.9
    Obr/min - 1196.9
    Da się z tym żyć, ale wiem, że malinę stać na więcej...

    Oto program: (to tylko demonstracja pomiaru...)
    Code: c
    Log in, to see the code
  • #7
    yego666
    Level 33  
    Sądzę że najprostszy attiny zrobiłby to dokładniej i taniej niż Malina.
    Chyba że inne argumenty przemawiają za przyjętym przez Ciebie sprzętem.
  • #8
    ks_robak
    Level 15  
    yego666 wrote:
    Sądzę że najprostszy attiny zrobiłby to dokładniej i taniej niż Malina.
    Chyba że inne argumenty przemawiają za przyjętym przez Ciebie sprzętem.


    Dotykowy panel kontrolny, pomiary temperatury, PWM...
  • #9
    khoam
    Level 42  
    ks_robak wrote:
    Program ma alarmować o spadku prędkości obrotowej poniżej zadanej normy. Czujnik hall'a będzie produkował 1 pik na jeden obrót wału. Na podstawie okresu czasu, pomiędzy zboczem rosnącym i następującym po nim zboczem rosnącym, program wylicza prędkość obrotową.

    Czegoś nie rozumiem. Nie lepiej jest po prostu zliczać same impulsy w zadanej jednostce czasu i na tej podstawie obliczać prędkość obrotową? Teraz to tak wygląda, jakbyś chciał wyliczyć prędkość obrotową po każdym, jednym obrocie.
  • #10
    ks_robak
    Level 15  
    Chodzi o prostotę wykonania enkodera - magnes w kole zębatym lub pasowym i po sprawie (6 wałów do kontroli). Najwolniejszy wał ma 225obr/min. Cel jest taki, by przy możliwie najprostszym enkoderze, otrzymać przyzwoitą szybkość reakcji. Wystarczy, że program "wysłucha" dwóch tików na jednym wale i może przeskoczyć na następny.
    Tak sobie to wymyśliłem, bo leniwy jestem ;-)
  • #11
    khoam
    Level 42  
    To mam inną propozycję. Zamiast w przerwaniu wyliczać długość impulsu, uruchamiaj dwa przerwania, bez timeout'ów. Jedno na zbocze narastające, które będzie tylko wstawiało znacznik czasu rozpoczęcia (nawet w mikrosekundach) do zmiennej globalnej. Drugie na zbocze opadające, które z kolei wyliczy deltę pomiędzy czasem rozpoczęcia a odczytanym w tym przerwaniu. Nie obliczaj nic więcej, ale porównuj wynik ze stałą, która będzie wzorcem "prawidłowych" obrotów. Możesz też użyć gpioSetISRFuncEx() w celu przekazania wskaźnika do zmiennej przechowującej znacznik czasu. Tak naprawdę nie interesuje Cię konkretna wartość obrotowa, ale to, czy nie odbiega od "normy" i ewentualnie ile.

    Jeżeli koniecznie chcesz wyświetlać bieżącą prędkość obrotową w obr/min, to możesz to wyliczać dopiero w programie głównym - ważne jest, aby wiadomo było, kiedy takie wyliczenie i następnie wyświetlenie można rozpocząć (drugie przerwanie na zbocze opadające też może zapisywać czas do innej zmiennej globalnej). Tu można pokusić się o wykorzystanie eventów (eventTrigger()), które byłyby generowane pod koniec obsługi "drugiego" przerwania - odpowiednie funkcje są dostępne w pigpio.
  • #12
    _jta_
    Electronics specialist
    Nie wiem, jak się programuje przerwania na RPi, robiłem to na PC - na starym PC/XT dało się wyciągnąć powtarzalny pomiar impulsu z rozdzielczością us, ale na zablokowanych przerwaniach (poza tym, które uruchamiał mierzony sygnał), na współczesnych, nawet niezbyt szybkich, ns pewnie jest do osiągnięcia. Ale jeśli jest obsługiwane przerwanie akurat wtedy, gdy przychodzi impuls, to się go wykryje z opóźnieniem, i część pomiarów będzie niedokładna. Pytanie, czy ten RPi4 ma 4-rdzeniowy procesor i da się ustawić używanie jednego rdzenia wyłącznie przez program, który będzie odmierzać czas? A może da się mierzyć jakoś sprzętowo, układem peryferyjnym, bez angażowania procesora w sam pomiar?

    Jakaś koncepcja: szybki licznik binarny liczący "w kółko", i każdy impuls powoduje przepisanie aktualnego stanu licznika do rejestru (a jeszcze lepiej FIFO), skąd procesor może go odczytać. Pytanie, czy ten RPi ma odpowiedni licznik wbudowany, czy trzeba go dodać na zewnątrz? Wypadałoby mieć licznik np. 32-bitowy, więc składanie z prostych scalaków o średniej skali integracji (są np. 12-bitowe liczniki CD4040), to sporo układów, i może lepiej byłoby użyć jakiegoś mikrokontrolera - ale nie Arduino, tylko modułu STM32, który ma dużo większe możliwości.

    Aha, i program może mierzyć czasy na wszystkich wałach naraz (jak ich jest tylko 6) - ale pożądana jest możliwość dedykowania jednego rdzenia tylko do akwizycji danych, wtedy można mieć dokładność około 0,1us (z procesorem STM32F1 - moduł w cenie klonu Arduino).

    Nie znam na pamięć możliwości Raspberry Pi, ale pamiętam, że ma SPI - może tego użyć do zbierania danych? Zegar SPI będzie dbał o stabilną szybkość pobierania danych. Tylko wygodnie byłoby pakować te dane do bufora cyklicznego - żeby nie uruchamiać tego SPI za każdym razem, jak się bufor skończy.

    Można jeszcze do pomiaru czasu użyć jakiegoś Time Stamp Countera (w PC jest TSC i instrukcja RDTSC, odczytuje czas z nanosekundową rozdzielczością; RPi ma coś pod inną nazwą), ale problemem jest złapanie zmiany bez opóźnienia spowodowanego ewentualnym przerwaniem - a SPI działa niezależnie od przerwań.

    Aha: jak chcesz z PC generować sygnał do testowania, to może użyj portu szeregowego ustawionego na małą szybkość - powinien mieć dobrą stabilność. Tylko on daje napięcia +-12V, więc musisz je obciąć do zakresu bezpiecznego dla RPi.
  • #13
    JacekCz
    Level 39  
    yego666 wrote:
    Sądzę że najprostszy attiny zrobiłby to dokładniej i taniej niż Malina.
    Chyba że inne argumenty przemawiają za przyjętym przez Ciebie sprzętem.


    Zgadzam się.
    Raspberry, czyli linux, NIE JEST systemem czasu rzeczywistego, i na milisekundowych właściwościach nie należy polegać.
    To jest istotny błąd projektowy. Przyjdzie chwila, że dostaniesz fałszywą straszną ilość milisekund.

    Tu mamy dodatkowo maszynę wirtualną pythona.
    Nieco mniej złe będzie nie macanie się z jednym cyklem, ale średnia z określonej ilości cykli, ale to pudrowanie s...a.

    Krótkofalowcy, aby nadawać z peceta alfabet morse'a (co wymaga precyzji czasowej) zawsze dają uK na interfejsie. I nie da się tego inaczej zrobić dobrze. (w kontekście tego wątku pecet to tak jak malinka)

    Dodano po 5 [minuty]:

    ks_robak wrote:
    yego666 wrote:
    Sądzę że najprostszy attiny zrobiłby to dokładniej i taniej niż Malina.
    Chyba że inne argumenty przemawiają za przyjętym przez Ciebie sprzętem.


    Dotykowy panel kontrolny, pomiary temperatury, PWM...


    Akurat PWN, pomiar to argumenty za mikrokontrolerem. Mam obawy, czy widzisz sprawy w prawidłowym horyzoncie.

    Z podanej trójki tylko frontend, kontakt z użytkownikiem są argumentem za czymś większym (choć są jakieś GUI na większy kontroler bare-metal)

    Dodano po 2 [minuty]:

    ks_robak wrote:
    Wystarczy, że program "wysłucha" dwóch tików na jednym wale i może przeskoczyć na następny.
    Tak sobie to wymyśliłem, bo leniwy jestem


    Bardzo.
  • #14
    ks_robak
    Level 15  
    Dziękuję za wszystkie uwagi, będę je trawił i testował, ale...

    W międzyczasie okazało się, że może z tym pomiarem nie jest tak źle.
    Tu akcent humorystyczny ;-)

    Podłączyłem "generator elektrowni" do Raspberry, żeby sprawdzić czy prawidłowo dawkują parę i pilnują obrotów generatora jak należy, i zobaczcie co pokazało:

    Obr/min - 2999.1
    Obr/min - 2998.8
    Obr/min - 3000.9
    Obr/min - 2997.3
    Obr/min - 2999.4
    Obr/min - 2999.7
    Obr/min - 2999.3
    Obr/min - 2997.5
    Obr/min - 2999.4
    Obr/min - 2998.2
    Obr/min - 2996.7
    Obr/min - 2999.0
    Obr/min - 3001.7

    Prędkość uśredniona: 2999

    Dodam że źródło impulsów to transformatorek plus jedna dioda. Żadnych filtrów, więc na zboczu narastającym jest pewnie sporo zakłóceń zwłaszcza, że jest słonecznie i okoliczne instalacje fotowoltaiczne pracują.
  • Helpful post
    #15
    _jta_
    Electronics specialist
    Można tak: czytasz na zmianę stan sygnału i TSC (czy jak to się nazywa). Jeśli wykryjesz zmianę stanu sygnału przy małej różnicy odczytu TSC (potrzebujesz złapać 3 kolejne odczyty TSC i między nimi 2 różniące się odczyty stanu sygnału), to możesz określić z niezłą dokładnością, kiedy zmienił się sygnał (na pewno w przedziale między skrajnymi TSC); jeśli różnica TSC jest zbyt duża, to czekasz na następną zmianę; jeśli zbyt długo nie ma zmiany, to wiadomo, że obroty są za wolne, włączasz alarm.

    Sprawdź, jak odczytywać TSC na RPi, oraz ile czasu zajmuje - a raczej, jaką masz różnicę skrajnych odczytów TSC w takiej sekwencji odczytów: TSC, sygnał, TSC, sygnał, TSC - taką możesz mieć niepewność określania czasu, w którym sygnał się zmienił, pewnie to będzie kilka mikrosekund. Oczywiście SPI da wynik dziesiątki razy dokładniejszy.
  • #17
    ks_robak
    Level 15  
    _jta_ wrote:
    Jeszcze jedna możliwoś...


    Dzięki _jta_. Okrutnie zarobiony jestem - może w niedzielę coś poczytam.
  • #18
    ks_robak
    Level 15  
    Przypomniałem sobie, że mam tachometr laserowy... Znalazłem jakiś silnik indukcyjny, zamontowałem na nim magnes i cewkę jako czujnik i ograniczyłem pik napięcia zenerką. Zmierzyłem obroty tachometrem - pokazał 1480,7 obr/min.

    Niedokładność pomiaru czasu pomiędzy zboczami narastającymi - RaspberryPi 4

    Podłączyłem czujnik do Raspberry. Oto wyniki i błąd procentowy w stosunku do tego, co pokazał tachometr:
    1481 0,02%
    1480,7 0%
    1481,1 0,03%
    1480,7 0%
    1480,8 0,01%
    1479 -0,11%
    1483,1 0,16%
    1481,3 0,04%
    1481,3 0,04%
    1481,8 0,07%
    1481,7 0,07%
    1482,1 0,09%
    1481,3 0,04%
    1482 0,09%
    1482,5 0,12%
    1481,6 0,06%
    1481,7 0,07%
    1481,4 0,05%
    1480,1 -0,04%
    1482,2 0,1%
    1480,9 0,01%
    1480,9 0,01%
    1480,8 0,01%
    1481 0,02%
    1479,5 -0,08%
    1481,8 0,07%
    1481 0,02%
    1481,3 0,04%
    1481,6 0,06%
    1481,7 0,07%

    Średnia: 1481,26

    W związku z tym, że błąd procentowy jest, jak na moje zastosowanie, bardzo mały, pozostanę przy tym sposobie pomiaru prędkości obrotowej.
    Zresztą nie dałbym rady rozkminić podpowiadanych przez Was alternatywnych sposobów ;-)
    Program ma alarmować o poślizgu pasa przy 5% odchyłce od wzorca, więc taka precyzja pomiaru wystarczy. Tak mi się wydaje.

    Dziękuję Wam za zainteresowanie. Pozdrawiam.

    (Na razie nie zamykam...)
  • #19
    _jta_
    Electronics specialist
    To jest programik testujący kilka sposobów pomiaru czasu.
    Code: c
    Log in, to see the code

    Mam wrażenie, że na Raspberry Pi sensowne wyniki daje getttimeofday() i CLOCK_MONOTONIC_RAW. U mnie kilka CLOCK_* ma deklarowaną rozdzielczość 1ns, ale różnice są wielokrotnością 1000ns - lepszej rozdzielczości się nie uzyska. Próbowałem jeszcze CLOCK_MONOTONIC (bez _RAW) - wyniki podobne, jak z _RAW, ale ten z _RAW nie jest zaburzany przez korektę czasu systemowego (a inne są). CLOCK_REALTIME (a podejrzewam, że getttimeofday() też) ma poważną wadę: zmienia się czas z zimowego na letni, i jako zmierzony czas między impulsami wychodzi cała godzina, i masz alarm.

    Jeśli chcesz wywoływać clock_gettime(), albo getttimeofday() z Python-a, to poczytaj o 'ctypes'. Na przykład gettimeofday() i CLOCK_MONOTONIC_RAW z Pythona można wywołać tak:
    Code: python
    Log in, to see the code

    Ale u mnie na Raspberry Pi różnica czasu była około 80-100us... kiepsko. Z C były ze 2us.

    No... trochę poprawiłem wynik:
    Code: python
    Log in, to see the code