🤖Have you ever tried Chat.M5Stack.com before asking??😎
    M5Stack Community
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Register
    • Login

    BMI270 on AtomS3R (C126) sign-flips in accelerometer output around high-acceleration events

    Atom
    1
    1
    5
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • D
      doop4
      last edited by

      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:

      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)

      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)
      
      1 Reply Last reply Reply Quote 0
      • First post
        Last post