Sensore assetto e orientamento

Purtroppo nun riesco a capire la calibrazione
Allora i chip sono calibrati in fabrica e all avvio si caricano le informazioni ?
Ma io ho questo dubbio
Il chip lo monto su una scheda , poi su un qualsiasi supporto , poi lo metto in una scatola , vuoi o non vuoi con le tolleranze il chip risulta inclinato in un lato .
Se lo accendo su un tavolo inclinato, lui cosa mi dice … la pendenza del tavolo o mi dice che è in piano ?
In teoria mi dovrebbe sommare tutti i difetti e darmi un valore preciso di come lui è inclinato
Io uso gli offset , non mi chiedo come sono le tolleranze dello strumento , lo metto su un piano vero, e lo calibro li sopra , così lo strumento è informato di come lui è inclinato nel montaggio .
Io lo sto usando in esperimenti per una bolla elettronica, per un piano autolivellante(stato embrionale ) , e ho notato che se non si fanno gli offset non ce niente di reale.
Allego il progetto e la foto del chip nello “strumento)

#include <SPI.h>
#include <Wire.h>
#include <Servo.h>
#include "I2Cdev.h"
#include "MPU6050.h"
#include <EEPROM.h>

//eeprom int ax_offset,ay_offset,az_offset,gx_offset,gy_offset,gz_offset;
// 2 celle per ogni offset inizio 00
int EEax_offset=0;
int EEay_offset=2;
int EEaz_offset=4;
int EEgx_offset=6;
int EEgy_offset=8;
int EEgz_offset=10;

#define MPU 0x68  // I2C address of the MPU-6050
MPU6050 accelgyro(0x68); 
int16_t ax, ay, az,gx, gy, gz;
int mean_ax,mean_ay,mean_az,mean_gx,mean_gy,mean_gz,state=0;
int ax_offset,ay_offset,az_offset,gx_offset,gy_offset,gz_offset;

//Change this 3 variables if you want to fine tune the skecth to your needs.
int buffersize=1000;     //Amount of readings used to average, make it higher to get more precision but sketch will be slower  (default:1000)
int acel_deadzone=8;     //Acelerometer error allowed, make it lower to get more precision, but sketch may not converge  (default:8)
int giro_deadzone=1;     //Giro error allowed, make it lower to get more precision, but sketch may not converge  (default:1)
#define LED 13                // LED collegato al pin digitale 13
#define LEDoffet 7
#define BUTTON 12              // pin di input dove è collegato il pulsante per calcolo offset
#define AvvioOff 11            // pin di input dove è collegato il pulsante per avviare routine di calibrazione 
int  val = 0;      

float Temp ; 
Servo ServoX, ServoY;
double AcX,AcY,AcZ;
double Pitch, Roll;
 
void setup(){
 
  pinMode(AvvioOff, INPUT);     // imposta il pin digitale come input
  pinMode(LEDoffet, OUTPUT); 
  Serial.begin(115200);
  ServoX.attach(8);
  ServoY.attach(9);
  init_MPU(); // Inizializzazione MPU6050

//Sensor readings with offsets: -5 -8 16371 1 1 1
//Your offsets: -3841 -2790 1430 -5 17 -43
//Data is printed as: acelX acelY acelZ giroX giroY giroZ
//Check that your sensor readings are close to 0 0 16384 0 0 0
//If calibration was succesful write down your offsets so you can set them in your projects using something similar to mpu.setXAccelOffset(youroffset)
//lettura eeprom offset
//-2781
//1480
//-186
//37
//42





accelgyro.setXAccelOffset(EEPROM.read(EEax_offset)*256+EEPROM.read(EEax_offset+1));
delay(5);
accelgyro.setYAccelOffset(EEPROM.read(EEay_offset)*256+EEPROM.read(EEay_offset+1));
delay(5);
accelgyro.setZAccelOffset(EEPROM.read(EEaz_offset)*256+EEPROM.read(EEaz_offset+1));
delay(5);
accelgyro.setXGyroOffset(EEPROM.read(EEgx_offset)*256+EEPROM.read(EEgx_offset+1));
delay(5);
accelgyro.setYGyroOffset(EEPROM.read(EEgy_offset)*256+EEPROM.read(EEgy_offset+1));
delay(5);
accelgyro.setZGyroOffset(EEPROM.read(EEgz_offset)*256+EEPROM.read(EEgz_offset+1));
delay(5);

}
 


