MATLAB ユーザーコミュニティー

MATLAB & Simulink ユーザーコミュニティー向け日本語ブログ

Simulink API で MATLAB から Simulink を操る② ~モデルの編集~

こんにちは、トレーニングエンジニアの遠藤です。

このブログでは「MATLAB と Simulink を繋ぐ」をテーマとして、MATLAB と Simulink を連携させる機能に関して技術的な記事を書いております。今回もこのテーマに則り、MATLAB と Simulink を繋ぐ代表的な機能の一つである「Simulink API」について書いていきたいと思います。

Simulink API については、以前投稿した「Simulink API で MATLAB から Simulink を操る」という記事でも紹介しました。その記事では、モデルのブロックパラメータやコンフィグレーションパラメータを取得・変更する方法を解説しましたが、今回の記事では、ブロックの追加や信号線の結線など、モデルの構造そのものをプログラムで編集する方法を紹介したいと思います。

プログラムでモデルを編集する方法は、

  • 複数のモデルに共通するミスを修正する
  • テスト用のモデルを自動で生成する
  • モデルを規格に準拠した構造に修正する

といったように、決まった編集方法を何度も繰り返さなければならない場合や、ブロックの見落とし、修正忘れなどのヒューマンエラーを防ぎたい場合に有効です。その分少し複雑なコードを書く必要がありますが、プログラミング好きな Simulink 使いの方は、新たなモデル編集方法に惹かれること間違いなし(?)です!

今回の目標

今回の記事では、MATLAB にデフォルトで入っている vdp というモデルを題材とし、Simulink API を使って出力端子1 の直前に Dead Zone ブロックを挿入してみたいと思います。

>> vdp % vdp モデルを起動

マウス操作では一瞬でできてしまう作業ですが、プログラムで行おうとすると結構手間がかかります。具体的には、以下の操作を順番に行っていく必要があります。

  1. Dead Zone ブロックをモデルに追加する
  2. モデル上の Outport ブロックを検出する
  3. 検出した Outport ブロックに繋がっている信号線を取得する
  4. 取得した信号線の接続元を保存し、信号線を削除する
  5. 新しい信号線を Dead Zone ブロックに繋ぐ

これらの操作を Simulink API でどうやって行うか、順番に確認してみましょう!

こちらのドキュメントも合わせてご確認いただくと、よりわかりやすいかと思います。

1. ブロックの追加

まずは Simulink モデルにブロックを追加する方法について説明します。モデル上にブロックを追加したい場合、add_block という関数を使います。

>> add_block('追加したいブロック名','モデル名/つけたい名前');1 つ目の入力は追加したいブロック名、2つ目の入力はモデル名と追加するブロックにつけたい名前を指定します。一見簡単な関数に見えますが、これが意外と厄介です。例えば、以下のようなコードはエラーとなります。

>> add_block('Dead Zone','vdp/deadzone');

 ‘Dead Zone’ という名前のブロックがありません。

実は、add_block で追加したいブロック名を指定する場合、そのブロックが入っているライブラリの階層も明示的に指定する必要があるのです。今回追加したい Dead Zone ブロックは、 simulink/Discontinuities というライブラリファイルの中に入っています。そのため、Dead Zone を追加する場合は、

>> add_block('simulink/Discontinuities/Dead Zone','vdp/deadzone'); % Dead Zone ブロックを deadzone という名前で vdp モデルに追加

のように、ライブラリの階層まできっちり指定しなくてはいけません。

Dead Zone ブロックが、deadzone という名前で追加されました!

Tips

Math Operations や Signal Attributes など、Simulink ライブラリ ブラウザー上で名前が改行されているようなライブラリは、ライブラリ名に改行文字が含まれています。

このようなライブラリ名を指定する場合は、newline 関数を使うなどの方法で改行コードを文字列の中に挿入する必要があるので注意が必要です。例えば、Math Operations ライブラリーに入っている Gain ブロックを追加する際、ブロック名を以下のようにして作成します。

