[Multicotteri] Elettronica : IMU, MCU, Sensori ed algoritmi di controllo

no, è un'operazione logica :slight_smile: creca su wikipedia o qualche manuale di C

lesto:
no, è un'operazione logica :slight_smile: creca su wikipedia o qualche manuale di C

Si l'operazione logica la conosco, non avevo capito il fatto degli interupt legati a N bit di arduino, in questo modo se non ho capito male riesci con un solo interupt a gestire tutti i 4 canali.

Hai qualche link di documentazione?! A questo punto sto pensando di cambiare scheda, prenderei uno mini pro 05 più leggera e ha tutto quello che serve se alla fine ogni pin di arduino può essere usato come interupt.

sì, la mini va benissimo, ma per sviluppare l'arduino classica è più comoda. Ti consiglio un doppio sistema, uno su UNO con cui fai i test e sviluppi la libreria e la mini che sta a bordo su cui carichi il codice. tra l'altro puoi evitare di comprare il chip FTDI se usi la uno come programmatore della mini (vedi guida di menniti)

Come ti dicevo gli interrupt sui pin sono gestiti 8 alla volta, perchè così in 3 registri di 1 byte ciascuno, ogni bit corrisponde ad un pin. In realtà per ogni gruppo ci sono 3 registri: lettura, scrittura(o pull up se settato a input), tipo(input/output), più i vari registri per gli interrupt.

una guida per iniziare a capirci qualcosa: Arduino Reference - Arduino Reference

#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Servo.h"
#include <avr/interrupt.h>  
#include <avr/io.h>

// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 accelgyro;

int16_t ax, ay, az;
int16_t gx, gy, gz;
Servo motoreSX;
Servo motoreDX;
float velSX;
float velDX;


float P=0;
float I=0;
float D=0;
float PID;

float kP=25;
float kI=0;
float kD=0;

float MaxP=30;
float MinP=-30;
float MaxI=40;
float MinI=-40;
float MaxD=15; //15
float MinD=-15;

int roll=0;
int rollOld=0;
float val_Consegna=0;

//interrupt timer, call PID_callback() every 5 ms
ISR(TIMER2_OVF_vect) {
  count++;               //Increments the interrupt counter
  if(count > 4){
     PID_callback();
    count = 0;           //Resets the interrupt counter
  }
  TCNT2 = 130;           //Reset Timer to 130 out of 255
  TIFR2 = 0x00;          //Timer2 INT Flag Reg: Clear Timer Overflow Flag
};  

void setup() {
  // join I2C bus (I2Cdev library doesn't do this automatically)
  Wire.begin();
  Serial.begin(9600);
  
  // initialize device

  accelgyro.initialize();

  /* verify connection
   Serial.println("Testing device connections...");
   Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
   */
  motoreDX.attach(8);
  motoreDX.writeMicroseconds(800);
  motoreSX.attach(7);
  motoreSX.writeMicroseconds(800);
  delay(5000);

  for(int x=800; x<=1200; x++){
    motoreDX.writeMicroseconds(x);
    motoreSX.writeMicroseconds(x);
  }
  velSX=velDX=1200;
  
  TCCR2B = 0x00;        //Disbale Timer2 while we set it up
  TCNT2  = 130;         //Reset Timer Count to 130 out of 255
  TIFR2  = 0x00;        //Timer2 INT Flag Reg: Clear Timer Overflow Flag
  TIMSK2 = 0x01;        //Timer2 INT Reg: Timer2 Overflow Interrupt Enable
  TCCR2A = 0x00;        //Timer2 Control Reg A: Normal port operation, Wave Gen Mode normal
  TCCR2B = 0x05;        //Timer2 Control Reg B: Timer Prescaler set to 128

}

