Main Content

Zero-Crossing Detection with Fixed-Step Simulation

Models with both continuous states and discontinuous signals may require a small fixed-step size to obtain accurate simulation results near discontinuities. With fixed-step zero crossing enabled, Simulink® can automatically detect and locate discontinuities and correct the continuous states in the model. This functionality allows you to use a larger step size and enables faster simulation times without affecting simulation accuracy.

You may find fixed-step zero-crossing detection useful if your model contains:

  • Continuous states

  • Blocks with zero-crossings driven by signals with continuous sample time

  • Frequent switching or other events that limit solver step-size

Effects of Zero-Crossing Detection in Fixed-Step

Consider a self-resetting integrator that can be used to generate a sawtooth wave where the state is reset to 1/3 each time it reaches a value of 1. Simulating in variable-step with zero-crossing detection shows the expected results, or baseline, for this model.

Block diagram of self-reset integrator

model = 'mSelfResetIntegrator';
open_system(model);

set_param(model, 'Solver', 'ode23');
simoutVS = sim(model);
figure();
plot(simoutVS.tout, simoutVS.xout);
legend('Variable-Step (baseline)',...
    'Location', 'southeast');

The sawtooth wave generated using variable-step simulation is used as a baseline.

Sawtooth wave

Simulate in Fixed-Step Without Zero-Crossing Detection

You can use a fixed-step solver like ode3 to simulate this model with default fixed-step size equal to 0.2. However, the simulation results are not very accurate for this step size. The fixed-step size must be decreased to about 0.001 for the simulation results that align with the expected result.

set_param(model, 'Solver', 'ode3', ...
    'FixedStep', '0.2');
simoutFS = sim(model);

plot(simoutVS.tout, simoutVS.xout, ...
    simoutFS.tout, simoutFS.xout, '-x');
title('Fixed-step size = 0.2');
legend('Variable-Step (baseline)', 'Fixed-step (Zero-Crossing Disabled)',...
    'Location', 'southeast');

Sawtooth waves showing variable-step vs Fixed-step behavior

set_param(model, 'FixedStep', '.001');
simoutFS2 = sim(model);

plot(simoutVS.tout, simoutVS.xout, ...
    simoutFS2.tout, simoutFS2.xout, '-x');
title('Fixed-step size = 0.001');
legend('Variable-Step (baseline)', 'Fixed-step (Zero-Crossing Disabled)',...
    'Location', 'southeast');

Enable Fixed-Step Zero-Crossing Detection

Zero-crossing detection can be enabled for this model by selecting Enable zero-crossing detection for fixed-step simulation. You can also set the step size back to 0.2 and simulate. Note that despite this substantial increase in step size, each state value in this simulation coincides with the expected result. Thus, zero-crossing detection for fixed-step simulation results in faster simulation times for this model.

set_param(model, 'FixedStep', '0.2', ...
    'EnableFixedStepZeroCrossing', 'on');

simoutFSZC = sim(model);
plot(simoutVS.tout, simoutVS.xout, ...
    simoutFSZC.tout, simoutFSZC.xout, '-x');
title('Fixed-step size = 0.2');
legend('Variable-Step (baseline)', 'Fixed-step (Zero-Crossing Enabled)',...
    'Location', 'southeast');

Set Parameters for Zero-Crossing Detection

You use two main parameters to control zero crossing for fixed-step simulation. These parameters help ensure that the detection and location of zero-crossing events is fixed-cost. Go to the Solver Details > Zero-crossing options section of the configuration parameters to access the parameters.

Screen shot of zero-crossing GUI

Maximum Number of Bracketing Iterations

The Maximum number of bracketing iterations parameter limits the number of search iterations used by Simulink for locating a zero-crossing event once the event has been detected. In general, a higher number of bracketing iterations will provide a more accurate event location, but this option is computationally costly.

