ドキュメンテーション

最新のリリースでは、このページがまだ翻訳されていません。 このページの最新版は英語でご覧になれます。

For Each Subsystem を使用したアルゴリズムの繰り返し

ブロックとサブシステムをコピーして貼り付けることでブロック線図内でアルゴリズムを繰り返すと、モデルの保守が困難になる可能性があります。個別の信号線およびサブシステムによってブロック線図が過密になり、可読性が低下したり、単純な変更を行うことが困難になる可能性があります。同時に、多くの変数がワークスペースに集まり、モデルの移植性が低下する可能性もあります。こうした効率性に関する問題は、設計に対する追加を行うにつれて徐々に蓄積されていきます。

アルゴリズムを繰り返す場合は、配列および構造体にグループ化された信号、サブシステムおよびパラメーターに対して、アルゴリズムを反復させることができます。この例は、非効率的に繰り返し実行する複雑なアルゴリズムを、管理が容易なコンパクトな形式に変換する方法を示します。

モデル例の確認

  1. モデル例 ex_repeat_algorithm を開きます。モデルはベース ワークスペース内で約 30 個の変数を作成します。

  2. サブシステム Burner_1_Analysis を検証します。このサブシステムは、ベース ワークスペースの変数をブロック内 (Constant および Discrete-Time Integrator など) でパラメーターとして使用することでアルゴリズムを実行します。

  3. サブシステム Burner_2_Analysis および Burner_3_Analysis を検証します。3 つのサブシステムはすべて同じアルゴリズムを実行しますが、ブロックをパラメーター化するために異なるワークスペース変数を使用します。

  4. 3 つの Analysis_Delay サブシステムを検証します。これらのサブシステムは、解析サブシステム内のアルゴリズムとは別のアルゴリズムを繰り返しています。

  5. モデルの最上位レベルに戻ります。Memory ブロックは、Analysis_Delay サブシステムに入力される前に、入力信号を遅延させます。

  6. [コンフィギュレーション パラメーター] ダイアログ ボックスの [データのインポート/エクスポート] ペインを確認します。モデルは変数 SensorsInput および t をシミュレーション入力として使用します。

    シミュレーション中は、行列変数 SensorsInput 内の 9 つの列それぞれがモデルの最上位レベルにある Inport ブロックに入力データを提供します。

バスによる信号線密度の低減

バスを使用して関連する信号をグループ化し、構造化された単一の信号を作成すると、信号線密度を低減したり、モデルの可読性を向上させることができます。

モデル例の各サブシステムには 3 つの信号入力が必要です。グループごとに 3 つの信号を組み合わせて、単一のバスを作成することができます。

モデル例のすべてのサブシステムがバスを使用するように変更できます。ただし、一部のサブシステムは同じものであるため、それらのサブシステムを削除し、後で For Each Subsystem ブロックと置換することができます。

  1. バス エディターを開きます。

    buseditor

  2. 3 つの信号要素 sensor1sensor2 および sensor3 をもつバス型 SensorData を作成します。

  3. 図に示すようにブロックを削除します。残りの 2 つのサブシステムへの入力として、Burner_1_Sensor1 ブロックおよび Burner_1_Delay1 ブロックのみを残します。

  4. Burner_1_Sensor1 の Inport ブロック ダイアログ ボックスの [信号属性] タブで、[データ型]Bus: SensorData に設定します。

    ブロックの出力は 3 つの信号要素 sensor1sensor2 および sensor3 を含むバス信号です。

  5. サブシステム Burner_1_Analysis を開きます。3 つの Inport ブロックの出力信号線を削除します。In2 および In3 の Inport ブロックを削除します。

  6. In1 の Inport ブロックの右側に Bus Selector ブロックを追加します。Inport ブロックの出力を Bus Selector ブロックに接続します。

  7. Bus Selector ブロック ダイアログ ボックスで、信号 sensor1sensor2 および sensor3 を選択します。

    Bus Selector ブロックは、入力バスから 3 つの信号要素を抽出します。モデル内の他のブロックは抽出された信号要素を使用できます。

  8. サブシステムでは、次のようにブロックを接続します。

  9. サブシステム Burner_1_Analysis_Delay では、Bus Selector ブロックを使用してバス内の信号を抽出します。サブシステム Burner_1_Analysis と同じ手法を使用します。

