いつもはゲーム関係のことを好き勝手書いているのですが、クリスマスも近いので(?)、今回は少し真面目にお仕事にも役立つような技術的な記事を書きたいと思います。
題材は、Simulink のパラメータである「単一のシミュレーション出力」についてです。
「単一のシミュレーション出力」とは
Simulink のコンフィギュレーションパラメータの一つで、「データのインポート/エクスポート」ペインにあります。
R2020a で確認すると、このパラメータはデフォルトでは ON になっており、編集ボックスのところには out という変数名が入っています。
さて、このパラメータ、一体何の意味があるかご存じでしょうか?
ヘルプを見ると次のような説明があります。
"このオプションを有効にした場合:
・[実行] をクリックしてシミュレーションを実行した場合、Simulink® によって、すべてのシミュレーション出力が単一の Simulink.SimulationOutput オブジェクトで返されます。
・シミュレーション出力を含む単一の出力オブジェクトの変数名を指定します。チェック ボックスの横のテキスト フィールドを使用して、この名前を指定します。名前を指定しないと、既定で out が使用されます。"
いまいちわかりづらいのですが、簡単に言うと、
「これが ON になると、シミュレーション結果をすべて一つの変数(規定では out)にまとめて出力しますよー」
というパラメータです。
シミュレーションを実行したときに、結果が tout や yout といった変数名で自動的にベースワークスペースに保存されている光景を見たことありませんか?
これは「単一のシミュレーション出力」が OFF の場合の挙動です。では ON になるとどうなるかというと、
このように、out という変数 1 つしか作成されません。中を見てみると、
>> out
out =
Simulink.SimulationOutput:
tout: [64x1 double]
yout: [64x2 double]
SimulationMetadata: [1x1 Simulink.SimulationMetadata]
ErrorMessage: [0x0 char]
というように、out の中に tout と yout の 2 つが格納されています。そのため、tout や yout にアクセスするには、out.tout
、out.yout
といった書き方をしなくてはいけません。
この仕様から、MATLAB と Simulink を連携する際、このパラメータが原因となってエラーとなってしまうことがよくあるのです。
「いやいや、こんなパラメータ変更しないから関係ないわ。というかそもそもこんなパラメータの存在自体知らなかったし」
という方、実はそういった人ほどこのパラメータの†餌食†となります。
なぜこのパラメータがエラーの原因になりやすいか
なんとこのパラメータ、R2018b 以前は規定値が OFF でしたが、R2019a 以降は規定値が ON になりました
(リリースノートも参照)。
R2018b 以前のバージョンを使っていて「単一のシミュレーション出力」が OFF の時の挙動に慣れていると、R2019a 以降にバージョンアップしてから新たなモデルを作成し、旧バージョンで動いていたスクリプトを実行すると、エラーとなる場合があります。
逆に、R2019a 以降のバージョンの挙動に慣れている人が R2018b 以前のバージョンで作成されたモデルを使用すると、意図せずこのパラメータが OFF になっているため、やはりエラーとなる場合があります。
このように、このパラメータはあまり知られてないにも関わらず予期せぬ動作の原因になりやすいのです。
ということで、この記事では「単一のシミュレーション出力」によって起こる主要な現象について解説していきます。
tout, yout が生成されること前提のスクリプトがエラーになる
最も簡単なエラーです。「単一のシミュレーション出力」が OFF であること前提でこんな感じのスクリプトを使っていると、
sim('myModel');
plot(tout,yout);
R2019a 以降のバージョンで新しくモデルを作成して実行した途端に悲しいことになります。
関数または変数 'tout' が認識されません。
この場合は、ちゃんとパラメータを OFF に変更するか、
sim('myModel');
tout = out.tout;
yout = out.yout;
plot(tout,yout);
みたいにちゃんと中身を取り出す必要があります。
To Workspace ブロックの表記が変な感じになる
これはエラーではないですが、知らないと結構戸惑うので書いておきます。
「単一のシミュレーション出力」を ON にすると、To Workspace ブロックの表記は変数名と完全に一致せず、"out."という余計な文字がくっつきます。
実は「単一のシミュレーション出力」は To Workspace ブロックに対しても有効になります。
つまり、To Workspace ブロックで変数名を指定してもその変数はワークスペースに直接生成されず、out 変数の中に格納されるのです。ブロックの表記もそれを表しています。
特に、R2018b 以前のバージョンで To Workspace ブロックをよく使っていた人は、R2019a 以降で新しいモデルに To Workspace ブロックを使ったときに
「あれ、なんか "out." が勝手について消えないんだけど。しかも設定した変数がワークスペースに作成されない……」
と困惑しがちなので注意が必要です。
sim 関数の出力がおかしなことになる
この現象は「単一のシミュレーション出力」が OFF になっている場合に起こります。R2018b 以前のバージョンで作ったモデルを R2019a 以降のバージョンで使用してるシチュエーションが主に該当します。
個人的にはこれが一番厄介だと思います。正直これを書きたいがためにこの題材を選んだと言っても過言ではありません。
sim 関数はパラメータの設定をしながらモデルのシミュレーションを実行できる関数ですが、出力引数を指定することでシミュレーション結果をその引数に格納できます。
out = sim('myModel','StopTime','5'); % シミュレーション時間を 5 に設定してシミュレーションを実行
関数の中でシミュレーションを実行する場合、ベースワークスペースではなく関数ワークスペース内にシミュレーション結果の変数を作成する必要があるので、出力引数を指定する必要があります。App Designer と連携する場合は必須のテクニックです。
しかし、ここで面倒くさいのが、
sim 関数に出力引数を 1 つ指定した場合、"特定の条件" を満たしていない限り「単一のシミュレーション出力」が ON の場合と同じ動作をする
ことです。
例えば vdp モデルの「単一のシミュレーション出力」を OFF に設定してあっても、下記のようなコードを実行すると out という一つの変数の中に tout と yout が格納されます。
>> out = sim('vdp','StopTime','5')
out =
Simulink.SimulationOutput:
tout: [56x1 double]
yout: [56x2 double]
SimulationMetadata: [1x1 Simulink.SimulationMetadata]
ErrorMessage: [0x0 char]
「単一のシミュレーション出力」を ON に設定したときと同じですね。
ただし、上述した通り、この挙動をするのは "特定の条件" を満たしたときだけです。シミュレーション時間を設定する部分を省くとどうなるかというと、
>> out = sim('vdp')
out =
0
0.0001
0.0006
0.0031
0.0157
0.0785
0.2844
謎のベクトルが返ってきます。このせいで、例えば out の中の yout にアクセスしようとしてout.yout
のようにコードを書いてしまうと、
この型の変数ではドット インデックスはサポートされていません。
というエラーが出てしまいます。
シミュレーション時間を設定しないだけで何故このような挙動の違いが出てしまうのでしょうか?
。
。
。
sim 関数のドキュメントを見ると、以下のような説明が書いてあります。
"simOut = sim(model) は、既存のモデル コンフィギュレーション パラメーターを使用して、指定されたモデルのシミュレーションを実行し、Simulink.SimulationOutput オブジェクト (単出力形式) または Simulink バージョン R2009a 以前と互換性のある時間ベクトルとして結果を返します。下位互換性のある構文を参照してください。"
さっきの謎ベクトルが返ってきた現象は、"Simulink バージョン R2009a 以前と互換性のある時間ベクトルとして結果を返し" ていることが原因で生じています。すなわち、謎のベクトルはシミュレーションの時間データ(tout)だったわけです。
これだけ見ると、何故こんなよくわからない仕様になっているのか、皆さん疑問に思うかと思います。
ここでキーワードとなるのが、ドキュメントにも書いてある「下位互換性のある構文」です。
R2009a 以前のバージョンでは、出力引数にシミュレーション結果を格納するためには、以下のようなコードでシミュレーションを実行する必要がありました。
[T,X,Y] = sim('model')
時間、状態、出力をそれぞれ独立した出力引数として指定する必要があったわけです。そして、下位互換性を保つため、R2009b 以降のバージョンでもこの表記は問題なく使えるようになっています。
さて、ここで思い出してほしいのが、
「複数の出力引数を持つ関数を出力引数一つで指定した場合、最初の一つのみを返す」
という MATLAB の仕様です。
つまり、上記のコードのような「下位互換性のある構文」を一つの出力引数指定で実行すると、最初の出力引数、すなわち時間ベクトル T のみが返ってきてしまいます。
前述した 2 つのコード で StopTime を指定するかだけで挙動が大きく変わってしまったのは、StopeTime を指定することで下位互換性のある構文の条件を満たさなくなったから なのです。
もちろん、この下位互換性のある構文が問題となる場面はそう多くありません。
StopeTime を指定したコードのように、out = sim(model,Name,Value)
の形で指定した場合は下位互換性のない構文としてのみ解釈されますし、逆に出力引数を 3 つに指定したり、TimeSpan, Options, UT といったオプションを指定した場合は下位互換性のある構文としてのみ解釈されます。基本的には MATLAB はこちらが意図した構文として解釈してくれるのです。
ただし、何もオプションを指定しないコード
out = sim('model')
は、どちらの構文としても解釈されうる可能性があるのです。
ではこのout = sim('model')
がどちらの構文として解釈されるかはどうやって決まっているかというと……
そう、「単一のシミュレーション出力」が ON かどうかです。ON であれば出力は一つで返す、OFF であれば出力は T,X,Y と 3 つで返そうとするが変数が一つしかないので T のみ返す、という挙動になるわけです。
……難解事件を解明したかのような達成感がありますね(苦笑)。
まとめ
色々書いてきましたが、伝えたかったことは、
sim 関数で変な挙動になったらとりあえず「単一のシミュレーション出力」の ON/OFF が間違ってないか確認しよう
ということです。知ってないとなかなか気づけない仕様なので、皆さん気を付けましょう!
P.S.
後半の MATLAB と Simulink の仕様の部分読んでテンション上がった人は、私と†同類†だと思うのでぜひお話しましょう。