>> name = ['Math' newline 'Operations/Gain']

name =
‘Math
Operations/Gain’

2. モデル上のブロックの検出

次は、モデル上の Outport ブロックを検出します。モデル上の特定のブロックを検出するには、find_system 関数を使用します。こちらの関数は前回の記事でも紹介しましたね。find_system 関数は、モデル名、パラメータ名、パラメータ値を指定することで、モデル内の特定のパラメータ値を持つブロックを検出することができます。

>> blocks = find_system('モデル名','パラメータ名1','パラメータ値1','パラメータ名2','パラメータ値2',...);

よく使うパラメータは BlockType で、特定の種類のブロックを簡単に検出することができます。

>> outports = find_system('vdp','BlockType','Outport') % vdp モデル内の Outport ブロックを検出

outports =
2×1 の cell 配列
{‘vdp/Out1’}
{‘vdp/Out2’}

vdp モデル内の 2 つの Outport ブロックを検出できました。中身はセル配列になっているので、outports{1} とすれば1個目の Outport ブロックにアクセスできます。

Tips

検出の際に使用可能なブロックのプロパティについては、共通のブロックプロパティブロック固有のパラメータ のページをご参考ください。

3. ブロックに繋がっている信号線の取得

次に、検出した Outport ブロックの入力端子に繋がっている信号線を取得します。そのためには、まず前回の記事でも紹介した get_param 関数を使い、Outport ブロックの PortHandles パラメータを指定を取得します。PortHandles パラメータには、そのブロックが持つ端子情報が格納されています。

>> ph_outport = get_param(outports{1},'PortHandles') % 1 つ目の Outport ブロックの端子情報を取得

ph =
フィールドをもつ struct:
Inport: 451.0006
Outport: []
Enable: []
Trigger: []
State: []
LConn: []
RConn: []
Ifaction: []
Reset: []

Inport フィールドに「451.0006」という数値が格納されていますね。これは Ourport ブロックの入力端子の「ハンドル」を表しています。ハンドルとは、モデル上のブロックや信号、端子などのオブジェクト 1 つ 1 つに割り振られた ID 番号のようなものです。

この入力端子のハンドルに対して再度 get_param 関数を使用することで、入力端子のプロパティにアクセスできます。例えば、Line プロパティを指定すれば、その端子に接続されている信号線を取得できます。

>> line = get_param(ph_outport.Inport,'Line') % Outport ブロックの入力端子に接続されている信号線を取得

line =
425.0007

またも数値が返ってきました。これが信号線のハンドルになります。ちなみに、このハンドルはモデルを開く度に値が変わるので注意が必要です。

Tips

得られたオブジェクトのハンドルに対して get 関数を使用すると、そのオブジェクトが持つプロパティの一覧を表示することができます。

>> get(line) % line オブジェクトのプロパティ一覧を表示

DataLogging: 0
DataLoggingNameMode: ‘Use signal name’
DataLoggingName: ”
DataLoggingDecimateData: 0
DataLoggingDecimation: ‘2’
DataLoggingSampleTime: ‘-1’
.
.
.

4. 信号線の接続元を保存し、信号線を削除する

次に、取得した信号線の接続元の情報を保存してから削除します。本当は既存の信号線を deadzone ブロックに繋ぎ変えられればよいのですが、残念ながら Simulink API では信号線の繋ぎ変えはできません。そのため、信号線の接続元の情報を保存し、その信号線を削除し、新しい信号線を接続する必要があります。

さて、それではまず信号線の接続元の情報を取得します。入力端子と同様、信号線も get_param 関数を使うことでプロパティにアクセスできます。今回は接続元の情報が欲しいので、SrcPortHandle プロパティを指定します。

>> src = get_param(line,'SrcPortHandle') % 信号線の接続元を取得

src =
447.0006

