Hallo alle
Ich bin den Grove - 3-axis digital compass v2.0 am Testen. Doch ich stelle fest, dass der ziemlich heftig jittert. Also wenn ich den absolut still halte schwanken die Messwerte bis zu +-20 Grad. Ist das normal?
Hab ihn calibriert und den Code so angepasst, dass er den Mittelwert von 20 Messungen nimmt. Aber damit ist er immer noch nicht wirklich genau und zeigt auch nicht korrekt nach Norden. Zudem speichere ich die Kalibrationswerte ins EEPROM.
Der Code:
#include <Arduino.h>
#include <Wire.h>
#include <EEPROM.h> // Add EEPROM library
#include "bmm150.h"
#include "bmm150_defs.h"
BMM150 bmm = BMM150();
bmm150_mag_data value_offset;
// EEPROM addresses
#define EEPROM_CALIBRATED_FLAG 0
#define EEPROM_OFFSET_X 1
#define EEPROM_OFFSET_Y 5
#define EEPROM_OFFSET_Z 9
void setup() {
Serial.begin(9600);
if (bmm.initialize() == BMM150_E_ID_NOT_CONFORM) {
Serial.println("Chip ID can not read!");
while (1);
} else {
Serial.println("Initialize done!");
}
// Try to load calibration from EEPROM
if (EEPROM.read(EEPROM_CALIBRATED_FLAG) == 1) {
readCalibrationFromEEPROM();
Serial.println("Calibration loaded from EEPROM");
} else {
Serial.println("Start figure-8 calibration after 3 seconds.");
delay(3000);
calibrate(10000);
saveCalibrationToEEPROM();
Serial.println("Calibrate done and saved to EEPROM");
}
}
void saveCalibrationToEEPROM() {
// Save flag to indicate calibration exists
EEPROM.write(EEPROM_CALIBRATED_FLAG, 1);
// Save X offset (int16_t = 2 bytes)
EEPROM.put(EEPROM_OFFSET_X, value_offset.x);
EEPROM.put(EEPROM_OFFSET_Y, value_offset.y);
EEPROM.put(EEPROM_OFFSET_Z, value_offset.z);
}
void readCalibrationFromEEPROM() {
EEPROM.get(EEPROM_OFFSET_X, value_offset.x);
EEPROM.get(EEPROM_OFFSET_Y, value_offset.y);
EEPROM.get(EEPROM_OFFSET_Z, value_offset.z);
}
/**
@brief Do figure-8 calibration for a limited time to get offset values of x/y/z axis.
@param timeout - seconds of calibration period.
*/
void calibrate(uint32_t timeout) {
int16_t value_x_min = 0;
int16_t value_x_max = 0;
int16_t value_y_min = 0;
int16_t value_y_max = 0;
int16_t value_z_min = 0;
int16_t value_z_max = 0;
uint32_t timeStart = 0;
bmm.read_mag_data();
value_x_min = bmm.raw_mag_data.raw_datax;
value_x_max = bmm.raw_mag_data.raw_datax;
value_y_min = bmm.raw_mag_data.raw_datay;
value_y_max = bmm.raw_mag_data.raw_datay;
value_z_min = bmm.raw_mag_data.raw_dataz;
value_z_max = bmm.raw_mag_data.raw_dataz;
delay(100);
timeStart = millis();
while ((millis() - timeStart) < timeout) {
bmm.read_mag_data();
/* Update x-Axis max/min value */
if (value_x_min > bmm.raw_mag_data.raw_datax) {
value_x_min = bmm.raw_mag_data.raw_datax;
// Serial.print("Update value_x_min: ");
// Serial.println(value_x_min);
} else if (value_x_max < bmm.raw_mag_data.raw_datax) {
value_x_max = bmm.raw_mag_data.raw_datax;
// Serial.print("update value_x_max: ");
// Serial.println(value_x_max);
}
/* Update y-Axis max/min value */
if (value_y_min > bmm.raw_mag_data.raw_datay) {
value_y_min = bmm.raw_mag_data.raw_datay;
// Serial.print("Update value_y_min: ");
// Serial.println(value_y_min);
} else if (value_y_max < bmm.raw_mag_data.raw_datay) {
value_y_max = bmm.raw_mag_data.raw_datay;
// Serial.print("update value_y_max: ");
// Serial.println(value_y_max);
}
/* Update z-Axis max/min value */
if (value_z_min > bmm.raw_mag_data.raw_dataz) {
value_z_min = bmm.raw_mag_data.raw_dataz;
// Serial.print("Update value_z_min: ");
// Serial.println(value_z_min);
} else if (value_z_max < bmm.raw_mag_data.raw_dataz) {
value_z_max = bmm.raw_mag_data.raw_dataz;
// Serial.print("update value_z_max: ");
// Serial.println(value_z_max);
}
Serial.print(".");
delay(100);
}
value_offset.x = value_x_min + (value_x_max - value_x_min) / 2;
value_offset.y = value_y_min + (value_y_max - value_y_min) / 2;
value_offset.z = value_z_min + (value_z_max - value_z_min) / 2;
}
float getAverageHeading(int samples) {
float headingSum = 0;
float sinSum = 0;
float cosSum = 0;
// Collect samples
for (int i = 0; i < samples; i++) {
// Read raw data and apply calibration
bmm.read_mag_data();
bmm150_mag_data value;
value.x = bmm.raw_mag_data.raw_datax - value_offset.x;
value.y = bmm.raw_mag_data.raw_datay - value_offset.y;
value.z = bmm.raw_mag_data.raw_dataz - value_offset.z;
// Calculate heading in radians
float heading = atan2(value.x, value.y);
// Add sin and cos components (avoids averaging issues at 0/360 degrees)
sinSum += sin(heading);
cosSum += cos(heading);
delay(10); // Short delay between readings
}
// Calculate average heading using arctangent of average sin/cos components
float avgHeading = atan2(sinSum, cosSum);
// Convert to degrees and normalize to 0-360
if (avgHeading < 0) {
avgHeading += 2 * PI;
}
return avgHeading * 180 / M_PI;
}
void loop() {
// Add serial command option for recalibration
if (Serial.available() > 0) {
char cmd = Serial.read();
if (cmd == 'c' || cmd == 'C') {
Serial.println("Start recalibration...");
calibrate(10000);
saveCalibrationToEEPROM();
Serial.println("Recalibration done and saved!");
}
}
// Get average heading from 20 readings
float avgHeading = getAverageHeading(20);
Serial.print("Avg Heading: ");
Serial.println(avgHeading);
delay(100);
}