アルゴリズムの繰り返し

For Each Subsystem ブロックは入力信号を分割し、各パーティションに対して順番にアルゴリズムを実行します。たとえば、サブシステムへの入力が 6 つの信号の配列である場合、その 6 つの信号それぞれに対して同じアルゴリズムを実行するようにサブシステムを設定することができます。

For Each subsystem を使用すると、アルゴリズムを反復形式で繰り返すことができます。このアプローチによって、モデルの可読性が向上し、繰り返し実行されるアルゴリズムを容易に変更することができます。

  1. 2 つの For Each Subsystem ブロックをモデルに追加します。サブシステムの 1 つに Burner_Analysis という名前を付けます。もう一方のサブシステムに Burner_Analysis_Delay という名前を付けます。

  2. サブシステム Burner_1_Analysis のコンテンツをサブシステム Burner_Analysis にコピーします。ブロックを貼り付ける前に、For Each subsystem 内の Inport ブロックと Outport ブロックを削除します。

  3. Burner_Analysis サブシステムの For Each ブロック ダイアログ ボックスで、チェック ボックスをオンにして入力 In1 を分割します。

  4. サブシステム Burner_1_Analysis_Delay のコンテンツをサブシステム Burner_Analysis_Delay にコピーします。

  5. Burner_Analysis_Delay サブシステムの For Each ブロック ダイアログ ボックスで、チェック ボックスをオンにして入力 In1 を分割します。

  6. モデルの最上位レベルで、サブシステム Burner_1_Analysis と Burner_1_Analysis_Delay を削除します。新しい For Each Subsystem ブロックを所定の場所に接続します。

  7. Burner_1_Sensor1 の Inport ブロック ダイアログ ボックスの [信号属性] タブで、[端子の次元]3 に設定します。

    ブロックの出力はバスの 3 要素配列です。モデルの For Each subsystem は、配列内の 3 つのバスそれぞれに対してアルゴリズムを繰り返し実行します。

  8. Inport ブロックがシミュレーション データをインポートするために使用できる Simulink.SimulationData.Dataset オブジェクトを作成します。次のコードを使用すると、オブジェクトを作成し、そのオブジェクトを変数 SensorsInput に保存することができます。

    % First, create an array of structures whose field values are
    % timeseries objects.
    
    for i = 1:3 % Burner number
        
        % Sensor 1
        eval(['tempInput(1,' num2str(i) ').sensor1 = ' ...
            'timeseries(t,SensorsInput(:,' num2str(3*(i-1)+1) '));'])
        
        % Sensor 2
        eval(['tempInput(1,' num2str(i) ').sensor2 = ' ...
            'timeseries(t,SensorsInput(:,' num2str(3*(i-1)+2) '));'])
        
        % Sensor 3
        eval(['tempInput(1,' num2str(i) ').sensor3 = ' ...
            'timeseries(t,SensorsInput(:,' num2str(3*(i-1)+3) '));'])
        
    end
    
    % Create the Dataset object.
    
    SensorsInput = Simulink.SimulationData.Dataset;
    SensorsInput = addElement(SensorsInput,tempInput,'element1');
    
    clear tempInput t i

    コードはまず、3 つの構造体の配列を含む変数 tempInput を作成します。各構造体には、バス型 SensorData の信号要素に対応する 3 つのフィールドがあり、各フィールドは MATLAB® timeseries オブジェクトを格納します。各 timeseries オブジェクトは、各センサーのシミュレーション入力データを保存していた変数 SensorsInput の 9 つのデータ列のいずれかを保存します。

    その後、コードは新しい Simulink.SimulationData.Dataset オブジェクトで SensorsInput を上書きし、オブジェクトの要素として tempInput を追加します。

  9. [入力] コンフィギュレーション パラメーターを SensorsInput に設定します。

    SensorsInput はシミュレーション入力データを timeseries オブジェクトの形式で提供するため、時間データを含む変数を指定する必要はありません。

  10. 残りの Memory ブロックを初期化し、その配列を変数 initForDelay に保存する構造体の配列を作成します。既存の初期化変数 (initDelay_1_sensor1 など) の値を使用して構造体フィールドを指定します。

    for i = 1:3 % Burner number
        
        % Sensor 1
        eval(['initForDelay(' num2str(i) ').sensor1 = ' ...
            'initDelay_' num2str(i) '_sensor1;'])
        
        % Sensor 2
        eval(['initForDelay(' num2str(i) ').sensor2 = ' ...
            'initDelay_' num2str(i) '_sensor2;'])
        
        % Sensor 3
        eval(['initForDelay(' num2str(i) ').sensor3 = ' ...
            'initDelay_' num2str(i) '_sensor3;'])
    end
    

    新しい変数 initForDelay の内容を表示するには、ベース ワークスペースで変数名をダブルクリックします。変数には 3 つの構造体の配列が含まれており、各構造体には 3 つのフィールド (sensor1sensor2 および sensor3) があります。

  11. Memory ブロックのダイアログ ボックスで、[初期条件]initForDelay に設定します。

    Memory ブロックの出力は、初期化が必要なバス配列です。バス配列内にある各信号要素は、構造体の配列内にある対応するフィールドから初期値を取得します。

