Problema automodello servosterzo+sensori sonar

Buongiorno,
sto svolgendo una tesi in cui ho preso un automodello radiocomandato, ho sostituito il computer presente all'interno con un arudino nano e ho aggiunto varie altre componenti (driver di potenza,mocoder,modulo bluetooth RN-42). Il passo successivo è stato quello di integrare al progetto due sensori sonar HC-SR04 per determinare e mantenere una certa distanza tra due automodelli. Problema: una volta collegati i due sensori sonar all'alimentazione di arduino il servo inizia a gira da una parte all'altra senza sosta e i sensori non determinano la distanza. Ho collegato inizialmente fili del servo e quelli dei sonar alla stessa alimentazione (5V) e alla stessa GND, poi ho provato ad usare un arduino esterno come alimentatore per i due sensori sonar ma senza risultato positivo. Vi allego il codice perchè a questo punto non capisco se sia un problema hardware o software.

#include <SoftwareSerial.h>
#include "constants1.h"
#include "Tweakly.h"

//Variabili sistema di controllo servo
float servomotorvalue, degree, mydegree, servoerror;  //K = guadagno controllore proporzionale servo
float K=8;

float offset;

//Filtro a media mobile servo
float servofilter[servofsize];
int i = 0;
float servoavg = 0.0;  //average

volatile unsigned long count;
unsigned long oldcount = 0;
unsigned long t1, dt;
float velocity;             //velocita' encoder

unsigned long iter = 1;

void isr() {
  count++;
}

//Filtro a media mobile mocoder
int const motorfsize = 5;
float motorfilter[motorfsize];
int i1 = 0;

//Variabili controllore PI
float myv, motorerror, motorvalue;
float Kp = 10, Ki = 1, integer = 0.0;
float m = pow(10, -3);

//Filtro a media mobile distanza
int const dsize = 10;
float distancefilter[dsize];
int i2 = 0;

//Variabili sistema di controllo distanza
float myd = 10; 
float distance_error1, distance_error2;
float Kd=10;

void setup() {
  Serial.begin(9600);
  delay(1000);
  Serial.println("Started");
  pinMode(pinRx, INPUT);
  pinMode(pinTx, OUTPUT);
  pinMode(servopwm, OUTPUT);
  pinMode(servodir, OUTPUT);
  pinMode(potref, INPUT);
  pinMode(motorpwm, OUTPUT);
  pinMode(motordir, OUTPUT);
  pinMode(interruptPin, INPUT);
  bluetooth.begin(115200);
  delay(100);
  bluetooth.print("$$$"); //
  delay(100);
  bluetooth.println("U,9600,N"); //
  delay(100);
  bluetooth.end();
  delay(100);
  bluetooth.begin(9600);
  delay(100);
  bluetooth.println("---<cr>"); //
  attachInterrupt(digitalPinToInterrupt(interruptPin), isr, RISING);
  sonarAttach(pin_echo1, pin_trigger1, getSonarValue1, SONAR_CENTIMETERS);
  sonarAttach(pin_echo2, pin_trigger2, getSonarValue2, SONAR_CENTIMETERS);
}

void loop() {
  TweaklyRun();
  ReadBT();
  computeVelocity();
  ControlloServo();
  ControlloMotore();
  ControlloDistanze();
}

void ControlloServo() {
  degree = ((ServoAverage() - MinAnalogRef) * (MaxSteeringAngle - MinSteeringAngle) / (MaxAnalogRef - MinAnalogRef) + MinSteeringAngle);
  degree += Offset();
  servoerror = mydegree - degree;
  servomotorvalue = abs((K * servoerror));
  if (servomotorvalue < minPwm) {
    analogWrite(servopwm, 0);
  } else {
    if (degree <= mydegree) {
      digitalWrite(servodir, HIGH);  //gira a destra
      analogWrite(servopwm, servomotorvalue);
    } else {
      digitalWrite(servodir, LOW);  //gira a sinistra
      analogWrite(servopwm, servomotorvalue);
    }
  }
}