//Implementatio of PID
float Pid(float val_Consegna){

  if(kP!=0){
    P =  roll / kP;
    if(P>MaxP) P=MaxP;
    if(P<MinP) P=MinP;
  }
  
  if(kI!=0){
    I = I + ((roll/2) * kI);
    if(I>MaxI) I=MaxI;
    if(I<MinI) I=MinI;
  }
  
  if(kD!=0){
    D = (roll - rollOld) * kD;
    if(D>MaxD) D=MaxD;
    if(D<MinD) D=MinD;
  }
  
  float PID = P + I + D;

  rollOld = roll;

  return PID;
}
//calback for PID and update motor state
void PID_callback(){
  
  velDX=1200-PID;
  velSX=1200+PID;

  //Guardie, fa rimanere la velocità nei limiti di sicurezza
  if(velDX>1400) velDX=1400;
  if(velSX>1400) velSX=1400;
  if(velDX<1100) velDX=1100;
  if(velSX<1100) velSX=1100;

  motoreSX.writeMicroseconds(int(velSX));
  motoreDX.writeMicroseconds(int(velDX));

  PID=Pid(0);

}

void loop() {
  // read raw accel/gyro measurements from device
  accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
  roll=map(constrain(ay, -16000, 16000), -16000, 16000, -1000, 1000);
  /*   Serial.print(P);
   Serial.print("\t");
   Serial.print(I);
   Serial.print("\t");
   Serial.print(D);
   Serial.print("\t");
   Serial.print(interval);
   Serial.print("\t");
   Serial.print(roll);
   Serial.print("\t");Serial.print(velSX); Serial.print("\t");Serial.println(velDX);
   */


  Serial.print(velSX);
  Serial.print(" ");
  Serial.print(velDX);
  Serial.print("\n");
  //delay(500);

}

Questo è il mio programma che implementa il PID, non ci salto fuori, mi è difficilissimo regolare il tutto, magari compio qualche errore grossolano che non vedo...

Il valore ay corrisponde al valore del giroscopio, se lo inclino è stabile quindi dovrebbe essre quello.

Grazie!

pid callback deve eseguire uil pid ma non fare la Servo.write,la write falla nel loop a meno di 50Hz

lesto:
pid callback deve eseguire uil pid ma non fare la Servo.write,la write falla nel loop a meno di 50Hz

Quindi un ciclo loop deve durare 20ms giusto?!

Il mio ciclo loop con le stampe dura dai 16 ai 18ms... Ci sta come valore?!

no, il loop fallo veloce, poi metti una if che se sono trascorsi almeno 20ms aggiorni l'output dei motori (vedi l'esempio nell'IDE blinkWithoutDelay)

Mi sono accorto che un motore gira più piano dell'altro e quindi bilanciare è difficile, così ho fatto questo sketch:

#include <Servo.h>

Servo motoreSX;
Servo motoreDX;

void setup(){
    pinMode(13,OUTPUT);
    motoreSX.attach(7);
    motoreDX.attach(8);
    motoreSX.writeMicroseconds(1800);
    motoreDX.writeMicroseconds(1800);
    
    delay(7000);
    motoreSX.writeMicroseconds(800);
    motoreSX.writeMicroseconds(800);
    
    delay(7000);
    digitalWrite(13,HIGH);
    
}

void loop()
{

}

In modo da tarare sulle stesse frequenze i due ESC flyduino 12A ma sorprendentemente uno gira sempre più forte e non di poco... Soluzioni?!

sicuro non sia un problema meccanico e/o di alimentazione? che cavi usi, quanti ampere mangiano i motori, con o senza eliche...

lesto:
sicuro non sia un problema meccanico e/o di alimentazione? che cavi usi, quanti ampere mangiano i motori, con o senza eliche...