パラメーターを構造体の配列に再構成

ベース ワークスペースにはモデル例がブロック パラメーター用に使用する多くの変数が含まれています。ワークスペース変数の数を削減するには、変数をパッケージ化して構造体の配列を作成し、個々の構造体フィールドを使用してブロック パラメーターを指定します。

For Each Subsystem ブロックは、マスク パラメーターとして指定する値の配列を分割できます。サブシステムの各反復では、配列の単一のパーティションを使用してブロック パラメーターを指定します。パラメーターを構造体の配列として指定する場合、サブシステムの各反復は配列内の構造体のうちの 1 つを使用することができます。

  1. Burner_Analysis の For Each subsystem をパラメーター化し、その配列を変数 paramsNormal に保存する構造体の配列を作成します。gainNormal_1offsetNormal_1initDelayed_1 といった既存のパラメーター変数の値を使用して、構造体フィールドを指定します。

    for i = 1:3
        eval(['paramsNormal(' num2str(i) ').gain = gainNormal_' num2str(i) ';'])    
        eval(['paramsNormal(' num2str(i) ').offset = offsetNormal_' num2str(i) ';'])
        eval(['paramsNormal(' num2str(i) ').init = initNormal_' num2str(i) ';'])
    end

    変数には 3 つの構造体の配列が含まれており、各構造体には 3 つのフィールド (gainoffset および init) があります。

  2. モデルで、Burner_Analysis の For Each subsystem を右クリックし、[マスク][マスクの作成] を選択します。

  3. ダイアログ ボックスの [パラメーターとダイアログ] ペインの [パラメーター] で、[エディット] をクリックします。新しいマスク パラメーターの [プロンプト]Parameter structure[名前]paramStruct に設定します。[OK] をクリックします。

  4. Burner_Analysis サブシステムのマスクで、[パラメーター構造体]paramsNormal に設定します。

  5. サブシステムを開きます。For Each ブロック ダイアログ ボックスの [パラメーターの分割] ペインで、チェック ボックスをオンにしてパラメーター paramStruct を分割します。[分割次元]2 に設定します。

  6. サブシステム内のブロックに対して、次のパラメーターを設定します。

    ブロックパラメーター名パラメーター値
    GainゲインparamStruct.gain
    Discrete-Time Integrator初期条件paramStruct.init
    Constant定数値paramStruct.offset

  7. Burner_Analysis_Delay の For Each subsystem をパラメーター化し、その配列を変数 paramsForDelay に保存する構造体の配列を作成します。

    for i = 1:3
        eval(['paramsForDelay(' num2str(i) ').gain = gainDelayed_' num2str(i) ';'])
        eval(['paramsForDelay(' num2str(i) ').offset = offsetDelayed_' num2str(i) ';'])
        eval(['paramsForDelay(' num2str(i) ').init = initDelayed_' num2str(i) ';'])
    end

  8. モデルの最上位レベルで、Burner_Analysis_Delay の For Each subsystem を右クリックし、[マスク][マスクの作成] を選択します。

  9. ダイアログ ボックスの [パラメーターとダイアログ] ペインの [パラメーター] で、[エディット] をクリックします。新しいマスク パラメーターの [プロンプト]Parameter structure[名前]paramStruct に設定します。[OK] をクリックします。

  10. For Each Subsystem ブロックのマスクで、[パラメーター構造体]paramsForDelay に設定します。

  11. サブシステムを開きます。For Each ブロック ダイアログ ボックスの [パラメーターの分割] ペインで、チェック ボックスをオンにしてパラメーター paramStruct を分割します。[分割次元]2 に設定します。

  12. サブシステム内のブロックに対して、次のパラメーターを設定します。

    ブロックパラメーター名パラメーター値
    GainゲインparamStruct.gain
    Discrete-Time Integrator初期条件paramStruct.init
    Constant定数値paramStruct.offset

  13. 不要な変数をベース ワークスペースからクリアします。

    % Clear the old parameter variables that you replaced 
    % with arrays of structures
    clear -regexp _
    
    % Clear the iteration variables
    clear i

    このモデルはベース ワークスペースの変数をあまり必要としません。

