-
Notifications
You must be signed in to change notification settings - Fork 97
Description
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 #123, sensor filter ringing, MEMS mechanical resonance, or the prolonged effect of the acceleration range saturation?
Are there any workarounds to avoid such flips?
https://m5stack.lang-ship.com/catalog/products/controller/c126_atoms3r/ links to https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/core/K128%20CoreS3/BMI270.PDF, which contains this table on page 30.
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)