void loop()
{


  if (digitalRead(AvvioOff)){
 Serial.println ();
 Serial.println ("Iterate through each byte of the EEPROM storage.");
 Serial.println (" Larger AVR processors have larger EEPROM sizes, E.g:");
 Serial.println ("- Arduno Duemilanove: 512b EEPROM storage.");
 Serial.println ("- Arduino Uno:        1kb EEPROM storage.");
 Serial.println ("- Arduino Mega:       4kb EEPROM storage.");
 Serial.println ("  Rather than hard-coding the length, you should use the pre-provided length function.");
 Serial.println ("  This will make your code portable to all AVR processors.");
 Serial.println ("  put your setup code here, to run once:"); 
 
  for (int i = 0 ; i < EEPROM.length() ; i++) {
    EEPROM.write(i, 0); /***
    Iterate through each byte of the EEPROM storage.

    Larger AVR processors have larger EEPROM sizes, E.g:
    - Arduno Duemilanove: 512b EEPROM storage.
    - Arduino Uno:        1kb EEPROM storage.
    - Arduino Mega:       4kb EEPROM storage.

    Rather than hard-coding the length, you should use the pre-provided length function.
    This will make your code portable to all AVR processors.
  ***/ // put your setup code here, to run once:
 
}
    digitalWrite(LEDoffet, HIGH);
  SetupCalCoffset();
  LooPoffset();
} 
 
  digitalWrite(LEDoffet, LOW);
  delay (1000);
  FunctionsMPU(); // Acquisisco assi AcX, AcY, AcZ.
    
  double Roll = FunctionsPitchRoll(AcX, AcY, AcZ);   //Calcolo angolo Roll
  double Pitch = FunctionsPitchRoll(AcY, AcX, AcZ);  //Calcolo angolo Pitch
 
  //controllo servomotori
  //double ServoRoll = map(Roll, -90.00, 90.00, 0.00, 179.00);
  //double ServoPitch = map(Pitch, -90.00, 90.00, 179.00, 0.00);
 
  //ServoX.write(ServoRoll);
  //ServoY.write(ServoPitch);
 // Serial.print("Temperatura C: "); Serial.print(Temp/340 + 36.53);
  Serial.print("\n");
   Serial.print("\n");
    Serial.println("_______________________");
     Serial.print("Temperatura C: "); Serial.println(Temp/340 + 36.53);
     Serial.print("Pitch: "); Serial.print(Pitch-90);
     Serial.print("\n");
     Serial.print("Pitch: "); Serial.print(Pitch);
     Serial.print("");
 
  Serial.print("                          ");
   for (double i=0; i <= Pitch*10; i++){
      Serial.print("<");
      }
      Serial.print("*");
  
   for (double i=0; i >= Pitch*10; i--){
      Serial.print(">");
      }
  
  
  
  
  Serial.print("\n");
  Serial.print("Roll: "); Serial.print(Roll-90);
  Serial.print("\n");
  Serial.print("Roll: "); Serial.print(Roll);
 
  Serial.print("                          ");
   for (double i=0; i <= Roll*10; i++){
      Serial.print("<");
      }
      Serial.print("*");
  
   for (double i=0; i >= Roll*10; i--){
      Serial.print(">");
      }
  
  
  
  
 
}
continua ..
void init_MPU(){
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  delay(1000);
}
 
//Funzione per il calcolo degli angoli Pitch e Roll
double FunctionsPitchRoll(double A, double B, double C){
  float DatoA, DatoB, Value;
  DatoA = A;
  DatoB = (B*B) + (C*C);
  DatoB = sqrt(DatoB);
  
  Value = atan2(DatoA, DatoB);
  Value = Value * 180/3.14;
  
  return (double)Value;
}
 
//Funzione per l'acquisizione degli assi X,Y,Z del MPU6050