Allora ho fatto così ho fatto uno sketch che incrementa/decrementa tramite serial.read la variabile velocità del motore, in questo modo decremento do di un punto ho trovato il minimo dei due motori, uno era a 800 e l'altro era a 570 quindi sicuramente facevo fatica ambuilanciare perchè aggiungendo e togliendo la stessa quantità da entrambi i motori l'asse nn si bilanciava. Chi lo sketch che ho postato prima ho impostati i valori minimi a 550 e ricontrollati e ora sono a posto. Nel mentre ho rotto un'elica sulla mia mano innescando una forte vibrazione sul tavolo, spero nn mi abbia rovinato ili gyro.
Ora devo ricomprare le eliche, poco male peró che dici, provo le 7 pollici, ora ho le 8,morrebbe avere un senso se gli ESC si scaldano??? Comunque sui flyduino non è vero che nn c'è da toccarli, potrebbero nn essere impostati uguali, e con gli sketch che ho fatto riesci a metterli a posto, il tutto letto senza eliche anche perchè una era rotta, le eliche se sono ben fatte dovrebbero dare i medesimi risultati. Che ne pensi???

le eliche non saranno mai uguali, sopratutto se non bilanciate, ma se il problema è dell'esc è un'altra storia.

Allora che io sappia così metti il gas al massimo e poi al minimo, entri in modalità programmazione, che non è sufficiente per settare le escursioni dell'esc... Ogni esc ha la sua sequenza di "beep" che ti aiuta a fare i settaggi, ovviamente non esistono 2 procedure uguali, dipende dalla marca/modello :slight_smile:

se gli ESC scaldano cl'elica più piccola o con meno passo più aiutare, ma se la temperatura è alta meglio un esc che regga qualche ampere in più così non risci di fare scintille in volo

edit: stm32 con comunicazione seriale + lettura sensori è ok. Ora devo sistemare il programma lato PC, che penso che posterò su github con spiegazione del protocollo da usare, così iniziamo a creare un pò di tool comodi.
Il primo credo di farlo così: si aspetta la stringa:
g: %f %f %f\n per i valori giro, a per accelerometro e m per magnetometro. Non sarà obbligatorio stamapare g, m, a in ordine, e anche m si potrà totalmente saltare.
Il risultato sarà che i valori sono "mangiati" da una DCM che stamperà il risualtato a video, in oltre ci sarà uno slider per ogni valore e la possibilità di "spastare" gli assi (exempio gx da invertire con gy, oppure gx diventa -gx) perchè così si settano più comodamente gli assi dei sensori per essere allineati tra loro.

lesto:
le eliche non saranno mai uguali, sopratutto se non bilanciate, ma se il problema è dell'esc è un'altra storia.

Allora che io sappia così metti il gas al massimo e poi al minimo, entri in modalità programmazione, che non è sufficiente per settare le escursioni dell'esc... Ogni esc ha la sua sequenza di "beep" che ti aiuta a fare i settaggi, ovviamente non esistono 2 procedure uguali, dipende dalla marca/modello :slight_smile:

se gli ESC scaldano cl'elica più piccola o con meno passo più aiutare, ma se la temperatura è alta meglio un esc che regga qualche ampere in più così non risci di fare scintille in volo

edit: stm32 con comunicazione seriale + lettura sensori è ok. Ora devo sistemare il programma lato PC, che penso che posterò su github con spiegazione del protocollo da usare, così iniziamo a creare un pò di tool comodi.
Il primo credo di farlo così: si aspetta la stringa:
g: %f %f %f\n per i valori giro, a per accelerometro e m per magnetometro. Non sarà obbligatorio stamapare g, m, a in ordine, e anche m si potrà totalmente saltare.
Il risultato sarà che i valori sono "mangiati" da una DCM che stamperà il risualtato a video, in oltre ci sarà uno slider per ogni valore e la possibilità di "spastare" gli assi (exempio gx da invertire con gy, oppure gx diventa -gx) perchè così si settano più comodamente gli assi dei sensori per essere allineati tra loro.

http://forum.autoquad.org/viewtopic.php?p=5891&sid=55012e6e48a0a3ee0c91b123c11b5fbc#p5878

Diciamo che ci dovrei essere, col mio codice simulo proprio quella procedura, reimposto ai valori di default e poi testo col programmino che comunica tramite monitor seriale la velocità dei motorini in questo modo controllo in modo preciso almeno il minimo valore.
Secondo me la diversità fra una elica e l'altra può essere sopperita dal PID, se un motorino gira in modo abbastanza diverso da un altro tutti i calcoli fatti col PID vanno a quel paese.