Consider this model with a sine wave driving a Hit Crossing block. Zero-crossings are detected when the Hit Crossing block detects that its input signal has crossed zero, which should occur at multiples of pi. The scope displays this result.

model = 'mFixedStepZcParameters';
open_system(model);
set_param(model, 'Solver', 'VariableStepAuto');
sim(model);

Too few bracketing iterations results in locating zero crossings with less accuracy. To see the number of bracketing iterations simulation results, enable fixed-step zero-crossing, change to a fixed-step solver, and set the Maximum number of zero-crossings per step parameter to 2. Use a fixed-step size of 0.5.

set_param(model, 'EnableFixedStepZeroCrossing', 'on',...
    'MaxZcBracketingIterations', '2',...
    'SolverName', 'ode3',...
    'FixedStep', '0.5');

Increasing the number of bracketing iterations to 4 results in more accurate location of the events in this model. The zero crossing is located closer to the expected value of pi.

set_param(model, 'MaxZcBracketingIterations', '4');

Maximum Number of Zero-Crossings per Step

Use Maximum number of zero-crossings per step parameter to limit the maximum number of zero crossings that Simulink will locate in one simulation time step. Once this maximum number is reached in a step, any additional zero-crossings that occur within that step are ignored. To see how this can affect simulation results, consider the following example. Set the Sine Wave block to output two sine waves that are slightly offset by using scalar expansion on the 'Bias' parameter of the sine wave block. Two zero crossing events should be located near pi.

sineWaveBlock = [model '/Sine Wave'];
set_param(sineWaveBlock, 'Bias', '[0.0 0.1]');
set_param(model, 'Solver', 'ode45');
simoutVS = sim(model);

Setting the maximum number of zero-crossings to locate per step to 1 will restrict Simulink to only locating the first zero crossing that occurs within a single time step. In this example, Simulink only locates the first zero-crossing in the step at pi in the step from t = 3 to t = 3.5. As a result, the second state of the Integrator block is less accurate due to the missed zero crossing.

set_param(model, 'Solver', 'ode3',...
    'EnableFixedStepZeroCrossing', 'on',...
    'MaxZcPerStep', '1');
simoutFS_1ZC = sim(model);
f = figure();
tiledlayout(f,'flow','TileSpacing','compact','Padding','compact');
nexttile;
plot(simoutVS.tout, simoutVS.xout.signals(1).values(:, 1),...
    simoutFS_1ZC.tout,simoutFS_1ZC.xout.signals(1).values(:,1), '-x');
title('State 1')
nexttile;
plot(simoutVS.tout, simoutVS.xout.signals(1).values(:,2),...
    simoutFS_1ZC.tout,simoutFS_1ZC.xout.signals(1).values(:,2), '-x');
title('State 2')
legend('Variable-Step (Expected Result)', 'Fixed-Step with One Zc Per Step',...
    'Location', 'northwest');

Since you know to expect a maximum of two zero crossings to be located within the fixed-step size of 0.5, setting the Maximum number of zero-crossings per step parameter to 2 should yield more accurate simulation results. This plot shows that both continuous states of the Integrator block match the expected result.

set_param(model, 'MaxZcPerStep', '2')
simoutFS_2ZC = sim(model);
f = figure();
tiledlayout(f,'flow','TileSpacing','compact','Padding','compact');
nexttile;
plot(simoutVS.tout, simoutVS.xout.signals(1).values(:, 1),...
    simoutFS_2ZC.tout,simoutFS_2ZC.xout.signals(1).values(:,1), '-x');
title('State 1')
nexttile;
plot(simoutVS.tout, simoutVS.xout.signals(1).values(:,2),...
    simoutFS_2ZC.tout,simoutFS_2ZC.xout.signals(1).values(:,2), '-x');
title('State 2')
legend('Variable-Step (Expected Result)', 'Fixed-Step with Two Zc Per Step',...
    'Location', 'northwest');

See Also

| | | |