MATLAB Function ブロックを使いこなせ!
皆さんこんにちは。トレーニングエンジニアの遠藤と申します。
このブログでは、「Simulink と MATLAB をつなぐ」をコンセプトとして、主に技術的な内容について書いております。
今回フォーカスを当てていくのは、「SimulinkとMATLABをつなぐ」機能の一つである「MATLAB Functionブロック」です。普段 MATLAB をメインで使用している方は、
- MATLAB で使っていたコードを Simulink でも使いたい!
- ブロック線図で複雑な演算を作るのが苦手!
といった理由で、このMATLAB Functionブロックを使用する場面が多いのではないでしょうか。
この記事では、そんなMATLAB Functionブロックを使う上での主要なつまづきポイントとその解決策についてまとめていきたいと思います!
MATLAB Functionブロックとは
まず初めに、MATLAB Function ブロックについて簡単に説明します。MATLAB Function ブロックは、MATLABの関数を用いて動作を自分でカスタマイズすることができるブロックです。
MATLAB Function ブロックは、関数形式でブロックの動作を設定します。通常のブロックはダブルクリックするとブロックダイアログが開きますが、MATLAB Function ブロックの場合は MATLAB の関数編集画面が開きます。
デフォルトでは入力をそのまま出力するような関数が設定されています。この関数を自分で編集していくことで、入力と出力の関係をカスタマイズできる、というわけです。
ちなみに、関数の入出力引数はブロックの入出力端子に対応してるので、関数の引数の数を変えると、ブロックの端子の数も変わります。複数の入出力が必要な場合は関数の引数を編集しましょう。
もっと詳しい使い方が知りたい方は、MATLAB Function ブロックのドキュメント もご確認ください。
MATLAB Function ブロックのつまづきポイント
MATLAB Function ブロックは、通常の MATLAB 関数のようにブロックの動作を書くことができるため、特にメインで MATLAB を使っている人は親しみやすいブロックかと思います。
しかし、MATLAB Function ブロックはあくまで Simulink の機能の一つですので、通常の MATLAB の関数と使い勝手が異なる部分もあります。そのため、MATLAB と同じような感覚でMATLAB Function ブロックを使っていると、思わぬところでエラーが発生してしまいます。
そこで、この記事では MATLAB Function ブロックを使う際に特につまづきやすい4つポイントにフォーカスを当て、それぞれの解決策を解説していきます!
【つまづきポイント1】一部の関数が使えない!
【つまづきポイント2】過去の値が使えない!
【つまづきポイント3】ワークスペースの変数にアクセスできない!
【つまづきポイント4】配列サイズのエラーが出る!
【つまづきポイント1】一部の関数が使えない!
例えば、MATLAB Function ブロックで以下のような処理を書いたとしましょう。
function y = myContrast(u)
I = imresize(u,[224,224])
y = localcontrast(I);
imresize 関数で画像データを224 x 224にリサイズしたあと、localcontrast 関数で局所的にコントラストを強調するような処理です。一見問題なく動きそうですが、実際にモデルを実行すると……
関数 ‘localcontrast’ はコード生成でサポートされていません。
関数 ‘MATLAB Function’ (#36.51.67)、行 3、列 5:
“localcontrast(I)”
エラーが出てしまいました。これは、localcontrast 関数がコード生成に対応していないことが原因です。
Simulink はシミュレーションを行う際にモデルから実行用のコードを生成します。そのため、MATLAB Functionブロック内でも、一部の関数を除いてコード生成に対応している関数しか使うことができません。
しかし、場合によってはどうしてもコード生成対応外の関数を MATLAB Function ブロック内で使わなければならないこともあります。そういったときに使うのが、coder.extrinsic というコマンドです。
coder.extrinsic は、特定の関数をコード生成対象外にするコマンドです。これを使うことで、コード生成に対応していない関数でも MATLAB Functionブロック内で使用することができるようになります。
それでは、先ほどのコードで coder.extrinsic を使ってみましょう。
function y = myContrast(u)
coder.extrinsic('localcontrast');
I = imresize(u,[224,224]);
y = localcontrast(I);
coder.extrinsic でエラーの原因であった localcontrast 関数をコード生成対象外に指定しました。これでOK!……かと思いきや、
このコンテキストでは、関数の出力 ‘y’ を mxArray にはできません。既知のタイプをもつ出力変数を事前に初期化することを検討してください。
今度は別のエラーが出てしまいました。どうやら出力 y が問題のようですが、何が原因なのでしょうか……?
実は、coder.extrinsic を使う際は、コード生成対象外に指定した関数から返ってくる値のデータ型やサイズをSimulinkに明確に教えてあげる必要があるのです。coder.extrinsic で指定された関数は Simulink ではなく MATLAB で実行されるため、Simulink はこの関数からどんな値が返ってくるか全く知りません。そのため、この状況でシミュレーションを行おうとしても、MATLAB Function ブロックから出力される y がどんなデータ型でどれくらいのサイズなのかを判断できず、モデルの整合性が確認できないのです。
ではどうするかというと、エラーメッセージに書いてあるように、変数 y を「初期化」してあげます。次のコードを見てください。
function y = myContrast(u)
coder.extrinsic('localcontrast');
y = zeros(224,'uint8');
I = imresize(u,[224,224]);
y = localcontrast(I);
2 行目に y を uint8 型の 224 x 224 のゼロ行列で初期化する処理を入れました。一見無駄な処理に見えるかもしれませんが、MATLAB Function ブロックにおいては非常に重要な処理になります。なぜなら、この行があることで、「loalcontrast 関数から返ってくる y という変数は、uint8 型の 224 x 224 の行列だ」ということを Simulink が判断できるようになるからです。実際、この初期化処理を入れることで、先ほどのエラーは発生しなくなります。
このように、coder.extrinsic を用いてコード生成対応外の関数を使う際は、変数の初期化を忘れないようにしましょう。
Tips:関数がコード生成に対応しているかどうかを調べるには?
既存のコードを MATLAB Function ブロックで使用する場合、使っている関数がコード生成に対応しているか確認する必要があります。
一番簡単なのは、とりあえず実行してみてエラーを見る、という方法です。一番手軽で原因もわかりやすいので、基本的にはこの方法で問題ないかと思います。ただし、規模の大きなモデルの場合は、実行ボタンを押してから MATLAB Function ブロックでエラーが出るまでに時間がかかってしまうため、実行する前に事前にコード生成対応しているか把握しておく方が効率的なこともあります。
こういった場合は、ドキュメントを使って確認する方法が有効です。
例えば、こちらのドキュメント に載っているコード生成対応関数リストを使うと、特定の関数がコード生成に対応しているか確認することができます。そのほかにも、それぞれの関数のドキュメントの下部に「C/C++コード生成対応」という表記があれば、その関数はコード生成に対応しています。
例:linspace 関数のドキュメント。コード生成に対応していることがわかります。
また、一部の関数はコード生成のための制約が記載されていることもあります。
例:max 関数のドキュメント。入力引数の与え方に関する制約が記載されています。
特定の関数でコード生成関連のエラーが発生した場合は、何かの制約に引っかかってないかドキュメントで確認してみましょう。
注意点
coder.extrinsic 使うと、一般的にモデルの実行速度は遅くなります。また、当たり前ですが coder.extrinsic が含まれるモデルはコード生成することができません。そのため、実行速度がクリティカルに効いてくるモデルやコード生成を行う前提のモデルは、coder.extrinsic を使わず、コード生成対応の別の関数で代用する必要がありますので注意しましょう。
【つまづきポイント2】過去の値が使えない!
それでは次のつまづきポイントです。前のステップと今のステップの入力の平均を計算する処理を MATLAB Function ブロックで実装するにはどうすればいいでしょうか?
function y = fcn(u)
y = (u_old+u)/2;
u_old = u;
それっぽいコードを書いてみたものの、これではエラーとなります。通常の MATLAB 関数と同様に、MATLAB Function ブロックの関数も実行ごとに変数がリセットされます。そのため、このままでは前のステップの入力を使うことができません。
このような場合に使用するのが、永続変数です。
永続変数は関数内で定義されていても実行後にリセットされず、値が残り続けます。これを使えば、過去の値を使って演算を行うことが可能です。先ほどのコードを、永続変数を使って書き換えてみます。
function y = fcn(u)
persistent u_old
if isempty(u_old)
u_old = 0;
end
y = (u_old+u)/2;
u_old = u;
まず、u_old の前に persistent と書き、u_old 変数を永続変数に設定します(変数名が青緑色に変わります)。一度永続変数に設定してしまえば、以後 u_old 変数は関数の実行後も値がリセットされなくなりますので、前のステップの入力を保存しておき、次のステップで使う、といった芸当が可能になります。
しかし、ここで気を付けなければならないのは、永続変数には初回実行時に何も値が入っていないという点です。そのため、初回実行時のみ初期値を与えてあげる必要があります。そこで、さきほどのコードの ように、一度 isempty 関数を用いて永続変数に値が入っているか確認し、入っていない場合は初期値を入れる、という処理を書く必要があります。この構文は永続変数を使う際に必ず書くものなので、セットで覚えておきましょう!
注意点
MATLAB Functionブロック内で使っている永続変数の値は、シミュレーションが終わってもメモリに保存されます。そのため、もう一度シミュレーションを行うと、前回の値が残ったまま実行されてしまいます。これを防ぐには、以下のようなコマンドをMATLABで実行し、永続変数をリセットします。
>> clear 関数名 %永続変数の名前ではないので注意!
何回もモデルを実行する際は、永続変数の値をクリアするのを忘れないようにしましょう。
【つまづきポイント3】ワークスペースの変数にアクセスできない!
MATLAB でスクリプトを用いて計算を行う場合、あらかじめベースワークスペースに .mat ファイルなどに保存しておいたパラメータを読み込み、スクリプト内でそれを用いることが多々あります。しかし、通常の MATLAB 関数と同様、MATLAB Function ブロックも直接ベースワークスペースの変数にアクセスすることはできません。
しかし、MATLAB Functionブロックの パラメータ―引数 という機能を使用すると、ベースワークスペースの変数をMATLAB Functionブロック内でパラメータとして使用することができます。
パラメータ引数の使い方:
1. 使用したいワークスペース上の変数と同じ名前の変数を、MATLAB Functionブロックの関数の入力引数に追加します(一時的にブロックの入力端子が増えます)。
2. エディタ上部の[データの編集]をクリックします。
3. 先ほど追加した変数を選び、「スコープ」を「パラメータ」に設定します(ブロックの入力端子の数がもとに戻ります)
このようにしてパラメータ引数を設定することで、ベースワークスペースの変数を MATLAB Function ブロック内で使用することができます。ワークスペースの変数と同名の入力変数を設定しなければならない点に注意です。
注意点
よく勘違いされやすいのですが、シミュレーション中にベースワークスペースの変数を変更しても、MATLAB Functionブロック内のパラメータ引数の値は変わりません。なぜなら、Simulinkはシミュレーション開始前にワークスペースの変数の値を確認し、それを固定値のパラメータとして使用するからです。また、その逆、すなわち MATLAB Function ブロック内からベースワークスペースの変数の値を変更することもできません。
どうしてもシミュレーション中にベースワークスペースの値を動的に参照したい、という場合は、evalin、assligninなどの関数を coder.extrinsic と組み合わせて使いましょう(詳細は各関数のドキュメントをご確認ください)。
Tips:使いたい変数が多すぎる場合!
パラメータ引数を使うと MATLAB Function ブロックからベースワークスペースの変数にアクセスすることができますが、必要な変数が多い場合、全ての変数に対してパラメータ引数を作成するのは大変です。
このような場合は、変数を 1 つの構造体にまとめてあげる方法が有効です。例えば、使いたい変数が “data.mat” というファイルに保存されている場合は、
>> data = load('data.mat')
とすることで .mat ファイル内の変数を data という 1 つの構造体として読み込むことができますので、この data 変数をパラメータ引数に設定するだけですべての変数を MATLAB Function ブロック内で使用することができます。
ただし、MATLAB Functionブロック内では figure オブジェクトなどの一部のデータ型の変数は使用できません。save 関数でワークスペースの変数をすべて .mat ファイルに保存した場合は構造体の中に対応していない変数が混入しやすいので注意しましょう。
【つまづきポイント4】配列サイズのエラーが出る!
以下の MATLAB Function ブロックの関数を見てみましょう。
function y = fcn(u)
y = 0:u:10*u;
入力に応じてベクトルを作成して出力するようなコードです。一見問題なく動くように見えますが、モデルを実行するとエラーとなります。
‘y’ は可変サイズの行列と推定されますが、そのサイズは継承または固定として指定されています。’y’ が、調整不可なパラメーターで定義されていることを確認するか、[可変サイズ] チェック ボックスを選択し、[サイズ] ボックスで上限を指定します。
今回エラーが発生してしまったのは、出力 y のサイズを Simulink が判断できないからです。Simulink の信号のサイズは、基本的にシミュレーション前に決定されている必要があります。先ほどの関数を見ると、配列 y の形が入力 u の値に依存するようなコードになっていますね。このような場合、Simulink はシミュレーション前に出力のサイズを判断できないため、エラーになってしまうのです。
このエラーの解決方法は2種類あります。
方法1:配列サイズが判断できる形に変更する
実行したい演算によっては、表記を変更することで Simulink が配列サイズを推測できるようになります。例えば先ほどのコードは、一見 u の値によって配列サイズが変化しそうに見えますが、実は配列サイズは必ず 11 になります。次のように書き換えるとそれが明確になりますね。
function y = fcn(u)
y = (0:1:10)*u
この形であれば、Simulink も出力信号のサイズが 11 だと判断することができるため、モデルはエラーなく実行されます。
このように、もし配列サイズが入力に依存しないはずなのに配列サイズに関するエラーが出てしまう場合は、より配列サイズが明確になるような形に変形できないか検討してみましょう。
方法2:可変サイズに設定する
先ほど「Simulink では信号のサイズは基本的にシミュレーション前に決定されている」と書きましたが、例外もあります。それが可変サイズ信号です。可変サイズ信号はシミュレーション中にサイズが変更する信号です。
MATLAB Function ブロックの出力は、[データの編集]画面で「可変サイズ」にチェックを入れ、「サイズ」に上限サイズを設定することで、可変サイズ信号に設定することができます。
この設定を行うことで、MATLAB Function ブロックからの出力信号 y は可変サイズ信号となり、シミュレーション中でサイズが変更できるようになります。
ただし、可変サイズ信号には多くの制約が存在します。例えば、可変サイズ信号のサンプル時間は離散である必要があります。制約はこちらのドキュメントにリスト化されていますが、多くのモデルに大きな影響を及ぼしますので、可変サイズ信号を用いるべきかどうかは慎重に検討必要があります。
まとめ
今回は MATLAB Function ブロックを使う上で問題となる 4 つのつまづきポイントについてまとめました。「MATLAB Function ブロックのエラーが解決できなくて Simulink 使うの諦めた……」という方は、 この記事のつまづきポイントに引っかかってしまっているかもしれませんので、今一度モデルを見返してみてはいかがでしょか?
皆さんもうまく MATLAB Function ブロックを用いて、快適なモデリングライフをお送りください!
- 类别:
- Simulink General
评论
要发表评论,请点击 此处 登录到您的 MathWorks 帐户或创建一个新帐户。