メインコンテンツ

Create Design of Experiments for PMSM and Run Design Study

This example shows you how to use Model-Based Calibration Toolbox™ (MBC) design of experiments (DOE) methods and the PMSM_IdIqInput.slx Simulink® model that contains a permanent magnet synchronous motor (PMSM) to characterize a PMSM. Characterizing a PMSM is an important step in calibrating optimal PMSM torque control. The data generated from the PMSM characterization is needed to optimize control lookup tables.

First, create a DOE to generate d- and q-axis current test points for a PMSM. Then, create a design study to run simulations at each d- and q-axis current combination to generate torque and d- and q-axis flux linkage data. Finally, export the simulation results.

This example requires the Parallel Computing Toolbox™ with the parallel preferences set to automatically create a parallel pool. For more information, see Specify Your Parallel Settings (Parallel Computing Toolbox).

Create DOE

Design an experiment to generate torque and d- and q-axis flux linkage data at various d- and q-axis currents, Id and Iq. To accurately model torque and produce optimal calibration tables for the d- and q-axis flux linkage data, more points are needed at low Iq. The experiment has low and high Iq current regions, where the boundary between the two regions is at ten percent of the maximum current magnitude.

Set Current Ranges

Set the current range values based on the current limit. The current values are limited by the maximum current magnitude Is,max.

currentMagnitudeLimit = 615;

currentIdRange = [-currentMagnitudeLimit, 0];
currentIqRange = [0, currentMagnitudeLimit];
inputRange = {currentIdRange, currentIqRange};
iqBorder = 0.10*currentMagnitudeLimit;

Define Torque Model Inputs

Define the torque model inputs. The currents Id and Iq are limited by -Is,maxId0 and 0IqIs,max.

varNames = ["IdCmd", "IqCmd"];
mdlInputObj = mbcmodel.modelinput( ...
    "Symbol", varNames, "Name", varNames, "Range", inputRange);

Create Space-Filling Design

Use the CreateDesign method to create a Sobol sequence design based on the model input object mdlInputObj for the low Iq and high Iq current regions.

spaceFillDesignType = "Sobol Sequence";

highIqCurrSpacefillDesign = CreateDesign(mdlInputObj, ...
    "Type", spaceFillDesignType);
lowIqCurrSpacefillDesign = CreateDesign(mdlInputObj, ...
    "Type", spaceFillDesignType);

Create Design Constraint

Use the CreateConstraint method to create an ellipsoid constraint for the current magnitude. The current magnitude is constrained by Id2+Iq2Is,max2.

ellipsoidConstraintMatrix = 1/currentMagnitudeLimit^2*eye(2); 
currLimitConstraint = CreateConstraint(highIqCurrSpacefillDesign, ...
    'Type', 'Ellipsoid');
currLimitConstraint.Matrix = ellipsoidConstraintMatrix;
currLimitConstraint.CenterPoint = [0,0];

Add Design Constraints

Use the AddConstraint method to add the constraints to the space-filling designs.

highIqCurrSpacefillDesign = AddConstraint(highIqCurrSpacefillDesign, ...
    currLimitConstraint);

lowIqCurrSpacefillDesign = AddConstraint(lowIqCurrSpacefillDesign, ...
    currLimitConstraint);

Generate Space-Filling Design

Define the number of boundary points and generate the points at the Is,max boundary. Then, add the points to a table.

numBoundaryPoints = 25;

boundAngles = linspace(pi/2, pi, numBoundaryPoints-1)';
boundPts = [cos(boundAngles), sin(boundAngles)]*currentMagnitudeLimit;
boundPts = [boundPts; [0,0]]; % Add point at Id = 0 and Iq = 0

boundTbl = array2table(boundPts, "VariableNames", varNames);

Define the number of points in the low Iq current space and high Iq current space. Then, use the ConstrainedGenerate method to generate the constrained space-filling designs, each with a size of 10.

numHighCurrSpaceFillPoints = 150;
numLowCurrSpaceFillPoints = 75;

highIqCurrSpacefillDesign.Generator.Limits = ...
[currentIdRange; iqBorder currentMagnitudeLimit];

highIqCurrSpacefillDesign = ...
    ConstrainedGenerate(highIqCurrSpacefillDesign, ...
    numHighCurrSpaceFillPoints, ...
    'UnconstrainedSize', 5*numHighCurrSpaceFillPoints, ...
    'MaxIter', 10);

lowIqCurrSpacefillDesign.Generator.Limits = ...
    [currentIdRange; 0 iqBorder];

lowIqCurrSpacefillDesign = ...
    ConstrainedGenerate(lowIqCurrSpacefillDesign, ...
    numLowCurrSpaceFillPoints, ...
    'UnconstrainedSize', 5*numLowCurrSpaceFillPoints, ...
    'MaxIter', 10);

Merge the resulting high and low current space-filling designs.

spacefillDesign = Merge( ...
    highIqCurrSpacefillDesign, lowIqCurrSpacefillDesign);

fillTbl = array2table(spacefillDesign.Points, "VariableNames", varNames);

Save the boundary and space-filling points to the table doeTable.

doeTbl = [boundTbl; fillTbl];

Plot Input Points

Display the input points IdCmd and IqCmd. Note that boundary points are covered and that the low Iq current space has extra points.