void FunctionsMPU(){
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3f (ACCEL_YOUT_H) & 0x40 (ACCEL_YOUT_L)
  Temp=Wire.read()<<8|Wire.read(); // 0x41 (ACCEL_YOUT_H) & 0x42 (ACCEL_YOUT_L
}


///////////////////////////////////   FUNCTIONS   ////////////////////////////////////

void SetupCalCoffset() {
  
  //pinMode(LED, OUTPUT);       // imposta il pin digitale come output
 // pinMode(BUTTON, INPUT);     // imposta il pin digitale come input
  
  // join I2C bus (I2Cdev library doesn't do this automatically)
  Wire.begin();
  // COMMENT NEXT LINE IF YOU ARE USING ARDUINO DUE
  TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz). Leonardo measured 250kHz.

  // initialize serial communication
  //Serial.begin(9600);

  // initialize device
  accelgyro.initialize();
 digitalWrite(LED, LOW);   //spegne il led
  // wait for ready
  while (Serial.available() && Serial.read()); // empty buffer
  while (!digitalRead(BUTTON)){
    //!Serial.available()
    Serial.println(F("Push Button to start sketch.\n"));
    delay(1500);
  }                
  while (Serial.available() && Serial.read()); // empty buffer again

  // start message
  Serial.println("\nMPU6050 Calibration Sketch");
  delay(2000);
  Serial.println("\nYour MPU6050 should be placed in horizontal position, with package letters facing up. \nDon't touch it until you see a finish message.\n");
  delay(3000);
  // verify connection
  Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
  delay(1000);
  // reset offsets
  accelgyro.setXAccelOffset(0);
  accelgyro.setYAccelOffset(0);
  accelgyro.setZAccelOffset(0);
  accelgyro.setXGyroOffset(0);
  accelgyro.setYGyroOffset(0);
  accelgyro.setZGyroOffset(0);



}




void LooPoffset() {
  if (state==0){
    Serial.println("\nReading sensors for first time...");
    meansensors();
    state++;
    delay(1000);
  }

  if (state==1) {
    Serial.println("\nCalculating offsets...");
    calibration();
    state++;
    delay(1000);
  }

  if (state==2) {
    meansensors();
    Serial.println("\nFINISHED!");
    Serial.print("\nSensor readings with offsets:\t");
    Serial.print(mean_ax); 
    Serial.print("\t");
    Serial.print(mean_ay); 
    Serial.print("\t");
    Serial.print(mean_az); 
    Serial.print("\t");
    Serial.print(mean_gx); 
    Serial.print("\t");
    Serial.print(mean_gy); 
    Serial.print("\t");
    Serial.println(mean_gz);
    Serial.print("Your offsets:\t");
    Serial.print(ax_offset); 
    Serial.print("\t");
    Serial.print(ay_offset); 
    Serial.print("\t");
    Serial.print(az_offset); 
    Serial.print("\t");
    Serial.print(gx_offset); 
    Serial.print("\t");
    Serial.print(gy_offset); 
    Serial.print("\t");
    Serial.println(gz_offset); 
    Serial.println("\nData is printed as: acelX acelY acelZ giroX giroY giroZ");
    Serial.println("Check that your sensor readings are close to 0 0 16384 0 0 0");
    Serial.println("If calibration was succesful write down your offsets so you can set them in your projects using something similar to mpu.setXAccelOffset(youroffset)");
//srittura valori offset EEprom
EEPROM.write(EEax_offset,highByte(ax_offset) ); 
delay(5);
EEPROM.write(EEax_offset+1,lowByte(ax_offset)); 
delay(5);
EEPROM.write(EEay_offset,highByte(ay_offset) ); 
delay(5);
EEPROM.write(EEay_offset+1,lowByte(ay_offset));
delay(5);
EEPROM.write(EEaz_offset,highByte(az_offset) ); 
delay(5);
EEPROM.write(EEaz_offset+1,lowByte(az_offset));
delay(5);
EEPROM.write(EEgx_offset,highByte(gx_offset) ); 
delay(5);
EEPROM.write(EEgx_offset+1,lowByte(gx_offset));
delay(5);
EEPROM.write(EEgy_offset,highByte(gy_offset) ); 
delay(5);
EEPROM.write(EEgy_offset+1,lowByte(gy_offset)); 
delay(5);
EEPROM.write(EEgz_offset,highByte(gz_offset) ); 
delay(5);
EEPROM.write(EEgz_offset+1,lowByte(gz_offset)); 
delay(5);

Serial.println("_____________________________________________________-");
Serial.println("lettura eeprom offset");
Serial.println(EEPROM.read(EEax_offset)*256+EEPROM.read(EEax_offset+1));
delay(5);
Serial.println(EEPROM.read(EEay_offset)*256+EEPROM.read(EEay_offset+1));
delay(5);
Serial.println(EEPROM.read(EEaz_offset)*256+EEPROM.read(EEaz_offset+1));
delay(5);
Serial.println(EEPROM.read(EEgx_offset)*256+EEPROM.read(EEgx_offset+1));
delay(5);
Serial.println(EEPROM.read(EEgy_offset)*256+EEPROM.read(EEgy_offset+1));
delay(5);
Serial.println(EEPROM.read(EEgz_offset)*256+EEPROM.read(EEgz_offset+1));
delay(5);
Serial.println("_________________________________________________________");
  
digitalWrite(LED, HIGH);  //accende il led
    while (1);
  }
}