変換されたモデルの検証

新しい信号とサブシステムの構成を表示するには、ブロック線図を更新します。

モデルの入力は 3 つのバス信号の配列です。モデルは 2 つの For Each subsystems を使用し、入力配列内の 3 つのバス信号それぞれに対して 2 つのアルゴリズムを実行します。

ベース ワークスペースでは、構造体の配列によって、モデルで使用した多くの変数が置換されます。数学的には、構造体の配列には従来のすべての変数の値が含まれているため、変更されたモデルの動作は開始時の動作と変わりません。

完成したモデルを見るには、モデル例 ex_repeat_algorithm_complete を開きます。

ヒント

For Each Subsystem の非バス信号をログに記録できます。ただし、For Each Subsystem 内からのバスまたはバス配列の信号に対して信号ログを使用することはできません。Bus Selector ブロックを使用してログを記録するバス要素信号を選択するか、サブシステムの外に Outport ブロックを追加してその信号のログを記録します。詳細については、For Each Subsystem における信号のログ記録を参照してください。

For Each Subsystem の操作例

For Each Subsystem を使用してアルゴリズムをベクトル化

この例では、ベクトル化されたアルゴリズムのモデル化を簡略化する方法を示します。ここでは、For Each Subsystem ブロックを使用して、3 つの同じ Transfer Fcn ブロックで 3 つの入力信号をフィルターするモデルを簡略化します。また、サブシステムの反復ごとに係数を変えてフィルターをより細かく制御する方法も示します。

このモデルでは、同じ Transfer Fcn ブロックを使用して入力正弦波信号の各要素を個別に処理します。Vector Concatenate ブロックは、結果として得られる出力信号を連結します。この反復処理はグラフ的に複雑であり、維持するのは困難です。さらに、信号に別の要素を追加するには、モデルを大幅に変更しなければなりません。

反復処理を 1 つの For Each Subsystem ブロックに差し替えることで、このモデルを簡略化することができます。

この For Each subsystem ブロックには、1 つの For Each ブロックと 1 つのモデルが含まれています。このモデルは、差し替えた 3 つの Transfer Fcn ブロックのアルゴリズムを表したものです。For Each ブロックで、入力信号ベクトルを個別要素に分割する方法と、処理された信号を連結して出力信号ベクトルを形成する方法を指定しています。状態をもつ各ブロックは所定の実行ステップで処理される入力要素ごとに独立した状態セットを維持します。

この例では、分割対象として入力信号が選択されています。For Each ブロックの [分割次元] パラメーターと [分割幅] パラメーターはどちらも入力に対して 1 に設定されています。

この方法を拡張すれば、モデルを大きく変更しなくても信号を追加していくことができます。この方法は拡張が簡単なうえ、グラフ的にもシンプルになります。

