Accelerometer GY-521 G-Kräfte berechnen

Hallo Leute,

Gerade experimentiere ich etwas mit einem GY-521 herum. Mit dem Sensor würde ich gerne Beschleunigung und Verzögerung bis ca. 5g an der Y-Achse unabhängig der Lage des Sensors messen. Nachdem der Messbereich auf ±8g eingestellt und der Filter testweise aktiviert war bekam ich erstaunlich schnelle Messungen im Plotter. Das Dividieren der Rohdaten funktioniert auch um auf die reinen g-Kräfte zu kommen. Nun müssen ja noch die Lagekräfte bereinigt werden, denn wenn ich den Sensor 90° neige bekomme ich natürlich 1g angezeigt.
Ein Versuch mit vector = sqrt ( x * x + y * y + z * z);
scheitert hier schon, es kommen total unplausible Werte heraus. Kann mir jemand sagen wie ich hier vorgehen muss?
Was ich auch nicht so ganz begreife ist wann genau man bei I2C Wire.endTransmission() true und wann false einsetzt. Hoffe es passt so…

#include "Wire.h"

int16_t acc_x, acc_y, acc_z, vector; // accelerometer raw data
float g;
int16_t gyro_x, gyro_y, gyro_z; // gyro raw data

int16_t tmp; // temperature data
char tmp_str[7]; // temporary variable used in convert function
char* convert_int16_to_str(int16_t i) { // converts int16 to string. Moreover, resulting strings will have the same length in the debug monitor.
  sprintf(tmp_str, "%6d", i);
  return tmp_str;
}

void setup() {
   Serial.begin(115200);

  Wire.begin();
  // Scaling
  Wire.beginTransmission(0x68);
  Wire.write(0x1C);  // accel config register
  Wire.write(0b00010000); //Setting the accel to +/- 8g
  Wire.endTransmission(true); 
  // Enable
  Wire.beginTransmission(0x68);
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0); // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  // Filter
  Wire.beginTransmission(0x68);
  Wire.write(0x1A);                    // accel filter register
  Wire.write(0x06);                    // 0x00=off, 0x01=184 Hz, 0x02=94 Hz, 0x03=44 Hz, 0x04=21 Hz, 0x05=10Hz, 0x06=5Hz
  Wire.endTransmission(true);
}
void loop() {
  Wire.beginTransmission(0x68);
  Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);

  Wire.requestFrom(0x68, 14); // request a total of 7*2=14 registers

  acc_x = Wire.read();
  acc_x <<= 8;
  acc_x |= Wire.read();
  acc_y = Wire.read();
  acc_y <<= 8;
  acc_y |= Wire.read();
  acc_z = Wire.read();
  acc_z <<= 8;
  acc_z |= Wire.read();

  tmp = Wire.read();
  tmp <<= 8;
  tmp |= Wire.read();

  gyro_x = Wire.read();
  gyro_x <<= 8;
  gyro_x |= Wire.read();
  gyro_y = Wire.read();
  gyro_y <<= 8;
  gyro_y |= Wire.read();
  gyro_z = Wire.read();
  gyro_z <<= 8;
  gyro_z |= Wire.read();

  //  vector = sqrt ( acc_x * acc_x + acc_y * acc_y + acc_z * acc_z);

  g = ( acc_y / 4.096); // hier sollte vector eingesetzt werden

  Serial.print("g = "); Serial.print(g);
  Serial.println();
}

vector = sqrt ( x * x + y * y + z * z);

Die quadratische Wurzel ist bei 2 Dimensionen richtig. Bei 3 Dimensionen braucht es 2 mal die Quadratwurzel (einmal den Vektor in 2 Dimensionen und dann dieses Resultat mit der dritten Dimension).
Grüße Uwe

uwefed:
Die quadratische Wurzel ist bei 2 Dimensionen richtig. Bei 3 Dimensionen braucht es 2 mal die Quadratwurzel (einmal den Vektor in 2 Dimensionen und dann dieses Resultat mit der dritten Dimension).
Grüße Uwe

Vorsicht, ich fürchte du hast vergessen, das die eine Quadratwurzel noch quadriert werden muss. Wodurch sie sich wieder heraus kürzt:
vector2d=sqrt(x²+y²)
vector3d=sqrt(vector2d²+z²)
vector3d=sqrt((sqrt(x²+y²))²+z²)
vector3d=sqrt((x²+y²)+z²))
vector3d=sqrt(x²+y²+z²)

