Skocz do zawartości

Pomocna odpowiedź

Witam Serdecznie.

Postanowiłem wydrukować sobie robota-kota. Podłączyłem czujnik odległości i  działa. Poprzez Sterownik 16 Serw 12bitowy podłączyłem również moje 11 serw które są w tym projekcie. Całość udało mi sie zsynchronizować do momentu w którym w zależności od odległości przeszkody kot przyjmuje odpowiednią pozycje i z tym problemu nie mam.  Aleeeeee....  Utknąłem na etapie poruszania się. Otóż nie przychodzi mi do głowy konstrukcja pętli "for" która pozwoli mi jednocześnie poruszać dwiema kończynami w przeciwnym kierunku jednocześnie.  Piszę o dwóch kończynach bo jeśli uda mi się zrozumieć gdzie się zatrzymałem to ruszę i z czterema kończynami.   Chciałbym w przyszłości opublikować projekt na Forbocie z całościa kodu, ale muszę być pewny, że dla kogoś będzie on przydatny i przede wszystkim działający.   
Moje serwa w kończynach nazwałem:

-lewyBark,

-lewyLokiec,

-prawyBark,

-prawyLokiec.

Gdy tworze pętle dla lewej łapy poruszającej się w przód,  pętla od prawej stoi nieruchoma czekając na swoją kolej i tak na zmianę.  Jestem prawie pewien, że rozwiązanie mojego problemu jest bardziej prozaiczne niż mi się wydaje, a szukam zbyt głęboko. I tak od kilku dni 🙂 

Pomożecie? Jakieś pomysły? 

 

Kot.jpg

Udostępnij ten post


Link to post
Share on other sites
2 godziny temu, Bemol napisał:

Chciałbym w przyszłości opublikować projekt na Forbocie z całościa kodu

Bardzo chwalebny zamiar, ale...

 

2 godziny temu, Bemol napisał:

Jakieś pomysły? 

Np. taki pomysł, żebyś na razie pokazał to co masz zrobione, to ktoś Ci powie jak to poprawić.

Wyobraź sobie, że gotujesz zupę i stwierdzasz że jest niesmaczna. Czy nie wiedząc, co tam do tej zupy nawrzucałeś ktoś jest w stanie Ci powiedzieć czego tam jest za dużo czy za mało?

Na razie powiem Ci tak: wyobraź sobie, że nie masz prawa w programie użyć żadnej instrukcji pętli (for, while i tak dalej). Spróbuj zrealizować ruch pojedynczej kończyny...

A tak w ogóle... @Treker - da się to przenieść do oddzielnego wątku?

Udostępnij ten post


Link to post
Share on other sites
1 godzinę temu, ethanak napisał:

A tak w ogóle... @Treker - da się to przenieść do oddzielnego wątku?

Gotowe 🙂

Udostępnij ten post


Link to post
Share on other sites

Kod który przedstawiam nie zawiera w tym momencie tylko działajacego HCSR04 który jak pisałem wcześniej wywołuje poszczególne pozycje kota pomiędzy "stac", "kucaj", "siad", i "leżec".

Staram się zachować przejrzystość w kodzie dlatego nie usu-

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm=Adafruit_PWMServoDriver();
int i=0;
int poz = 350;
#define MIN 150
#define MAX 500
#define MED 350
const int SERVO0 = 0; 		 // Szyja
const int SERVO1 = 1;		//Kolano P
const int SERVO2 = 2;		//Udo P
const int SERVO3 = 3;
const int SERVO4 = 4;
const int SERVO5 = 5;
const int SERVO6 = 6;		//Bark P
const int SERVO7 = 7;		//Lokiec P
const int SERVO8 = 8;		//Bark L
const int SERVO9 = 9;		//Lokiec L
const int SERVO10 = 10;		//Udo L	
const int SERVO11 = 11;		//Kolano L
const int SERVO12 = 12;	
const int SERVO13 = 13;
const int SERVO14 = 14;		//Szyja
const int SERVO15 = 15;		//Ogon
int pos;

#define Trig 6		// do HCSR04
#define Echo 5		// 