float ServoAverage() {  //media mobile
  int s = analogRead(potref);
  servofilter[i] = s;
  if (i < (servofsize - 1)) i++;
  else i = 0;
  servoavg = 0.0;
  for (int j = 0; j < servofsize; j++) {
    servoavg += (float)servofilter[j];
  }
  return servoavg = servoavg / (float)(servofsize);
}

float Offset() {
  return offset = motorvalue * max_offset / maxPwm;
}

void ReadBT() {

  while (bluetooth.available() > 0) {
    String command = bluetooth.readString();
    if (command != "") {
      char op = command.c_str()[0];
      command.replace(String(op), "");
      float value = command.toFloat();

      Serial.println("op = " + String(op) + " value = " + String(value));
      switch (op) {
        case 's':
          mydegree = value;
          break;

        case 'r':
          digitalWrite(motordir, LOW);
          myv = value;
          integer = 0;
          break;

        case 'f':
          digitalWrite(motordir, HIGH);
          myv = value;
          integer = 0;
          break;

        default: break;
      }
    }
  }
}

void computeVelocity() {
  dt = millis() - t1;
  if (dt >= interval) {
    noInterrupts();
    float ve = (float)(count - oldcount) / (float)(interval);
    velocity = ve * gr * wheelcircumference;  //cm/s
    oldcount = count;
    t1 = millis();
    interrupts();
  }
}

void getSonarValue1(unsigned long centimeters) {
  //Print sonar value
  distance1 = centimeters;
}

void getSonarValue2(unsigned long centimeters) {
  //Print sonar value
  noInterrupts();
  distance2 = centimeters;
  interrupts();
}

float MotorAverage() {  //Media mobile velocita'
  motorfilter[i1] = velocity;
  if (i1 < (motorfsize - 1)) {
    i1++;
  } else {
    i1 = 0;
  }
  float motoravg = 0.0;
  for (int j1 = 0; j1 < motorfsize; j1++) {
    motoravg += motorfilter[j1];
  }
  motoravg = motoravg / (float)(motorfsize); 
  return motoravg;
}

void ControlloMotore() {  //Controllo Proporzionale Integrale
  float motAvg = MotorAverage();
  motorerror = myv - motAvg;
  motorvalue = Kp * motorerror + integer;
  integer = Ki * (integer + (motorerror * (interval * m)));
  motorvalue = constrain(motorvalue, 0, maxPwm);
  analogWrite(motorpwm, (uint8_t)motorvalue);
}

void ControlloDistanze() {
  Serial.println(distance1);
  Serial.println(distance2);
}

Non so se sia questo il tuo problema, ma io da anni ho abbandonato gli SR04 perché assolutamente inaffidabili, a volte si "bloccavano" addirittura e non davano più valori diversi da zero! Prendi gli SFR05, costano qualche cent in più ma sono mooolto più affidabili.

PS: vedi intanto QUESTA libreria che scrissi a suo tempo proprio per cercare di ovviare al problema degli SR04, magari con questa ti funziona (la libreria comunque gestisce anche gli SRF05)...

Intanto grazie per la risposta, proverò ad usarla. Ma potrebbe essere un problema dovuto all'alimentazione? Perchè arduino nano può erogare fino a 40mA per pin, i due sensori ne richiedono 15mA a testa, in più nello stesso pin è collegata l'alimentazione del servo (del quale non riesco a trovare la corrente assorbita necessaria). Ma come ho specificato sopra anche utilizzando un altro arduino cone alimentatore il problema persiste...

E' anche che non ho idea di cosa siano le include "constants1.h" e "Tweakly.h" e quindi anche come funzioni "sonarAttach()". Perché non inizi con uno sketch semplice per verificare il funzionamento dei sensori ad ultrasuoni (anche lasciando collegato tutto il resto), senza cose strane?

https://github.com/filoconnesso/Tweakly
questa è la libreria che uso per i sonar.

//Pins and objects
const int servopwm = 11,  servodir = 12;
const int pinRx = A1, pinTx = A2;
const int potref = A0;
const int motorpwm = 9, motordir = 8;
const int interruptPin = 3;