Diese Formel ist also korrekt:

vector = sqrt ( x * x + y * y + z * z);

Uhlfred:

int16_t acc_x, acc_y, acc_z, vector; // accelerometer raw data

Aber vector sollte kein Integer sein, sondern ein Fleißkommatyp wie z.B. float. Sonst wirds verdammt ungenau.

gruß lorenz

Also so wie es aussieht hat es geklappt. Meine unplausiblen Werte kamen aber daher dass ich versucht habe in der Formel int16_t zu multiplizieren und habe natürlich darauf Überläufe bekommen und ein absolutes Chaos an Ergebnissen. Der Sensor hat bei der Skalierung auf ±8g einen Wert von 4069 pro 1g je Achse. Daher musste ich auf int32_t berechnen, da es aber nur positive Ergebnisse gibt reicht in dem Fall ja uint_32t.

vector = sqrt ( (uint32_t)acc_x * acc_x + (uint32_t)acc_y * acc_y + (uint32_t)acc_z * acc_z);

Nun noch 1g abziehen und einen kleinen Nullabgleich machen (wohl ziemlich Grob das Ganze da nicht für jede Achse einzeln aber für meinen Zweck ausreichend).

Jetzt sollte bei einem “Aufprall” von 1g bei länger als 50ms ein Ausgang schalten bis man über einen Taster wieder quittiert, was auch augenscheinlich funktioniert…
Die für mich irrelevanten Register Temp und Gyro habe ich entfernt.

#include "Wire.h"

// BASIC SETTINGS
const int impactlimit = 1000;    // trigger limit
const int impactduration = 50;    // minimum time exceeded trigger limit
const int zero = 1042;
const int filter = 0x02;    // filter setting 0x00=off, 0x01=184 Hz, 0x02=94 Hz, 0x03=44 Hz, 0x04=21 Hz, 0x05=10Hz, 0x06=5Hz
// -------------------------------------------------------------------------------------------------------------------------

int16_t acc_x, acc_y, acc_z;    // accelerometer raw data
float vector, g;
int RXLED = 17;
unsigned long previousMillis = 0;

void setup() {
  pinMode(7, INPUT_PULLUP);
  digitalWrite(RXLED, HIGH);

  Serial.begin(115200);

  Wire.begin();
  Wire.setClock(400000);
  // Enable
  Wire.beginTransmission(0x68);
  Wire.write(0x6B);    // PWR_MGMT_1 register
  Wire.write(0);    // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  // Scaling
  Wire.beginTransmission(0x68);
  Wire.write(0x1C);    // accel config register
  Wire.write(0b00010000);    // Setting the accel to +/- 8g
  Wire.endTransmission(true);
  // Filter
  Wire.beginTransmission(0x68);
  Wire.write(0x1A);    // accel filter register
  Wire.write(filter);    // filter setting
  Wire.endTransmission(true);
}

void loop() {
  Wire.beginTransmission(0x68);
  Wire.write(0x3B);    // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);

  Wire.requestFrom(0x68, 6);    // request of 6 registers for acc (additional 8 for temp + gyro)

  acc_x = Wire.read();
  acc_x <<= 8;
  acc_x |= Wire.read();
  acc_y = Wire.read();
  acc_y <<= 8;
  acc_y |= Wire.read();
  acc_z = Wire.read();
  acc_z <<= 8;
  acc_z |= Wire.read();

  vector = sqrt ( (uint32_t)acc_x * acc_x + (uint32_t)acc_y * acc_y + (uint32_t)acc_z * acc_z);
  g = ( vector / 4.096) - zero;

  unsigned long currentMillis = millis();

  if (digitalRead(7) == LOW) {
    digitalWrite(RXLED, HIGH);
  } else if ((g >= impactlimit) & (currentMillis - previousMillis >= impactduration)) {
    digitalWrite(RXLED, LOW);
  } else if (g < impactlimit) {
    previousMillis = currentMillis;
  }

  Serial.print(-5000);  // To freeze the lower limit
  Serial.print(" ");
  Serial.print(5000);  // To freeze the upper limit
  Serial.print(" ");
  Serial.print("g = "); Serial.print(g);
  Serial.println();
}

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