void setup() 
{
   pwm.begin();			//inicjalizowanie sygnału PWM
  pwm.setPWMFreq(60);  	//częstotliwość pracy PCA9685


  Serial.begin(9600);		//do testowania HCSR04
pinMode(Trig, OUTPUT);
pinMode(Echo, INPUT);

pinMode(7, INPUT_PULLUP);	//Przycisk na plecach nr 1
pinMode(8, INPUT_PULLUP);	//Przycisk nr 2

}

void loop() 
{
lezec();		//
delay(10);
}
void stac()
{
    pwm.setPWM(SERVO0, 0, MED);     //     Szyja
    pwm.setPWM(SERVO1, 0, MIN);     //     P kolano
    pwm.setPWM(SERVO2, 0, MED);     //     P Udo
    pwm.setPWM(SERVO3, 0, MED);     //
    pwm.setPWM(SERVO4, 0, MED);     //
    pwm.setPWM(SERVO5, 0, MED);     //
    pwm.setPWM(SERVO6, 0, MED);     //      P Bark
    pwm.setPWM(SERVO7, 0, MAX);     //      P Lokiec
    pwm.setPWM(SERVO8, 0, MED);     //      L BArk
    pwm.setPWM(SERVO9, 0, MIN);     //      L Lokiec
    pwm.setPWM(SERVO10, 0, MED);    //      L Udo
    pwm.setPWM(SERVO11, 0, MAX);    //      L Kolano
    pwm.setPWM(SERVO12, 0, MED);    //       
    pwm.setPWM(SERVO13, 0, MED);    //       
    pwm.setPWM(SERVO14, 0, MED);    //     Szyja  
    pwm.setPWM(SERVO15, 0, MED);    //     Ogon  
}
void kucaj()
{
    pwm.setPWM(SERVO0, 0, MED);     //     Szyja
    pwm.setPWM(SERVO1, 0, MED);     //     P kolano   G  200
    pwm.setPWM(SERVO2, 0, 250);     //     P Udo   D   100
    pwm.setPWM(SERVO3, 0, MED);     //
    pwm.setPWM(SERVO4, 0, MED);     //
    pwm.setPWM(SERVO5, 0, MED);     //
    pwm.setPWM(SERVO6, 0, 250);     //      P Bark
    pwm.setPWM(SERVO7, 0, 250+20);     //      P Lokiec
    pwm.setPWM(SERVO8, 0, 450);     //      L BArk
    pwm.setPWM(SERVO9, 0, 400);     //      L Lokiec
    pwm.setPWM(SERVO10, 0, 250+10);    //      L Udo
    pwm.setPWM(SERVO11, 0, 250);    //      L Kolano
    pwm.setPWM(SERVO12, 0, MED);    //       
    pwm.setPWM(SERVO13, 0, MED);    //       
    pwm.setPWM(SERVO14, 0, MED);    //     Szyja  
    pwm.setPWM(SERVO15, 0, MED);    //     Ogon  
}
void siad()
{ 
    pwm.setPWM(SERVO0, 0, 200);     //     Glowa
    pwm.setPWM(SERVO1, 0, 450);     //     P kolano
    pwm.setPWM(SERVO2, 0, MED);     //     P Udo
    pwm.setPWM(SERVO3, 0, MED);     //
    pwm.setPWM(SERVO4, 0, MED);     //
    pwm.setPWM(SERVO5, 0, MED);     //
    pwm.setPWM(SERVO6, 0, 250);     //      P Bark
    pwm.setPWM(SERVO7, 0, MED);     //      P Lokiec
    pwm.setPWM(SERVO8, 0, 450);     //      L BArk
    pwm.setPWM(SERVO9, 0, 300);     //      L Lokiec
    pwm.setPWM(SERVO10, 0, MED);    //      L Udo
    pwm.setPWM(SERVO11, 0, 150);    //      L Kolano
    pwm.setPWM(SERVO12, 0, MED);    //       
    pwm.setPWM(SERVO13, 0, MED);    //       
   pwm.setPWM(SERVO14, 0, MED);    //       
    pwm.setPWM(SERVO15, 0, 200);    //     Ogon  

        for(i=250; i<=500; i=i+2)			//pętla do poruszania szyją od lewej do prawej
      {
         pwm.setPWM(SERVO14, 0, i); //Szyja   MIN to P
         delay(50);  
      }
    for(i=500; i>=250; i=i-2)
      {
         pwm.setPWM(SERVO14, 0, i); // Szyja  MAX to L
         delay(50); 
      }

}
void lezec()
{
    pwm.setPWM(SERVO0, 0, MED);     //     Szyja
    pwm.setPWM(SERVO1, 0, 450);     //     P kolano  ---
    pwm.setPWM(SERVO2, 0, 250);     //     P Udo
    pwm.setPWM(SERVO3, 0, MED);     //
    pwm.setPWM(SERVO4, 0, MED);     //
    pwm.setPWM(SERVO5, 0, MED);     //
    pwm.setPWM(SERVO6, 0, MED);     //      P Bark
    pwm.setPWM(SERVO7, 0, 200);     //      P Lokiec ---
    pwm.setPWM(SERVO8, 0, MED);     //      L BArk
    pwm.setPWM(SERVO9, 0, 450);     //      L Lokiec ---
    pwm.setPWM(SERVO10, 0, 250);    //      L Udo
    pwm.setPWM(SERVO11, 0, 150);    //      L Kolano ---
    pwm.setPWM(SERVO12, 0, MED);    //       
    pwm.setPWM(SERVO13, 0, MED);    //       
    pwm.setPWM(SERVO14, 0, MED);    //     Szyja  
    pwm.setPWM(SERVO15, 0, 190);    //     Ogon  
}

