06-01-2019, 22:07
Dziękuję bardzo za miłe słowa
Wprowadziłem znów kilka zmian według wskazówek.
Częstotliwość występowania przerwań zewnętrznych zależy od wartości PWM podawanej na sterownik silników, a więc od prędkości obrotowej. Próbowałem na większych i mniejszych wartościach. Zauważyłem, że przy wartościach większych, występował jedynie błąd wynikający z pędu pojazdu. Silniki są hamowane natychmiastowo, jednak pęd zawsze pchnie trochę pojazd w stronę poruszania się. Przy mniejszych wartościach nic się nie zmieniało.
Miałem już wcześniej problem z przerwaniami zewnętrznymi (przerwania pojawiały się bez zmiany stanu na pinie 2), ale pomogło wykorzystanie innego pinu do przerwań zewnętrznych (zmiana D2 z D7).
Aktualnie problem błędnego odtwarzania trasy udało mi się wyeliminować zmianą pinów przerwań zewnętrznych miejscami między enkoderami.
Enkoder A był wpięty na pin 3, natomiast enkoder B na pin 7. Teraz wpięte są odwrotnie.
Pozostaje mi dojść do tego, z jakiego powodu podczas jazdy na wprost pojazd najpierw zaczyna kręcić prawym kołem, a po chwili (milisekundy) dopiero dołącza drugie koło. Podczas jazdy do tyłu problem ten nie występuje.
Próbowałem z nastawami regulatora PD, ale jeśli to byłoby przyczyną, to problem występował by także podczas jazdy do tyłu.
W funkcji stop() celowo zastosowałem delay(200), ponieważ wtedy silniki prawidłowo zatrzymują się natychmiastowo, a podczas wykonywania tej funkcji program nie musi wykonywać w tym czasie absolutnie nic innego. Stosuję uśpienie sterownika w celu oszczędzania energii, ale głównie z powodu późniejszego wystawienia odpowiednich stanów wyjść, w celu ustawienia jazdy w konkretnym kierunku i o odpowiedniej prędkości, po czym dopiero wybudzam sterownik. Dzięki temu silniki powinny startować dokładnie w tej samej chwili czasowej. Jednak podczas jazdy na wprost dzieje się inaczej i nie wiem co jest tego przyczyną.
Zastosowałem Watchdog'a, jednak proszę ocenić, czy zrobiłem to prawidłowo. Przyznaję się, że stosuję tę funkcję po raz pierwszy Podczas odtwarzania trasy (pętla, której wcześniej program nie chciał opuszczać) za każdym razem kasuję licznik WDG, ponieważ może to być długa trasa w danym kierunku, co może trwać nawet ponad 10 sekund.
Poniżej zamieszczam aktualny kod programu:
Wprowadziłem znów kilka zmian według wskazówek.
Częstotliwość występowania przerwań zewnętrznych zależy od wartości PWM podawanej na sterownik silników, a więc od prędkości obrotowej. Próbowałem na większych i mniejszych wartościach. Zauważyłem, że przy wartościach większych, występował jedynie błąd wynikający z pędu pojazdu. Silniki są hamowane natychmiastowo, jednak pęd zawsze pchnie trochę pojazd w stronę poruszania się. Przy mniejszych wartościach nic się nie zmieniało.
Miałem już wcześniej problem z przerwaniami zewnętrznymi (przerwania pojawiały się bez zmiany stanu na pinie 2), ale pomogło wykorzystanie innego pinu do przerwań zewnętrznych (zmiana D2 z D7).
Aktualnie problem błędnego odtwarzania trasy udało mi się wyeliminować zmianą pinów przerwań zewnętrznych miejscami między enkoderami.
Enkoder A był wpięty na pin 3, natomiast enkoder B na pin 7. Teraz wpięte są odwrotnie.
Pozostaje mi dojść do tego, z jakiego powodu podczas jazdy na wprost pojazd najpierw zaczyna kręcić prawym kołem, a po chwili (milisekundy) dopiero dołącza drugie koło. Podczas jazdy do tyłu problem ten nie występuje.
Próbowałem z nastawami regulatora PD, ale jeśli to byłoby przyczyną, to problem występował by także podczas jazdy do tyłu.
W funkcji stop() celowo zastosowałem delay(200), ponieważ wtedy silniki prawidłowo zatrzymują się natychmiastowo, a podczas wykonywania tej funkcji program nie musi wykonywać w tym czasie absolutnie nic innego. Stosuję uśpienie sterownika w celu oszczędzania energii, ale głównie z powodu późniejszego wystawienia odpowiednich stanów wyjść, w celu ustawienia jazdy w konkretnym kierunku i o odpowiedniej prędkości, po czym dopiero wybudzam sterownik. Dzięki temu silniki powinny startować dokładnie w tej samej chwili czasowej. Jednak podczas jazdy na wprost dzieje się inaczej i nie wiem co jest tego przyczyną.
Zastosowałem Watchdog'a, jednak proszę ocenić, czy zrobiłem to prawidłowo. Przyznaję się, że stosuję tę funkcję po raz pierwszy Podczas odtwarzania trasy (pętla, której wcześniej program nie chciał opuszczać) za każdym razem kasuję licznik WDG, ponieważ może to być długa trasa w danym kierunku, co może trwać nawet ponad 10 sekund.
Poniżej zamieszczam aktualny kod programu:
Kod:
#include <avr/wdt.h>
#define A_ENC 7 //enkoder silnika A
#define B_ENC 3 //enkoder silnika B
#define STDBY 4 //uśpienie sterownika silników
#define ALR 5 //sygnał alarmu
#define A_IN1 2 //zmiana kierunku obrotów silnika prawego
#define A_IN2 6 //
#define A_PWM 9 //wyjście PWM na silnik prawy
#define B_IN1 8 //zmiana kierunku obrotów silnika prawego
#define B_IN2 11 //
#define B_PWM 10 //wyjście PWM na silnik lewy
volatile unsigned int A_enc = 0; // impulsy enkodera silnika A
volatile unsigned int B_enc = 0; // impulsy enkodera silnika B
byte line = 0; //numer wiersza macierzy (krok wykonania ruchu powrotnego)
unsigned int macierz [126][4] = {}; //tablica danych trasy przejazdu
byte Speed1 = 120; //prędkość obrotów silników w trybie normalnym
byte Speed2 = 60; //prędkość obrotów silników w trybie nauki
byte Speed3 = 60; //prędkość obrotów silników w trybie powrotu
const int w_zad = 0; //wartość zadana
const int k_p = 4; //współczynnik wzmocnienia członu P regulatora
const int k_d = 1; //stała różniczkowania regulatora
volatile int p; //wartość wyjściowa członu typu P
volatile int d; //wartość wyjściowa członu typu D
volatile int u; //aktualny uchyb regulacji
volatile int u_p = 0; //poprzedni uchyb regulacji
volatile int out; // sygnał wyjściowy regulatora
volatile int diff = 0; //różnica wskazań enkoderów
volatile int x; // regulowana wartość PWM A
volatile int y; // regulowana wartość PWM B
boolean Forward = 0; //znacznik jazdy do przodu
boolean Backward = 0; //znacznik jazdy do tyłu
boolean Left = 0; //znacznik jazdy w lewo
boolean Right = 0; //znacznik jazdy w prawo
char state = 0; //odczyt z bluetooth(SerialPort1)
int meas_sum = 0; // suma wartości odczytanych z ADC
byte meas_count = 0; //licznik próbek pomiaru napięcia
int meas_av = 0; // uśredniona wartość napięcia
float volt; // obliczona wartość napięcia
void setup() {
wdt_enable(WDTO_1S);
pinMode(A_ENC, INPUT_PULLUP); //kofiguracja wejść i wyjść
pinMode(B_ENC, INPUT_PULLUP);
pinMode(ALR, OUTPUT);
pinMode(STDBY, OUTPUT);
pinMode(A_IN1, OUTPUT);
pinMode(A_IN2, OUTPUT);
pinMode(A_PWM, OUTPUT);
pinMode(B_IN1, OUTPUT);
pinMode(B_IN2, OUTPUT);
pinMode(B_PWM, OUTPUT);
Serial1.begin(9600); //włączenie transmisji szeregowej i ustawienie prędkości
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(A_ENC), COUNT_A, CHANGE); // włączenie przerwań zewnętrznych
attachInterrupt(digitalPinToInterrupt(B_ENC), COUNT_B, CHANGE);
wdt_reset();
}
void loop() {
if(meas_count == 10){
meas_av = meas_sum/11;
meas_count = 0;
meas_sum = 0;
}
meas_sum += analogRead(A0); // pomiar napięcia baterii
meas_count ++;
volt = meas_av * (0.0097752); // przeliczenie na wartość dziesiętną
Serial1.print(" "); // wysłanie wartości przez bluetooth
Serial1.print(volt); //
/*while (volt < 5.0){ //kontrola napięcia
digitalWrite(ALR, !digitalRead(ALR)); // napięcie poniżej 5V włącza alarm
Stop();
delay(500);
wdt_reset();
}*/
digitalWrite(ALR, LOW); // wyłączenie alarmu
if (state == '0') { // czyszczenie odczytów z enkoderów
clearenc();
}
if (Serial1.available()) { // odczyt danych z bluetooth
state = Serial1.read(); // przypisanie odebranej danej do zmiennej
}
switch (state) {
case '0': //stop
Stop();
clearenc();
break;
case '1': //
forward(Speed1); //do przodu
break;
case '2':
backward(Speed1); //do tyłu
break;
case '3':
left(Speed1); //w lewo
break;
case '4':
right(Speed1); //w prawo
break;
case '5': // nauka
clearenc();
break;
case '6': // powrót
for (char i = 125; i >= 0; i--) {
clearenc();
if (macierz[i][0] > 0) {
while (A_enc < macierz[i][0]) { //do tyłu
backward(Speed3);
wdt_reset();
}
Stop();
macierz[i][0] = 0;
}
else if (macierz[i][1] > 0) { // do przodu
while (A_enc < macierz[i][1]) {
forward(Speed3);
wdt_reset();
}
Stop();
macierz[i][1] = 0;
}
else if (macierz[i][2] > 0) { // w prawo
while (A_enc < macierz[i][2]) {
right(Speed3);
wdt_reset();
}
Stop();
macierz[i][2] = 0;
}
else if (macierz[i][3] > 0) { // w lewo
while (A_enc < macierz[i][3]) {
left(Speed3);
wdt_reset();
}
Stop();
macierz[i][3] = 0;
}
}
line = 0;
break;
case '7': //do przodu w trybie nauki
forward(Speed2);
Forward = 1;
break;
case '8': //do tyłu w trybie nauki
backward(Speed2);
Backward = 1;
break;
case '9': //w lewo w trybie nauki
left(Speed2);
Left = 1;
break;
case 'A': //w prawo w trybie nauki
right(Speed2);
Right = 1;
break;
case 'B':
Stop(); // zatrzymanie w trybie nauki + zapamiętanie przejechanej trasy
if (Forward == 1) { //zapisanie drogi do przodu
macierz [line] [0] = A_enc;
}
else if (Backward == 1) { //zapisanie drogi do tyłu
macierz [line] [1] = A_enc;
}
else if (Left == 1) { //zapisanie obrotu w lewo
macierz [line] [2] = A_enc;
}
else if (Right == 1) { //zapisanie obrotu w prawo
macierz [line] [3] = A_enc;
}
line ++;
Forward = 0;
Backward = 0;
Left = 0;
Right = 0;
clearenc();
break;
}
wdt_reset();
}
void COUNT_A() { //zliczanie impulsów ennkodera silnika A
A_enc++;
}
void COUNT_B() { //zliczanie impulsów ennkodera silnika B
B_enc++;
}
void forward(byte Speed) { //funkcja jazdy do przodu + synchronizacja prędkości obrotowej silników
diff = A_enc - B_enc;
u = w_zad - diff;
p = k_p * u;
d = k_d * (u_p - u);
out = p + d;
u_p = u;
x = Speed - out;
y = Speed + out;
if(x > 255){
x = 255;
}else if(x < 35){
x = 0;
}
if(y > 255){
y = 255;
}else if(y < 35){
y = 0;
}
digitalWrite(A_IN1, HIGH);
digitalWrite(A_IN2, LOW);
digitalWrite(B_IN1, LOW);
digitalWrite(B_IN2, HIGH);
analogWrite(B_PWM, x);
analogWrite(A_PWM, y);
digitalWrite(STDBY, HIGH);
}
void backward(byte Speed) { //funkcja jazdy do tyłu + synchronizacja prędkości obrotowej silników
diff = A_enc - B_enc;
u = w_zad - diff;
p = k_p * u;
d = k_d * (u_p - u);
out = p + d;
u_p = u;
x = Speed - out;
y = Speed + out;
if(x > 255){
x = 255;
}else if(x < 35){
x = 0;
}
if(y > 255){
y = 255;
}else if(y < 35){
y = 0;
}
digitalWrite(A_IN1, LOW);
digitalWrite(A_IN2, HIGH);
digitalWrite(B_IN1, HIGH);
digitalWrite(B_IN2, LOW);
analogWrite(B_PWM, x);
analogWrite(A_PWM, y);
digitalWrite(STDBY, HIGH);
}
void left(byte Speed) { //funkcja jazdy w lewo + synchronizacja prędkości obrotowej silników
diff = A_enc - B_enc;
u = w_zad - diff;
p = k_p * u;
d = k_d * (u_p - u);
out = p + d;
u_p = u;
x = Speed - out;
y = Speed + out;
if(x > 255){
x = 255;
}else if(x < 35){
x = 0;
}
if(y > 255){
y = 255;
}else if(y < 35){
y = 0;
}
digitalWrite(A_IN1, HIGH);
digitalWrite(A_IN2, LOW);
digitalWrite(B_IN1, HIGH);
digitalWrite(B_IN2, LOW);
analogWrite(B_PWM, x);
analogWrite(A_PWM, y);
digitalWrite(STDBY, HIGH);
}
void right(byte Speed) { //funkcja jazdy w prawo + synchronizacja prędkości obrotowej silników
diff = A_enc - B_enc;
u = w_zad - diff;
p = k_p * u;
d = k_d * (u_p - u);
out = p + d;
u_p = u;
x = Speed - out;
y = Speed + out;
if(x > 255){
x = 255;
}else if(x < 35){
x = 0;
}
if(y > 255){
y = 255;
}else if(y < 35){
y = 0;
}
digitalWrite(A_IN1, LOW);
digitalWrite(A_IN2, HIGH);
digitalWrite(B_IN1, LOW);
digitalWrite(B_IN2, HIGH);
analogWrite(B_PWM, x);
analogWrite(A_PWM, y);
digitalWrite(STDBY, HIGH);
}
void Stop() {
digitalWrite(A_IN1, HIGH); // stop
digitalWrite(A_IN2, HIGH);
digitalWrite(B_IN1, HIGH);
digitalWrite(B_IN2, HIGH);
analogWrite(A_PWM, 0);
analogWrite(B_PWM, 0);
delay(200);
digitalWrite(STDBY, LOW);
}
void clearenc() { //funkcja czyszczenia odczytów z enkoderów
A_enc = 0;
B_enc = 0;
}