Main Content

Create Open-Loop Control Design App

Since R2025a

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:

  1. Add UI components for importing a plant, exporting a compensator, and displaying stability margins.

  2. Define app properties for the open-loop editor and step response objects.

  3. Define function to update the step response and stability margins based on the latest plant and compensator models.

  4. Define callback functions to update the step plot and margins when the compensator design changes.

  5. Create and configure properties in startup callback function

  6. Add support for importing a plant model.

  7. 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 and sysCL, 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

See Also

Functions

Objects

Tools

Topics