void chod()
{
   for(i=300; i<=400; i=i+1)
    {
        pwm.setPWM(SERVO8, 0, i);     //      L BArk    
        pwm.setPWM(SERVO6, 0, i);     //      P Bark
        pwm.setPWM(SERVO9, 0, 200);     //      L Lokiec
        pwm.setPWM(SERVO7, 0, 200);     //      P Lokiec ---
       delay(20);      
    }
 for(i=400; i>=300; i=i-1)
    {
        pwm.setPWM(SERVO8, 0, i);     //      L BArk    
        pwm.setPWM(SERVO6, 0, i);     //      P Bark
        pwm.setPWM(SERVO9, 0, 400);     //      L Lokiec
        pwm.setPWM(SERVO7, 0, 400);     //      P Lokiec ---
       delay(20);
    }


   
}

wałem jeszcze w wersji roboczej obiektów serw nr. 3,4,5,12 i 13. na sam koniec jesli beda niepotrzebne wylecą. 

Udostępnij ten post


Link to post
Share on other sites

Przede wszystkim zmień te definicje, bo program jest absolutnie nieczytelny. Czy za miesiąc będziesz pamiętał że SERVO11 to (no, odpowiedz bez zastanowienia, udo czy kolano?) Nie byłoby lepiej cos takiego:
 

#define SERVO_KOLANO_P 1
#define SERVO_UDO_P 2

i dalej w setup:
 

pwm.setPWM(SERVO_KOLANO_P, MED);
pwm.setPWM(SERVO_UDO_P, 250); // czymkolwiek by było 250

Dalej: tak jak mówiłem, na dzień dobry nie możesz mieć żadnych pętli wewnątrz programu z wyjątkiem głównej loop. Wyobraź sobie, że w tej głównej pętli wywołujemy funkcje przesuwające każde serwo o jakiś kawałek o ile jest taka potrzeba, aby osiągnąć jakieś docelowe położenie, i ta funkcja wywoływana jest okresowo np. co 20 msec. Wtedy pozostaje Ci tylko ustalić czasy i docelowe położenia serw, a one w jakiś magiczny sposób same się poruszą...

Czyli w skrócie funkcja loop będzie wyglądać tak:

void loop() {
	if (trzeba_coś_zmienić()) {
  		ustaw_docelowe_pozycje();
	}
    porusz_serwami();
    delay(20);
}

A funkcja poruszająca serwami to dla każdego serwa:
 

