0

我想知道是否可以在 Simulink 的 Matlab Level-2 S-Function 中导入对象。

过去,我在 Matlab 中将强化学习应用于动态模型。因此,我创建了一些处理策略生成和更新的类。现在,我需要迁移到 Simulink,因为我有一个更复杂的动态系统。我熟悉 C S-Function,但因为我已经在两个类中拥有 Matlab 代码,所以我正在考虑使用使用这些对象的 Matlab S-Function。

我的工作流程如下:初始化策略对象的主要Matlab函数调用带有动态模型的Simulink文件。在 S-Function 中,策略对象被调用来选择一个动作(它是控制系统的输出)。在对 Simulink 文件进行多次模拟后,策略对象(实际上是其权重)会在 Matlab 主函数中更新。

所以,我需要一种policy在 Simulink 的 Matlab S-Function 中导入对象的方法。我试图将其作为参数导入,但只接受数值。我不能只将对象保留在 S 函数中(因此,在初始化函数中对其进行初始化),因为我需要在主 Matlab 脚本中更新其权重。

这可能吗?任何建议将不胜感激!

策略类的示例如下:

classdef Policy
    %% Accessible properties:
    properties
        a;                  % selected action index
        actions;            % actions list
        basis;              % type of basis function
        centres;            % list of centres of the RBFs
        exploration_rate;   % exploration rate
        mu;                 % width of each RBF
        nbasis;             % no. basis functions overall
        states;             % list of discrete states
        weights;            % weights of the linear function approximation
    end

    %% Protected properties:
    properties (Access = protected)
        na;                 % no. actions
        ns;                 % no. discrete states
        nrbf;               % no. radial basis functions per action
        state;              % current state
        Q;                  % Q value for each action-state pair
    end

    %% Accessible methods:
    methods %(Access = protected)
        %% Initialization function:
        function obj = Policy(actions,states,epsilon,basis,mu)  
            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
            % Input:
            % actions: actions list
            % states:  states list or centres of the RBFs
            % epsilon: initial exploration rate
            % delta:   discount factor
            % basis:   type of basis functions
            % mu:      width of each RBF
            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

            if nargin<4
                basis = 'basis_exact';
            end

            obj.actions = actions;
            obj.states = states;
            obj.exploration_rate = epsilon;

            switch basis
                case 'basis_exact'
                    obj.basis = basis;
                    obj.states = states;
                    obj.ns = size(states,1);
                case 'basis_rbf'
                    obj.basis = basis;
                    obj.centres = states;
                    obj.mu = mu;
                    obj.nrbf = size(states,1);
                otherwise
                    error(['Only exact and radial basis functions',...
                    'supported']);
            end
        end

        %% Setter function for the features' weights:
        function obj = set_weights(obj,weights)
            obj.weights = weights;
        end

        %% Update the exploration rate with the given rate:
        function obj = update_epsilon(obj,rate)
            obj.exploration_rate = obj.exploration_rate*rate;
        end

        %% Select an action:
        function obj = select_action(obj,state)            
            % Store the current state:
            obj.state = state;
            % Compute the state-action values for the current state:
            obj = obj.qvalues();
            % Get the current action with an epsilon-greedy policy:
            obj.a = obj.eGreedy();
        end

        %% Evaluate the features:
        function phi = get_features(obj,state,action)
            % Store the current state:
            obj.state = state;
            % Get the features:
            phi = feval(obj.basis,action);
        end
    end

    %% Protected methods:
    methods (Access=protected)
        %% Find the discrete state:
        function s = discretizeState(obj,x)            
            % Copy the row vector entries (continuous states) to all rows:
            x = repmat(x,obj.ns,1);
            % Select the row using the minimum Eucledian distance:
            [~,s] = min(sum((obj.states-x).^2,2).^0.5);
        end

        %% Get the Q-value function for current state and action:
        function q = qvalue(obj,action)
            phi = feval(obj.basis,action);
            q = phi' * obj.weights;
        end

        %% Get the Q-value functions for the current state:
        function obj = qvalues(obj)            
            % Initialize the Q-values for the current state:
            obj.Q = zeros(obj.na,1);
            % Calculate the state-action values for the current state:
            for a=1:obj.na
                obj.Q(a) = obj.qvalue(a);
            end
        end

        %% Get an action with an epsilon-greedy exploration policy:
        function a = eGreedy(obj)
            % Generate a random number:
            r = rand;

            % Select the action that maximises Q(s)
            if (r>obj.exploration_rate)             
                [~,a] = max(obj.Q); % value, action 
            % Choose a random action:
            else                       
                a = randi(obj.na);  % random integer based on a uniform
            end                     % distribution
        end

        %% Find the features for the exact basis functions:
        function phi = basis_exact(obj,action)
            %Initialize the features:
            phi = zeros(obj.nbasis,1);

            % Find the current discrete state:
            s = discretizeState(obj.state);

            % Find the starting position of the block:
            base = (action-1) * obj.ns;

            % Set the indicator:
            phi(base+s) = 1;   
        end

        %% Find the features for the radial basis functions:
        function phi = basis_rbf(obj, action)
            %Initialize the features:
            phi = zeros(obj.nbasis,1);

            % Find the starting position:
            base = (action-1) * (obj.nbasis/obj.na);
            % This is because the matrix Theta is converted into a line 
            % vector

            % Compute the RBFs:
            for i=1:obj.nrbf
                phi(base+i) = exp(-norm(obj.state-obj.centres(i,:))^2/...
                    (2*obj.mu));
            end
            % ... and the constant:
            phi(base+obj.nrbf+1) = 1;
        end
    end
