Simulinkのカスタムタブ機能でラクをしよう!
本日は、Simulinkの達人Simulinkの中の人からの記事です。Simulink機能の話ですが、中身はかなりハードコアなMATLABコードです。
こんにちは、Simulinkの中の人です。
今回は、R2021bで実装されたカスタムタブを使ってSimulinkのモデリング作業を効率化した話をしようと思います。実は、すでにTwitterで私のカスタムタブ機能についての全機能を紹介してしまっているのですが、本ブログでは、一部の特にお気に入りの機能について、厚めに紹介しようと思います。また、なぜこのようなものを作ろうとしたのか、またその機能、実はこうなってますというような裏話についても話します😄
なお、私のカスタムタブはこちらからダウンロードし、有効化することができます。手順もリンク先に記載しています。
目次
背景
私は、MathWorks に入社する前は某メーカーで制御設計をしていました。基礎研究から量産開発まで、幅広く携わらせていただきましたが、その中で、MATLAB, Simulinkは毎日のように使い続けていました。開発現場で作るモデルというのは、非常にたくさんのブロックを並べて、信号線を接続する作業が必要になります。また量産開発ではそれに加えて制御モデリングガイドラインというルールもあります。そんな中モデルを見やすく作るには、大量のマウス操作が必要でした。私は手を痛めたことが何度かあります。私は実は両利きなので、痛めたら反対の手で操作する、みたいなことをして乗り越えていましたが、私の同僚の中には腱鞘炎になった人もいました。
当時は R2015a というバージョンでしたが、その後バージョンが上がるにつれて、効率的に操作ができるように機能改善がなされていっています。公式機能だけに着目しても、十分ラクに作業できるようになっています。しかし、私としてはまだまだ不十分でした。そんな中、R2021bでカスタムタブという機能が実装され、Simulink のツールストリップ部分に自分だけのボタンを作ることができるようになりました。これにより、従来よりも非常に少ないマウス操作で自作の関数が実行できるようになりました。そうして作ったのが、今回紹介するカスタムタブというわけです。
従って、そもそもこのカスタムタブは、公式機能ではありません。動作保証などはしていないということは留意ください。また、完全に「私の作業」を効率化するために作ったものですので、必ずしも皆さんの作業スタイルにマッチするとは限りません。それでも公開した理由は、これを使って作業がラクになる人が少なからずいるだろう、と思ったからです。また、これをベースに皆さんの方でカスタマイズしてもらえれば、結果的に多くの人の利益になるはず、と思いました。
ポートで揃える
以下の動画のように、ブロックの上下位置を調整し、接続先のポートの位置とぴったり合わせることができる機能です。
この動画のようにポートを揃えると、可読性が良くなります。しかし、やってみると分かりますが、これを手作業でやるには大変な労力なのです。なので、それを全自動で行う本機能は、非常に画期的な機能です。
最初にこの機能(関数)を作り始めた時、ブロックが移動した後、そのブロックに依存するブロックの移動先を決めるような処理にしていました。そのため、複雑な接続構造を持つブロック群に対して、動かし方の条件がどんどん複雑になっていきました。しまいには”スパゲッティコード”となりまして、手が付けられなくなってしまったのが、実は現在のコードです笑(興味のある人は見てみてください)
本当は、事前に動かす位置を全部計算してから動かした方が良かったですね。現状では、どうしても変な位置に動いてしまうパターンがありまして、その時はそのブロックを個別に選んで再度実行するしかないです。
最近傍を接続
Simulink のモデリング作業で最も苦痛なのは、信号線を接続する作業です。少しであれば全然問題ありませんが、大量にブロックがあった時、狙ったポートに向かってマウスカーソルを動かすことは、かなりの負担になります。全自動で信号線を接続することは、全 Simulink ユーザーの悲願でもあります。この「最近傍を接続」機能は、その夢の第1歩となる画期的な機能です!
実はこの機能は、何となく思いつきで作り始めました。ふと「入出力ポートの取り得る組み合わせの中で、一番距離が近いパターンを探せば全自動で結線できるのでは🤔」と思いまして、それを形にしたものになります。
要するに、やっていることは以下の通りです。
- 未接続の入力、出力ポートの組み合わせをリスト化する。
- 全組み合わせの中から、接続する入出力ポートの距離の総合計が最も小さいものを探す。
- 見つけた組み合わせで信号線を結線する。
当初、「これは簡単にできそう♪」と思いましたが、やってみると、実はそこには深淵がありました。
まず、入出力ポートの組み合わせ総数は、入力グループと出力グループから一つずつ取り出してペアを作る、その組み合わせ総数になります。具体的には、
となります。(ただし、入力ポートの数をn、出力ポートの数をm、m >= nであるとする。m < nの時は、mとnを入れ替えることで同様になる。)
例えば、入力ポート3個、出力ポート4個であれば24通りになります。よく見ると分かりますが、この式は、ほとんど「階乗」と同じ計算をしています。よって階乗と同じスピードで組み合わせ数が増大します。現実的には、入出力ポート数が8個(8! = 40320)が1秒以内で処理しきれる最大ラインでした。9個、10個と増えていくと、数十秒、数十分と処理時間が伸びていきました。もっと計算効率化をすれば良いかもしれませんが、そもそも指数関数的に計算時間が増大するのでは、何をしても焼け石に水です。局所解を探索するアルゴリズムを設計する必要があります。
また、評価関数も単純に「入出力ポートの距離の総合計が最も小さいもの」としただけでは、感覚的に良いものにはなりませんでした。縦方向よりも横方向に近いペアを優先するとか、右から左へ繋ぐことを避けるとか、考慮すべき要素はたくさんあります。それらの重みづけも悩みました。「人間の感覚」を数式で表現しようとすると、とんでもなく非線形になるということがよく分かりました😑
私は最適化の専門家ではないので、このような非線形の評価関数を持つ巡回セールスマン問題のような問題を解くアルゴリズムは、すぐに作れませんでしたので、総当たりで行う形にしました。現状、入出力ポート8個まで対応しているだけでも、十分に作業効率の向上となります。是非使ってみてください!
入力を継承してテストハーネスを作成
以前の記事「Simulink Test によるテスト駆動モデリングのすすめ」で、Simulink Test のテストハーネスを使って開発を効率化する方法を紹介しました。その中で、カスタムタブの「作成 / 開く」「閉じる / 削除」ボタンについて解説しました。今回はそこから1歩進んで、「入力を継承してテストハーネスを作成」を解説します。
1回のシミュレーションに非常に時間がかかるモデルがあるとします。そのモデルの中に、何らかの不具合が発生しているとします。その時、何度もモデルを実行し直して実行結果を解析しなければなりません。でも、シミュレーションに時間がかかるのでは、したくなくなりますよね。そんな時、この「入力を継承してテストハーネスを作成」が使えます。
おそらく不具合が起きているであろうサブシステムを対象として、「入力を継承してテストハーネスを作成」を実行すると、そのサブシステムの入力信号をログする形で、まず1回モデルを実行します。実行が終わった後、そのサブシステムに対してテストハーネスを作成し、その入力部分にログした信号データを流すように設定します。これにより、あたかも全体シミュレーションを行ったように、サブシステム単体を実行できます。時間のかかる計算を省略できることで、繰り返し実行して調査する業務が格段に捗りますよ!
具体的な動作については、上記の動画をご確認ください。
ルート実行
「参照モデル」機能をたくさん活用しているモデルを編集している時、そのモデルファイルごとにたくさんのウィンドウを開いて作業することがあります。R2019aから、参照モデルブロックをダブルクリックすると、同じSimulinkキャンバスで別のモデルファイルを開くように仕様が変わりましたが、やっぱり別ウィンドウで作業したい場合もあります。この時、一番上の親モデルを実行するつもりで、子モデルの実行ボタンを押してしまうことがあります。「ルート実行」を使うと、それを回避して常に親モデルを実行できます。
この関数の中では、今 MATLAB が開いているモデルファイルをリスト化し、各モデルファイルが参照している先のファイルをさらにリスト化します。その中で、最も参照されている回数が少ないモデルファイルを探しています。そのモデルが最上位階層の親モデルであるはずですので。
ちなみに、RoadRunner と連携しているモデルであった場合は RoadRunner から実行しないといけないので、その時は RoadRunner を実行するようになっています。
変更を保存
「参照モデル」機能をたくさん活用しているモデルを編集している時、保存ボタンや Ctrl + S でモデルを保存すると、編集していないはずのモデルファイルまで保存された、という経験はありませんでしょうか。特に Git や Subversion を使っている人は、なぜか変更済みマークが付いていることに疑問を持ったことがあるはずです。これを回避して、本当に変更したモデルだけを保存するのが、この「変更を保存」機能です。
実は、Simulinkモデルには、ブロック図やパラメーター値以外にも、様々な「メタ情報」が含まれています。例えば、”ウィンドウの位置”などがそうです。皆さんがモデルを編集して保存し、その後 MATLAB を再起動した後にそのモデルを再度開くと、また同じウィンドウ位置で開きますよね? それは、モデルファイルの中にウィンドウ位置が保存されているからなのです。
上記の動画の例で、例えば親モデル(simulation_top_layer.slx)のウィンドウ位置が変更された場合は、内部的には「変更された」扱いになっており、保存ボタンを押すと、ファイル内の情報が更新されます。分かりにくいことに、この変更には*マークが付かないので、ユーザー側は変更されたかどうかに気づけないのです。この仕様には良し悪しがありますが、ひとまず Git などで変更点を管理している場合には困ります。ユーザーが設計するモデルには何も変化がないのに、変更されたことになるからです。
そこで、この「変更を保存」を使えば、*が付いたモデルだけを保存できます。ただし注意点としては、参照モデルと参照サブシステムが複雑に混ざり合った大規模モデルでは、変更された全部のモデルをリスト化できない場合がありますので、保存しきれない可能性があります。完璧な機能を作るのは、私には難しかったです。大規模モデルでは、なるべく個別のモデルを別ウィンドウで開いて編集することをおすすめします。
まとめ
このように、Simulink の API とカスタムタブ機能を組み合わせれば、いろんな作業効率化ができます。皆さんも是非このカスタムタブをベースに、皆さんオリジナルのタブを作ってみてください😄
- Category:
- Simulink General
Comments
To leave a comment, please click here to sign in to your MathWorks Account or create a new one.