if (serwo_nie_jest_na_pozycji()) {
   porusz_serwem_kawałek();// gdzie ten kawałek zależny jest od zadanej prędkości poruszania się serwa
}

Czy coś Ci to mówi?

Udostępnij ten post


Link to post
Share on other sites

W drugim wariacie myślowym przewidywałem właśnie coś podobnego. Konstrukcja, którą przedstawiłeś jest bardzo podobna do tej z kursu arduino o sterowaniu serwami. Teraz skłonie się ku tej metodzie.    
 

Z tym nazewnictwem masz rację, z początku chciałem to w ten sposób rozwiązać, jednakże wiedziałem ile lini kodu będę miał do pisania więc korzystając ze ściąg nie gubię się w nazewnictwach.  Czy taki sposób pisania zadziała wg. Ciebie? 

void loop() 
{
  mojaFunkcja();
}
  
  
void mojafunkcja();
{
	if (trzeba_coś_zmienić()) {
  		ustaw_docelowe_pozycje();
	}
    porusz_serwami();
    delay(20);
}

 

I mam jeszcze jedno ważne dla mnie pytanie. 

Jeśli mam bardzo dużo lini kodu, czy mogę jego części typu "void stac()" przenosić do "nowych zakładek" i stamtąd je wywoływać  typu "stac()" ?. Przyznam się, że testowałem takie rozwiąznie w celu zwiększenia przejrzystości i odnajdowania się i nie za każdym razem chciało mi to zadziałać. Czy wiadomo coś o jakichś ograniczeniach z tym związanych.?

Udostępnij ten post


Link to post
Share on other sites

oczywiście @ethanak pomogłeś i wielkie dzięki 🙂    biorę się za poprawianie wszystkiego.   Troszkę czasu na pewno minie zanim w pełni pokaże projekt i jeszcze pewnie kilka pytań będzie więc prosiłbym @Treker nie zamykać wątku jeszcze przez jakiś czas. 

Udostępnij ten post


Link to post
Share on other sites
23 minuty temu, Bemol napisał:

Czy taki sposób pisania zadziała wg. Ciebie? 

Owszem, zadziała. Tyle że lepiej od razu przyzwyczaić się do normalnego C++ bez Arduinowych ułatwień dla leni, czyli:

void mojafunkcja(void); // tu średnik jest potrzebny

void loop() 
{
  mojafunkcja();
}
  
  
void mojafunkcja(void) // a tu oczywiście nie
{
	if (trzeba_coś_zmienić()) {
  		ustaw_docelowe_pozycje();
	}
    porusz_serwami();
    delay(20);
}

Chodzi o deklaracje wyprzedzające - czyli funkcja loop musi wiedzieć jakiego typu jest mojafunkcja().

 

27 minut temu, Bemol napisał:

czy mogę jego części typu "void stac()" przenosić do "nowych zakładek" i stamtąd je wywoływać  typu "stac()" ?.

Oczywiście, pod warunkiem że będziesz je poprawnie deklarować. Najlepiej zrobić sobie plik np. "common.h" z zawartością:

// żeby drugi raz się przypadkowo nie włączył
#ifndef COMMON_H
#define COMMOH_H
  
//i tu deklaracje funkcji
  
extern void stac(void);
extern void siad(void);
extern void idz(int tempo);

// a tu zmienne porozrzucane po różnych plikach

extern int pozycje_serw[16];


//i co tam sobie jeszcze wymyślisz
#endif

Wtedy w każdym pliku .ino (głównym) i .cpp (z funkcjami) musisz tylko wpisać włączenie tego pliku i wszystkie funkcje będą się widzieć nawzajem. Przykładowo:

w pliku głównym wstawiasz coś w stylu:
 

#include "common.h"

int pozycje_serw[16];

a w pliku funkcje.cpp:

#include <Arduino.h> // tym razem samo się nie wstawi
#include "common.h"
  
void stac(void)
{
   // tu ciało funkcji
}

W ten sposób w pliku .ino możesz wywoływać funkcję "stac", a w funkcji "stac" możesz odwołać się do tablicy pozycje_serw.