///////////////////////////////////   FUNCTIONS   ////////////////////////////////////
void meansensors(){
  long i=0,buff_ax=0,buff_ay=0,buff_az=0,buff_gx=0,buff_gy=0,buff_gz=0;

  while (i<(buffersize+101)){
    // read raw accel/gyro measurements from device
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
    
    if (i>100 && i<=(buffersize+100)){ //First 100 measures are discarded
      buff_ax=buff_ax+ax;
      buff_ay=buff_ay+ay;
      buff_az=buff_az+az;
      buff_gx=buff_gx+gx;
      buff_gy=buff_gy+gy;
      buff_gz=buff_gz+gz;
    }
    if (i==(buffersize+100)){
      mean_ax=buff_ax/buffersize;
      mean_ay=buff_ay/buffersize;
      mean_az=buff_az/buffersize;
      mean_gx=buff_gx/buffersize;
      mean_gy=buff_gy/buffersize;
      mean_gz=buff_gz/buffersize;
    }
    i++;
    delay(2); //Needed so we don't get repeated measures
  }
}

void calibration(){
  ax_offset=-mean_ax/8;
  ay_offset=-mean_ay/8;
  az_offset=(16384-mean_az)/8;

  gx_offset=-mean_gx/4;
  gy_offset=-mean_gy/4;
  gz_offset=-mean_gz/4;
  while (1){
    int ready=0;
    accelgyro.setXAccelOffset(ax_offset);
    accelgyro.setYAccelOffset(ay_offset);
    accelgyro.setZAccelOffset(az_offset);

    accelgyro.setXGyroOffset(gx_offset);
    accelgyro.setYGyroOffset(gy_offset);
    accelgyro.setZGyroOffset(gz_offset);

    meansensors();
    Serial.println("...");

    if (abs(mean_ax)<=acel_deadzone) ready++;
    else ax_offset=ax_offset-mean_ax/acel_deadzone;

    if (abs(mean_ay)<=acel_deadzone) ready++;
    else ay_offset=ay_offset-mean_ay/acel_deadzone;

    if (abs(16384-mean_az)<=acel_deadzone) ready++;
    else az_offset=az_offset+(16384-mean_az)/acel_deadzone;

    if (abs(mean_gx)<=giro_deadzone) ready++;
    else gx_offset=gx_offset-mean_gx/(giro_deadzone+1);

    if (abs(mean_gy)<=giro_deadzone) ready++;
    else gy_offset=gy_offset-mean_gy/(giro_deadzone+1);

    if (abs(mean_gz)<=giro_deadzone) ready++;
    else gz_offset=gz_offset-mean_gz/(giro_deadzone+1);

    if (ready==6) break;
  }
}

Il software non é tutta farina del mio sacco, non ne sarei capace.
Io ho aggiunto le funzioni di calibrazione , pulito cose che non mi servivano, e aggiunto tutto quello che serve per la memorizzazione offset , inversione valori, e stampa con vari Print di controllo
C'è anche una piccola parte , per due servomotori , ma sono a passi di un grado e non vanno bene

