このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。
For Each Subsystem を使用したアルゴリズムの繰り返し
アルゴリズムを繰り返す場合は、配列および構造体にグループ化された信号、サブシステムおよびパラメーターに対して、アルゴリズムを反復させることができます。このページでは、非効率的に繰り返し実行する複雑なアルゴリズムを、管理が容易なコンパクトな形式に変換する方法の例を示します。
ブロックとサブシステムをコピーして貼り付けることでブロック線図内でアルゴリズムを繰り返すと、モデルの保守が困難になる可能性があります。個別の信号線およびサブシステムによってブロック線図が過密になり、可読性が低下したり、単純な変更を行うことが困難になる可能性があります。変数がワークスペースに集まり、モデルの移植性が低下する可能性もあります。こうした効率性に関する問題は、設計に対する追加を行うにつれて徐々に蓄積されていきます。
モデル例の確認
モデル ex_repeat_algorithm
は、それぞれ 3 つのセンサーを搭載した 3 つのバーナーのセンサー データを処理および解析します。それぞれのバーナーに対して、独立した同一のサブシステムで同じアルゴリズムを使用しています。このモデルは For Each Subsystem を使用して簡略化できます。
モデル例
ex_repeat_algorithm
を開きます。モデルはベース ワークスペース内で約 30 個の変数を作成します。Burner_1_Analysis
という名前のサブシステムを検査します。このサブシステムは、ベース ワークスペースの変数をブロック内 (Constant ブロックや Discrete-Time Integrator ブロックなど) でパラメーターとして使用することでアルゴリズムを実行します。Burner_2_Analysis
およびBurner_3_Analysis
という名前のサブシステムを検査します。3 つのサブシステムはすべて同じアルゴリズムを実行しますが、ブロックをパラメーター化するために異なるワークスペース変数を使用します。3 つの
Analysis_Delay
サブシステムを検査します。これらのサブシステムは、Analysis
サブシステム内のアルゴリズムとは別のアルゴリズムを繰り返しています。モデルの最上位レベルに戻ります。Memory ブロックは、
Analysis_Delay
サブシステムに入力される前に、入力信号を遅延させます。[コンフィギュレーション パラメーター] ダイアログ ボックスの [データのインポート/エクスポート] ペインを確認します。モデルは変数
SensorsInput
およびt
をシミュレーション入力として使用します。シミュレーション中は、行列変数
SensorsInput
内の 9 つの列それぞれがモデルの最上位レベルにある Inport ブロックに入力データを提供します。
バスによる信号線密度の低減
バスを使用して関連する信号をグループ化し、構造化された単一の信号を作成すると、信号線密度を低減したり、モデルの可読性を向上させることができます。
モデル例の各サブシステムには 3 つの信号入力が必要です。グループごとに 3 つの信号を組み合わせて、単一のバスを作成することができます。
モデル例のすべてのサブシステムがバスを使用するように変更できます。ただし、一部のサブシステムは同じものであるため、それらのサブシステムを削除し、後で For Each Subsystem ブロックと置換することができます。
型エディターを開きます。
typeeditor
sensor1
、sensor2
、sensor3
の 3 つの要素をもつSensorData
という名前のベース ワークスペースに、Simulink.Bus
オブジェクトを作成します。次の図のようにブロックを削除します。残りの 2 つのサブシステムへの入力として、
Burner_1_Sensor1
およびBurner_1_Delay1
という名前のブロックのみを残します。Burner_1_Sensor1
Inport ブロック ダイアログ ボックスの [信号属性] タブで、[データ型] を [Bus: SensorData
] に設定します。ブロックの出力は 3 つの信号要素
sensor1
、sensor2
およびsensor3
を含むバスです。Burner_1_Analysis
という名前のサブシステムを開きます。3 つの Inport ブロックの出力信号線を削除します。In2
およびIn3
という名前の Inport ブロックを削除します。In1 という名前の Inport ブロックの右側に
Bus Selector
ブロックを追加します。Inport ブロックの出力を Bus Selector ブロックに接続します。Bus Selector ブロック ダイアログ ボックスで、信号
sensor1
、sensor2
およびsensor3
を選択します。Bus Selector ブロックは、入力バスから 3 つの信号要素を抽出します。モデル内の他のブロックは抽出された信号要素を使用できます。
サブシステムでは、次のようにブロックを接続します。
Burner_1_Analysis_Delay
という名前のサブシステムでは、Bus Selector ブロックを使用してバス内の信号を抽出します。Burner_1_Analysis
という名前のサブシステムと同じ手法を使用します。
アルゴリズムの繰り返し
For Each Subsystem ブロックは入力信号を分割し、各パーティションに対して順番にアルゴリズムを実行します。たとえば、サブシステムへの入力が 6 つの信号の配列である場合、その 6 つの信号それぞれに対して同じアルゴリズムを実行するようにサブシステムを設定することができます。
For Each Subsystem を使用すると、アルゴリズムを反復形式で繰り返すことができます。このアプローチによって、モデルの可読性が向上し、繰り返し実行されるアルゴリズムを容易に変更することができます。
2 つの For Each Subsystem ブロックをモデルに追加します。サブシステムの 1 つに
Burner_Analysis
という名前を付けます。もう一方のサブシステムにBurner_Analysis_Delay
という名前を付けます。Burner_1_Analysis
という名前のサブシステムのコンテンツをBurner_Analysis
という名前のサブシステムにコピーします。ブロックを貼り付ける前に、For Each Subsystem 内の Inport ブロックと Outport ブロックを削除します。Burner_Analysis サブシステムの
For Each
ブロック ダイアログ ボックスで、チェック ボックスをオンにして入力In1
を分割します。Burner_1_Analysis_Delay
という名前のサブシステムのコンテンツをBurner_Analysis_Delay
という名前のサブシステムにコピーします。Burner_Analysis_Delay サブシステムの
For Each
ブロック ダイアログ ボックスで、チェック ボックスをオンにして入力In1
を分割します。モデルの最上位レベルで、
Burner_1_Analysis
およびBurner_1_Analysis_Delay
という名前のサブシステムを削除します。新しい For Each Subsystem ブロックを所定の場所に接続します。Burner_1_Sensor1
の Inport ブロック ダイアログ ボックスの [信号属性] タブで、[端子の次元] を3
に設定します。ブロックの出力はバスの 3 要素配列です。モデル内の For Each Subsystem は、配列内の 3 つのバスそれぞれに対してアルゴリズムを繰り返し実行します。
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(SensorsInput(:,' num2str(3*(i-1)+1) '),t);']) % Sensor 2 eval(['tempInput(1,' num2str(i) ').sensor2 = ' ... 'timeseries(SensorsInput(:,' num2str(3*(i-1)+2) '),t);']) % Sensor 3 eval(['tempInput(1,' num2str(i) ').sensor3 = ' ... 'timeseries(SensorsInput(:,' num2str(3*(i-1)+3) '),t);']) 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
を追加します。[入力] コンフィギュレーション パラメーターを
SensorsInput
に設定します。SensorsInput
はシミュレーション入力データをtimeseries
オブジェクトの形式で提供するため、時間データを含む変数を指定する必要はありません。残りの 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 つのフィールド (sensor1
、sensor2
およびsensor3
) があります。Memory ブロックのダイアログ ボックスで、[初期条件] を
initForDelay
に設定します。Memory ブロックの出力は、初期化が必要なバス配列です。バス配列内にある各信号要素は、構造体の配列内にある対応するフィールドから初期値を取得します。
パラメーターを構造体の配列に再構成
ベース ワークスペースにはモデル例がブロック パラメーター用に使用する多くの変数が含まれています。ワークスペース変数の数を削減するには、変数をパッケージ化して構造体の配列を作成し、個々の構造体フィールドを使用してブロック パラメーターを指定します。
For Each Subsystem ブロックは、マスク パラメーターとして指定する値の配列を分割できます。サブシステムの各反復では、配列の単一のパーティションを使用してブロック パラメーターを指定します。パラメーターを構造体の配列として指定する場合、サブシステムの各反復は配列内の構造体のうちの 1 つを使用することができます。
Burner_Analysis
という名前の For Each Subsystem ブロックをパラメーター化し、その配列を変数paramsNormal
に保存する構造体の配列を作成します。gainNormal_1
、offsetNormal_1
、initDelayed_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 つのフィールド (
gain
、offset
およびinit
) があります。モデルで、
Burner_Analysis
For Each Subsystem を右クリックし、[マスク] 、 [マスクの作成] を選択します。ダイアログ ボックスの [パラメーターとダイアログ] ペインの [パラメーター] で、[エディット] をクリックします。新しいマスク パラメーターの [プロンプト] を
Parameter structure
、[名前] をparamStruct
に設定します。[OK] をクリックします。Burner_Analysis
サブシステムのマスクで、[パラメーター構造体] をparamsNormal
に設定します。サブシステムを開きます。For Each ブロック ダイアログ ボックスの [パラメーターの分割] ペインで、チェック ボックスをオンにしてパラメーター
paramStruct
を分割します。[分割次元] を2
に設定します。サブシステム内のブロックに対して、次のパラメーターを設定します。
ブロック パラメーター名 パラメーター値 Gain ゲイン paramStruct.gain
Discrete-Time Integrator 初期条件 paramStruct.init
Constant 定数値 paramStruct.offset
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
モデルの最上位レベルで、
Burner_Analysis_Delay
For Each Subsystem を右クリックし、[マスク] 、 [マスクの作成] を選択します。ダイアログ ボックスの [パラメーターとダイアログ] ペインの [パラメーター] で、[エディット] をクリックします。新しいマスク パラメーターの [プロンプト] を
Parameter structure
、[名前] をparamStruct
に設定します。[OK] をクリックします。For Each Subsystem ブロックのマスクで、[パラメーター構造体] を
paramsForDelay
に設定します。サブシステムを開きます。For Each ブロック ダイアログ ボックスの [パラメーターの分割] ペインで、チェック ボックスをオンにしてパラメーター
paramStruct
を分割します。[分割次元] を2
に設定します。サブシステム内のブロックに対して、次のパラメーターを設定します。
ブロック パラメーター名 パラメーター値 Gain ゲイン paramStruct.gain
Discrete-Time Integrator 初期条件 paramStruct.init
Constant 定数値 paramStruct.offset
不要な変数をベース ワークスペースからクリアします。
% Clear the old parameter variables that you replaced % with arrays of structures clear -regexp _ % Clear the iteration variables clear i
このモデルはベース ワークスペースの変数をあまり必要としません。
変換されたモデルの検証
新しい信号とサブシステムの構成を表示するには、ブロック線図を更新します。
モデルの入力は 3 つのバスの配列です。モデルは 2 つの For Each Subsystem を使用し、入力配列内の 3 つのバスそれぞれに対して 2 つのアルゴリズムを実行します。
ベース ワークスペースでは、構造体の配列によって、モデルで使用した多くの変数が置換されます。数学的には、構造体の配列には従来のすべての変数の値が含まれているため、変更されたモデルの動作は開始時の動作と変わりません。
ヒント
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 ブロックの各反復で、フィルター係数の配列の分割が Transfer Fcn ブロックに送られます。
モデル
ex_ForEachSubsystem_Partitioning
を開きます。For Each Subsystem ブロックのマスクを作成し、編集可能なマスク パラメーターを追加します。名前を
FilterCoeffs
に設定し、プロンプトをFilter Coefficient Matrix
に設定します。マスク パラメーターの追加方法については、簡単なマスクの作成を参照してください。For Each Subsystem ブロックを開きます。サブシステム内で For Each ブロックのダイアログ ボックスを開きます。
[パラメーターの分割] タブで、[FilterCoeffs] パラメーターの横にあるチェック ボックスをオンにして、このパラメーターの分割を有効にします。[分割幅] パラメーターと [分割次元] パラメーターについては、既定値の 1 のままにします。
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 次フィルターを実装します。For Each Subsystem ブロックで Transfer Fcn ブロックをダブルクリックし、[分母係数] パラメーターに
FilterCoeffs
と入力します。これにより、係数がマスク パラメーターから取得されるようになります。
For Each Subsystem ブロックにより、入力パラメーターが幅 1 の水平分割にスライスされます。これは 1 行の係数に対応します。係数のパラメーターは 1 つの配列として開始されます。
1 つの配列から 3 行のパラメーターに変換されます。
For Each Subsystem を使用したコードの再利用の向上
この例では、同じ For Each Subsystem ブロックが複数ある場合にコードの再利用を高めるようにモデルを設計する方法を示します。ForEachReuse
モデルについて考えます。
3 つのサブシステム Vector SS1
、Vector SS2
、Vector SS3
で、同じ処理をベクトル信号の各スカラー要素にそれぞれの入力で適用することを目的としています。これら 3 つのサブシステムは同じ処理を実行するため、このモデルのために生成されたコード内の 3 つのサブシステムすべてに対して 1 つの共有 Output 関数 (と Update 関数) を生成することが望ましいでしょう。たとえば、Vector SS3
サブシステムには 3 つのブロックが含まれています。
3 つのサブシステムに 1 つの共有関数を生成するには、入力信号に対して実行する分割の構成が同じでなければなりません。Vector SS1
と Vector SS3
では、このコンフィギュレーションは簡単です。分割の次元と幅を 1 に設定できるからです。ところが、Vector SS2
も入力信号を次元 1 で分割するためには、Math Function ブロックを挿入して 1 行 8 列の行ベクトルを 8 行 1 列の列ベクトルに転置します。サブシステムの出力を 1 行 8 列の行ベクトルに戻すには、[転置]
演算子に設定された 2 番目の Math Function ブロックを使用します。再利用可能な共有関数は、行列信号を処理する残り 2 つのサブシステムに対しても生成できます。モデル用の C コードの生成に関する詳細については、Generate Reusable Code from For Each Subsystems (Simulink Coder)を参照してください。
For Each Subsystem の制限
For Each Subsystem ブロックには、次の制限事項と回避方法があります。
制限 | 回避方法 |
---|---|
For Each subsystem 内のバスまたはバス配列のログを直接記録できない。 | 次のいずれかの方法を使用します。
|
次のいずれかの条件が成立する場合、For Each Subsystem 内にある参照モデル内の信号のログを記録できない。
| 1 つ目の条件については、アクセラレータ モードを使用してください。 2 つ目の条件については、ノーマル モードまたはラピッド アクセラレータ モードを使用してください。 |
For Each Subsystem 内のブロックの状態のログを記録できない。 | シミュレーション状態を保存し、元に戻します。 |
For Each Subsystem 内の Model ブロックをシミュレートするのに、ノーマル モードが使用できない。 | アクセラレータ モードまたはラピッド アクセラレータ モードを使用してください。 |
入力信号と出力信号がベクトル (1 次元か 2 次元の行ベクトルまたは列ベクトル) である場合、内容が同じ 2 つの For Each Subsystem に対して再利用可能コードが生成される。n 次元の入力信号と出力信号の場合、再利用可能なコードが生成されるのは、信号が分割される次元が最も高い次元であるときだけである。 | 信号次元を並べ替えて、分割次元と連結次元を n 次元信号の最も高い大きさが 1 でない次元に変換します。 |
For Each Subsystem ブロックでは以下の機能はサポートされません。
For Each Subsystem 内に以下のブロックや S-Function を含めることはできません。
Data Store Memory ブロック、Data Store Read ブロック、または Data Store Write ブロック
入力データが
Structure with Time
形式を使用しており、時間フィールドが空の場合の From Workspace ブロックTo File ブロック
サブシステム境界を越える Goto ブロックと From ブロック
シミュレーション モードがノーマルに設定された参照モデル
シャドウ Inport
ERT S-Function
For Each Subsystem をサポートするブロックの完全なリストを確認するには、MATLAB コマンド ウィンドウに「
showblockdatatypetable
」と入力します。
以下の信号のタイプは使用できません。
システム内での外部ストレージ クラスがある信号
サブシステムの入力境界と出力境界でのフレーム信号
1 次元より大きい可変サイズの信号
サブシステム内での線形化点の作成
サブシステム内のブロックのヤコビアン フラグの伝播。
J.Mi.BlockAnalyticFlags.jacobian
を使用すれば、MATLAB でこの状態を確認できます。このとき、J
がヤコビアン オブジェクトです。For Each Subsystem ブロックのヤコビアンの正確性を検証するには、次を実行します。For Each Subsystem のヤコビアンのタグを調べます。
“not_supported”
の場合、ヤコビアンは正しくありません。For Each Subsystem から各ブロックを移動して、そのヤコビアンを計算します。いずれかのブロックが
“not_supported”
の場合、または警告タグが含まれている場合は、For Each Subsystem のヤコビアンは正しくありません。
以下の種類のコード生成は実行できません。
Simulink® Coder™ S-Function ターゲットの生成
以下の両方の条件を満たす Simulink Coder コード生成:
Stateflow® または MATLAB Function ブロックがサブシステム内にある場合。
このブロックが、Data Store Memory ブロックや
ExportedGlobal
ストレージ クラスのSimulink.Signal
オブジェクトなどのサブシステム外のグローバル データにアクセスしようとしている場合。
PLC コード生成
場合によっては、For Each Subsystem を使用したモデルの方が、個別のサブシステムを使用して実装したモデルよりも、ノーマル モードでのシミュレーションにかかる時間が長くなることがあります。