Dobrze również wszelkie definicje (np numerki serw) umieścić właśnie w common.h - po to, aby wszyskie funkcje miały dostęp do tych informacji (o ile jest to pottrzebne).

 

Udostępnij ten post


Link to post
Share on other sites

"Przyzwyczaić do normalnego C++... bez ułatwień dla leni" i używać externów/dawać sobie dostęp do modyfikacji zmiennych z dowolnego miejsca bez jakiejkolwiek kontroli? Według mnie to nie jest poprawne rozwiązanie i złe podejście na start.

Udostępnij ten post


Link to post
Share on other sites

Zaproponuj lepsze rozwiązanie.

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Czyli według Ciebie externowanie zmiennych pomiędzy wszystkimi plikami jak to pokazałeś dla tablicy "pozycje_serw" to "normalne C++" i nie jest to "ułatwienie dla leni"?
Lepsze rozwiązanie - nie rozumiem do końca sedna problemu - dlaczego zamiast robić to od początku poprawnie z plikami h i c/cpp do nich interfejsy dostępowe, zapewnić jakąkolwiek enkapsulację sugerujesz z góry żeby to wszystko olać i dawaj globale na cały program, a potem kilkanaście rzeczy na raz z wielu miejsc będą modyfikować to samo. To się u podstaw gryzie z ideą OOP i jeszcze określasz to jako "normalne C++".

@Bemol Zapoznaj się np z tym: https://www.learncpp.com/cpp-tutorial/4-2a-why-global-variables-are-evil/

 

Edytowano przez dambo
dodanie linku do materiałów

Udostępnij ten post


Link to post
Share on other sites

Nie określiłbym siebie leniem. Zauważyłem tylko, że przenoszenie części kodu do innych zakładek ułatwia mi orientacje w programie i odszukiwanie szybciej miejsca w którym coś chce poprawić. Być może jako laik nabieram niepoprawnych nawyków, ale moje pytanie brzmiało czy mogę takie praktyki stosować i czy sa ku temu ograniczenia. Odpowiedź otrzymałem więc jest ok. 

Udostępnij ten post


Link to post
Share on other sites

@damboTo "ułatwienie dla leni"  to po prostu Arduinowy dodatkowy preprocesor (mam nadzieję że wiesz o co chodzi) i nie ma to nic wspólnego z poprawnością czy niepoprawnością danego rozwiązania.

Zawsze moje rozwiązanie będzie lepsze od jednego wielgaśnego pliku .ino, w którym i tak wszyscy do wszystkiego mają dostęp. Nauka ma postępować etapami, czyli najpierw odzwyczajenie się od nawyków automatycznego wstawiania deklaracji funkcji, voidów czy inkludowania Arduino.h (za czym idzie uświadomienie sobie istnienia deklaracji) - potem możemy bawić się w enkapsulację, interfejsy, pięćset plików nagłówkowych i inne mądre rzeczy które robią wyłącznie Wielcy Mistrzowie C++ z kolegą @dambo na czele 🙂

3 minuty temu, Bemol napisał:

Nie określiłbym siebie leniem.

Problem w tym, że autorzy Arduinowego preprocesora zakładają, że każdy użytkownik to leń i jełop co nie potrafi sobie - za przeproszeniem - tyłka podetrzeć i trzeba zrobić Wielki Wspaniały Automat który zrobi to za użytkownika. Na szczęście można z owych ułatwień nie korzystać, co każdemu z pewnością może wyjść na zdrowie.

Udostępnij ten post


Link to post
Share on other sites

Ładnie umiesz dopowiadać po tym jak ktoś zwróci uwagę - i trzeba było tak od początku napisać. I uświadomić kolegę, że to co proponujesz to tylko jeden z kroków w nauce i że to nie jest idealne rozwiązanie, ma swoje wady, potem się tego nie stosuje itp - zamiast nazywać to "normalnym C++". Stąd też mój link do poczytania.

Wstawki typu "pięćset plików nagłówkowych", "Wielcy mistrzowie z kimkolwiek na czele" sobie możesz darować.

 

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...