Code covered by the BSD License  

Highlights from
SimEvents Entity Animation in 3D

image thumbnail

SimEvents Entity Animation in 3D

by

 

29 Apr 2011 (Updated )

Basic infrastructure S-Function for hooking up a SimEvents model to Simulink 3D Animation

simevents_animation(block)
function simevents_animation(block)
% The goal of this code is to make the S-function behave like a Simulink
% block. During simulation of a model, the Simulink engine invokes this function at different times during the simulation
% Initialization step, update and output steps and the subfunctions
% corresponding to them get invoked in that same order. The initialization function gets invoked only once at the beginning of the 
% simulation. Update and output functions get invoked at every time step (major & minor). Also, observe that the level-2
% S-Function has a lot of similarity with C S-Function that the Level-1
% S-Function.
setup(block)

    function setup(block)
        % We setup and register callback methods that are directly called during simulation.
        
        % Number of input ports and dimensionality
        % (1) indicates the output port number-change it to add multiple ports.
        % Also do bookkeeping of these ports by documenting your code
        block.NumInputPorts  = 2;
        block.InputPort(1).Dimensions        = [2 1];
        block.InputPort(1).DirectFeedthrough = false;
        block.InputPort(2).Dimensions        = [1 1];
        block.InputPort(2).DirectFeedthrough = false;
        
        % Number of output ports and dimensionality
        block.NumOutputPorts = 0;
        % (1) indicates the output port number-change it to add multiple ports.
        % Also do bookkeeping of these ports by documenting your code
        % block.OutputPort(1).Dimensions= 0;
        
        % Number of continuous states, in our case we are assuming the state to be
        % discrete, so set this variable to zero. Note that this choice will impact
        % the choice of sample times. Try changing block.NumContStates = 1 and block.SampleTimes = [0 0] and you will
        % see that the plot looks very discrete as we are depending on the solver
        % to take the time steps. So, slowing the simulation down was a requirement
        % that could be enforced by making this block discrete and giving it a
        % sample time. The solver cannot bypass this "slow" block in the model. You
        % end up getting a nice plot with deterioration in simulation performance.
        block.NumContStates = 0;
        
        % Register the sample times.
        %  [0 offset]            : Continuous sample time
        %  [positive_num offset] : Discrete sample time
        %
        %  [-1, 0]               : Inherited sample time
        %  [-2, 0]               : Variable sample time
        block.SampleTimes = [0.01 0];
        
        %
        
        % Register the various block methods with names of the functions mathcing
        % appropriately with the steps
        
        % Remember that DWork variables can only be set inside a
        % PostPropagationSetup method. So, we need to register that
        block.RegBlockMethod('PostPropagationSetup',@DoPostPropSetup);
        block.RegBlockMethod('InitializeConditions',@InitConditions);
        block.RegBlockMethod('Update', @Update);
        % Commenting this out as no output is required
        %block.RegBlockMethod('Outputs' ,@Output)
        
        % Code for SimState: idea of splitting simulations into stages and doing
        % that by using the end x of one stages as the x0 of the following stage. Look up the
        % documentation for more information on this
        block.SimStateCompliance = 'DefaultSimState';
        
        function DoPostPropSetup(block)
            % Setup Dwork
            % The PostPropagationSetup method initializes the DWork vector that stores the state
            % DWork vectors are blocks of memory that an S-function asks the Simulink engine to allocate to each instance of the S-function in a model.
            % If multiple instances of your S-function can occur in a model, your S-function must use DWork vectors instead of global or static memory to store
            % instance-specific values of S-function variables. Otherwise, your S-function runs the risk of one instance overwriting data needed by another instance,
            %causing a simulation to fail or produce incorrect results. The ability to keep track of multiple instances of an S-function is called reentrancy
            block.NumDworks = 1;
            % The (1) indicates this as the first state. You could add other states buy
            % subscripting (2), (3) and so on. Note that you have to do book-keeping
            % of these states so that you do not mix up-hence good to write some
            % documentation.
             block.Dwork(1).Name = 'x0';
            % DWork is a vector and so its dimension must be a positive integer
             block.Dwork(1).Dimensions      = 2;
             block.Dwork(1).DatatypeID      = 0;
             block.Dwork(1).Complexity      = 'Real';
             block.Dwork(1).UsedAsDiscState = false;
            
            
            function InitConditions(block)
                % Initialize Dwork
                %block.Dwork(1).Data = (block.DialogPrm(1).Data)';
                % Visualization in a MATLAB GUI
                % Initialization code. Note that the GUI is being initialized as part of
                % the model execution. If you close the GUI during the Simulink model
                % execution, you will see a new GUI but this GUI will vanish because the
                % Simulink engine has no way of knowing that you undid what you set up
                % during the initialization step. Here is an enhanecment to think of:
                % Prevent the user from closing the GUI as long as the simulation is
                % running. How would you incorporate that?
                % Also, observe that the GUI code is not very sophisticated. The objects
                % that make this GUI are not connected in a rigid fashion to one figure
                % window. If there were multiple plotting functions inside your Simulink
                % model, this could cause a lot of overlap. Enhancement to think of: How to
                % design more robust GUIs so that they are delinked from each other?
                world = vrworld('conveyor.wrl');
                open(world);
                fig = vrfigure(world);
                % get the path to the wrl file with marker PROTOs
                pathtomarkers = which('vr_markers.wrl');
                % use the tetrahedron shape
                MarkerName = 'Marker_Sphere';
                % create an EXTERNPROTO with specified marker
                try
                    addexternproto(world, pathtomarkers, MarkerName);
                catch ME
                    % if required PROTO is already contained don't throw an exception
                    if ~strcmpi(ME.identifier, 'VR:protoexists')
                        throwAsCaller(ME);
                    end
                end
                
                function Update(block)
                    persistent markerTime;
                    MarkerName = 'Marker_Sphere';
                
                    
                    % You want to hold the current input as the state(memory) for the next
                    % computation because you need it for the line command
                   
                    world = vrworld('conveyor.wrl');
                    conveyor= vrnode(world, 'conveyor');
                    conveyor.translation=[block.InputPort(2).Data 0 0];
                    
                    if (block.InputPort(1).Data(1)>block.Dwork(1).Data(1))
                        newMarker = vrnode(world, sprintf('%s_%d', 'Marker', block.InputPort(1).Data(1)), MarkerName);
                        newMarker.markerTranslation = [-20 0 0];
                        newMarker.markerScale=[0.1 0.1 0.1];
                        %color table   R   G   B
                        %BLACK         0   0   0
                        %RED         255   0   0
                        %ORAGERED    255  69   0
                        %darkorange1 255 127   0
                        %DARKORANGE  255 140   0
                        %ORANGE      255 165   0
                        %GOLD        255 215   0
                        %YELLOW      255 255   0
                        %LIGHTYELLOW 255 255 224
                        %IVORY       255 255 240
                        %WHITE       255 255 255
                        a=[0 0 0];
                        j=block.InputPort(1).Data(2);
                        a(1,j)=1;
                        newMarker.markerColor=a;
                        markerTime(block.InputPort(1).Data(1),1)=block.InputPort(2).Data;
                    end
                    
                    for i=1:(block.Dwork(1).Data(1))
                       
                       newMarker = vrnode(world,['Marker_' num2str(i)]);
                       newMarker.markerTranslation = [10*((block.InputPort(2).Data)-markerTime(i,1))-20 0 0];
                    end
                    
                    vrdrawnow;
                    
                    block.Dwork(1).Data = block.InputPort(1).Data;
                    % No output required here
                    %function Output(block)
                    % block.OutputPort(1).Data = block.Dwork(1).Data;

Contact us