信号線の接続元のハンドルを取得することができました。それではこの信号線を削除してしまいましょう。信号線の削除は delete_line 関数を使用します。

>> delete_line(line); % 信号線を削除

Ourpot に繋がっていた信号線が削除されました。

5. 信号線を繋ぐ

あとは削除した信号線の接続元と、追加した Dead Zone ブロックの入力端子、Dead Zone ブロックの出力端子と Outport ブロックの入力端子をそれぞれ接続するだけです。信号線を接続するときは、接続元と接続先の端子のハンドルが必要です。接続元と Outport の入力端子のハンドルは既に取得していますので、Dead Zone の入出力端子を get_param 関数で取得しましょう。

>> ph_deadzone = get_param('vdp/deadzone','PortHandles') % deadzone ブロックの端子情報を取得

ph_deadzone =
フィールドをもつ struct:
Inport: 118.0002
Outport: 120.0001
Enable: []
Trigger: []
State: []
LConn: []
RConn: []
Ifaction: []
Reset: []

Inport が入力端子のハンドル、Outport が出力端子のハンドルです。どちらも問題なく取得できてそうですね。

それでは信号線を接続していきましょう。add_line 関数を使うことで、端子間に信号線を繋ぐことができます。先ほど取得した、削除した信号の接続元ポート src と deadzone ブロックの入力端子を接続します。

>> add_line('vdp',src,ph_deadzone.Inport); % 接続元からDead Zone ブロックに信号を接続

Dead Zone ブロックと Outport ブロックの接続も同様です。

>> add_line('vdp',ph_deadzone.Outport,ph_outport.Inport); % Dead Zone ブロックから Outport ブロックに接続

deadzone ブロックに信号が接続されました!

オプション:ブロックの移動

以上の操作でモデル内にブロックを挿入することができました。モデルとしてはこれで正常に実行可能なのですが、追加したブロックが変なところにあって、見栄えが悪いですね。追加した Dead Zone ブロックを適当な位置に配置してみましょう。

ブロックの位置は、Position パラメータによって定まっています。現在の deadzone ブロックの位置を、get_param で確認してみましょう。

>> pos = get_param('vdp/deadzone','Position') % deadzone ブロックの位置を取得

pos =
160 45 190 75

4 要素のベクトルが返ってきました。これらは [左端 上端 右端 下端] の位置を表しています。これを使うことで、所望の位置にブロックを配置することが可能です。

pos_outport = get_param(outports{1},'Position');
left = pos_outport(1)-50; % Outport ブロックより 50 だけ左
middle = (pos_outport(2)+pos_outport(4))/2; % Outport ブロックと同じ高さ
size = 30; % 30 の大きさ
set_param('vdp/deadzone','Position',[left middle-size/2 left+size middle+size/2]);

いい感じの位置に配置することができました。マウス操作でブロックを追加したときと遜色のないモデルが完成しましたね!

Tips

Simulink.BlockDiagram.arrangeSystem 関数や Simulink.BlockDiagram.routeLine 関数を使うと、ブロックや信号線をいい感じに配置してくれます。具体的な位置を求める必要がない場合はこちらの方が手軽に位置調整ができるのでオススメです。詳しい使い方については各関数のドキュメントをご確認ください。

おわりに

今回の記事では、Simulink API の関数を駆使してモデルの特定の場所にブロックを挿入する方法を紹介しました。もちろんマウス操作で編集したほうが楽な場合が多いですが、冒頭でも記載した通り、単純な編集作業の繰り返しや修正ミスを防ぎたい場合は重宝します。

また、GUI 的な操作を CUI 的に行うというのには何とも言えぬロマンがありますよね。プログラムが得意な人は、スクリプトから華麗にモデルを編集することで、周りの人をあっと驚かせることができるかもしれませんよ!

|
  • print

Comments

To leave a comment, please click here to sign in to your MathWorks Account or create a new one.