diff --git a/motor_map_model.asv b/OUTDATED_motor_map_model.asv similarity index 100% rename from motor_map_model.asv rename to OUTDATED_motor_map_model.asv diff --git a/motor_map_model_function.m b/OUTDATEDmotor_map_model_function.m similarity index 100% rename from motor_map_model_function.m rename to OUTDATEDmotor_map_model_function.m diff --git a/motor_map_model.m b/motor_map_model.m deleted file mode 100644 index b37fd00..0000000 --- a/motor_map_model.m +++ /dev/null @@ -1,77 +0,0 @@ -% updated motor map demo with datasheet limits - -clear; clc; close all; -% Datasheet constants -p = 5; % pole pairs -R_s = 0.135; % stator resistance [ohm] -Ld = 0.24e-3; % [H] -Lq = 0.12e-3; % [H] -kt = 0.26; % Nm/Arms -J = 2.74e-4; % [kg*m^2] - -I_max = 105; % Arms -V_dc = 350; % V (nominal DC bus) -V_margin= 30; % V reserve margin - -% Load MAT files -data80 = load('A2370DD_T80C.mat'); -data100 = load('A2370DD_T100C.mat'); -data120 = load('A2370DD_T120C.mat'); - -% Axes setup (hopefully this is right) -rpm_vec = data100.Speed(:,1); -T_vec = data100.Shaft_Torque(1,:); - -% Build gridded interpolnts -fields = {'Shaft_Torque','Id_RMS','Iq_RMS','Stator_Current_Phase_RMS',... - 'Vd_RMS','Vq_RMS','Total_Loss','Stator_Copper_Loss',... - 'Iron_Loss','Magnet_Loss','Mechanical_Loss'}; - -maps80 = struct(); maps100 = struct(); maps120 = struct(); - -for i = 1:length(fields) - f = fields{i}; - maps80.(f) = griddedInterpolant({rpm_vec, T_vec}, data80.(f), 'linear','nearest'); - maps100.(f) = griddedInterpolant({rpm_vec, T_vec}, data100.(f), 'linear','nearest'); - maps120.(f) = griddedInterpolant({rpm_vec, T_vec}, data120.(f), 'linear','nearest'); -end - -% Example query (test with limits) -rpm_q = 8000; % rpm -Tq_req = 15; % requested torque [Nm] -Tmotor = 95; % degC - - -% Temperature blending (temp solution... I think) -tempBlend = @(map80,map120,rpmq,Tq,Tm) ... - clip((Tm-80)/(120-80), 0, 1) .* map120(rpmq,Tq) + ... - (1-clip((Tm-80)/(120-80), 0, 1)) .* map80(rpmq,Tq); -% Interpolated values -Iq_val = tempBlend(maps80.Iq_RMS, maps120.Iq_RMS, rpm_q, Tq_req, Tmotor); -Id_val = tempBlend(maps80.Id_RMS, maps120.Id_RMS, rpm_q, Tq_req, Tmotor); -Iphase = tempBlend(maps80.Stator_Current_Phase_RMS, maps120.Stator_Current_Phase_RMS, rpm_q, Tq_req, Tmotor); -Vd_val = tempBlend(maps80.Vd_RMS, maps120.Vd_RMS, rpm_q, Tq_req, Tmotor); -Vq_val = tempBlend(maps80.Vq_RMS, maps120.Vq_RMS, rpm_q, Tq_req, Tmotor); -Ploss = tempBlend(maps80.Total_Loss, maps120.Total_Loss, rpm_q, Tq_req, Tmotor); -Torque = tempBlend(maps80.Shaft_Torque, maps120.Shaft_Torque, rpm_q, Tq_req, Tmotor); - -% Enforce current limit -if Iphase > I_max - scale = I_max / Iphase; - Iq_val = Iq_val*scale; - Id_val = Id_val*scale; - Torque = Torque*scale; - Iphase = I_max; -end - -% Enforce voltage limit -V_lim = (V_dc - V_margin)/sqrt(3); % approximate phase RMS -V_req = sqrt(Vd_val^2 + Vq_val^2); -if V_req > V_lim - Torque = Torque * (V_lim/V_req); % scale torque down -end - -fprintf('At %.0f rpm, %.1f Nm req, %.0f °C:\n', rpm_q,Tq_req,Tmotor); -fprintf(' -> Torque=%.2f Nm, Iphase=%.1f A, Vreq=%.1f V, Loss=%.1f W\n',... - Torque,Iphase,V_req,Ploss); - diff --git a/motor_op_point b/motor_op_point new file mode 100644 index 0000000..5bc9311 --- /dev/null +++ b/motor_op_point @@ -0,0 +1,169 @@ +function res = motor_op_point(V_dc, T_dmd, Temp) +% +% Inputs: +% V_dc - DC bus / peak-phase voltage limit (V) +% T_dmd - requested electromagnetic torque (Nm) +% Temp - temperature selector (use 80-120 ideally; nearest file chosen otherwise) +% +% Output (struct res): +% res.I_op_idx - column index in table +% res.I_op - approximated phase current (A) corresponding to column +% res.T_emg_op - Electromagnetic torque value found (Nm) +% res.V_op - phase-peak voltage found (same units as Voltage_Phase_Peak) +% res.S_op - speed (rpm) corresponding to V_op +% res.T_Shaft - shaft torque (usable torque) (Nm) +% res.I_phase - stator phase current (ARMS) +% res.Mech_loss - mechanical loss (W) +% res.Pf - power factor +% res.currents - vector of current axis (A) +% res.speeds - vector of speed axis (rpm) +% res.notes - cell array of explanatory strings + +%% 0) Tolerances +epsV = 1e-4; % V tolerance (rename of deltaV) +epsT = 1e-4; % torque tolerance (rename of deltaS) + +%% 1) Load the appropriate data file (choose nearest available Temp) +availableTemps = [80 100 120]; +[~, idxNearest] = min(abs(availableTemps - Temp)); +chosenTemp = availableTemps(idxNearest); + +switch chosenTemp + case 80 + dat = load('A2370DD_T80C.mat'); + case 100 + dat = load('A2370DD_T100C.mat'); + case 120 + dat = load('A2370DD_T120C.mat'); + otherwise + error('Unexpected temperature selection.'); +end + +notes = {}; +notes{end+1} = sprintf('Using data file for %d C (closest to requested %g C).', chosenTemp, Temp); + +Tmat = dat.Electromagnetic_Torque; +Vmat = dat.Voltage_Phase_Peak; + +% Matrix sizes +[NR, NC] = size(Tmat); +% Derive speed and current axes from matrix dimensions: +% assume speeds from 0 to 20000 rpm across NR rows (uniform) +% assume currents from 0 to 105 A across NC columns (uniform) +speeds = linspace(0,20000,NR).'; % column vector, rpm +currents = linspace(0,105,NC); % row vector, A + +% Save axes +res.currents = currents; +res.speeds = speeds; + +%% 2) Sweep matrices according to flowchart +% Outer loop: high RPM -> low (NR down to 1) +% Inner loop: low current -> high (1 to NC) +% Objective: find first (row,col) where: +% Vmat(row,col) <= V_dc + epsV +% Tmat(row,col) >= T_dmd - epsT (note: use >= T_dmd - epsT to allow small tolerance) +% If V OK but T < demanded, track the best max T encountered under V condition (fallback). + +found_exact = false; +selected_row = NaN; +selected_col = NaN; + +% Track fallback candidate where V satisfied but T not; pick the one with max T +fallback_exists = false; +fallback_T = -Inf; +fallback_row = NaN; +fallback_col = NaN; + +for row = NR:-1:1 % highest speed first + for col = 1:NC % lowest current first + Tv = Tmat(row,col); + Vv = Vmat(row,col); + % Check voltage satisfied (with tolerance) + v_ok = (Vv <= V_dc + epsV); + % Check torque satisfied (with tolerance downward) + t_ok = (Tv >= T_dmd - epsT); + if v_ok && t_ok + % Exact acceptable operating point found (prefer high RPM, then low current) + selected_row = row; + selected_col = col; + found_exact = true; + notes{end+1} = sprintf('Exact operating point found at row %d (%.1f rpm), col %d (I=%.3g A): T=%.3g, V=%.3g.', ... + row, speeds(row), col, currents(col), Tv, Vv); + break; % break inner loop (we found the best point for this high RPM) + else + % If voltage satisfied but torque not, consider as fallback candidate + if v_ok && ~t_ok + if Tv > fallback_T + fallback_T = Tv; + fallback_row = row; + fallback_col = col; + fallback_exists = true; + end + end + % Otherwise (V not ok) we do nothing special, continue scanning + end + end + if found_exact + break; % exit outer loop + end +end + +%% 3) Decide final operating point based on sweep results +if found_exact + final_row = selected_row; + final_col = selected_col; + final_T = Tmat(final_row, final_col); + final_V = Vmat(final_row, final_col); + final_S = speeds(final_row); + final_I = currents(final_col); +elseif fallback_exists + % Use fallback (best torque among those that satisfied voltage) + final_row = fallback_row; + final_col = fallback_col; + final_T = Tmat(final_row, final_col); + final_V = Vmat(final_row, final_col); + final_S = speeds(final_row); + final_I = currents(final_col); + notes{end+1} = sprintf(['Torque was either unachievable or limited by supplied voltage. Using next best operating point where voltage requirement was satisfied. \n ', ... + 'Chosen fallback at row %d (%.1f rpm), col %d (I=%.3g A) with T=%.3g and V=%.3g.'], ... + final_row, final_S, final_col, final_I, final_T, final_V); +else + % No valid point and no fallback -> voltage preventing operation + res.I_op_idx = NaN; + res.I_op = NaN; + res.T_emg_op = NaN; + res.V_op = NaN; + res.S_op = NaN; + % The additional outputs are required; fill with NaN + res.T_Shaft = NaN; + res.I_phase = NaN; + res.Mech_loss = NaN; + res.Pf = NaN; + notes{end+1} = 'No operating point found. Voltage constraint prevents operation'; + res.notes = notes; + return; +end + +%% 4) Populate outputs from chosen final indices +res.I_op_idx = final_col; +res.I_op = final_I; +res.T_emg_op = final_T; +res.V_op = final_V; +res.S_op = final_S; + +% Extract additional outputs from guaranteed maps +% Use final_row, final_col indexing +res.T_Shaft = dat.Shaft_Torque(final_row, final_col); +res.I_phase = dat.Stator_Current_Phase_RMS(final_row, final_col); +res.Mech_loss = dat.Mechanical_Loss(final_row, final_col); +res.Pf = dat.Power_Factor(final_row, final_col); + +notes{end+1} = sprintf('Final outputs extracted from maps at row %d, col %d.', final_row, final_col); + +%% 5) Finalize notes & return +res.currents = currents; +res.speeds = speeds; +res.notes = notes; + +end