Motor Model & System Resistance Calibration
Overview
PyThrust models the electric propulsion system using motor parameters (\(K_v\), \(R_m\), \(I_0\)) taken directly from the manufacturer datasheet, which are treated as known and fixed. In practice, the system's power delivery is affected by electrical transmission losses outside the motor core: namely the internal resistance of the battery, cable resistance, connectors/solder joints, and the ESC MOSFET conduction resistance.
The PropulsionCalibrator identifies a single lumped parameter, system.resistance_ohm (\(R_{\text{system}}\)), from measured test-stand data (RPM, Thrust, and Current) using a physically-motivated power balance model.
Physical Loss Model
Symbols
| Symbol | Meaning |
|---|---|
| \(K_v\) | Motor speed constant from the datasheet |
| \(R_m\) | Motor winding resistance from the datasheet |
| \(I_0\) | Motor no-load current from the datasheet |
| \(R_{\text{system}}\) | Lumped resistance being calibrated |
| \(V_{\text{bat}}\) | Battery voltage during the test |
| \(V_{\text{applied}}\) | Average voltage commanded by throttle |
| \(V_{\text{back}}\) | Motor back-EMF voltage |
| \(V_m\) | Motor terminal voltage |
| \(I_{\text{motor}}\) | Motor winding current predicted from propeller torque |
| \(I_{\text{bat,pred}}\) | Predicted battery current |
| \(\tau\) | Propeller shaft torque from the aerodynamic database |
Step 1: Applied Voltage
At a given throttle and battery voltage, the ideal average voltage applied by PWM switching is:
Some of this voltage is lost before it reaches the motor because the battery, ESC, wires, and connectors all have finite resistance.
Step 2: Motor State at the Measured RPM
For each measured RPM, PyThrust first evaluates the propeller torque from the propeller database. That torque determines the motor current needed to spin the propeller:
The motor back-EMF voltage is:
The motor terminal voltage is then:
Step 3: Add System Losses
The calibrated resistance is added outside the motor winding resistance:
Equivalently:
This is the voltage-balance view of the same physical loss model.
Step 4: Predict Battery Current
The battery must supply both motor electrical power and the extra conduction loss in the system resistance:
The predicted battery current for a candidate resistance value is:
Here, \(R_{\text{system}}\) is the only unknown parameter being fitted.
Identification Procedure
Given N test points from a thrust stand, each point contains:
The calibrator chooses the system resistance that minimizes the normalized current error:
This is a linear optimization problem in \(R_{\text{system}}\) and is solved using scipy.optimize.least_squares with bound constraints to prevent non-physical negative resistance.
Input Format
CSV
The CSV file must contain columns for RPM, Thrust (in grams), and Battery Current (in Amps):
rpm— shaft speed in RPMthrust_g— static thrust in gramscurrent_a— battery current in Amps
Python Dict List
Quality Metrics
The calibration outcome returns residuals and \(R^2\) values to evaluate propeller and model compatibility:
- Thrust \(R^2\): Coefficient of determination for thrust. A value \(\ge 0.95\) shows good propeller aerodynamic match.
- Thrust RMSE: RMS error in thrust predictions compared to measured data.
- Current RMSE: RMS error in battery current predictions compared to measured data.
Usage
from pythrust.propulsion.autotune import ManufacturerTestPoint, PropulsionCalibrator
from pythrust.propulsion import MotorSpec, BatterySpec, SystemSpec, PropellerSpec
motor = MotorSpec(kv_rpm_per_v=860, resistance_ohm=0.0258, no_load_current_a=1.3, current_max_a=65)
battery = BatterySpec(voltage_v=14.8)
system = SystemSpec(resistance_ohm=0.05) # starting guess
propeller = PropellerSpec(diameter_m=0.3302)
cal = PropulsionCalibrator()
points = cal.load_csv("table.csv")
result = cal.calibrate(points, motor, battery, system, propeller, prop_entry)
print(result.system_resistance_ohm) # e.g. 0.095 ohm
system_calibrated = result.to_system_spec()