Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 0 additions & 61 deletions OVERVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
- [Implementation Details](#implementation-details)
- [Filter Response Curves](#filter-response-curves)
- [Bode Plot Analysis (Optional)](#bode-plot-analysis-optional)
- [Optimal P Estimation (Optional, Experimental)](#optimal-p-estimation-optional-experimental)
- [Step-Response Comparison with Other Analysis Tools](#step-response-comparison-with-other-analysis-tools)
- [Compared to PIDtoolbox/Matlab (PTstepcalc.m)](#compared-to-pidtoolboxmatlab-ptstepcalcm)
- [Compared to PlasmaTree/Python (PID-Analyzer.py)](#compared-to-plasmatreepython-pid-analyzerpy)
Expand Down Expand Up @@ -190,66 +189,6 @@ The system provides intelligent P:D tuning recommendations based on step-respons
- Shows recommendations only when the step response needs improvement (skips optimal peak 0.95–1.04)
- **Note:** Peak value measures the first maximum after crossing the setpoint; the initial transient dip is normal system behavior

#### Optimal P Estimation (Optional, Experimental)

Physics-aware P gain optimization based on response timing analysis:

- **Activation:** Disabled by default; enable with `--estimate-optimal-p` flag
- **⚠️ Status:** This feature is **experimental**. Frame-class Td targets are provisional empirical estimates requiring flight validation. Use as initial guidelines only; validation data collection is ongoing.
- **Prop Size Selection:** Use `--prop-size <size>` to specify **propeller diameter** in inches (1-15, integer values only). **This flag is required when `--estimate-optimal-p` is used.**
- **Critical:** Must match your actual prop size exactly (e.g., 6" frame with 5" props → use `--prop-size 5`)
- Supports whole numbers 1 through 15 only
- **No default is assumed** — you must explicitly specify the prop size. This prevents misleading recommendations when the wrong default is used.
- Prop size is a proxy for rotational inertia (I ∝ mass × radius²) which directly affects response time
- Each prop size has empirically-derived Td (time to 50%) targets based on observed flight data
- **Theory Foundation:** Based on BrianWhite's (PIDtoolbox author) insight that optimal response timing is aircraft-specific, not universal.
- **Theoretical Principle:** The relationship between response time and rotational inertia is approximate heuristic expressed as **Td ∝ √(I/τ)**, where I is the total rotational inertia (moment of inertia) of the airframe and τ is the available motor torque at the operating point (accounting for battery voltage, propeller aerodynamic load, and ESC response characteristics). This heuristic suggests that faster-rotating airframes (lower I) or higher available torque achieve quicker response times, while heavier/larger frames (higher I) or lower available torque naturally respond more slowly.
- **Real-World Factors:** In practice, Td is modified by many physical parameters beyond this simplified heuristic: mass distribution across the frame, motors, battery, and propeller placement; motor torque characteristics and efficiency across throttle range; propeller aerodynamic loading and blade pitch; battery voltage and sag during maneuvers; and ESC throttle response lag. Rotational inertia (influenced by mass × radius²) and propeller size both contribute significantly to these variations.
- **Empirical Approach:** The frame-class targets below are **empirical estimates derived from flight data**, not pure physics calculations. Propeller size is used as a practical proxy for rotational inertia because it correlates strongly with frame mass and arm length. Targets must be validated against actual flight logs for each specific build configuration, as the theoretical model cannot account for all real-world complexities.
- **Frame-Class Targets (Provisional — `TD_TARGETS` in `src/constants.rs` is authoritative):**
- Targets below are ±25% acceptance bands for pilots. If measured Td falls within target ± tolerance, the tune is acceptable. These are NOT measurement uncertainty values.

| Prop Size | Frame Type | Target Td | Tolerance |
|-----------|------------|-----------|-----------|
| 1" | tiny whoop | 40 ms | ± 10.0 ms |
| 2" | micro | 35 ms | ± 8.75 ms |
| 3" | toothpick/cinewhoop | 30 ms | ± 7.5 ms |
| 4" | racing | 25 ms | ± 6.25 ms |
| 5" | freestyle/racing | 20 ms | ± 5.0 ms |
| 6" | long-range | 28 ms | ± 7.0 ms |
| 7" | long-range | 37.5 ms | ± 9.375 ms |
| 8" | long-range | 47 ms | ± 11.75 ms |
| 9" | cinelifter | 56 ms | ± 14.0 ms |
| 10" | cinelifter | 65 ms | ± 16.25 ms |
| 11" | heavy-lift | 75 ms | ± 18.75 ms |
| 12" | heavy-lift | 85 ms | ± 21.25 ms |
| 13" | heavy-lift | 95 ms | ± 23.75 ms |
| 14" | heavy-lift | 105 ms | ± 26.25 ms |
| 15" | heavy-lift | 115 ms | ± 28.75 ms |

- **Reading Td deviations:** Faster than target + low noise = headroom for P increase; slower + high noise = mechanical issues or wrong prop size; within target + high noise = P at physical limits.
- **Developer validation:** A stricter ±10% statistical criterion (across ≥10 flights per frame class) is used to confirm that TD_TARGETS predictions match actual measurements. See GitHub issues for current validation status.
- **Analysis Components:**
- Collects individual Td measurements from all valid step response windows
- Calculates response consistency metrics (mean, std dev, coefficient of variation)
- Compares measured Td against frame-class targets
- Classifies Td deviation (significantly slower, moderately slower, within target, faster)
- Provides P gain recommendations based on deviation and noise levels
- **Recommendation Types:**
- **P Increase:** When Td is slower than target with acceptable noise levels
- **Optimal:** When Td is within target range or at physical limits
- **P Decrease:** When Td is faster than target with high noise (rare)
- **Investigate:** When measurements suggest mechanical issues or incorrect frame class
- **Output Format:** Detailed console report with:
- Current P gain and measured Td statistics
- Frame class comparison and deviation percentage
- Physical limit indicators (response speed, noise level, consistency)
- Clear recommendation with reasoning
- **Relationship to P:D Recommendations:**
- P:D ratio recommendations (existing feature): Analyze peak overshoot → adjust D-term
- Optimal P estimation (new feature): Analyze response timing → adjust P magnitude
- Both features are complementary and can run simultaneously for complete tuning guidance

### Step-Response Comparison with Other Analysis Tools

This implementation provides detailed and configurable analysis of flight controller performance. The modular design and centralized configuration system make it adaptable for various analysis requirements.
Expand Down
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ Usage: ./BlackBox_CSV_Render <input1> [<input2> ...] [OPTIONS]

--butterworth: Show Butterworth PT1 cutoffs on gyro/D-term spectrum plots.
--dps <value>: Deg/s threshold for detailed step response plots (positive number).
--estimate-optimal-p: Enable optimal P estimation with frame-class targets (requires --prop-size).
--prop-size <size>: Propeller diameter in inches (1-15, whole-number only). Required with --estimate-optimal-p.

=== GENERAL ===

Expand Down Expand Up @@ -78,9 +76,6 @@ Arguments can be in any order. Wildcards (e.g., *.csv) are shell-expanded and wo
```shell
./target/release/BlackBox_CSV_Render path/to/ --step --setpoint --motor --output-dir ./all-selective
```
```shell
./target/release/BlackBox_CSV_Render path/to/BTFL_Log.csv --step --estimate-optimal-p --prop-size 5
```

### Output

Expand Down
20 changes: 8 additions & 12 deletions src/axis_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,25 @@
/// Panics if index is greater than 2
#[allow(dead_code)]
pub fn axis_name(index: usize) -> &'static str {
AXIS_NAMES.get(index).copied().unwrap_or_else(|| {
panic!(
match index {
0 => "Roll",
1 => "Pitch",
2 => "Yaw",
_ => panic!(
"Invalid axis index: {}. Expected 0 (Roll), 1 (Pitch), or 2 (Yaw)",
index
)
})
),
}
}

/// Number of axes (Roll, Pitch, Yaw)
pub const AXIS_COUNT: usize = 3;

/// Number of primary control axes (Roll, Pitch only - excludes Yaw)
pub const ROLL_PITCH_AXIS_COUNT: usize = 2;

/// Get all axis names as a static array
pub const AXIS_NAMES: [&str; AXIS_COUNT] = ["Roll", "Pitch", "Yaw"];

// Compile-time assertions to prevent invariant drift
// Compile-time check to prevent drift between AXIS_COUNT and AXIS_NAMES.len()
const _: [(); AXIS_COUNT] = [(); AXIS_NAMES.len()];
const _: () = assert!(
ROLL_PITCH_AXIS_COUNT > 0 && ROLL_PITCH_AXIS_COUNT < AXIS_COUNT,
"ROLL_PITCH_AXIS_COUNT must be > 0 and < AXIS_COUNT"
);

#[cfg(test)]
mod tests {
Expand Down
105 changes: 1 addition & 104 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,107 +259,4 @@ pub const PSD_EPSILON: f64 = 1e-12; // Guard against division by zero for PSD va
pub const MAGNITUDE_PLOT_MARGIN_DB: f64 = 10.0; // Padding above/below magnitude data for plot range
pub const PHASE_PLOT_MARGIN_DEG: f64 = 30.0; // Padding above/below phase data for plot range

// Optimal P Estimation Constants
// Frame-class-aware Td (time to 50%) targets in milliseconds
// Provisional estimates based on torque-to-rotational-inertia scaling: Td ∝ 1/(mass × radius²)
// TODO: Validate with bench tests and actual flight data across all frame classes

/// Td target specification for a frame class
#[derive(Debug, Clone, Copy)]
pub struct TdTargetSpec {
pub target_ms: f64,
pub tolerance_ms: f64,
}

impl TdTargetSpec {
/// Create without typical weight (for existing empirical targets)
pub const fn new_simple(target_ms: f64) -> Self {
Self {
target_ms,
tolerance_ms: target_ms * 0.25,
}
}

/// Get TdTargetSpec for a given frame size in inches (1-15)
/// Returns None if the size is out of valid range
pub fn for_frame_inches(inches: usize) -> Option<&'static TdTargetSpec> {
if (1..=15).contains(&inches) {
Some(&TD_TARGETS[inches - 1])
} else {
None
}
}
}

/// Td targets for all frame classes (1" through 15")
/// Index: 0=1", 1=2", ..., 14=15"
/// Note: These values are intentionally non-monotonic — Td decreases from 1" to 5" because
/// 5" frames are the most optimized racing platform and typically have the best
/// thrust-to-inertia ratio (lower Td). For frames 6" and larger, Td increases to reflect
/// heavier craft that prioritize stability and exhibit larger rotational inertia.
/// TODO: These are provisional empirical values that require systematic flight validation;
/// keep the explanatory rationale above so future maintainers understand the non-monotonic shape.
pub const TD_TARGETS: [TdTargetSpec; 15] = [
TdTargetSpec::new_simple(40.0), // 1" tiny whoop (30-50ms)
TdTargetSpec::new_simple(35.0), // 2" micro (26-44ms)
TdTargetSpec::new_simple(30.0), // 3" toothpick/cinewhoop (23-38ms)
TdTargetSpec::new_simple(25.0), // 4" racing (19-31ms)
TdTargetSpec::new_simple(20.0), // 5" freestyle/racing (15-25ms, common baseline)
TdTargetSpec::new_simple(28.0), // 6" long-range (21-35ms)
TdTargetSpec::new_simple(37.5), // 7" long-range (28-47ms)
TdTargetSpec::new_simple(47.0), // 8" long-range (35-59ms)
TdTargetSpec::new_simple(56.0), // 9" cinelifter (42-70ms)
TdTargetSpec::new_simple(65.0), // 10" cinelifter (49-81ms)
TdTargetSpec::new_simple(75.0), // 11" heavy-lift (56-94ms)
TdTargetSpec::new_simple(85.0), // 12" heavy-lift (64-106ms)
TdTargetSpec::new_simple(95.0), // 13" heavy-lift (71-119ms)
TdTargetSpec::new_simple(105.0), // 14" heavy-lift (79-131ms)
TdTargetSpec::new_simple(115.0), // 15" heavy-lift (86-144ms)
];

// High-frequency noise analysis for P headroom estimation
// D-term energy above this frequency threshold indicates noise constraints
pub const DTERM_HF_CUTOFF_HZ: f64 = 200.0; // Frequency above which high-frequency noise is measured
pub const DTERM_HF_ENERGY_THRESHOLD: f64 = 0.15; // 15% of total D-term energy (high noise level)
pub const DTERM_HF_ENERGY_MODERATE: f64 = 0.10; // 10% of total D-term energy (moderate noise level)

// Response consistency quality control
// Ensures Td measurements are reliable across multiple step responses
pub const TD_CONSISTENCY_MIN_THRESHOLD: f64 = 0.85; // 85% of responses should be within ±1 std dev
pub const TD_COEFFICIENT_OF_VARIATION_MAX: f64 = 0.20; // 20% CV (std/mean) is acceptable

// P headroom estimation multipliers
// Conservative approach for users who want safe incremental improvements
pub const P_HEADROOM_CONSERVATIVE_MULTIPLIER: f64 = 1.05; // +5% from current P
// Moderate approach for experienced pilots
pub const P_HEADROOM_MODERATE_MULTIPLIER: f64 = 1.10; // +10% from current P
// Aggressive approach for optimization (use with caution)
#[allow(dead_code)]
pub const P_HEADROOM_AGGRESSIVE_MULTIPLIER: f64 = 1.15; // +15% from current P (reserved for future use)

// P reduction multipliers (when Td is too fast or noise is too high)
pub const P_REDUCTION_MODERATE_MULTIPLIER: f64 = 0.95; // -5% from current P
#[allow(dead_code)]
pub const P_REDUCTION_AGGRESSIVE_MULTIPLIER: f64 = 0.90; // -10% from current P

// Td statistics computation constants
pub const MIN_TD_MS: f64 = 0.1; // Minimum valid Td (time to 50%) in milliseconds (domain-appropriate threshold)
pub const TD_MEAN_EPSILON: f64 = 1e-12; // Threshold for near-zero mean values (avoid division by zero)
pub const TD_SAMPLES_MIN_FOR_STDDEV: usize = 2; // Minimum samples needed for std dev calculation

// Td deviation thresholds (percentage deviation from target)
// Deviation thresholds for classifying Td behavior
// Note: The thresholds are intentionally asymmetric — there is no separate
// 'moderately faster' threshold. Faster-than-target deviations are treated
// more strictly because they often indicate potential oscillation or unsafe
// aggressive tuning. Therefore any significant speed-up beyond
// TD_DEVIATION_SIGNIFICANTLY_FASTER_THRESHOLD is flagged immediately. Slower
// deviations are given two thresholds (moderate and significant) to allow
// finer-grained handling when Td is lagging behind the target.
pub const TD_DEVIATION_SIGNIFICANTLY_SLOWER_THRESHOLD: f64 = 30.0; // > 30% slower
pub const TD_DEVIATION_MODERATELY_SLOWER_THRESHOLD: f64 = 15.0; // > 15% slower
pub const TD_DEVIATION_SIGNIFICANTLY_FASTER_THRESHOLD: f64 = -15.0; // < -15% faster

// Optimal P estimation data collection thresholds
pub const OPTIMAL_P_MIN_DTERM_SAMPLES: usize = 100; // Minimum D-term samples for noise analysis
pub const OPTIMAL_P_SECONDS_TO_MS_MULTIPLIER: f64 = 1000.0; // Convert seconds to milliseconds
// src/constants.rs
1 change: 0 additions & 1 deletion src/data_analysis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ pub mod derivative;
pub mod fft_utils;
pub mod filter_delay;
pub mod filter_response;
pub mod optimal_p_estimation;
pub mod spectral_analysis;
pub mod transfer_function_estimation;

Expand Down
Loading
Loading