plot(doeTbl.IdCmd, doeTbl.IqCmd, 's', 'MarkerFaceColor', 'b')
xlabel("Id [A]")
ylabel("Iq [A]")
title("Experiment Set Points")
axis([-currentMagnitudeLimit, 0, 0, currentMagnitudeLimit]);

Figure contains an axes object. The axes object with title Experiment Set Points, xlabel Id [A], ylabel Iq [A] contains a line object which displays its values using only markers.

Save DOE Data

Select the check box to save the DOE Id and Iq command points to a file.

if false
    writetable(doeTbl,"PMSMDoEPoints.xlsx");
end

Create Design Study

Using a simulink.multisim.DesignStudy object and the parsim function, create a design study to run simulations on the PMSM_IdIqInput.slx model in parallel at each d- and q-axis current combination.

Specify the values from the table doeTble to the simulink.multisim.Variable object designVariables.

mdlName = "PMSM_IdIqInput";

for i=1:length(varNames)
    designVariables(i) = simulink.multisim.Variable( ...
        varNames(i), doeTbl.(varNames(i)));
end

Create a sequential parameter combination with the simulink.Multisim.Sequential object.

sequentialDoE = simulink.multisim.Sequential(designVariables);

Create a simulink.multisim.DesignStudy object.

designStudy = simulink.multisim.DesignStudy(mdlName, sequentialDoE);

Use the PostSimFcn function to return only the final values.

function simOut = getResults(simOut)
% Get final values
simOut.Fluxd = simOut.Fluxd.Data(end);
simOut.Fluxq = simOut.Fluxq.Data(end);
simOut.Trq = simOut.Trq.Data(end);
end
designStudy.PostSimFcn = @getResults;

Run simulations with the simulink.multisim.DesignStudy object using the parsim function.

Note that the parsim function runs the simulations in serial if a parallel pool cannot be created or if Parallel Computing Toolbox is not used.

jobObj = parsim(designStudy, "UseFastRestart", "on");
[15-Jan-2026 12:52:42] Checking for availability of parallel pool...
Starting parallel pool (parpool) using the 'Processes' profile ...
Preserving jobs with IDs: 1 because they contain crash dump files.
You can use 'delete(myCluster.Jobs)' to remove all jobs created with profile Processes. To create 'myCluster' use 'myCluster = parcluster('Processes')'.
15-Jan-2026 12:54:27: Job Queued. Waiting for parallel pool job with ID 2 to start ...
15-Jan-2026 12:55:29: Job Queued. Waiting for parallel pool job with ID 2 to start ...
Connected to parallel pool with 4 workers.
[15-Jan-2026 12:56:50] Starting Simulink on parallel workers...
[15-Jan-2026 12:57:57] Configuring simulation cache folder on parallel workers...
[15-Jan-2026 12:57:58] Loading model on parallel workers...
[15-Jan-2026 12:58:31] Running simulations...

Extract the results.

simOut = jobObj.fetchOutputs();

Iterate through the results to extract the final steady-state values and save the values to a table.

exportVarNames =  ["I_d", "I_q", "Psi_d", "Psi_q", "Trq_msr"];
resultsTbl = array2table(zeros(length(simOut), length(exportVarNames)), ...
    "VariableNames", exportVarNames);
for i = 1:length(simOut)
    % Inputs
    resultsTbl.I_d(i) = doeTbl.IdCmd(i);
    resultsTbl.I_q(i) = doeTbl.IqCmd(i);

    % Logged values
    resultsTbl.Psi_d(i) = simOut(i).Fluxd;
    resultsTbl.Psi_q(i) = simOut(i).Fluxq;
    resultsTbl.Trq_msr(i) = simOut(i).Trq;
end

Visually Check Results

Plot Fluxd with respect to Id and Iq.

stem3(resultsTbl.I_d,resultsTbl.I_q,resultsTbl.Psi_d,"LineStyle","none");
title("Flux d [Wb]");
xlabel("Id [A]");ylabel("Iq [A]");
zlabel("Flux d [Wb]")

Figure contains an axes object. The axes object with title Flux d [Wb], xlabel Id [A], ylabel Iq [A] contains an object of type stem.

Plot Fluxq with respect to Id and Iq.

stem3(resultsTbl.I_d, resultsTbl.I_q, resultsTbl.Psi_q,"LineStyle","none");
title("Flux q [Wb]");
xlabel("Id [A]");
ylabel("Iq [A]");
zlabel("Flux q [Wb]")

Figure contains an axes object. The axes object with title Flux q [Wb], xlabel Id [A], ylabel Iq [A] contains an object of type stem.

Plot Id and Iq with respect to Trq.

stem3(resultsTbl.I_d, resultsTbl.I_q, resultsTbl.Trq_msr, ...
"LineStyle","none");
title("Measured Torque [Nm]");
xlabel("Id [A]");
ylabel("Iq [A]");
zlabel("Measured Torque [Nm]");

Figure contains an axes object. The axes object with title Measured Torque [Nm], xlabel Id [A], ylabel Iq [A] contains an object of type stem.

Export Results

Select the check box to export the simulation results to an Excel® file.

if false 
writetable(resultsTbl,"PMSMDesignStudyResultsExample.xlsx");
end

To preprocess the exported data and generate current controller calibration tables for optimal torque control, see Preprocess Permanent Magnet Synchronous Motor (PMSM) Data and Autogenerate Current Controller Calibration Tables.

See Also

| |

Topics