Ho creato una piccola applicazione con processing che grafica l'andamento di GX (giroscopio su asse x) e ho notato che in presenza di urti (sbilanciamento con la mano per simulare un'interazione esterna nn prevedibile) il valore ha oscillazioni, questo con la DCM dovrebbe venire attenuato giusto?!

se le fluttuazioni sono sinosoidali devi pulirle a mano, alcuni sensori non danno uno 0 "fisso" ma è una funzione sinouidale (astro mi corregga se dico baggianate)

lesto:
se le fluttuazioni sono sinosoidali devi pulirle a mano, alcuni sensori non danno uno 0 "fisso" ma è una funzione sinouidale (astro mi corregga se dico baggianate)

Traducendo?! come faccio a pulire a mano?! (strofinaccio e olio di gomito?! :grin:)

la sinusoide ha una frequenza, ovvero il numero di ripetizioni al secondo (vedi nel datasheet) e se te fai la media dei dati in quel range di tempo annulli l'effetto sinusoidale.

lesto:
la sinusoide ha una frequenza, ovvero il numero di ripetizioni al secondo (vedi nel datasheet) e se te fai la media dei dati in quel range di tempo annulli l'effetto sinusoidale.

Nel datasheet http://www.cdiweb.com/datasheets/invensense/PS-MPU-6000A.pdf ho trovato questo:

"11.4.3 Trace Routing
Routing traces or vias under the gyro package such that they run under the exposed die pad is prohibited.
Routed active signals may harmonically couple with the gyro MEMS devices, compromising gyro response.
These devices are designed with the drive frequencies as follows: X = 33±3Khz, Y = 30±3Khz, and
Z=27±3Khz. To avoid harmonic coupling don’t route active signals in non-shielded signal planes directly
below, or above the gyro package. Note: For best performance, design a ground plane under the e-pad to
reduce PCB signal noise from the board on which the gyro device is mounted. If the gyro device is stacked
under an adjacent PCB board, design a ground plane directly above the gyro device to shield active signals
from the adjacent PCB board."

Che dovrebbe corrispondere appunto al discorso del segnale sinusoidale giusto? Quindi facendo il valore medio di ogni spezzone a 33Khz dovrei eliminare il disturbo del quale lamento, quindi ogni 30 microsecondi faccio la media dei valori e il valore che andrò ad inserire nel PID è proprio la media, e poi azzero la media e riparte dal primo valore successivo ai 30 microsecondi?

Ragazzi dopo mesi di tentativi e letture sono ancora arenato non riesco a capire come diavolo funziona questa STM32F3 i problemi sono molteplici:

  1. Nell'IDE uKeil non riesco a complilare progetti complessi ma solo progetti stupidi come accendere luci o simili perchè il compiler mi da sempre problemi nell'includere la libreria della ST
  2. Non capisco come leggere i dati dei sensori, mi riferisco a velocità angolari, valori acc. e valori del campo magnetico.
  3. utilizzo dei timer per realizzare dei segnali PPM

Premetto che c'ho perso tempo tanto tempo anche se in parallelo approfondivo i miei studi di Kalman, tecniche di controllo etc etc. ho praticamente progettato tutto ma la mia poca esperienza in ambito PIC mi sta bloccando.
C'è qualche buona anima che può aiutarmi anche inviandomi del codice ben documentato che mi spieghi come diavolo si programma sta scheda.
Grazie mille Fabio
P.s. sono uno studente di automazione quindi i controlli sono il mio pane quotidiano ma questo non significa che sono un esperto di programmazione e protocolli per questo vi pregherei di non usare terminologia troppo tecnica

per il 2 c'è un ottimo esemio fornito sul sito.. però se non conosci i2c e SPI ti conviene prima farti un'idea osservando come si usano i sensori i2c e SPI su arduino, capisci la logica di comunicazione, e poi passi al STM32.