//Sonar pins
const int pin_trigger1 = 7;
const int pin_echo1 = 6;
const int pin_trigger2 = 5;
const int pin_echo2 = 4;

const int max_distance = 10; //distanza massima misurabile

//Distances

int distance1, distance2;

SoftwareSerial bluetooth(pinRx, pinTx);
const long interval = 10;  //milliseconds

const float MaxSteeringAngle = 20;   //angolo max sterzata
const float MinSteeringAngle = -20;  //angolo min sterzata
const float MinAnalogRef = 393;      //valore min potenziometro
const float MaxAnalogRef = 614;      //valore max potenziometro
const uint8_t minPwm = 15, maxPwm = 255;

const float max_offset = 6.00;  //offset angolo

const int servofsize = 30;  //grandezza vettore filtro


const float wheelray = 1.10;  //cm, raggio = 11mm
const float wheelcircumference = (2 * 3.14 * wheelray);

const float Diffteeth = 44.00;
const float Mocoteeth = 33.00;
const float gr = 1.333;  //gears ratio (diffteeh/mocoteeth)

questo invece è il contenuto del file constans1, cioè contiene tutte le costanti. Ho già provato ad utilizzare solo i 2 sonar con un semplice programma e funzionano entrambi correttamente. Il fatto strano è che i due sonar calcolano la distanza anche con il programma intero ma il servo sterzo "impazzisce" e giro da una parte all'altra.

