This commit is contained in:
Paweł Sadowski 2025-10-22 10:20:00 +00:00
parent 60d09632a9
commit 7c020fc1f3
4 changed files with 158 additions and 8 deletions

View File

@ -1,11 +1,13 @@
from dataclasses import dataclass
from functools import cache, cached_property
import math
import re
from typing import Collection, Mapping, Self, Sequence
from utils import lin_fun_interpolate, with_ops_scalar, with_ops_unary, with_ops_vector
from utils import inrange, lin_fun_interpolate, with_ops_scalar, with_ops_unary, with_ops_vector
MPH = 0.44704
RPM = 2 * math.pi / 60
@with_ops_unary('neg')
@with_ops_vector('sub', 'add')
@ -17,6 +19,10 @@ class PropOppoint:
torque: float
rpm: float
@cached_property
def rot_speed(self) -> float:
return self.rpm * RPM
def __neg__(self) -> Self: ...
def __truediv__(self, other: float) -> Self: ...
@ -30,8 +36,19 @@ class ApcPerfdata:
oppoints: Collection[PropOppoint]
@cached_property
def rpms(self) -> Collection[float]:
return { op.rpm for op in self.oppoints }
def rpms(self) -> Sequence[float]:
return sorted({ op.rpm for op in self.oppoints })
def speed_range_at_rpm(self, rpm: float) -> tuple[float, float]:
speeds = [op.speed for op in self.rpm_series[rpm]]
return min(speeds), max(speeds)
def rpms_at_speed(self, speed: float):
return [rpm for rpm in self.rpms if inrange(speed, *self.speed_range_at_rpm(rpm))]
def rpm_range_at_speed(self, speed: float) -> tuple[float, float]:
rpms = self.rpms_at_speed(speed)
return min(rpms), max(rpms)
def _rpm_serie(self, rpm: float) -> Sequence[PropOppoint]:
return sorted([op for op in self.oppoints if op.rpm == rpm], key=lambda op: op.speed)
@ -46,6 +63,12 @@ class ApcPerfdata:
return lambda: op
return lin_fun_interpolate([op.speed for op in s], [mk_op(op) for op in s], speed)
def get_op_interp(self, rpm: float, speed: float) -> PropOppoint:
loc_rpms = self.rpms_at_speed(speed)
def mk_op(_rpm):
return lambda: self.get_op_interpspeed(_rpm, speed)
return lin_fun_interpolate(loc_rpms, [mk_op(_rpm) for _rpm in loc_rpms], rpm)
@classmethod
def from_file(cls, path: str):
@ -72,8 +95,8 @@ def parse_apc_data(raw_data: str) -> Collection[PropOppoint]:
continue
speed = vals[0] * MPH
thrust = vals[9]
torque = vals[10]
torque = vals[9]
thrust = vals[10]
data_points.append(PropOppoint(speed, thrust, torque, current_rpm))
return data_points

84
mot.py Normal file
View File

@ -0,0 +1,84 @@
from dataclasses import dataclass
from functools import cached_property
import math
from scipy import optimize
from apc_perf import RPM, ApcPerfdata
VOLT_DUTY_MAX = 0.97
@dataclass(frozen=True)
class MotorOppoint:
voltage: float
current: float
rpm: float
torque: float
@cached_property
def rot_speed(self) -> float:
return self.rpm * RPM
@property
def power_in(self) -> float:
return self.voltage * self.current
@property
def power_out(self) -> float:
return self.torque * self.rot_speed
@property
def efficiency(self) -> float:
return self.power_out/self.power_in
@dataclass(frozen=True)
class MotorData:
mot_kv: float
mot_ir: float
mot_idle_i: float
@cached_property
def mot_kv_si(self) -> float:
return self.mot_kv * RPM
@property
def effective_ir(self) -> float:
return self.mot_ir*1.5
return self.mot_ir
def mot_full_pizda(self, rpm: float, voltage: float) -> MotorOppoint:
rotspd = rpm * RPM
voltage = VOLT_DUTY_MAX * voltage
u_active = (rotspd / self.mot_kv_si)
u_heat = voltage - u_active
current = u_heat / self.effective_ir
torq = (current-self.mot_idle_i) / self.mot_kv_si
return MotorOppoint(
voltage = voltage,
current = current,
rpm = rpm,
torque = torq,
)
def op_full_pizda(mot: MotorData, prop: ApcPerfdata, speed: float, volt: float):
def f(rpm):
return prop.get_op_interp(rpm, speed).torque - mot.mot_full_pizda(rpm, volt).torque
opt = optimize.root_scalar(f, bracket = prop.rpm_range_at_speed(speed))
# print(opt)
rpm = opt.root
p_op = prop.get_op_interp(rpm, speed)
m_op = mot.mot_full_pizda(rpm, volt)
# print(p_op)
# print(m_op)
return p_op, m_op

37
test1.py Normal file
View File

@ -0,0 +1,37 @@
from apc_perf import ApcPerfdata
from mot import MotorData, op_full_pizda
at2826_900 = MotorData(
mot_kv = 900,
mot_ir = 0.035,
mot_idle_i =2.5
)
at2826_1100 = MotorData(
mot_kv = 900,
mot_ir = 0.11,
mot_idle_i = 2.5
)
rcf = MotorData(
mot_kv = 1100,
mot_ir = 0.02,
mot_idle_i = 5
)
m_at100c = MotorData(
mot_kv = 90,
mot_ir = 0.02,
mot_idle_i=4,
)
prop_12x6 = ApcPerfdata.from_file('data/PERFILES2/PER3_12x6E.dat')
# Tutaj symulacja
op_prop, op_mot = op_full_pizda(mot= at2826_900,
prop= prop_12x6,
speed= 10,
volt= 14.6)
print(op_prop)
print(op_mot)

View File

@ -7,16 +7,22 @@ class Interpolable(Protocol):
def __mul__(self: Self, other: float, /) -> Self: ...
def __neg__(self: Self, /) -> Self: ...
def inrange(x: float, xmin: float, xmax: float) -> bool:
return xmin <= x <= xmax
EXTRAPOLATE_NONE = 0
EXTRAPOLATE_CONST = 1
EXTRAPOLATE_LIN = 2
class InterpolationRangeError(ValueError):
pass
T1 = TypeVar('T1', bound=Interpolable)
def lin_fun_interpolate(x_values: Sequence[float], y_values: Sequence[Callable[[], T1]], x: float, extr_down = EXTRAPOLATE_NONE, extr_up = EXTRAPOLATE_NONE) -> T1:
# x0,x1,y0,y1 = None
if x < x_values[0]:
if extr_down == EXTRAPOLATE_NONE:
raise ValueError("x is outside the interpolation range!")
raise InterpolationRangeError("x is outside the interpolation range!")
elif extr_down == EXTRAPOLATE_CONST:
return y_values[0]()
elif extr_down == EXTRAPOLATE_LIN:
@ -28,11 +34,11 @@ def lin_fun_interpolate(x_values: Sequence[float], y_values: Sequence[Callable[[
x0, x1 = x_values[i-1], x_values[i]
y0, y1 = y_values[i-1](), y_values[i]()
if x_values[i] > x:
if x_values[i] >= x:
break
else:
if extr_up == EXTRAPOLATE_NONE:
raise ValueError("x is outside the interpolation range!")
raise InterpolationRangeError("x is outside the interpolation range!")
elif extr_up == EXTRAPOLATE_CONST:
return y1
elif extr_up == EXTRAPOLATE_LIN: