BMI270 on AtomS3R (C126) sign-flips in accelerometer output around high-acceleration events
-
Hi,
This is copy of the question already asked at https://github.com/m5stack/M5Unified/issues/230
Is my test correct? Is this a bad sensor, an an expected sensor limitation? Can it be somehow corrected in hardware settings or software (I'm using M5Unified) ?
BMI270 sensor (in M5Stack ATOM S3R C126) produces sign-flips in accelerometer output during high-acceleration events.
By sign-flips I mean that consecutive samples show oscillations where acceleration values flip sign within 10 ms.To test, use the provided code and perform this experiment:
- Sensor held in hand, palm facing down
- Soft pillow placed on rigid table
- Hand dropped from ~30cm onto pillow
- At impact, hand pressed hard through pillow until table was felt underneath (reducing bounce)
Example sign-flips, focusing on z-axis (normal to the display) sensor movement trajectory.
grep "14827 " -B 2 -A 10 output.txt 14807 | raw:( -2285, -997, -6448) | cal:( -0.236, 0.570, -1.576) (sensor descent) 14817 | raw:( -3578, 235,-11929) | cal:( 0.065, 0.885, -2.914) (sensor descent) 14827 | raw:( -3527, 1539,-23443) | cal:( 0.383, 0.873, -5.725) (sensor descent) 14837 | raw:( -1190, 1725,-32768) | cal:( 0.429, 0.302, 7.999) FLIP (saturated: raw=-32768) 14847 | raw:( 2501, 1160,-32768) | cal:( 0.291, -0.599, 7.999) (saturated) 14857 | raw:( 7932, -168,-32580) | cal:( -0.033, -1.925, -7.955) FLIP BACK 14867 | raw:( 16790, -344,-27327) | cal:( -0.076, -4.087, -6.673) 14881 | raw:( 32767, -7830, 9186) | cal:( -1.904, -7.988, 2.241) GAP (14867+10=14877, but shows 14881) 14891 | raw:( 31414,-11571, 31595) | cal:( -2.817, -7.658, 7.712) 14901 | raw:( 12632, -802, 32767) | cal:( -0.188, -3.072, 7.998) (saturated again) 14911 | raw:( -4568, -9861, 24476) | cal:( -2.400, 1.127, 5.974) 14921 | raw:( 5535, -2038,-16586) | cal:( -0.490, -1.339, -4.051) ANOTHER FLIP 14931 | raw:( -5016, 12606, 9124) | cal:( 3.085, 1.236, 2.226)What could be the reason for this behavior?
I'm not very familiar with IMUs, so below there are some guesses I was able to find online.
Could this be due to the lack of FIFO reading https://github.com/m5stack/M5Unified/issues/123, sensor filter ringing, MEMS mechanical resonance, or the prolonged effect of the acceleration range saturation?Are there any workarounds to avoid such flips?
The measurement code:
/** * @file accel_drop_impact.ino * @brief Detect acceleration sign-flips during controlled hand-assisted impacts * * Test procedure: * * 1. Sensor held in hand, palm facing down * 2. Soft pillow placed on rigid table * 3. Hand dropped from ~30cm onto pillow * 4. At impact, hand pressed hard through pillow until table was felt underneath (reducing bounce) */ #include <M5Unified.h> static const uint32_t SAMPLING_FREQUENCY_HZ = 100; static const uint32_t SAMPLING_INTERVAL_MS = 1000 / SAMPLING_FREQUENCY_HZ; // 10ms void setup() { auto cfg = M5.config(); cfg.serial_baudrate = 115200; M5.begin(cfg); Serial.println("\n=== BMI270 HAND-CONTROLLED IMPACT SIGN-FLIP DETECTION ==="); Serial.println("Using M5Unified defaults: 100 Hz sampling, ±8g accel range"); Serial.println(); // Re-initialize I2C M5.In_I2C.begin((i2c_port_t)I2C_NUM_0, 45, 0); delay(100); M5.Imu.update(); delay(100); // M5Unified default calibration Serial.println("Calibrating accelerometer baseline (2 seconds)..."); M5.Imu.setCalibration(64, 64, 64); delay(2000); M5.Imu.setCalibration(0, 0, 0); Serial.println(); Serial.println("Output format:"); Serial.println(" timestamp_ms | raw:(x,y,z) LSBs | cal:(x,y,z) g"); Serial.println(" (all three axes to detect erratic values and cross-talk)"); Serial.println(); Serial.println("Ready. Start hand-assisted drops..."); Serial.println("---"); } void loop() { static uint32_t lastPrint = 0; uint32_t now = millis(); if (M5.Imu.update()) { int16_t raw_x = M5.Imu.getRawData(0); int16_t raw_y = M5.Imu.getRawData(1); int16_t raw_z = M5.Imu.getRawData(2); auto data = M5.Imu.getImuData(); float cal_x = data.accel.x; float cal_y = data.accel.y; float cal_z = data.accel.z; // Print all samples at configured sampling frequency to capture erratic sign-flips // (even low values following high values are important for detecting oscillations) if (now - lastPrint >= SAMPLING_INTERVAL_MS) { Serial.printf("%6lu | raw:(%6d,%6d,%6d) | cal:(%7.3f,%7.3f,%7.3f)\n", now, raw_x, raw_y, raw_z, cal_x, cal_y, cal_z); lastPrint = now; } } delay(1); }The serial output capture code:
#!/usr/bin/env python3 """ Capture full-frequency serial output from drop impact test to file. Usage: python capture_serial.py /dev/ttyACM0 output.txt The script will: 1. Connect to serial port at 115200 baud 2. Read all incoming data 3. Save to file in real-time 4. Press Ctrl+C to stop """ import sys import serial import time def capture_serial(port: str, output_file: str) -> None: try: ser = serial.Serial(port, 115200, timeout=1) print(f"Connected to {port} at 115200 baud") print(f"Saving to {output_file}") print("Ctrl+C to stop\n") with open(output_file, 'w') as f: start_time = time.time() line_count = 0 while True: if ser.in_waiting: line = ser.readline().decode('utf-8', errors='ignore') if line: f.write(line) f.flush() # Flush to disk immediately line_count += 1 # Print progress every 50 lines if line_count % 50 == 0: elapsed = time.time() - start_time print(f"[{elapsed:.1f}s] {line_count} lines captured") except KeyboardInterrupt: elapsed = time.time() - start_time print(f"\n\nCapture complete!") print(f"Total lines: {line_count}") print(f"Elapsed: {elapsed:.1f}s") print(f"Saved to: {output_file}") except serial.SerialException as e: print(f"Serial error: {e}") sys.exit(1) finally: if 'ser' in locals(): ser.close() if __name__ == '__main__': if len(sys.argv) != 3: print("Usage: python capture_serial.py <device> <output_file>") sys.exit(1) port = sys.argv[1] output_file = sys.argv[2] capture_serial(port, output_file)