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); end
Save 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
답변 (0개)
참고 항목
카테고리
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!