Create Open-Loop Control Design App
Using App Designer, you can create an app that includes an open-loop editor that allows users to design a compensator for a SISO plant model. This example assumes that you are familiar with creating apps that include linear analysis plots. For an example, see Create App with Linear Analysis Response Plots.
This example describes the following steps for supporting an open-loop editor workflow in your app:
Add UI components for importing a plant, exporting a compensator, and displaying stability margins.
Define app properties for the open-loop editor and step response objects.
Define function to update the step response and stability margins based on the latest plant and compensator models.
Define callback functions to update the step plot and margins when the compensator design changes.
Create and configure properties in startup callback function
Add support for importing a plant model.
Add support for exporting the compensator model.
For information on how to design a compensator using an open-loop editor, see Design Compensator Using Open-Loop Editor.
SISO Designer App
This example creates an app that includes an open-loop response editor with which users can design a compensator to control a specified SISO plant model. With this app, users can:
Specify a plant model using a MATLAB® expression or a variable from the MATLAB workspace.
Interactively specify the zeros, poles, and gain of a compensator.
View the closed-loop step response and stability margins as they update the compensator.
Export the compensator to the MATLAB workspace.
To run the app from the command line, type the app name.
SISODesigner
Add UI Components
To allow user interaction, add these UI components to the app using a grid layout:
Text label for displaying gain margin.
Text label for displaying phase margin.
Text box for specifying plant model.
Button for importing specified plant model.
Button for exporting compensator model.
When you configure the grid layout, leave space for the step plot and open-loop editor plots.
App Properties
To share data within your SISO design app, define the following private properties:
OLEditor
— Open-loop editor object, which stores the latest plant and compensator models.CLStep
— Step response chart object for plotting the closed-loop response
properties (Access = private) OLEditor % Open-loop editor object CLStep % Step plot chart object end
Update UI Using Latest Models
Create a private function that updates the margins and step plot based on the plant and compensator stored in the open-loop editor object. The app uses this function in its startup callback function and compensator change callback function.
On the Editor tab, select Function > Private Function.
The app adds a new private methods section to the app code and defines an empty private function that takes the app object as an input argument. Using this argument, the function can access the app UI elements and the open-loop editor and step response objects.
Rename this function updateStepAndMargins
and perform the following
steps.
Compute the open-loop and closed-loop responses,
sysOL
andsysCL
, using the latest plant and compensator values from the open-loop editor.sysOL = app.OLEditor.Compensator*app.OLEditor.Plant; sysCL = feedback(sysOL,1);
Update the step response object to plot the new closed-loop response.
app.CLStep.Responses(1).SourceData.Model = sysCL;
If the closed-loop response is stable, compute the gain and phase margins and update their respective display values.
if (isstable(sysCL)) [GM,PM] = margin(sysOL); GM = 20*log10(GM); app.GMValueLabel.Text = string(GM); app.PMValueLabel.Text = string(PM); else app.GMValueLabel.Text = "unstable"; app.PMValueLabel.Text = "unstable"; end
The following code shows the final update function.
function updateStepAndMargins(app) % Compute open-loop response. sysOL = app.OLEditor.Compensator*app.OLEditor.Plant; % Compute closed-loop response. sysCL = feedback(sysOL,1); % Update closed-loop response for step plot. app.CLStep.Responses(1).SourceData.Model = sysCL; % If closed-loop response is stable, compute gain and phase % margins and update corresponding UI elements. if (isstable(sysCL)) [GM,PM] = margin(sysOL); GM = 20*log10(GM); app.GMValueLabel.Text = string(GM); app.PMValueLabel.Text = string(PM); else app.GMValueLabel.Text = "unstable"; app.PMValueLabel.Text = "unstable"; end end
Compensator Change Callback Function
You can configure your open-loop editor to perform calculations and update UI components by defining these callback functions, which are properties of the open-loop editor object:
CompensatorChangedFcn
— This function is called when the compensator value is changed such as after drag operations, after editor UI interactions, and after edits in the Compensator Editor.CompensatorChangingFcn
— This function is called when the compensator value is changing during a drag operation.
For this example, you set both callback function properties to the same callback
function. Add a new private function to the app code and rename it
compensatorChangeFcn
.
This function simply calls the updateStepAndMargins
function.
function compensatorChangeFcn(app,ole,event) % Update step response and margins based on new compensator. updateStepAndMargins(app) end
Here, ole
and event
contain the current open-loop
editor and compensator update event data, respectively. These values are passed to the
callback function by the open-loop editor.
In this example, to update the UI components, you require only the new compensator
value, which is available in the app.OLEditor.Compensator
property.
Therefore, you do not need to pass the ole
and event
arguments to updateStepAndMargins
.
Startup Callback Function
To initialize the app configuration, create a startup callback function that performs the following steps.
Define a default plant model and set the plant edit field to the same value.
defaultPlant = tf(1,[1 2 1 0]); app.PlantEditField.Value = "tf(1,[1 2 1 0])";
Create an open-loop editor, specifying the grid layout as its parent and setting its plant model to the default plant. This open-loop editor uses the Bode Editor view by default. To facilitate the compensator design process, enable the major and minor grid lines in the Bode plot.
app.OLEditor = openloopeditor(app.GridLayout,Plant=defaultPlant); app.OLEditor.Layout.Row = 1; app.OLEditor.Layout.Column = [1 2]; app.OLEditor.View.AxesStyle.GridVisible = "on"; app.OLEditor.View.AxesStyle.MinorGridVisible = "on";
Set the compensator change callback functions to call the
compensatorChangeFcn
. To do so, use an anonymous function handle.app.OLEditor.CompensatorChangedFcn = ... @(src,event) compensatorChangeFcn(app,src,event); app.OLEditor.CompensatorChangingFcn = ... @(src,event) compensatorChangeFcn(app,src,event);
Create a step response object, specifying the grid layout as its parent and its response model as the default closed-loop response. Enable the plot grid.
OL = app.OLEditor.Compensator*app.OLEditor.Plant; app.CLStep = stepplot(app.GridLayout,feedback(OL,1)); app.CLStep.Layout.Row = 1; app.CLStep.Layout.Column = [3 5]; app.CLStep.AxesStyle.GridVisible = "on";
Display the peak-response and settling-time characteristics for the step plot.
app.CLStep.Characteristics.PeakResponse.Visible = "on"; app.CLStep.Characteristics.SettlingTime.Visible = "on";
Update the UI components by calling the
updateStepAndMargins
function.updateStepAndMargins(app)
The following code shows the final startup callback function.
function startupFcn(app) % Define default plant. defaultPlant = tf(1,[1 2 1 0]); app.PlantEditField.Value = "tf(1,[1 2 1 0])"; % Create open-loop editor for default plant app.OLEditor = openloopeditor(app.GridLayout,defaultPlant); app.OLEditor.Layout.Row = 1; app.OLEditor.Layout.Column = [1 2]; app.OLEditor.View.AxesStyle.GridVisible = "on"; app.OLEditor.View.AxesStyle.MinorGridVisible = "on"; % Specify compensator callback functions for open-loop editor. app.OLEditor.CompensatorChangedFcn = ... @(src,event) compensatorChangeFcn(app,src,event); app.OLEditor.CompensatorChangingFcn = ... @(src,event) compensatorChangeFcn(app,src,event); % Create step plot for viewing closed-loop response. OL = app.OLEditor.Compensator*app.OLEditor.Plant; app.CLStep = stepplot(app.GridLayout,feedback(OL,1)); app.CLStep.Layout.Row = 1; app.CLStep.Layout.Column = [3 5]; app.CLStep.AxesStyle.GridVisible = "on"; % Enable peak response and settling time characteristics for % the step plot. app.CLStep.Characteristics.PeakResponse.Visible = "on"; app.CLStep.Characteristics.SettlingTime.Visible = "on"; % Initialize step plot and margins. updateStepAndMargins(app) end
Plant Import
To import the plant model, define a button-pushed callback function for the Import Plant button. This callback performs the following steps.
Store the old plant value.
oldPlant = app.OLEditor.Plant;
Evaluate the expression from the plant edit field in the MATLAB® workspace and assign it to the plant model.
If the plant expression is invalid, set the plant back to the old plant and generate an error dialog box.
try app.OLEditor.Plant = evalin("base",app.PlantEditField.Value); catch exception app.OLEditor.Plant = oldPlant; message = ["Unable to import plant:","", ... " " + app.PlantEditField.Value, ... "","Keeping previous plant value."]; uialert(app.SISODesignerUIFigure,message,"Invalid Plant"); end
Update the UI components by calling the
updateStepAndMargins
function.updateStepAndMargins(app)
The following code shows the callback function for the import button.
function ImportPlantButtonPushed(app,event) % Store previous plant value. oldPlant = app.OLEditor.Plant; % Evaluate plant expression from text box in the base MATLAB % workspace and assign to plant model. Assign new plant to % open-loop editor. try app.OLEditor.Plant = evalin("base",app.PlantEditField.Value); catch exception % If plant expression does not evaluate to a valid dynamic % system model, restore the previous plant value and % generate an error dialog box. app.OLEditor.Plant = oldPlant; message = ["Unable to import plant:","", ... " " + app.PlantEditField.Value, ... "","Keeping previous plant value."]; uialert(app.SISODesignerUIFigure,message,"Invalid Plant"); end % Update step response and margin values. updateStepAndMargins(app) end
Compensator Export
To export the plant, define a button-pushed callback function for the Export
Compensator button. This function assigns the current compensator value to
variable C
in the MATLAB workspace.
function ExportCompensatorButtonPushed(app,event) assignin("base","C",app.OLEditor.Compensator) end