Elektroda.pl
Elektroda.pl
X
IGE-XAO
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[Rozwiązano] Programowanie rotomatu na serwie MG996R

25 Lut 2019 18:46 363 17
  • Poziom 8  
    Szanowni Koledzy,

    próbuję zbudować pierwszy w życiu rotomat oparty na sterowaniu za pomocą płytki Arduino Uno. Część z was może kojarzyć temat z wątku o wyborze programów za pomocą przycisków - ten problem został opanowany. Obecnie zmagam się z trudnościami w sterowaniu serwem, próbuję wymóc, aby na pierwszym programie serwo poruszało się wolno, na drugim szybciej. W standardowej bibliotece Servo.h osiąga się to za pomocą definiowania minimalnego kąta przeskoku oraz delaya. Ja na delayach nie mogę pracować ze względu na chęć zachowania możliwości zmiany programu w dowolnym momencie (bez czekania na zakończenie ruchu serwa, albo jego przejście przez "0"). Z pomocą kilku forumowiczów zbudowałem kod oparty o funkcję millis() - niestety realne sterowanie prędkością odbywa się tylko przez definiowanie kąta przeskoku, wprowadzanie interwałów między kolejnymi ruchami nie daje rezultatów. Prędkości przy kącie 1 stopnia są za duże nawet jak na program turbo w rotomacie - nie chcę uszkodzić zegarka. Samo serwo z niewiadomych przyczyn robi od 10 do 20 wychyleń w obie strony i staje. Dodatkowo ekran LCD wyświetlający napis z nazwą używanego programu zachowuje się jak tester dla epileptyków. Zasugerowano mi już aby kod wymuszający wyświetlenie napisu wywalić z programu obrotu i walnąć do pętli loop - efekt takiego zabiegu był żaden.
    Kod: c
    Zaloguj się, aby zobaczyć kod


    Spróbowałem jeszcze wymienić bibliotekę Servo.h na VarSpeedServo.h i na skorzystać z prostszej metody kontroli serwa. Prędkość w końcu mogę kontrolować w zadowalającym mnie zakresie, ale serwo robi maks. jedno wychylenie i się zatrzymuje. Ekran również miga jak stroboskop.

    Przestaje to już rozumieć - zastanawiam się nad podłączeniem LCD przez kondensator, choć w sumie nie wiem czego się po tym spodziewać.
    Na serwo nie mam aktualnie pomysłu. Poniżej kod z biblioteką VarSpeedServo.h

    Kod: c
    Zaloguj się, aby zobaczyć kod
  • IGE-XAO
  • Pomocny post
    Poziom 30  
    Nie licz na gotowe biblioteki, które najczęściej zawierają masę błędów. Napisz swoją obsługę serwa. Timerem wygeneruj impuls zależnie od tego jaki kąt chcesz uzyskać. Wiele serw nie wymaga przerwy pomiędzy impulsami ok 20ms, jeśli jednak ma ona być to też jest proste, po wygenerowaniu impulsu, wywołane zostanie przerwanie, które zmieni konfigurację timera aby uzyskać 20ms.
    Jak już to zrobisz, to innym timerem, modyfikuj kąt co wybrany czas.
    Potrzebujesz więc timera do impulsu serwa i np T1 i drugiego do zmiany kąta o zadany czas. Można wykorzystać przerwania od OCR1 co ok 1ms.

    Dodano po 5 [minuty]:

    T1, 10-bit, preskaler 64 (nie pamiętam czy 32 można ustawić) pozwoli generować max 4ms z rozdzielczością 4us. Daje to ok 1000możliwych pozycji serwa. Jak wystarczy mniej, to preskaler 256 (max ok 16ms) rozdzielczość 16us co daje ok 64 pozycji dla standardowego serwa 500us..1,5ms.

    Dodano po 5 [minuty]:

    Należy przyznać, że ARM maja bardziej elastyczne timery i bez kombinowania, na jednym preskalerze (aby uzyskać 300ns) można uzyskać czasy ok 300ns...20ms z rozdzielczością 300ns, co daje ponad 3000 pozycji serwa.
  • Pomocny post
    Poziom 33  
    Koldarius napisał:
    Ekran również miga jak stroboskop.

    Ponieważ za każdym razem wywołujesz lcd.begin(16, 2), a powinieneś tylko raz w setup().

    Ponadto digitalRead() może wielokrotnie odczytać stan LOW wskutek drgań styków czy też wskutek "nieco" dłuższego naciśnięcia przycisku, a co za tym idzie w pętli loop() będą wielokrotnie wywoływały się funkcje programX() po tylko "jednokrotnym" naciśnięciu przycisku.

    Ponadto lcd.blink() włącza wyświetlanie kursora, a nie wyłącza. Do wyłączania służy lcd.noBlink(). Nie bardzo rozumiem też, po co za każdym razem wywołujesz lcd.blink() w funkcjach programX().

    Wymienione wyżej uwagi dotyczą obu wersji Twojej aplikacji.

    Koldarius napisał:
    Przestaje to już rozumieć - zastanawiam się nad podłączeniem LCD przez kondensator, choć w sumie nie wiem czego się po tym spodziewać.

    Niczego dobrego :)
  • Poziom 8  
    Lchucki - można nieco bardziej przystępnie dla początkującego? Sugerujesz wywołanie jakiegoś innego timera? Czyli millis() liczące czas od uruchomienia Arduino się nie sprawdzi?

    EDIT - jak w przypadku wersji z biblioteką VarSpeedServo.h wpiszę true (czyli czekamy na wykonanie ruchu przez serwo) - to serwo idzie stabilnie i się nie zatrzymuje - niestety tracimy wtedy możliwość zmiany programu w każdym momencie.

    Korekcja LCD zgodnie z sugestiami dała piękny efekt - jest ok!
  • IGE-XAO
  • Poziom 33  
    Koldarius napisał:
    Korekcja LCD zgodnie z sugestiami dała piękny efekt - jest ok!

    Super. No to jeszcze debouncing dla przycisków i wywalenie niepotrzebnych zmiennych prog1, prog2 oraz prog3 :)
    Jeżeli chodzi o biblioteki do debouncingu, to najczęściej cytowanymi w ostatnim okresie na tym forum są: Bounce2 oraz EasyButton.
  • Poziom 30  
    Koldarius napisał:
    Lchucki - można nieco bardziej przystępnie dla początkującego?

    To bym musiał książkę o timerach AVR napisać. Jest to bez sensu, bo takie książki już są.

    Koldarius napisał:
    Czyli millis() liczące czas od uruchomienia Arduino się nie sprawdzi?

    Jeśli spowodujesz, że millis wygeneruje równomierne przerwania to się sprawdzi.
  • Poziom 33  
    Koldarius napisał:
    Sugerujesz wywołanie jakiegoś innego timera?

    Możesz na początek użyć biblioteki TimerOne do generowania i obsługi przerwań jeżeli chcesz realizować obsługę serw lub przycisków w przerwaniach.
  • Poziom 17  
    Koldarius napisał:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Przecież to nie ma sensu. Ani poprzedniczas nie przechowuje poprzedniego czasu, ani aktualnyczas nie ma nic wspólnego z aktualnym czasem. Twój program nie robi tego o czym piszesz. Napisz to może po polsku, w kilku linijkach opisz działanie algorytmu a później zacznij go tłumaczyć na język w którym programujesz. Nie wszytko jednocześnie.

    Koledzy piszą o słabych bibliotekach, przerwaniach i timerach. Autor toczy się samochodem z lekkiej górki, można mu doradzić zmianę opon, zamontowanie lżejszych kół i chip tuning. Ale gdyby tak najpierw silnik uruchomić i wbić bieg? To nawet we trzech go do takiej prędkości nie rozpędzili, a z wiatrem było.
  • Poziom 33  
    mpier napisał:
    Przecież to nie ma sensu. Ani poprzedniczas nie przechowuje poprzedniego czasu, ani aktualnyczas nie ma nic wspólnego z aktualnym czasem.


    Zmienna 'aktualnyczas' ustawiana jest każdorazowo przez millis() w funkcjach prog2() oraz prog3() (odnoszę się do pierwszej wersji zamieszczonego programu).
    W dalszej kolejności, jeżeli różnica pomiędzy 'aktualnyczas' a 'poprzedniczas' przekroczy ustaloną wartość 'servoFastInterval' to zmienna 'poprzedniczas' przyjmie nową wartość powiększoną o 'servoFastInterval'. Warunek if zadziała ponownie, kiedy znowu upłynie czas określony wartością 'servoFastInterval'.

    Generalnie więc jest to poprawne odliczanie interwałów czasowych, za wyjątkiem pierwszego interwału, ponieważ wartości 'aktualnyczas' oraz 'poprzedniczas' są inicjalizowane wartością zero, a powinny obie przyjąć wartość millis(). Zakładam, że dla Autora istotne było odmierzanie interwałów czasowych, a to czy zmienne 'aktualnyczas' oraz 'poprzedniczas' odnoszą się do czasu letniego, czy zimowego, nie było istotne :)
  • Poziom 17  
    Khoam, sugerujesz się nazwami zmiennych i komentarzem, a kompilator nie czyta komentarzy. Może czegoś nie widzę, ale na pierwszy rzut oka, to program3 uruchomiony w piątej sekundzie od włączenia urządzenia powinien wykonać max 3 ruchy servem.
    khoam napisał:
    Zakładam, że dla Autora istotne było odmierzanie interwałów czasowych, a to czy zmienne 'aktualnyczas' oraz 'poprzedniczas' odnoszą się do czasu letniego, czy zimowego, nie było istotne :)
    Tak?
  • Poziom 36  
    Koldarius napisał:

    ........
    Dodatkowo ekran LCD wyświetlający napis z nazwą używanego programu zachowuje się jak tester dla epileptyków.
    .................
    Przestaje to już rozumieć - zastanawiam się nad podłączeniem LCD przez kondensator, choć w sumie nie wiem czego się po tym spodziewać.

    Zazwyczaj program pracuje tak jak jest napisany.

    Niżej masz gotowy, względnie pracujący program pod twoje założenia.
    Zaczym zaczniesz go testowac, to kilka słów:
    Twój opis funkcjonalności nie jest zgodny z ostatnim załaczonym listingiem: Masz tam trzy podprogramy , a nie dwa:
    - pierwszy (1) to stop [myservo.detach();]
    - drugi program (2), istotnie ma kontrolowac wolniejsze cykle,
    - i trzeci (3) - odpowiednio zwiększona aktywnośc.
    Jedno zasadnicze pytanie: jakim napięciem zasilasz servo (?) i czy jest to oddzielne dostatecznej mocy źródło (?)
    4.8V to minimum; te serva lepiej pracują przy 5.5 lub 6V.
    Drugie; czy masz na zasilaniu serva dostatecznej wiekości capacitor (ok. 200 do 500 uF).

    Troche przerobiłem ten twój program; głównie odmierzanie czasu opóźnienia, oraz obsługę display.
    Dla testu jest zastosowane LCD z interface I2C - mniej przewodów (4) , zatem mniejsza szansa na błędy. Taki był pod ręką!
    Możesz to sobie łatwo przerobic na swój układ; zmieniając pierwsze 4 linie kodu.

    W podprogramie 2 i 3 dodałem odczyt aktualnego położenia kątowego dźwigni serva, który możesz obserwowac w Serial Monitor.
    Byc może zaobserwujesz pewne anomalia w płynności ruchu , szczególnie w skrajnych położeniach; jest to wynikiem pewnej niezgodności parametrów pracy testowanego serva z pewnego rodzaju standardową biblioteką.
    Biblioteka ta zakłada skrajne położenia serva o czasie trwania impulsu 544 usec i drugie skrajne 2400 usec i dla tych limitów ‘nakładane są’/ kalkulowane są wartości wychyleń katowych (0 do 180 st.) Niektóre serva mogą pracowac w mniejszym zakresie na przykład 0 do 170 , a nawet spotyka się ograniczenia 0 do165 stopni pomiędzy ‘twardymi stopami’. Jak również dla skrajnych wychyleń mogą odpowiada inne wartości czasów trwania impulsu.
    Dobrą praktyką jest poznac parametry pracy swojego serva i uwzględnic poprawki.
    Testuj , próbuj zmieniac parametry wg potrzeb....

    Nawet nie wiem do czego to ma praktycznie służyc.... do jakiego mechanizmu tak relatywnie duże serwo?

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Powodzenia,

    e marcus
  • Poziom 30  
    emarcus napisał:
    Biblioteka ta zakłada skrajne położenia serva o czasie trwania impulsu 544 usec i drugie skrajne 2400 usec

    Niezgodne z jakąkolwiek normą dla serw. Jak wszystko dla Arduino zrobione na od...... dlatego pisałem aby samemu napisać obsługę serwa. W końcu to nie szczyt możliwości uC wygenerować timerem odpowiednie impulsy. Obsługa timera w trybie PWM jest opisana w KAŻDEJ książce.

    Dodano po 10 [minuty]:

    Masz gotowca
    Kod: c
    Zaloguj się, aby zobaczyć kod

    dekodowanie DMX i sterowanie serwami. Wytniesz co potrzebne.
  • Poziom 17  
    Jeszcze można usunąć całą pętle while z program3, bo nic nie wnosi a blokuje pętle loop. Program 2 i 1 analogicznie. Zostałoby coś takiego:
    Kod: c
    Zaloguj się, aby zobaczyć kod
    To jest cała obsługa serwa w trzech przypadkach. Przycisków do tego nie wplatasz. Zamiast trzech "programów" masz jeden. Przyciski i wybór prędkości robisz tak samo. Też będzie kilka linijek.
  • Pomocny post
    Poziom 33  
    mpier napisał:
    sugerujesz się nazwami zmiennych i komentarzem, a kompilator nie czyta komentarzy.

    Nie sugerowałem się komentarzami w kodzie, ani jakimikolwiek innymi. Analizowałem tylko sam kod źródłowy pierwszej wersji programu w poście #1.

    mpier napisał:
    Może czegoś nie widzę, ale na pierwszy rzut oka, to program3 uruchomiony w piątej sekundzie od włączenia urządzenia powinien wykonać max 3 ruchy servem.

    Naprawdę? :) To może przeprowadź analizę tego przypadku.

    Dodano po 8 [minuty]:

    mpier napisał:
    Jeszcze można usunąć całą pętle while z program3, bo nic nie wnosi a blokuje pętle loop. Program 2 i 1 analogicznie. Zostałoby coś takiego

    A jaką wartość początkową powinna mieć lokalna zmienna statyczna 'kierunek" w tym pseudokodzie? Dodatkowo stosowanie odrębnej zmiennej lokalnej funkcji 'aktualnyczas' jest kompletnie zbędne. Wystarczyłoby w warunku dać same millis(). Brak również aktualizacjij wartości 'poprzedniczas', skoro to jest też lokalna zmienna statyczna funkcji - w Twoim kodzie będzie miała ona cały czas wartość 0.
    Zmienne 'aktualnyczas' oraz 'poprzedniczas' powinny być typu unsigned long, bo taką wartość zwraca millis().
    Dobrze jest przynajmniej skompilować kod, zanim się go opublikuje.

    Dodano po 31 [minuty]:

    emarcus napisał:
    Troche przerobiłem ten twój program; głównie odmierzanie czasu opóźnienia, oraz obsługę display.

    Trochę bardziej przerobiłem :). Zmienne prog1, prog2 oraz prog3 wydają się być zbędne tak, jak warunki z nimi związane. Użyłem callbacks, dzięki temu pętla loop() oraz poszczególne progX() trochę się uproszczą:
    Kod: c
    Zaloguj się, aby zobaczyć kod
  • Poziom 17  
    Masz racje khoam. Znalezienie błędów w moim przykładzie zajęło Ci ile? Kilka minut? Kod jest oczywisty. Od razu wiesz o co chodzi. Spróbuj tego samego z kodem autora. Sam autor dwa dni nie może się w nim połapać . Kierunek 0 można zamienić z działaj, odpadnie jedną zmienna.
  • Poziom 8  
    Dziękuję za odpowiedzi - spróbuję to wszystko przeanalizować, jako że jestem zielony zajmie to pewnie trochę czasu.
    Co do pytań które powstały - serwo jak wspomniałem ma za zadanie obracać pojemniczkiem z zegarkiem automatycznym w środku (tak aby nakręcić mechanizm tego zegarka). Do utrzymania jest zatem pewien ciężar (niezbyt wielki, ale zawsze), a ruchy nie mogą być zbyt szybkie (żeby mechanizm nawijał się normalnie). Nie mam żadnego kondensatora na zasilaniu serwa (choć po tej sugestii dołożę), servo zasilane jest ze styku 5v (płytka Arduino jest na zewn. zasilaczu stabilizowanym 12V 1A).
    Tak w całym kodzie są 3 zasadnicze programy - pominąłem 1-wszy ze względu, że jest to program postojowy.
    Nie obchodził mnie za bardzo absolutny czas - chciałem aby zgadzały się interwały, które warunkują poprawną pracę serwa. Brnąłem w funkcję if/break dlatego aby przerywać program w dowolnym momencie, myślałem że jest to prostsze do realizacji.
    Kod wygląda w niektórych miejscach jak wielojęzykowy zlepek programów, bo w niektórych miejscach jest zlepkiem - różnych patentów testowanych przeze mnie na bieżąco. Utrzymywałem go jedynie w reżimie formatowania i układu kodu wg. funkcji by był on dostatecznie czytelny.
    Serdeczne dzięki!

    emarcus - zrobiłem tak, jak zapisałeś. Wywaliłem unsigned long aktualnyczas = 0, a potem wszystkie aktualne czasy i zastąpiłem samym millis()
    Kod działa dokładnie tak, jak miał. Serdecznie Ci dziękuję!

    Kod działający
    Kod: c
    Zaloguj się, aby zobaczyć kod
  • Pomocny post
    Poziom 36  
    Koldarius napisał:
    Wywaliłem unsigned long aktualnyczas = 0, a potem wszystkie aktualne czasy i zastąpiłem samym millis()
    Kod działa dokładnie tak, jak miał.


    Skoro ten mechanizm służy do ‘huśtania’ zegarka, które z założenia wymaga raczej bezpośredniego nadzoru z ręcznym sterowaniem przyciskami, niemniej jednak możesz zastosowac 'automatyczne' wyjście z pętli (i z podprogramów), stosując funkcję warunkową ‘OR’ po osiągnięciu pewnej ilości wahnięc lub po upływie zadanego czasu, a nie tylko zmiennej ‘prog(x)’ kontrolowanej przyciskami . W praktyce stan tych zmiennych mógłby byc zależny też od wybranych parametrów, które mogą byc ustalone w programie ( jako 'const'), bądź ustawiane jako zmienne .
    Użyj troche konceptu i rozbuduj nieco program poza nic nie znaczących wyświetlanych na LCD oznajmień stanów, dublujących informacje odczytywane z diod. Dodaj jakieś chocby ‘prymitywne’ meniu połączone z ustawianiem w nim tych zmiennych.
    Wszystkie kombinacje prog(x) mogą byc zastąpine jedną zmienną numeryczną, zamiast kilku zm. logicznych przybierających wartosci tylko 1 albo 0.
    Do tego powinny wystarczyc te trzy przyciski i LCD, które już masz w układzie.
    W pamięci uControllera jest dostatecznie dużo nie wykorzystanego miejsca.

    Zawsze możesz pozostac przy już funkcjonującym programie..

    e marcus
  • Poziom 8  
    Dziękuję wszystkim za pomoc! Poniżej ostateczny kod. Dodano dwie diody migające naprzemiennie w trakcie pracy serwa (w takt interwałów), serwo 180 zastąpiono serwem ciągłym. Kod o dziwo działa - trzeba tylko kombinować z ustawieniem maksymalnego wychylenia w jedną i drugą stronę oraz wartością delty kąta. Efekt może przypominać silnik startujący na soft starcie ;)
    Kod: c
    Zaloguj się, aby zobaczyć kod