end
4

1 回答 1

0

好的,经过一些试验,我终于发现最好的方法实际上是使用全局变量,即在主Matlab脚本和Level 2 S-Function中都将对象设置为全局。

在这里你可以找到一个简单的例子,我希望它可以帮助你节省一天的工作。

Test.m班级:

classdef Test
    properties
        a;
        b;
    end

    methods
        function obj = Test(a,b)
            obj.a = a;
            obj.b = b;
        end

        function obj = change_a(obj,c)
            obj.a = obj.a + c;
        end

        function c = get_c(obj)
            c = obj.a*obj.b;
        end
    end
end

主要 Matlab 脚本 - trial.m

clear;
close all;

global test;

test = Test(0,1);

% Simulink file:
sfile = 't1';
% Load the Simulink file:
load_system(sfile);

% Run the simulation:
sout = sim(sfile,'StopTime','5.0');

% Plot data:
t = sout.tout;
c = sout.get('logsout').getElement('c').Values.Data;
figure;
plot(t,c);

t1.slxSimulink 文件: <code>t1.slx</code> Simulink 文件 test_class.m 2 级 Matlab S-Function:

function test_class(block)
% rl_control.m      e.anderlini@ucl.ac.uk     23/10/2017
    setup(block);
end

%% Set up the block:
function setup(block)
%   % Register number of dialog parameters:   
%   block.NumDialogPrms = 3;

    % Register number of input and output ports:
    block.NumInputPorts  = 1;
    block.NumOutputPorts = 1;

    % Set up functional port properties to dynamically inherited:
    block.SetPreCompInpPortInfoToDynamic;
    block.SetPreCompOutPortInfoToDynamic;

    % Set up the input ports:
    block.InputPort(1).Dimensions        = 1;        
    block.InputPort(1).DirectFeedthrough = true;

    % Set up the output port:
    block.OutputPort(1).Dimensions       = 1;        

    % Set block sample time to continuous:
    block.SampleTimes = [0 0];

%     % Setup Dwork:
%     block.NumContStates = 1;

    % Set the block simStateCompliance to default:
    block.SimStateCompliance = 'DefaultSimState';

    % Register methods:
%     block.RegBlockMethod('InitializeConditions',    @InitConditions);  
    block.RegBlockMethod('Outputs',                 @Output);  
%     block.RegBlockMethod('Derivatives',             @Derivative);  
end

% %% Initial conditions:
% function InitConditions(block)
%     % Initialize Dwork:
%     block.ContStates.Data = block.DialogPrm(3).Data;
% end

%% Set up the output:
function Output(block)
    global test;
    test = test.change_a(block.InputPort(1).Data);
    c = test.get_c();
    block.OutputPort(1).Data = c;
end

我已经对其进行了测试并检查了它是否有效。全局变量允许我使用相同的对象并根据需要更改它。

于 2017-10-24T10:13:40.603 回答