How to dynamically manipulate input variables during simulation from within Simulink M-code S-function?
In Simulink, I am trying to setup the following problem: An ODE which right-hand side is depending on a discrete input value. (Eventually, this should also work as part of a bigger system.) You can recreate the Simulink sheet with the following code:
% Script to demonstrate a feedback problem in Simulink fname = 'simpleModelWithFeedback'; outputTime = 0:1:12;
new_system(fname);
add_block('simulink/Sources/From Workspace', [gcs, '/input'],...
'VariableName', 'inputArray', 'Interpolate', 'Off',...
'OutputAfterFinalValue', 'Holding final value',...
'Position', [50 47 175 93]);
add_block('simulink/User-Defined Functions/Level-2 MATLAB S-Function',...
[gcs, '/myModel'], 'FunctionName', 'simpleAlgebraicModel',...
'Position', [450 32 575 108]);
%'DialogParameters', {0.37, [0 1]}, read-only! Hence set manually
add_block('simulink/Sinks/Out1' , [gcs, '/results'], 'Position', [650 57 700 83]);
% Connect blocks
add_line(gcs, 'input/1', 'myModel/1');
add_line(gcs, 'myModel/1', 'results/1');
% Set solver parameters
set_param(gcs, 'OutputOption', 'SpecifiedOutputTimes', 'Solver', 'ode15s',...
'MaxStep', '1e-1');
% Save created system to *.slx files
save_system(fname);
% Discrete input array inputArray = [0:12; 0 0 1 1 1 1 0 0 1 1 0 0 0]';
%% Run and Plot results in Matlab figure(); hold on; grid on; stairs(inputArray(:,1), inputArray(:,2), 'DisplayName', 'Orignal schedule'); [~, ~, volume_1] = sim(fname, outputTime);
plot(outputTime, volume_1, '-^', 'DisplayName', 'Volume_1', 'MarkerSize', 4);
plot(outputTime, 0.15*ones(size(outputTime)), 'DisplayName', 'Lower limit Off');
stairs(inputArray(:,1), inputArray(:,2), '-.', 'DisplayName', 'Adapted schedule');
legend('Location', 'Best');
ylim([min(-0.1, min(volume_1)-0.1) 1.1]);
hold off;
Since 'DialogParameters' is read-only, manually paste the values 0.37, [0 1] into the Arguments field of the mask of myModel. The S-function needed is this:
function simpleAlgebraicModel(block)
%simpleAlgebraicModel Perform some simple calculations
setup(block);
function setup(block)
block.NumInputPorts = 1;
block.NumOutputPorts = 1;
block.NumDialogPrms = 2; % Initial value, lower/upper limit
% Setup functional port properties to dynamically inherited
block.SetPreCompInpPortInfoToDynamic;
block.SetPreCompOutPortInfoToDynamic; block.InputPort(1).Dimensions = 1;
% block.InputPort(2).Dimensions = 1;
block.OutputPort(1).Dimensions = 1; block.DialogPrmsTunable = {'Nontunable', 'Nontunable'};
block.SampleTimes = [0 0];
block.NumContStates = 1; % Set the block simStateCompliance to default (i.e., same as a built-in block)
block.SimStateCompliance = 'DefaultSimState'; block.RegBlockMethod('SetInputPortSamplingMode', @SetInputPortSamplingMode);
block.RegBlockMethod('InitializeConditions', @InitConditions);
block.RegBlockMethod('Outputs', @Outputs);
block.RegBlockMethod('Derivatives', @Derivative);function SetInputPortSamplingMode(block, idx, fd)
% Boilerplate code
block.InputPort(idx).SamplingMode = fd;
block.OutputPort(1).SamplingMode = fd;
function InitConditions(block)
% Initialize Dwork
block.ContStates.Data(1) = block.DialogPrm(1).Data;
function Outputs(block) block.OutputPort(1).Data = block.ContStates.Data(1);
function Derivative(block)
productionRate = 5.5/24; % Constant value instead of input port
consumptionRate = 2*productionRate;
onOff = block.InputPort(1).Data(1);
dVolume = productionRate - onOff*consumptionRate;
block.Derivatives.Data(1) = dVolume;
% For debugging purposes
% disp([block.CurrentTime, block.ContStates(1).Data, dVolume]);
% Lower/upper limit
lowerLimit = block.DialogPrm(2).Data(1);
upperLimit = block.DialogPrm(2).Data(2);
lowerLimitOff = 0.15*(upperLimit - lowerLimit);
if block.OutputPort(1).Data <= lowerLimitOff && onOff
warning('Lower limit Off reached at %.2f!', block.CurrentTime);
inputArray = evalin('base', 'inputArray');
% Overwrite 1s in current ON period with 0s
currentIndex = find(inputArray(:,1) >= block.CurrentTime, 1, 'first');
nextOffIndex = find(inputArray(currentIndex:end, 2) == 0, 1, 'first');
lastOnIndex = nextOffIndex - 2;
inputArray(currentIndex:currentIndex+lastOnIndex, 2) = 0;
% Write modified net schedule back to base workspace
assignin('base', 'inputArray', inputArray);
endSave it by its name in the same folder.
Now you can either go to Simulink, enable logging on line 1 and run the simulation (til time = 12) and inspect the results in the Simulation Data Inspector, or use the second part of the script. The result should look like this:

As you can see, the overwriting works, but not dynamically. Hence, the if statement in Derivatives still holds true between 5 <= t <= 7, although the corresponding value of volume_1 should now be on the rise.
What I am trying to achieve is that the S-function actually overwrites the 1s in the second column of inputArray with 0s dynamically, so that volume_1 never falls below the threshold (0.15 in this case). The result would be, for example:
inputArray = [0:12; 0 0 1 1 1 0 0 0 1 1 0 0 0]';
Any ideas how this can be accomplished with Simulink?
0 Commenti
Risposte (0)
Vedere anche
Categorie
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!