Not finished, testing webp for Foregjo

This commit is contained in:
John Poole 2026-04-16 14:55:29 -07:00
commit c6576c80fe
4 changed files with 373 additions and 0 deletions

View file

@ -0,0 +1,296 @@
```markdown
<!-- 20260416 ChatGPT | $Header$ -->
# Exercise 21 — Six-Axis IMU Characterization
## Overview
Exercise 21 establishes a foundational understanding of the six-axis inertial measurement unit (IMU) on the T-Beam Supreme. The IMU (QMI8658) provides:
- **Accelerometer** (X, Y, Z) — measures acceleration, including gravity
- **Gyroscope** (X, Y, Z) — measures angular velocity
This exercise focuses on interpreting **accelerometer data** to determine device orientation relative to gravity.
---
## Objective
The goals of this exercise are:
1. Demonstrate that IMU outputs are **frame-dependent but physically consistent**
2. Show that **startup orientation does not define the IMU axes**
3. Compute **roll and pitch** from raw accelerometer data
4. Quantify real-world deviations from ideal values
5. Identify sources of measurement error
---
## Test Setup
Two static orientations of the device were evaluated:
### Orientation A — Sideways
Device resting on its side (edge of AlleyCat case)
![Sideways Orientation](./img/sideways_DSC_5195.webp)
Measured accelerometer values:
```
Ax = -0.951
Ay = -0.043
Az = -0.003
```
---
### Orientation B — Upright
Device standing upright (antenna vertical)
![Upright Orientation](img/upright_DSC_5194.webp)
Measured accelerometer values:
```
Ax = 0.028
Ay = 0.987
Az = 0.012
```
---
## Methodology
Roll and pitch were computed using standard inertial navigation formulas:
```
roll = atan2(Az, Ay)
pitch = atan2(-Ax, sqrt(Ay² + Az²))
```
These equations derive orientation from the **gravity vector projection** onto the sensor axes.
---
## Implementation
A Perl script was used to compute roll and pitch:
```
scripts/imu_roll_pitch_demo.pl
```
This script:
- Accepts predefined accelerometer values
- Computes roll and pitch in radians and degrees
- Uses Perls built-in `atan2()` for accurate quadrant handling
---
## Results
### Sideways Orientation
```
roll = -176.009°
pitch = 87.405°
```
Interpretation:
- Pitch ≈ 90° → confirms device is rotated onto its side
- Roll near ±180° is expected due to sign conventions when vertical
---
### Upright Orientation
```
roll = 0.697°
pitch = -1.625°
```
Interpretation:
- Both values near 0° → device is nearly level
- Small deviations indicate real-world imperfections
---
## Key Findings
### 1. IMU Axes Are Fixed
The IMU coordinate system is defined by the sensor hardware and PCB layout:
- It does **not change at startup**
- It is independent of how the device is oriented when powered on
---
### 2. Orientation Is Derived from Gravity
The accelerometer measures the gravity vector:
- Different orientations produce different raw values
- The underlying physics is consistent
---
### 3. Roll and Pitch Are Orientation-Invariant
While raw values differ between orientations:
- Computed roll/pitch reflect true physical orientation
- Results are consistent regardless of startup pose
---
## Sources of Error
The observed deviations (~12°) are expected and arise from several factors:
### Surface Imperfection
- Table or support surface not perfectly level
- Enclosure geometry (AlleyCat case) introduces tilt
---
### Sensor Bias (Offset)
- Example: Ax ≠ 0 when it should be
- Typical of MEMS sensors
---
### Scale Error
Measured magnitude:
```
|A| ≈ 0.988 g (ideal = 1.000 g)
```
Indicates slight gain inaccuracy.
---
### Axis Misalignment
- Sensor axes are not perfectly orthogonal
- Manufacturing tolerances introduce small angular errors
---
### Noise and Quantization
- Finite precision (3 decimal places)
- Minor fluctuations expected
---
## Limitations of Exercise 21
This exercise deliberately omits several important elements:
### No Absolute Heading
- Without a magnetometer, yaw (heading) is undefined
- Only roll and pitch can be determined
---
### No Sensor Fusion
- Gyroscope data is not integrated
- No filtering (e.g., complementary or Kalman)
---
### No Calibration
- Raw sensor values are used directly
- Bias and scale errors are uncorrected
---
### Static Analysis Only
- No dynamic motion analysis
- Gyroscope output not utilized
---
## Significance
Exercise 21 provides a critical baseline:
- Confirms correct IMU operation
- Establishes device coordinate frame
- Demonstrates physical interpretation of accelerometer data
- Quantifies real-world sensor error
This forms the foundation for:
- Magnetometer integration (Exercise 22)
- Tilt-compensated compass
- Full sensor fusion (AHRS)
---
## Conclusion
The IMU behaves as expected:
- Raw outputs vary with orientation
- Derived angles correctly reflect physical pose
- Measured errors are consistent with typical MEMS performance
Most importantly:
> The IMU does not define orientation — it measures vectors.
> Orientation is derived through mathematical interpretation of those vectors.
---
## Next Steps
Exercise 22 will extend this work by introducing:
- Magnetometer (QMC6310)
- Absolute heading (yaw)
- Tilt compensation using roll and pitch
---
## References
- Perl Script: `scripts/imu_roll_pitch_demo.pl`
- Images:
- `img/upright_DSC_5194.webp`
- `img/sideways_DSC_5195.webp`
---
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

View file

@ -0,0 +1,77 @@
#!/usr/bin/env perl
# 20260416 ChatGPT
# $Header$
#
# Example:
# perl imu_roll_pitch_demo.pl
# perl imu_roll_pitch_demo.pl sideways
# perl imu_roll_pitch_demo.pl upright
#
# Notes:
# * Computes roll and pitch from accelerometer values.
# * Uses:
# roll = atan2(Az, Ay)
# pitch = atan2(-Ax, sqrt(Ay^2 + Az^2))
# * Angles are printed in both radians and degrees.
#
use strict;
use warnings;
use POSIX qw(atan2);
my %cases = (
sideways => {
Ax => -0.951,
Ay => -0.043,
Az => -0.003,
},
upright => {
Ax => 0.028,
Ay => 0.987,
Az => 0.012,
},
);
my @requested_cases;
if (@ARGV) {
for my $name (@ARGV) {
if (!exists $cases{$name}) {
die "Unknown case '$name'. Valid cases: sideways upright\n";
}
push @requested_cases, $name;
}
}
else {
@requested_cases = qw(sideways upright);
}
for my $name (@requested_cases) {
my $ax = $cases{$name}->{Ax};
my $ay = $cases{$name}->{Ay};
my $az = $cases{$name}->{Az};
my $roll_rad = atan2($az, $ay);
my $pitch_rad = atan2(-$ax, sqrt($ay * $ay + $az * $az));
my $roll_deg = rad_to_deg($roll_rad);
my $pitch_deg = rad_to_deg($pitch_rad);
printf "%s\n", ucfirst($name);
printf " A: X=% .3f Y=% .3f Z=% .3f\n", $ax, $ay, $az;
printf " roll (rad) = % .6f\n", $roll_rad;
printf " roll (deg) = % .3f\n", $roll_deg;
printf " pitch (rad) = % .6f\n", $pitch_rad;
printf " pitch (deg) = % .3f\n", $pitch_deg;
print "\n";
}
exit 0;
sub rad_to_deg {
my ($rad) = @_;
return $rad * 180.0 / pi();
}
sub pi {
return 4.0 * atan2(1.0, 1.0);
}