モデルの構造を変えずにパラメーターの変化をモデル化-  この例では、アルゴリズムのパラメーターの変化をモデル化する方法を示します。ここでは、For Each Subsystem を使用してアルゴリズムをベクトル化の For Each Subsystem 分割モデルを使用して、モデルを複雑にすることなく入力信号ごとに異なるフィルターを作成します。フィルター係数の配列を分割対象としてマークしたマスク パラメーターとして For Each subsystem ブロックに送ります。For Each subsystem ブロックの各反復で、フィルター係数の分割が Transfer Fcn ブロックに送られます。

  1. モデル ex_ForEachSubsystem_Partitioning を開きます。For Each Subsystem ブロックのマスクを作成し、編集可能なマスク パラメーターを追加します。名前を FilterCoeffs に設定し、プロンプトを Filter Coefficient Matrix に設定します。マスク パラメーターの追加方法については、簡単なマスクの作成を参照してください。

  2. For Each subsystem ブロックを開きます。サブシステム内で For Each ブロックのダイアログ ボックスを開きます。

  3. [パラメーターの分割] タブで、[FilterCoeffs] パラメーターの横にあるチェック ボックスをオンにして、このパラメーターの分割を有効にします。[分割幅] パラメーターと [分割次元] パラメーターについては、既定値の 1 のままにします。

  4. For Each Subsystem ブロックをダブルクリックし、各行が各入力信号のフィルター係数となる行列を入力します。たとえば、[0.0284 0.2370 0.4692 0.2370 0.0284; -0.0651 0 0.8698 0 -0.0651; 0.0284 -0.2370 0.4692 -0.2370 0.0284] と入力して入力信号ごとに異なる 4 次フィルターを実装します。

  5. For Each Subsystem ブロックで Transfer Fcn ブロックをダブルクリックし、[分母係数] パラメーターに FilterCoeffs と入力します。これにより、係数がマスク パラメーターから取得されるようになります。

For Each Subsystem ブロックにより、入力パラメーターが幅 1 の水平分割にスライスされます。これは 1 行の係数に対応します。係数のパラメーターは、次のような 1 つの配列で表されます。

これが次のような 3 行のパラメーターに変換されます。

For Each Subsystem を使用したコードの再利用の向上-  この例では、同じ For Each Subsystem ブロックが複数ある場合にコードの再利用を高める方法を示します。次のモデル rtwdemo_foreachreuse を考えます。

3 つのサブシステム Vector SS1、Vector SS2、Vector SS3 で、同じ処理をベクトル信号の各スカラー要素にそれぞれの入力で適用することを目的としています。これら 3 つのサブシステムは同じ処理を実行するため、このモデルのために生成されたコード内の 3 つのサブシステムすべてに対して 1 つの共有 Output 関数 (と Update 関数) を生成することが望ましいでしょう。たとえば、Vector SS3 サブシステムには次のブロックが含まれています。

3 つのサブシステムに 1 つの共有関数を生成するには、入力信号に対して実行する分割の構成が同じでなければなりません。Vector SS1 とVector SS3 では、このコンフィギュレーションは簡単です。分割の次元と幅を 1 に設定できるからです。ところが、Vector SS2 も入力信号を次元 1 で分割するためには、Math Function ブロックを挿入して 1 行 8 列の行ベクトルを 8 行 1 列の列ベクトルに転置しなければなりません。その後、[転置] 演算子に設定された 2 番目の Math Function ブロックを使用して、サブシステムの出力を 1 行 8 列のベクトルに戻すことができます。

Ctrl + B キーを押してコードを生成すると、結果のコードでは単一の出力関数が使用されます。この関数は、3 つすべての For Each Subsystem インスタンスで共有されます。

/*
 * Output and update for iterator system:
 *    '<Root>/Vector SS1'
 *    '<Root>/Vector SS2'
 *    '<Root>/Vector SS3'
 */
void VectorProcessing(int32_T NumIters, const real_T rtu_In1[], 
                      real_T rty_Out1[],
                      rtDW_VectorProcessing *localDW)

関数には入力パラメーター NumIters があります。これは、各 For Each Subsystem が処理することになる独立スカラーの数を示します。この関数は、パラメーター NumIters が 10、8、および 7 に設定されて、3 回呼び出されます。

このモデルの残り 2 つのサブシステムは、For Each Subsystem ブロックを使用して処理される行列信号に対しても再利用可能なコードが生成される方法を示します。ここでも同様に Ctrl + B キーを押してコードを生成すると、単一の関数のコードを再利用できるようになります。

参考

|

関連する例

詳細