Per la prima domanda mai usato quell'ambiente, e per l'ultima non so, devo ancora passare ad analizzare i timer, ma anche lì tutti gli esempi SMT usano una funzione che setta il timer principale ad una certa frequenza, io modificando quel valore ottengo un iterrupt ogni microsecondo, però non mi piace come cosa, preferirei andare sul PWM con giusta frequenza e dutyCicle.

Fabio_automatico:

  1. Nell'IDE uKeil non riesco a complilare progetti complessi ma solo progetti stupidi come accendere luci o simili perchè il compiler mi da sempre problemi nell'includere la libreria della ST
  2. Non capisco come leggere i dati dei sensori, mi riferisco a velocità angolari, valori acc. e valori del campo magnetico.
  3. utilizzo dei timer per realizzare dei segnali PPM
  1. Devi copiare nella cartella principale del compilatore (C:\keil se hai fatto l'installazione standard) le cartelle "Libraries", "Peripheral_Examples" e "Utilities" che trovi nel sorgente del demo precaricato sulla scheda, in alternativa le puoi copiare all'interno del progetto, assieme alla cartella CMSIS se la usi, e cambi i percorsi delle relative path.

  2. Ci sono le funzioni già pronte nella libreria di ST, comunque sono normali device I2C e vi accedi tramite questo bus.

  3. Il PPM non lo generi tramite timer, va implementato tramite specifica routine software che si appoggia ad un timer, e relativo interrupt, come base dei tempi sia per generare i singoli impulsi che per il frame.

astrobeed:

Fabio_automatico:

  1. Nell'IDE uKeil non riesco a complilare progetti complessi ma solo progetti stupidi come accendere luci o simili perchè il compiler mi da sempre problemi nell'includere la libreria della ST
  2. Non capisco come leggere i dati dei sensori, mi riferisco a velocità angolari, valori acc. e valori del campo magnetico.
  3. utilizzo dei timer per realizzare dei segnali PPM
  1. Devi copiare nella cartella principale del compilatore (C:\keil se hai fatto l'installazione standard) le cartelle "Libraries", "Peripheral_Examples" e "Utilities" che trovi nel sorgente del demo precaricato sulla scheda, in alternativa le puoi copiare all'interno del progetto, assieme alla cartella CMSIS se la usi, e cambi i percorsi delle relative path.

  2. Ci sono le funzioni già pronte nella libreria di ST, comunque sono normali device I2C e vi accedi tramite questo bus.

  3. Il PPM non lo generi tramite timer, va implementato tramite specifica routine software che si appoggia ad un timer, e relativo interrupt, come base dei tempi sia per generare i singoli impulsi che per il frame.

  1. Devo copiare semplicemente le cartelle nel percorso che mi hai indicato senza impostare nessun path all'interno del keil?

2)le funzioni le ho trovate per entrambi i sensori purtroppo non sono riuscito a capire in che unità di misura le rilascia anche leggendo l'esempio DEMO, in cui vengono usate queste funzioni. Inoltre, sempre spulciando nella funzioni definite in DEMO noto che i dati rilasciati dalla funzione READ vanno trattati ad esempio in DEMO leggo:

L3GD20_Read(&tmpreg,L3GD20_CTRL_REG4_ADDR,1);
  
  L3GD20_Read(tmpbuffer,L3GD20_OUT_X_L_ADDR,6);
  
  /* check in the control register 4 the data alignment (Big Endian or Little Endian)*/
  if(!(tmpreg & 0x40))
  {
    for(i=0; i<3; i++)
    {
      RawData[i]=(int16_t)(((uint16_t)tmpbuffer[2*i+1] << 8) + tmpbuffer[2*i]);

di cui non capisco pienamente il significato.

  1. La routine ho idea di come si fa ma da come ho capito ho bisogno di una variabile temporale generata da un timer interno alla scheda, ed è li che mi blocco.

So che forse faccio troppe domande e che forse me la devo piangere un po' da solo però vi sarei grato se potreste darmi una mano anche fornendo codici di esempio di funzioni che generano segnali PPM e che leggono i segnali dei sensori nel SI per asse.