🤖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
    2
    2
    43
    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)
      
      easytargetE 1 Reply Last reply Reply Quote 0
      • easytargetE
        easytarget @doop4
        last edited by easytarget

        @doop4
        I'm not familiar with the M5Unified driver; but I've worked with the bmi270 and written a micropython driver for it..

        Despite your pillow; you will still get a bit of bounce.. (I've spent time testing this chip, it's very hard to not get any bounce when you do this sort of test.)

        But, I think you are seeing the device going out of range; at which point you get weird/invalid readings.

        I dont see anywhere in your code setting the acceleration range; the max is +/- 16G, but it defaults to +/- 8g, which is where your readings appear to fail. I guess M5Unified has a method for setting this? Try the biggest range.

        Also; you can set filtering based on an average mode; the default is to average 4 readings (the sensor runs at 20MHz) but this can be set all the way up to 128.

        The M5Unified driver is using 'direct readings' (just like my driver), do not confuse this with 'FIFO' mode; which is an automated, IRQ triggered high-speed burst capture method. You read the dataset out from the chip buffer after the event has ended. AFIK the M5 drivers do not support this.

        1 Reply Last reply Reply Quote 0
        • First post
          Last post