Credo di aver capito.
Tu con quella libreria Tweakly (per me è una complicazione inutile, ma tant'è...) agganci due ISR, getSonarValue1 e getSonarValue2 dentro alle quali aggiorni le variabili distance1 e distance2: quando in una ISR modifichi una variabile, questa deve essere definite "volatile":

volatile int distance1, distance2;

Prova a correggere questa cosa e vedi se funziona.

PS: le due ISR sono leggermente diverse:

void getSonarValue1(unsigned long centimeters) {
  //Print sonar value
  distance1 = centimeters;
}

void getSonarValue2(unsigned long centimeters) {
  //Print sonar value
  noInterrupts();
  distance2 = centimeters;
  interrupts();
}

C'è un motivo per cui disabiliti e riabiliti gli interrupt solo nella seconda?

ho provato a inserire

volatile int distance1, distance2;

ma non è cambiato nulla, il servo continua a muoversi ad intervalli regolari da una parte all'altra. Per quanto riguarda le due ISR ho fatto una prova e poi mi sono dimenticato di rimuovere le modifiche prima di copiare il programma in questo argomento, in ogni caso ho provato sia con:

  noInterrupts();
  distance2 = centimeters;
  interrupts();

in entrambe le ISR, sia senza i comandi per gli interrupts ... ma senza risultato positivo.

Si ma comunque va usato "volatile", per cui devi lasciarlo.

Se il servo si muove comunque senza apparente motivo, il problema deve essere nel calcolo che fai o nei valori che utilizzi.
Aggiungi un po' di Seral.print di debug, ad esempio:

void ControlloServo() {
  degree = ((ServoAverage() - MinAnalogRef) * (MaxSteeringAngle - MinSteeringAngle) / (MaxAnalogRef - MinAnalogRef) + MinSteeringAngle);
  degree += Offset();
  servoerror = mydegree - degree;
  servomotorvalue = abs((K * servoerror));

  Serial.print("servoavg=");Serial.print(servoavg);
  Serial.print(" offset=");Serial.print(offset);
  Serial.print(" degree=");Serial.println(degree);
  Serial.print("servomotorvalue=");Serial.println(servomotorvalue);

  if (servomotorvalue < minPwm) {
    analogWrite(servopwm, 0);
  } else {
    if (degree <= mydegree) {
      digitalWrite(servodir, HIGH);  //gira a destra
      analogWrite(servopwm, servomotorvalue);
    } else {
      digitalWrite(servodir, LOW);  //gira a sinistra
      analogWrite(servopwm, servomotorvalue);
    }
  }
}

e vedi qual è l'output sulla seriale. Dai valori potresti capire dove sia il problema, nel caso ovviamente posta qui l'output.

PS: ma sicuro che ti servano tutti valori "float" ossia in virgola mobile?? Ad esempio nell'analogWrite tu qui scrivi un valore float quando dovrebbe essere un "int", perché?

Sisi il "volatile" lo lascio, grazie. E si, alcuni "float" sono di troppo, preciso che la maggior parte del programma è stata fatta dal precedente tesista, io ho aggiunto la parte di codice per il cambio di bound-rate del bluetooth e poco più quindi magari guardo meglio tutte le variabili e dove vengono usate e poi in caso le modifico, grazie.
Per quanto riguarda l'output una volta aggiunti i comandi di print:


penso quindi che il problema sia che arrivano comandi al servo che non dovrebbero arrivare... consigli?

Ma da quell'output (magari la prossima volta invece di JPG fai semplicemente copia del testo ed incolla dentro al tag "code") non vedo movimenti "ad intervalli regolari da una parte all'altra", c'è una certa variabilità ma di soli 10 gradi (e di conseguenza come posizione PWM tra 525 e 641). In occasione di quell'output, come si muovevano i servo?

Intanto prova ad aggiungere qualche altra print nella funzione ControlloServo per verificare se stia "sterzando" o meno, ed anche un delay() per "rallentare" un poco l'elaborazione (penso che 5 iterazioni al secondo siano più che sufficienti, non è mica un jet militare...:wink: ):

...
void loop() {
  TweaklyRun();
  ReadBT();
  computeVelocity();
  ControlloServo();
  ControlloMotore();
  ControlloDistanze();
  delay(200); // AGGIUNTO
}
...
void ControlloServo() {
  degree = ((ServoAverage() - MinAnalogRef) * (MaxSteeringAngle - MinSteeringAngle) / (MaxAnalogRef - MinAnalogRef) + MinSteeringAngle);
  degree += Offset();
  servoerror = mydegree - degree;
  servomotorvalue = abs((K * servoerror));

  Serial.print("servoavg=");Serial.print(servoavg);
  Serial.print(" offset=");Serial.print(offset);
  Serial.print(" degree=");Serial.println(degree);
  Serial.print("servomotorvalue=");Serial.println(servomotorvalue);

  if (servomotorvalue < minPwm) {
    analogWrite(servopwm, 0);
    Serial.println("Fermo."); // AGGIUNTO
  } else {
    if (degree <= mydegree) {
      digitalWrite(servodir, HIGH);  //gira a destra
      analogWrite(servopwm, servomotorvalue);
      Serial.println("Gira a destra"); // AGGIUNTO
    } else {
      digitalWrite(servodir, LOW);  //gira a sinistra
      analogWrite(servopwm, servomotorvalue);
      Serial.println("Gira a sinistra"); // AGGIUNTO
    }
  }
}

Ovviamente dovresti sapere/verificare tu se i valori di servomotorvalue, servoavg ed offset mostrati nel monitor seriale siano quelli previsti e validi, e quell'espressione calcolata per ottenere "degree".
Mi sembra che il tutto sia legato al calcolo della media mobile delle ultime 30 letture da un pin analogico collegato ad un potenziometro. Ma in prima battuta ho l'impressione che dopo la partenza tu debba attendere di aver riempito il buffer delle 30 letture prima di avere una media realistica, altrimenti alla prima iterazione visto che tutti gli elementi contengono inizialmente zero, avrai una media prossima allo zero, e poi in crescita fino al "vero" valore. Non dovresti dividere per la dimensione del buffer (30) ma per il numero di letture effettuate. Ad esempio, usando una variabile che indichi il numero di elementi presenti:

...
//Filtro a media mobile servo
float servofilter[servofsize];
int i = 0;
// AGGIUNTO
int servoelem = 0; //Numero di elementi presenti nel filtro

float servoavg = 0.0;  //average
...
float ServoAverage() {  //media mobile
  int s = analogRead(potref);
  servofilter[i] = s;
  if (i < (servofsize - 1)) {
    i++;
    // AGGIUNTO
    if (servoelem < servofsize) servoelem = i;
  }
  else {
    // Array completo!
    i = 0;
    // AGGIUNTO
    servoelem = servofsize;
  }
  servoavg = 0.0;
  // MODIFICATO
  for (int j = 0; j < servoelem; j++) {
    servoavg += (float)servofilter[j];
  }
  // MODIFICATO
  servoavg = servoavg / servoelem;
  return servoavg;
}
...

Non so se sia questo a causare il problema lamentato, ma sicuramente alla partenza viene ora calcolata meglio la vera media.

Poi ci sono comunque altre cose secondarie ma da verificare prima o poi: per dire, la variabile "mydegree" non la inizializzi mai e la imposti solamente dentro la routine di gestione della comunicazione Bluetooth: mai lasciare variabili senza una inizializzazione. E dallo sketch attualmente non vedo poi correlazione tra il servo ed i sensori, le misure vengono solo inviare alla seriale per debug (nell'output che hai riportato non si vedono) quindi suppongo che non sia ancora stato implementato.

Ho inizializzato tutte le variabili e aggiunto gli altri print, l'arduino riceve il segnale analogico dal servo e rimane fermo o sterza a seconda del valore di "servomotorvalue". Questo è il nuovo output:

servoavg= 633.00
offset= 0.00
mydegree= 0.00
degree= 23.44
servomotorvalue= 187
Gira a sinistra
servoavg= 470.00
offset= 0.00
mydegree= 0.00
degree= -6.06
servomotorvalue= 48
Gira a destra
servoavg= 524.33
offset= 0.00
mydegree= 0.00
degree= 3.77
servomotorvalue= 30
Gira a sinistra
servoavg= 497.50
offset= 0.00
mydegree= 0.00
degree= -1.09
servomotorvalue= 8
Fermo
servoavg= 474.00
offset= 0.00
mydegree= 0.00
degree= -5.34
servomotorvalue= 42
Gira a destra
servoavg= 512.14
offset= 0.00
mydegree= 0.00
degree= 1.56
servomotorvalue= 12
Fermo

e questo il nuovo codice:

#include <SoftwareSerial.h>
#include "constants1.h"
#include "Tweakly.h"

//Variabili sistema di controllo servo
int servomotorvalue = 0;
float degree = 0.0, mydegree = 0.0, servoerror = 0.0;  
float K=8; //K = guadagno controllore proporzionale servo

float offset = 0.0;

//Filtro a media mobile servo
float servofilter[servofsize];
int i = 0;
float servoavg = 0.0;  //average

volatile unsigned long count = 0;
unsigned long oldcount = 0;
unsigned long t1 = 0, dt = 0;
float velocity = 0.0;             //velocita' encoder

unsigned long iter = 1;

void isr() {
  count++;
}

//Filtro a media mobile mocoder
int const motorfsize = 5;
int servoelem = 0; // numero elementi presenti nel filtro
float motorfilter[motorfsize];
int i1 = 0;

//Variabili controllore PI
float myv = 0.0, motorerror = 0.0, motorvalue = 0.0;
float Kp = 10, Ki = 1, integer = 0.0;
float m = pow(10, -3);

//Filtro a media mobile distanza
int const dsize = 10;
float distancefilter[dsize];
int i2 = 0;

//Variabili sistema di controllo distanza
float myd = 10; 
float distance_error1 = 0.0, distance_error2 = 0.0;
float Kd=10;

void setup() {
  Serial.begin(9600);
  delay(1000);
  Serial.println("Started");
  pinMode(pinRx, INPUT);
  pinMode(pinTx, OUTPUT);
  pinMode(servopwm, OUTPUT);
  pinMode(servodir, OUTPUT);
  pinMode(potref, INPUT);
  pinMode(motorpwm, OUTPUT);
  pinMode(motordir, OUTPUT);
  pinMode(interruptPin, INPUT);
  bluetooth.begin(115200);
  delay(100);
  bluetooth.print("$$$"); //
  delay(100);
  bluetooth.println("U,9600,N"); //
  delay(100);
  bluetooth.end();
  delay(100);
  bluetooth.begin(9600);
  delay(100);
  bluetooth.println("---<cr>"); //
  attachInterrupt(digitalPinToInterrupt(interruptPin), isr, RISING);
  sonarAttach(pin_echo1, pin_trigger1, getSonarValue1, SONAR_CENTIMETERS);
  sonarAttach(pin_echo2, pin_trigger2, getSonarValue2, SONAR_CENTIMETERS);
}

void loop() {
  TweaklyRun();
  ReadBT();
  computeVelocity();
  ControlloServo();
  ControlloMotore();
  ControlloDistanze();
  delay(200);
}

void ControlloServo() {
  degree = ((ServoAverage() - MinAnalogRef) * (MaxSteeringAngle - MinSteeringAngle) / (MaxAnalogRef - MinAnalogRef) + MinSteeringAngle);
  degree += Offset();
  servoerror = mydegree - degree;
  servomotorvalue = abs((K * servoerror));

  Serial.print("servoavg= ");Serial.println(servoavg);
  Serial.print("offset= ");Serial.println(offset);
  Serial.print("mydegree= ");Serial.println(mydegree);
  Serial.print("degree= ");Serial.println(degree);
  Serial.print("servomotorvalue= ");Serial.println(servomotorvalue);
  delay(500);

  if (servomotorvalue < minPwm) {
    analogWrite(servopwm, 0);
    Serial.println("Fermo");
  } else {
    if (degree <= mydegree) {
      digitalWrite(servodir, HIGH);  //gira a destra
      analogWrite(servopwm, servomotorvalue);
      Serial.println("Gira a destra");
    } else {
      digitalWrite(servodir, LOW);  //gira a sinistra
      analogWrite(servopwm, servomotorvalue);
      Serial.println("Gira a sinistra");
    }
  }
}

float ServoAverage() {  //media mobile nuovo
  int s = analogRead(potref);
  servofilter[i] = s;
  if (i < (servofsize - 1)) {
    i++;
    if (servoelem < servofsize) servoelem = i; 
  }else { //array completo
    i = 0;
    servoelem = servofsize; 
  }
  servoavg = 0.0;
  for (int j = 0; j < servofsize; j++) {
    servoavg += (float)servofilter[j];
  }
  servoavg = servoavg / servoelem;
  return servoavg;
}

float Offset() {
  return offset = motorvalue * max_offset / maxPwm;
}

void ReadBT() {

  while (bluetooth.available() > 0) {
    String command = bluetooth.readString();
    if (command != "") {
      char op = command.c_str()[0];
      command.replace(String(op), "");
      float value = command.toFloat();

      Serial.println("op = " + String(op) + " value = " + String(value));
      switch (op) {
        case 's':
          mydegree = value;
          break;

        case 'r':
          digitalWrite(motordir, LOW);
          myv = value;
          integer = 0;
          break;

        case 'f':
          digitalWrite(motordir, HIGH);
          myv = value;
          integer = 0;
          break;

        default: break;
      }
    }
  }
}

void computeVelocity() {
  dt = millis() - t1;
  if (dt >= interval) {
    noInterrupts();
    float ve = (float)(count - oldcount) / (float)(interval);
    velocity = ve * gr * wheelcircumference;  //cm/s
    oldcount = count;
    t1 = millis();
    interrupts();
  }
}

void getSonarValue1(unsigned long centimeters) {
  //Print sonar value
  distance1 = centimeters;
}

void getSonarValue2(unsigned long centimeters) {
  //Print sonar value
  distance2 = centimeters;
}

float MotorAverage() {  //Media mobile velocita'
  motorfilter[i1] = velocity;
  if (i1 < (motorfsize - 1)) {
    i1++;
  } else {
    i1 = 0;
  }
  float motoravg = 0.0;
  for (int j1 = 0; j1 < motorfsize; j1++) {
    motoravg += motorfilter[j1];
  }
  motoravg = motoravg / (float)(motorfsize); 
  return motoravg;
}

void ControlloMotore() {  //Controllo Proporzionale Integrale
  float motAvg = MotorAverage();
  motorerror = myv - motAvg;
  motorvalue = Kp * motorerror + integer;
  integer = Ki * (integer + (motorerror * (interval * m)));
  motorvalue = constrain(motorvalue, 0, maxPwm);
  analogWrite(motorpwm, (uint8_t)motorvalue);
}
/*
float DistanceAverage(int d) {  //Media mobile distanza
  distancefilter[i2] = d;
  if (i2 < (dsize - 1)) {
    i2++;
  } else {
    i2 = 0;
  }
  float distanceavg = 0.0;
  for (int j2 = 0; j2 < dsize; j2++) {
    distanceavg += distancefilter[j2];
  }
  distanceavg = distanceavg / (float)(dsize);
  return distanceavg;
}
*/
void ControlloDistanze() {
  Serial.println(distance1);
  Serial.println(distance2);
  delay(300);
  /*float distance_avg1 = DistanceAverage(distance1);
  Serial.println(distance_avg1);
  float distance_avg2 = DistanceAverage(distance2);
  Serial.println(distance_avg2);
  distance_error1 = myd - distance_avg1;
  Serial.println(distance_error1);
  distance_error2 = myd - distance_avg2;
  Serial.println(distance_error2);
  if (distance_error1 < 0) {
    motorvalue = abs((Kd * distance_error1));
    motorvalue = constrain(motorvalue, 0, maxPwm);
    analogWrite(motorpwm, (uint8_t)motorvalue);
  }else if(distance_error2 > 0){
    motorvalue = abs((Kd * distance_error2));
    motorvalue = constrain(motorvalue, 0, maxPwm);
    analogWrite(motorpwm, (uint8_t)motorvalue);
  }*/
}

la parte che riguarda il controllo sulla distanza è,per il momento, commentata. La media ora viene calcolata meglio, ma rimane il problema che "servomotorvalue", cioè il valore letto dal potenziometro del servo, dovrebbe essere minore di minPwm (=15) in modo tale che il servo rimanga fermo fino al comando inviato tramite bluetooth.

Non mi è affatto chiaro, perdonami, come sia questo algoritmo. Proviamo a capire, partiamo dalla lettura del potenziometro.
Vedo che leggi il valore del potenziometro con la analogRead() e lo inserisci nel buffer, e da questo calcoli ad ogni iterazione il valore medio tramite "ServoAverage()" che lo inserisce in "servoavg" e lo restituisce al chiamante.

Il problema iniziale sembra quindi essere legato essenzialmente al fatto che il valore "servoavg" nell'output che hai riportato assume valori non regolari: 633 470 524.33 497.5 474.
Assumendo che questi siano i primi 5 cicli dall'attivazione del codice (lo sono?), devo supporre che la analogRead() abbia restituito per i primi valori 633, 307, 633, 417, 380, e questo non è assolutamente normale (a meno che tu non stia continuamente spostando rapidamente e violentemente il potenziometro!).

Quindi allo stato attuale tutto questo sembra indicare un problema proprio con il potenziometro o con il suo collegamento. Come è collegato esattamente?

E nel frattempo prova ad inserire la scrittura su seriale anche delle letture dal potenziometro, e non andare avanti fino a che, con il potenziometro fermo, tu non otterrai un valore essenzialmente stabile (ossia piccole variazioni, diciamo di non oltre 3 o 4). Ossia usa questo:

float ServoAverage() {  //media mobile nuovo
  int s = analogRead(potref);
  servofilter[i] = s;

  Serial.print("analogRead=");Serial.println(s);

  if (i < (servofsize - 1)) {
    i++;
    if (servoelem < servofsize) servoelem = i; 
  }else { //array completo
    i = 0;
    servoelem = servofsize; 
  }
  servoavg = 0.0;
  for (int j = 0; j < servofsize; j++) {
    servoavg += (float)servofilter[j];
  }
  servoavg = servoavg / servoelem;
  return servoavg;
}

Solo quando i valori mostrati per "analogRead" saranno stabili, allora si potrà ragionare sul resto.

si, quelli sono effettivamente i valori dei primi 5 cicli. Ho inserito la stampa e il nuovo output me lo conferma:

analogRead=473
servoavg= 473.00
offset= 0.00
mydegree= 0.00
degree= -5.52
servomotorvalue= 44
Gira a destra
Started
analogRead=473
servoavg= 473.00
offset= 0.00
mydegree= 0.00
degree= -5.52
servomotorvalue= 44
Gira a destra
analogRead=471
servoavg= 472.00
offset= 0.00
mydegree= 0.00
degree= -5.70
servomotorvalue= 45
Gira a destra
analogRead=464
servoavg= 465.50
offset= 0.00
mydegree= 0.00
degree= -6.88
servomotorvalue= 55
Gira a destra
analogRead=475
servoavg= 467.40
offset= 0.00
mydegree= 0.00
degree= -6.53
servomotorvalue= 52
Gira a destra
analogRead=462
servoavg= 466.50
offset= 0.00
mydegree= 0.00
degree= -6.70
servomotorvalue= 53
Gira a destra
analogRead=472
servoavg= 467.29
offset= 0.00
mydegree= 0.00
degree= -6.55
servomotorvalue= 52
Gira a destra
analogRead=473
servoavg= 468.00
offset= 0.00
mydegree= 0.00
degree= -6.43
servomotorvalue= 51
Gira a destra
analogRead=451
servoavg= 466.11
offset= 0.00
mydegree= 0.00
degree= -6.77
servomotorvalue= 54
Gira a destra

i valori in ingresso dal potenziometro sono instabili...il potenziometro ha tre fili: uno collegato all'alimentazione a 5V dell'arduino nano,uno al GND dell'arduino nano e l'ultimo collegato al pin analogico A0 dell' arduino nano. E no, una volta caricato il programma non "forzo" il servo manualmente.

Ora però mi sembrano molto meno instabili (che hai fatto?), ossia i valori letti da analogRead vanno da 451 a 473, mi aspetterei qualcosa di meno ma diciamo che rispetto al range 0-1023 abbiamo un delta che si aggira sul 2%. Ma la media mobile serve anche a mitigare queste variabilità, ed infatti la media "servoavg" vedo che ora va da 473 a 466.

Quando dici "l'ultimo" intendi il cursore del potenziometro,ossia il contatto centrale, vero si?... E questi collegamenti come sono fatti? Sono lunghi? Sono cavetti Dupont o fili stagnati? E arrivano direttamente nei pin o passano per una breadboard?

Se quindi il problema del potenziometro è (quasi) risolto, ora devi capire come quel valore vada ad incastrarsi nell'algoritmo di "ControlloServo()" e da questo quindi quali comandi dà al servo e se sono in linea con quanto atteso.

Ad esempio assumiamo che ServoAverage() restituisca 470:

  degree = ((ServoAverage() - MinAnalogRef) * (MaxSteeringAngle - MinSteeringAngle) / (MaxAnalogRef - MinAnalogRef) + MinSteeringAngle);
  degree += Offset();
  servoerror = mydegree - degree;
  servomotorvalue = abs((K * servoerror));

Da cui sostituendo i valori e costanti:

degree + Offset() = ((470 - 393) * (20 - -20) / (614 - 393) + -20) + 0 =
= (77 * 40 / 221 - 20) = 13.93 - 20 = -6.06
servoerror = mydegree - degree = 0 - -6.06 = 6.06
servomotorvalue = abs(8 * servoerror) = 8 * 6.06 = 48.5

Ora, questo valore di "degree" cosa dovrebbe rappresentare? E per te è valido?
E "servoerror"? E alla fine quindi "servomotorvalue"?

Secondo me devi ragionare su questi valori per capire.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.