alessanddrob:
Se lo accendo su un tavolo inclinato, lui cosa mi dice … la pendenza del tavolo o mi dice che è in piano ?

Ti dice l'inclinazione, però tu stai usando l'MPU in modo raw, non stai usando il DMP pertanto i dati di calibrazione non sono accessibili dal bus e non vengono usati.
Inoltre stai usando solo i dati dell'accelerometro per calcolare pitch e roll, cosa che può andare bene solo in ambiente di lavoro statico, ovvero fai la misura solo quando il piano è fermo, se cerchi di fare la misura mentre si muove ottieni valori errati perché falsati dalle accelerazioni dovute dal movimento.

**>alessanddrob: ** NON è permesso "sporcare" un "Megatopic" con argomenti e problemi "propri" NON strettamente relativi al progetto in "Megatopic" (... come in questo caso in cui ti sei aggangiato con un TUO problema di un TUO progetto). In tal caso di apre un nuovo topic nella giusta area.

Per questa volta ho diviso il topic, la prossima cancello direttamente i post.

Guglielmo

NON è permesso "sporcare" un "Megatopic"

Anche io stavo pensando la stessa cosa , anche se non fosse stato un megatopic , il mio argomento era diverso,
lo avevo inserito solo per il discorso calibrazione
grazie dello spostamento

Per come lo uso io la misura viene fatta da fermo sicuramente , come se fosse una livella , io ho provato a mettere gli off set a 0 e mi da tutto sballato , per usarlo come bolla , devo settore gli off set su un piano e poi usarlo , ieri ho provato il tuo , ma non ho potuto fare un confronto o una comparazione con una livella,
una settimana fa confrontando i mie valori con una livella bosh , ho raggiunto i 0,2 gradi di precisione nell off set
Avevo notato che i valori sono poco stabili , anche se respiro , variano di tanto(l'accelerometro ).
questo mio progetto dovrebbe servire per mettere in piano una testa EQ6, visto che il cielo non permette visuale , passo il tempo con Arduino

alessanddrob:
Per come lo uso io la misura viene fatta da fermo sicuramente , come se fosse una livella , io ho provato a mettere gli off set a 0 e mi da tutto sballato

Ovvio che è sballato se non usi i valori di calibrazione fatti in fabbrica, non mi chiedere che procedura usano perché lo sanno solo loro, come è anche ovvio che il sensore rivela piccolissime vibrazioni, basta che appoggi una mano sul tavolo, vista la grande sensibilità e l'adc a 16 bit interno, se setti per il fondo scala +/- 2G la risoluzione è 0.061 mg.
La sensor fusion serve anche per eliminare le vibrazioni, in tutti i casi non aspettarti una risoluzione reale migliore di 0.1° e una precisione reale migliore di +/-0.2°, sono le stesse prestazioni degli inclinometri digitali che trovi su ebay/amazon per 40-50 E.

infatti sono soddisfatto da quando ho fatto il confronto con una livella bosch da 400 euro :slight_smile:
voglio vedere se il tuo sistema è più stabile del mio .
ieri per esempio , dopo averlo caricato e attivato , ogni tanto mi dava delle letture anomale scorrevano i dati e poi tic un po di letture strane , non ho indagato a fondo

ieri sera ho fatto una prova(poco attendibile penso )
ho preso una livella a bolla
ho preso iPad e iPhone e li ho messi sul tavolo e attivato la bolla elettronica
mi davano come valori una pendenza circa di 0.4 0.8 gradi (è una media tra i due apparecchi)

poi ho provato il tuo programma e mi dava 1.3 e 1.9 sono sempre medie (comunque letture nel tempo stabili)

poi ho ricaricato il mio dove uso gli offset e mi dava 0.4 .08 frasi di media , ma dato che è sensibilissimo la
misura sballa parecchio magari passa da 0.1 0.3 -03 (devo fare una media per averla piu stabile in lettura)

La livella vera era sotto un grado (ho fatto un segno sul vetro con il confronto Bosch )
le misure del tuo sono stabili ma hanno 1 grado di più (media)
gli apple deviano di pochi decimi
il mio è schizofrenico , ma non aggiunge di base 1 grado