最小構成のMBD事例 第2章 バックナンバー

最小構成のMBD事例 第2章 バックナンバー事例
最小構成のMBD事例 第2章 バックナンバー
スポンサーリンク

はじめに

第1章のバックナンバーはこちら

Modelicaによるプラント設計。
それをFMUにして他のプラットフォーム(Python等)での再利用。
さらに制御器との様々な接続方法(ASAM XCP等)の事例を紹介していく話。
Modelica用ツールとしてはOpenModelicaを使用する。

OpenModelica

ASAM XCP

参考書籍等

スポンサーリンク

導入編

  • 恒例の太郎くんの悩み事からスタート。
  • プラントモデルを作成する必要がある。
    • それっぽいものを作るだけならば過去の記事を参照すればOK。
      • 一次遅れ系。
      • モーター伝達関数。
  • 本シリーズはModelicaを使用した話に踏み込む予定。
    • Modelica用のツールは雰囲気的にはSimulinkに似ていることが多い。
スポンサーリンク

Modelica

  • Modelicaの説明。
    • オブジェクト指向のマルチドメイン・モデリング言語。
    • 機械、電気、電子、油圧、熱、制御などの領域を横断して記述できる。
  • Modelicaはプログラミング言語だが、一般的な言語とは性質が異なる。
    • シミュレーション特化言語。
    • モデル定義としてパラメータと方程式を内包。
  • Modelicaのプログラミング言語として性質を知るためモデル定義確認。
    • Massモデルを参照。
    • ある質量とある長さをもった完全剛体を1Dとして両端に力を加えられるモデル。
  • 一般的なプログラムと異なり代入式ではなく方程式を定義していく。
    • シーケンシャル且つ代入になる書き方もあるが一般的なプログラムのそれとは異なる。
  • Massモデルの中にあるextendsは継承を示すキーワード。
  • PartialキーワードはModelの不完全性を示しており、継承以外の利用ができないよう制約を掛けている。
    • OOPの抽象class的なもの。
  • PartialRigidモデルは剛体モデル。
    • 直線運動する剛体モデルを作る際はこのモデルを継承すると吉。
  • Flangeの説明。
    • 様々な領域で使われる用語ということもあり「円筒形の物体」程度の定義。
  • ModelicaのFlange。
    • connectorタイプでModel間の接続用インターフェース。
    • flow接頭辞は運動の第3法則(作用・反作用)を実現するのに便利。
      • 力(Force)の宣言で利用されてる。

スポンサーリンク

Modelicaツール

  • Modelicaを扱えるツールは様々。
    • Amesim、Simplorer、Dymola、MapleSim、SimulationX、OpenModelica。
  • 自動車業界ではMapleSim、SimulationXが多め。
    • HILS、Simulinkの相性の都合。
  • OpenModelicaはオープンソースなツール。

スポンサーリンク

OpenModelica

  • OpenModelicaの説明。
    • Wikiepdia英語ページから引用。
    • 自動車、水処理、発電所の領域で使われている。
    • 自動車業界でも開発フェーズだと相互運用性都合でプロプライエタリ品を使うことが多い。
  • 複数のツールで構成されている。
    • コンパイラ、エディタ、インターフェス、プラグインなど。
  • OpenModelica Compiler (OMC)はコンパイラ。
    • C言語を生成する。
    • インタプリタ用言語を生成してデバッグ動作を実現。
  • OpenModelica Connection Editor (OMEdit)
    • グラフィックエディタ。
    • C++/Qtで作成されているためマルチプラットフォーム。
  • OpenModelica Shell (OMShell)について説明。
    • その名の通りシェル。
    • MATLABのコマンドウィンドウに近い。
  • OpenModelica Notebook (OMNotebook)の説明。
    • コマンドの実行とその結果を含めてドキュメント化できる機能。
    • Jupyter Notebookに近い。
  • OpenModelica Python Interface (OMPython)
    • Python自動化インターフェース
  • OpenModelica Matlab Interface (OMMatlab)
    • 上記のMATLAB版
  • Modelica Development Tooling (MDT)
    • Eclipseインターフェース
  • OpenModelicaのダウンロード。
    • Windows、Linux、Mac版がある。
    • 32bit、64bit用に分かれている。
  • OpenModelicaのインストール。
    • 基本はウィザードに従って「次」へ進んでいくだけ。
    • トータルで10Gbyteほどのサイズになるので、15Gbyteくらいの空き容量があった方が良い。

スポンサーリンク

massモデル

  • 使用するモデルはMassとconstantForce。
  • 直線運動をふんわり知って置いた方が良い。
    • 加速度、速度、距離、力、運動量、仕事、仕事率。
  • OpenModelica Connection Editorで各モデルを配置&接続
    • Modelica→Mechanics→Translationalに目的のモデルがある。
  • 前回作成したモデルが何を示しているか確認。
    • massを引っ張り合ってるモデル。
    • グラフィックエディタだと分かりにくいがマイナス符号を付けないと逆向きの力にはならない。
  • シミュレーション結果を事前に予測してみる。
    • 運動方程式を使用する。
    • 質量と力が分かっているので、加速度が求められる。
    • 加速度から速度、速度から距離。
  • シミュレーションするモデルと前回の予測を再掲。
    • constantForce、massの組み合わせ。
    • \(1[m/s^2]\)の加速度。
  • シミュレーション開始方法。
    • OpenModelica Connection Editor上部の矢印アイコンをクリックするだけ。
  • シミュレーション結果確認。
    • 予測通りの結果が得られた。
  • 他のシミュレーション結果のパラメータを確認
    • 加速度に加えて、速度と移動距離。
    • OpenModelica Connection Editorのプロットの画面の変数ブラウザでチェックを入れるだけで確認可能。
  • それぞれのパラメータの関係性を確認。
    • 加速度を積分して速度。
    • 速度を積分して移動距離。
  • OpenModelicaはローコード、ノーコードの性質がある。
    • しかし、コードの読み書きも出来ていた方が良い。
  • massモデルのソースコードを確認。
    • equationではconnectキーワードで接続と定義。
    • annotationキーワードでグラフィカルな情報が追記されソースコード内で情報が完結している。
  • Modelicaコードを弄ることでいろいろと効率化される可能性がある。
  • 実際にparameterキーワードを使って変数を定義。そして、それをconstantForceに設定。
    • 今のままでは動作は何も変わらない。
    • ソースコード上でパラメータ調整をし易くなったくらいの効能しかない。
    • 即値で調整するのでは労力に差はさなそう。
  • 「Modelicaのソースコードに変数を設置」の効能はモデル編集ではなくシミュ―ション時。
  • シミュレーションをするためには毎回モデル検査、Cコード生成、コンパイルが入る。
    • 規模が大きくなれと結構待たされる。
  • parameterキーワードで宣言した変数はコンパイル後にも修正可。
    • コンパイルせずに再シミュレーション可能。
  • Modelicaコードで変数追加後のOpenModelica Connecter Editor上の見え方確認。
  • まずは普通にシミュレーション。
  • 変数ブラウザで変数を書き換えたあとに再シミュレーション。
  • モデルチェック、コンパイル無しで即シミュレーション結果が得られた。
  • ちょっとしたテクニックをしってるだけで効率化可能。
  • Modelicaコードに追加したパラメータが増えると管理が大変。
    • 注釈が欲しい。
  • 他のモデルもModelicaコードで書かれたものであるが変数ブラウザで注釈が確認できる。
    • つまり、注釈が入れられるはず。
  • 変数の隣にダブルクォーテーションでくくった文字を入れれば注釈。
    • 変数ブラウザでも確認可能。
  • ソースブロックによっては様々な信号を入力できる。
    • ソースブロックは信号を生成してくれるブロック。
  • 信号を生成するソースブロックと物理量に変換するソースブロックがある。
    • 今回はtrapezoidが信号生成、forceが物理量変換。
  • Modelicaライブラリは大量にあるがライブラリブラウザで検索ができるようになってる。
  • trapezoidブロックとForceブロックを配置。
  • ForceブロックはModelica→Mechanics→Translational→Sourcesにある。
  • trapezoidブロックはModelica→Blocks→Sources→Trapezoidにある。
  • 本来はtrapezoidを修正する必要があるが今回は不要。
  • trapezoidの設定もせずにおもむろにシミュレーション。
    • 矩形波的な出力になった。
  • 変数ブラウザでtrapezoidの設定を編集。
    • 台形波的な出力に変化。
  • このように、変数ブラウザからパラメータ変更が可能なソースブロックはそこそこある。
    • これらを知っているといろいろとサボれて楽できる。
スポンサーリンク

DCモータ

  • ModelicaのDCモータモデルのサンプルの位置をライブラリブラウザで確認。
  • DCモータモデルをとりあえずシミュレーション。
    • 制御電圧、制御電流、角速度の結果を確認。
  • 今回は電圧をランプ関数で制御したシンプルなもの。
    • ランプ関数は0を起点に徐々に上がっていく関数。
  • ModelicaのDCモータモデルをちょっと掘り下げ。
    • 以下が絡んでくる。
      • 電気/電子領域。
      • 古典力学領域。
      • 幾何学(材料力学)領域。
  • 物理モデリングは伝達経路、伝達関数、微分方程式解決、シミュレーションの4つの工程がある。
    • Modelicaは伝達関数、微分方程式解決をサボれるツール。
  • DCモータモデルのModelicaコードを確認。
    • 半分くらいはannotationなのでそれほど規模は大きくない。
  • 見るべきポイントを列挙。
    • 先頭のparameter部。
    • 中間のモデル宣言部。
    • 真ん中DcPermanentMagnetData。
      • これが今回のサボりポイントの目玉となる。
  • Modelicaコードのparameter部を確認。
    • parameterに関しては以前やった。
    • しかし、今回はReal型ではない。
    • 厳密には、Real型に単位の定義を付加したもの。
      • 電圧だったら”V”。
      • トルクなら”N.m”。
  • SI単位系で存在するものはModelica.SIunitsの中にすでに定義済み。
  • Modelicaコード モデル宣言部を確認した。
    • 以下が存在しており、OpenModelica Connection Editor上にもある。
      • DC_PermanentMagnet、Ramp、SignalVoltage、Inertia、TorqueStep。
  • DcPermanentMagnetDataが特殊な位置づけ。
  • DcPermanentMagnetDataをOpenModelica Connection Editorで確認。
    • UI上で様々なパラメータを設定可能。
    • さらにそのパラメータをDC_PermanentMagnetに渡すことでモデル初期化している。
  • 初期化するモデルが一個だとあまり意味がないかもだが、同特性モデルが複数あるとサボれる。

スポンサーリンク

FMI(Fumction Mockup Interface)

  • Modelicaモデルを外部から利用する手段は一応ある。
    • OpenModelicaからFMIをもったFMUを出力可能。
  • FMIは物理モデルをモジュール化したものの標準インターフェース。
    • MODELISARプロジェクトで策定。
    • その後、Modelica Association Project(MAP)で管理。
  • FMI仕様の公開場所確認。
    • FMI-Standardにて公開されている。
  • FMI/FMUはMATLAB/Simulinkで言うところのS-Functionみたいなもの。
    • コンセプトとしてはほぼ一緒。
    • FMI/FMU側は標準仕様と言うことでもうマルチプラットフォームを意識したものとなっている。
  • FMU/FMIの存在価値について確認。
    • S-Functionと同等とすると存在価値が薄くなる。
  • 自動車業界なりの狙いはある。
    • サプライヤから納入される部品と同等の振る舞いするモデルモジュールをもらい、完成車メーカ側で統合する。
  • FMU/FMIはSimulink、LabViewをプラットフォームとして入出力を繋げられる。
  • 知ってる範囲でFMU/FMIに対応しているツールを調べてきた。
    • 自動車業界限定且つメジャー所だと5社ほど。
      対応Versionやアドオン追加などのの制約はある。
      対応ツールは多いので結構使えそう。
      • ただし、Vector社製品のようにCANoeは対応しているが、CANapeは対応していない。などはある。
  • 各社ツールでFMU/FMIの利用で追加費用は発生しない。
    • 非競争領域と考えて広めることを重要視している可能性が高い。
    • 2016年くらいから流行り始めている。
      • SDKのリリースが2014年なのが理由かも。
  • FMU/FMIのVersionは1.0と2.0がある。
    • ただし、互換性はない点に注意が必要。
  • FMU/FMIのシミュレーション方式は2種類ある。
    • Model Exchange(通称ME)。
      • 外部にSolver。
    • Co-Simulation(通称CS)。
      • 内部にSolver。
  • SolverはODE Solverのこと。
    • 常微分方程式を解決する機能。
      • オイラー法、ホルン法などが有名
  • FMU/FMIのシミュレーション方式とSolverの位置づけを図解した。
  • MEは近似精度を調整したい場合に有利。
    • ECUの粗い制度を再現したい。
    • プラントの演算負荷を下げてシミュレーションを高速化したい。
  • CSは内部にSolverがあり、繋ぐだけで動くので設定が簡単。
    • CSのみのサポートしかしていないツールもある。
  • FMU/FMIはあまり一般的に知られているものではないので利用方法の情報が皆無。
    • よって、仕様に踏み込まないと利用方法も見えない。
  • 仕様書を読み込むのも大変なのでFMU自体の中身を見て行った方が理解としては楽そう。
    • 実はFMUは特定のファイルとフォルダ構成をzip圧縮したもの。
      • つまり解凍して中身を参照できる。
  • FMUとzipとして解凍してみた。
    • 何個かのフォルダとxmlファイルがあった。
  • binariesにプラットフォーム別のライブラリが格納。
  • resourcesフォルダに依存関係があるファイル群を格納。
  • modelDescription.xmlにinput。output、内部パラメータが定義されている。
  • FMU/FMIのプラットフォーム上での位置づけを再確認。
  • FMUのユーザ視点に於いての位置づけを確認。
    • modelDescription.xmlとMotor.DLLの位置づけなど。
    • これを元に仕様の性質から予測される利用手順を列挙。
  • やはりmodelDescription.xmlの中身が気になるので、簡単に説明予定。
  • modelDescription.xmlの中身を確認。
    • name、valueReference、description、variability、causality、Real unitが存在。
  • valueReferenceについて仕様確認。
    • 変数ハンドル用で数値の衝突は禁止。
    • ただし、エイリアスはその限りではない。
  • modelDescription.xmlのvariabilityの仕様について。
    • constant(定数)、fixed(固定値)、tunable(調整可能値)、discrete(離散)、continuous(連続)。
      • fixedはシミュレーション前であれば変更可能。
      • tunableは変更時にODE演算のイベントが発生。
  • modelDescription.xmlのcausalityの内容を確認。
    • parameter、calculatedParameter、input、output、local、independentの6種が存在。
    • parameterはModelicaのparameter相当。
    • calculatedParameterは初期値関連。
  • FMU/FMIを読めそうなツールを確認。
    • 以前、太郎くんが調査した情報をベースに確認。
      • CANoeがすぐ使えそうだった。
  • CANoeでFMU/FMIの変数確認。
    • 問題無く確認できた。
    • FMU/FMIの変数をCANoeのシステム変数に割り付ける形。
      • よって、CAPLからでも簡単に制御できそう。

スポンサーリンク

FMILibrary

  • C言語からFMU/FMIを制御するライブラリはFMILibrary
  • Windows版のFMILibraryはあるにはあるが…。
    • win32版のみ。
    • 64bit版は自分でビルドする必要がある。
  • OpenModelica64bit版のFMUは恐らく64bitビルド。
    • FMILibrary 64bit版を用意することに。
  • FMILibraryのビルド環境準備。
    • Visual Studio 2017 express。
    • cmake。
      • コンパイラ非依存なビルド自動化ツール。
  • FMILibraryのソースコード。
    • zipアーカイブかGithubで入手できる。
    • 今回はFMILibrary-2.0.3-src.zipを使用。
  • FMILibraryのビルド手順を確認。
    • 開発者コマンドプロンプト起動、cmakeでビルド、ライブラリインストール。
  • cmakeへのオプションがちょっとややこしい。
    • FMILIB_INSTALL_PREFIXでインストール先。
    • FMILIB_FMI_PLATFORMでプラットフォーム。
    • -Gでビルドする環境指定。
  • cmakeの前回のコマンドを使ってコンフィグレーション開始。
    • 問題無く完了。
  • そのままcmakeでビルド開始。
    • ビルドの途中でエラーが発生。
    • xmlparse.cの中でBLOCKとsという構造体とメンバ変数の絡みでエラーが起きたようだが・・・。
    • とりあえず、頑張って調べてみる。
  • FMILibraryのビルドエラーの原因究明。
    • stddef.hにoffsetofが定義されていなかった。
    • 厳密にはC++向けには定義されていたが、C言語向けには定義されていなかった。
    • Visual Studio自体がC++を想定した環境であるためC言語のケアが薄いためなのかもしれない。
  • 再びFMILibraryのビルドにチャレンジ。
    • 今回は無事ビルドが通った。
      • ライブラリが生成されたのも確認。
    • その後、指定したインストール先にライブラリ及びヘッダファイルが配置された。
  • FMILibraryビルド時にサンプルプロジェクトが生成されているので、今後はこれをベースに話を進める予定。
  • FMILibraryのサンプルプロジェクトを確認。
    • 大量にある。
    • 今回はfmi2_import_cs_testを使用。
    • すでにビルド済みのものが存在。
  • fmi2_import_cs_testは引数を要求するタイプの実行プログラム。
    • よって、ただ起動しただけでは何もしてくれない。
    • 引数については次回説明予定。
  • fmi2_import_cs_testの引数について確認。
    • fmu_fileとtemporary_dir。
  • fmu_fileはその名の通りFMUを指定。
    • 今回はFMI2.0且つCSのFMUであるBouncingBall2_cs.fmuが該当。
  • temporary_dirはFMUを展開するためのワーク用。
  • fmi2_import_cs_testの起動時パラメータ確認。
    • FMUとテンポラリディレクトリのPathを設定。
  • 実行と結果を取得。
    • 「log level = VERBOSE」ってのはFMILibraryの内部のデバッグログ。
    • Ball height、Ball speedとその次に続いている数値がシミュレーション上重要。
  • fmi2_import_cs_testの実行結果のうちシミュレーション部分のところだけ抜き出し。
    • Ball heightとBall speedのパラメータがある。
  • 本シミュレーションはボールを投げたあとのバウンドに伴うボールの高さと縦方向の速度を示したもの。
    • Excelでグラフ表示してみたところ確かにそんな感じ。
  • シミュレーション時間とシミュレーションステップはFMUの外側の制御の仕方次第で確定する。
    • FMU処理自体はfmi2_import_do_stepという関数の中で指定時間分実施する動き。
  • サンプルプロジェクトfmi2_import_cs_testの場合はhstepとtendを調整すればOK。
    • 時間は「秒」である点に注意。
  • fmi2_import_cs_testのシミュレーションステップとシミュレーション時間を変えてみた。
  • 上記のシミュレーションを実行。
    • 精度を細かくしたのと、シミュレーション時間を延ばしたことでデータ量は増えた。
  • グラフで確認。
    • 前回の100msサンプリングと同じ特性で精度、時間が変わっていた。
  • FMUのシミュレーションパラメータを変更することができる。
    • ただし、イニシャルモード中。
    • イニシャルモードを指定するAPIが存在。
  • fmi2_import_set_realというAPIでパラメータ変更が可能。
    • 型に応じたAPIになっており、他にinteger、boolean、string用のAPIが存在する。
  • fmi2_import_set_realのAPI仕様確認。
  • FMI statusの定義確認。
  • モデル記述オブジェクトはfmi2_import_parse_xmlで取得できるfmi制御用のハンドル。
    • FMUを展開した後に出てくるのmodelDescription.xmlを指定する必要がある。
  • modelDescription.xmlでインターフェース定義を確認。
  • fmi2_import_set_realに渡すvalueReferenceはmodelDescription.xmlに定義されてるvalueReferenceを渡せばOK。
  • これを踏まえた上で最もシンプルと思われるコードサンプル提示。
  • 修正コードができたので確認。
  • 内容の詳細説明。
    • 読み出すvalueReference群の定義。
    • fmi2_import_get_realで一気に読み出し。
    • ボールの初期の高さだけ変更。
    • fmi2_import_set_realで一気に書き戻し。
  • 数値解析ツール由来のベクトルで一気に制御する方式になっている。
  • 修正済みfmi2_import_cs_testを実行。
    • パッと見変化がわからないので以前の実行結果と比較。
      • 明らかに初期のボールの高さは変わっている。
    • グラフにして確認。
      • 初期のボールの高さが変わっているので、跳ね方も変わる。
  • このように初期パラメータもFMILibrary経由で変更可能。
  • FMUはパラメータ名とvalueReferenceの紐づけが出来た方が運用し易い。
  • FMILibraryはmodelDescription.xmlの内部情報を構造的に抱えている。
    • よって、APIで各種情報を取得可能。
  • 「modelDescription.xmlの内容を列挙」までの流れを確認。
    • 手順は多いが、流れはシンプル。
  • fmi2_import_parse_xmlについては以前やったのでスルー。
  • fmi2_import_get_variable_listはmodelDescription.xmlの情報取得の起点。
    • ソートルールを切り替えられる。
      • 型/valueReferenceでソートがちょっと特殊。
        • ベクトル的アクセスで使えそう。
  • 「fmi2_import_get_variable_list_sizeによる変数リスト数の取得」の仕様確認。
    • やってることはそのままでリストの要素数を取得。
  • 「fmi2_import_get_variableによる変数オブジェクトの取得」の仕様確認。
    • 変数オブジェクトは変数関連の情報にアクセスするハンドルのようなもの。
  • valueReferenceの取得方法確認。
    • 変数オブジェクトを渡すと取得できる。
  • 変数名の取得。
    • これも変数オブジェクトを渡すと取得できる。
  • その他のdescription、variability、causality、initial。
    • これも一緒で変数オブジェクトを渡して取得。
  • fmi2_import_get_variability、fmi2_import_get_causality、fmi2_import_get_initialと併用して使う便利APIが存在。
    • 上記関数戻り値のenumに準じて文字列を返してくれるAPI。
    • 中身はswich分で実現してるだけ。
  • 次回は実際にソースコード作成。
  • modelDescription.xml内部変数列挙の処理手順確認。
  • コード追加箇所説明。
    • fmi2_import_enter_initialization_modeとfmi2_import_exit_initialization_modeの間。
  • コード提示。
    • 前回までに説明したAPI(文字列変換含む)を全部使用した。
  • 「modelDescription.xml内の変数情報を列挙」を実施。
    • 問題無く動作。
    • modelDescription.xmlに記載されてる変数がすべて列挙されていることを確認。
  • ソートルールは「XMLファイルに記載されているオリジナルの順序」
    • こちらも想定通りの動作になっていることを確認。
  • 変数リストのソートルールが複数あることを思い出した。
  • よって、他のソートルールも試した。
  • それぞれ想定通りの動作になっていることを確認。
  • ソートの変更は現実的にはあまり出番は無さそう。
    • HILS、RAPIDコントローラで使うかもしれないが、それらもそこそこの性能があるのでやはり使わない?
  • 変数リストを取得する以外のvalueReference取得方法がある。
  • パラメータ名文字列を指定してvalueReference取得したいが、直接それができるAPIは無い。
    • パラメータ名文字列を元に変数オブジェクトを取得するAPIはある。
  • 変数オブジェクトが取得できれば、そこからvalueReferenceは取得できる
  • 「パラメータ名文字列から変数オブジェクト取得」のAPI確認。
    • fmi2_import_get_variable_by_nameというAPI。
    • パラメータ名文字列を渡せば、変数オブジェクトが返ってくる。
  • 修正箇所は恒例のイニシャルモード中。
    • 今後のことも考え複数のvalueReferenceを取得する予定。
  • パラメータ名文字列を元にFMU内パラメータ変更の実験コード提示。
    • 変数オブジェクトを取得し、そこからvalueReference取得。
    • valueReferenceが分かれば、FMU内パラメータは好き勝手できる。
  • これにより今後、他のFMUを使う場合になっても比較的楽にパラメータアクセスができそう。
  • パラメータ名文字列からFMUの制御までを動作確認。
    • valueReferenceの取得OK。
    • その後のvalueReferenceを使用したパラメータ変更も当然OK。
  • 今後の予定としては、OpenModelicaがexportしたFMUを使っていろいろやっていこうと画策している。
    • ぶっちゃけると手探り状態。

スポンサーリンク

FMIライブラリ(DCモータ)

  • 「OpenModelicaで作ったFMUをFMILibraryで制御する」のプランを提示。
  • モデルは以前扱ったDCモータモデルとする。
    • Rampをそのまま電圧としてDCモータに印加するモデル。
  • ただし、そのまま使わずPID制御を追加してみる。
    • オープンループ制御からクローズループ制御のモデルに変更。
  • 以前使ったDCモータモデルにPID制御器を付けた。
    • PID制御器はModelicaライブラリに最初から存在。
    • 実際にはLimPID。
    • パラメータはKp、Ki、KdではなくKp、Ti、Tdな点に注意。
  • とりあえず、クローズループ(PID)制御のDCモータモデルが出来た。(つもり)
  • クローズループ制御にしたDCモータモデルの動作確認を実施。
    • ちゃんと動いてるっぽい。
  • Outputブロック設置
    • 各種信号にエイリアスを振るために設置。
      • 目標値をtarget。
      • 指令電圧値をvoltage。
      • 電流センサで取れる電流をcurrent。
      • 角速度センサで取れる角速度をspeed。
  • OpenModelicaからFMUをexportするための設定確認が必要。
    • FMIオプション内を確認&修正。
      • バージョン:2.0。
      • タイプ:Co-Simulation。
      • Platforms:Static。
        • Linux環境だとDynamicでもOKだろうが、Windows環境だとStatic推奨。
  • OpenModelicaから無事FMUをexport。
  • FMU内部のmodelDescription.xmlを参照。
    • Outputブロック名のパラメータの存在を確認。
    • 上記のvalueReferenceと同値のパラメータも確認。
      • モデル上、同一の信号線上のパラメータが該当。
      • 利用するのはOutputブロック側。
  • OpenModelicaからexportしたFMUをFMILibraryで読み込んでみた。
    • 無事読み込み成功。
    • 変数リストによる列挙もできた。
      • 必要なパラメータの情報は問題無く取得出来ている。
  • シミュレーションをするために若干の改造が必要。
    • シミュレーションループにvalueReferenceが渡るように修正。
  • シミュレーションループにvalueReferenceを渡すためのfmi2_import_cs_testのソースコード修正を確認。
  • 流れは以下。
    • 欲しいパラメータ名文字列列挙。
    • 変数オブジェクト取得。
    • valueReference取得。
    • valueReferenceをシミュレーションループで利用。
  • 改造版fmi2_import_cs_testの実行してみた。
    • 問題無く動作している様子。(目標値、制御電圧、モータ電流、モータ角速度)
    • 試しにグラフで表示。
      • 期待通りの波形が得られた。
  • これに伴い、OpenModelicaからexportしたFMUもFMILibraryで制御可能と言える。
  • FMU内部の固定値パラメータの変更ができるか?
    • 以前やった方法で実現可能。
  • これのソースコード改造実施。
    • Rampの開始タイミング、\(0→1\)の期間を設定できるように改造。
      • “ramp.duration”が\(0.8[s]→1.5[s]\)。
      • “ramp.startTime”が\(0.2[s]→0.3[s]\)。
  • 固定値パラメータの変更の挙動確認。
    • Rampの挙動を変えた。
  • 目標値(target)の挙動を変えたため、それに合わせて全体の挙動が変化。
    • 狙った挙動になっている。
  • パラメータになっていれば、おおよそ変更可能。
    • 変えられないのはアルゴリズムそのものや信号線の繋ぎぐらい。
  • FMILibraryについての感想。
    • 標準仕様であるが故の恩恵であるが、他ツールで出力したものを再利用できるのは助かる。
    • CAN、A/D、D/Aなどと繋げるとさらに強力な使い方ができるかも?
  • 今後はPythonベースの環境を構築してみる。
    • ググっても情報少ないので手探り状態の失敗覚悟状態で進める。

スポンサーリンク

PyFMI

  • PyhthonからFMUを制御するPyFMIの紹介。
    • 内部でFMILibraryを使用している。
  • JModelicaの一部。
    • JModelicaはmodelon AB社のModelicaPlatform。
      • 2019年にClosed Sourceに移行。
      • FMILibraryも開発元はmodelon AB社。
  • PyFMIのインスト―ルについてあれこれ。
  • 依存関係がヤバイ。
    • FMILibraryの64bitが必要。
    • Assimuloが依存したsundials、GLIMDAのsolverの64bit品が必要。
  • condaだと依存関係を一撃で解決してくれる。
  • python-canなどはconda管理になっていないなど万能では無い。
  • PyFMIの動作確認方法を列挙。
    • 実験用のFMUを作って、それをPyFMIで制御しつつmatplotlibで波形表示する。って流れ。
  • DCモータモデル改造。
    • 改造と言ってもInputブロックを追加した程度。
    • InputブロックもOutputブロックと同様にエイリアスは生成される。
    • このエイリアスにアクセスする予定。
  • FMUをPythonで使用する上で必要ライブラリのimport。
    • PyFMIのload_fmu。
    • numpy。
    • matplotlib。
  • load_fmuの戻りのオブジェクトはFMU次第。
    • FMUModelCS1。
    • FMUModelCS2。
    • FMUModelME1。
    • FMUModelME2。
  • 時間軸作った。
    • とりあえず、0秒から2秒の等差数列で作った。
  • Ramp作った。
    • 等差数列で斜めにプロットした後にmax,minでサチらせた。
  • 時間軸とRamp入力と統合&縦方向に。
    • vstackとtransposeを使用。
  • 入力オブジェクト作った。
    • voltageに入力行列を紐づけた。
  • シミュレーション実施。
    • 開始時刻、終了時刻、入力オブジェクトを渡すことで実施可能。
  • シミュレーション結果取得。
    • simulate関数の戻り値が連想配列になっている。
    • voltage = res[‘voltage’]のような指定方法。
  • シミュレーション結果のグラフ表示。
    • matplotlibでプロットするのみ。
  • PyFMIでFMU制御するPythonコードを開示。
    • 割とあっさり実現。
    • Pythonなのでmatpotlibでそのままグラフ表示。
  • FMILibraryと比べるとvalueReferenceに振り回されることが無い点がとても良い。
    • PyFMIによるFMU制御の有用性がなんとなく見えてきた。

スポンサーリンク

マルチFMU

  • 「完璧に把握したかもしれん」は幻。
    • ダニング=クルーガー効果。
  • FMUの本体の目的は「完成車メーカがサプライヤからの提供されたFMUを統合する」
    • よって、複数のFMUを作成。
      • DCモータモデルを分解して複数のFMUを作ってみる方針。
  • とりあえず上記をやってみて課題が出たら、それを次のネタにする。
  • 元にするDCモータモデルは使いまわし。
    • Ramp、PID制御器、DCモータの構成が一番部品が多い。
  • モデルの分解もRamp、PID制御器、DCモータの単位でやってく予定。
  • 念のため、OpenModelicaで現状の動作結果を取得しておく。
    • PyFMIで統合したときの成功/失敗の判定用。
  • 次はモデル分解。
  • DCモータモデル分解とFMU exportを実施。
    • 前回までのやり方で簡単にできるはず。
    • (よって詳細説明は端折った)
  • PyFMIからのFMU呼び出しをする際のおおまかな構成を提示。
    • 各FMUの信号はPython側で接続するイメージ。
      • マルチFMU制御ならではの処理。
      • (それだけでは解決しない話もあったり)
  • PyFMIでマルチFMU制御する際にある程度の前提知識が必要になる。
  • FMUロード。
    • 以前はload_fmuを使ったが、今回はFMUModelCS2を使用。
    • 引数が増えている。
      • PATHの指定がファイル名のPATHに分けることが可能。
      • DLLロード有無の引数がある。
        • つまりロードしない使い方が・・・。
  • Masterが土台になるが必要な情報がある。
    • モデルセット。
      • FMUをロードしたモデルをリスト化。
    • モデル間接続セット。
      • モデルの出力、入力を1セットとしたリスト。
  • モデルセットとモデル間接続セットをMasterに渡せばシミュレーションをする準備はおおよそ完了。
    • まだ、若干の調整はある。
  • Masterにオプション設定が可能。
    • 今回はstep_sizeを調整。
      • デフォルト値が0,01秒なので0,001秒に変更。
  • step_size以外にも大量のオプションがある。
    • 補間の仕方、並列処理の有無、結果出力、ログ出力などなど。
    • どういうものがあるかだけ把握し、必要になった際に再度確認すればOK。
  • マルチFMU制御用のコード提示。
    • 前半は前回まで説明した内容。
      • ロード、モデルセット、モデル間接続セット、Master定義
    • 後半はシミュレーション結果取得と波形表示。
  • FMILibraryの時とは異なり、結果取得と波形表示が楽なのは本当に有難い。
    • 結果は連想配列で取得。
    • 波形表示はmatplotlib使用。
  • マルチFMU制御用のコード実行結果を提示。
    • 事前に取っていたOpenModelicaのシミュレーション結果も一緒に提示。
  • 今回の方法だと目的のHILS環境としては課題がある。
    • シミュレーションが1関数の中で閉じてるのでリアルタイムに外部とのやり取りができない。
    • ググっても情報出て来ないのでいろいろ模索するしかない。

スポンサーリンク

ダミーFMU

  • HILSに於いてのシミュレーション時間と実時間の合わせこみ方法は大きく2種類。
    • HILS自体が時間保証。
    • モデルの一部で時間の辻褄合わせ。
  • 今回はモデルの一部で辻褄合わせの方針。
    • PyFMIでダミーFMUを定義できそう。
      • 「Undocumented specification」なので当たって砕けろ方式。

めだよ。

  • ダミーFMU定義に向けての方針を提示。
  • FMUのロードの仕方を再確認。
    • _connect_dllという引数があった。
  • _connect_dllをFalseにするとFMUがロードされずインターフェースだけが定義される。
    • インターフェースを利用してでPython側からFMU出力制御可。
    • しかし、時間の制御はできない。
  • FMUロード関数を一旦整理。
    • Dummy_*という謎関数が各FMUタイプ別に存在。
  • Dummy_FMUModelCS2を題材として掘り下げ。
    • FMUModelCS2を継承している。
    • オーバーライドしているメソッド多数。
      • 重要なのはdo_step。
      • cpdef定義なので外部から上書きできないのを回避している。
  • do_stepをオーバーライドしている理由の説明。
    • Cythonによる静的関数でそのままではPython側からの上書きができない。
  • do_stepの重要性の説明。
    • masterモジュールからシミュレーションステップ毎に呼ばれるメソッドだから。
    • これを自由に書き換えられれば時間制御ができる。
    • 実時間に追いつくまで待たせる。
  • Dummy_FMUModelCS2を使用する実験構成を提示。
    • 以前作ったマルチFMUの構成をベースにちょい修正の方針。
    • ついでにPythonからstep毎の出力も制御してみる。
  • 各FMUのロードのコード。
    • Dummy_FMUModelCS2で既存のFMUを指定しておくとインターフェース仕様だけは取り込める。
  • do_stepの実装方針を決めた。
    • Ramp UpとRamp Down。
  • 出力信号のソースコード。
    • シミュレーション時間を見ながら出力信号を決定する方式。
  • 時刻同期のソースコード。
    • 単にタイマーを使ってシミュレーション時間が実時間に追いつくのを待つ。
  • do_stepメソッドの上書きはそのまま作成関数で上書きすればOK。
  • ダミーFMU実験用ソースコードは以前のマルチFMUの時の物を流用。
    • do_step周りの追加が主な修正部分。
  • ソースコード開示。
    • FMUModelCS2の一部をDummy_FMUModelCS2。
    • do_stepをdo_dummyで上書き。
    • 時刻同期ができてるかを確認できるよう一部printを入れている。
  • ダミーFMU実験の動作確認。
    • タイミングによっては若干ズレるがシミュレーション時間と実時間の同期はOK。
  • シミュレーション波形確認。
    • Ramp Up、Ramp Downの台形状の波形が出ており、期待通りの指令値になっている。
  • 上記によりダミーFMUによる実験がし易くなる。
    • 簡単なアルゴリズムであればPythonで実施

スポンサーリンク

リアルタイム描画

  • 前回までやってたシミュレーション時間と実時間の同期には課題がある。
    • シミュレーション時間の方が長いと破綻。
      • Python側でグラフ表示等すると破綻し易い。
  • よって、別案。
    • 前回まではシミュレーション時間に実時間を追いつかせる。
    • 対して、新方式は実時間に対してシミュレーション時間を追いつかせる。
  • 実験環境は「シミュレーションしながらリアルタイムで波形表示」。
  • 上記以外にも以下を組み込む。
    • スライダー等で入力を手動操作。
    • sin波、のこぎり波などを入力。
    • リアルタイム表示の一時停止。
  • tkinterを使用する予定。
    • Tk GUIツールキットをPythonから呼ぶライブラリ。
  • 実験構成はGUIから決めていく。
    • GUIでおおよそのインターフェースが確定しそうだから。
  • GUIの概要図を描いた。
    • 各種チェックボックス、Scale、plot画面。
    • 上記の機能、目的を定義。
      • 処理負荷が分かるようにする。
      • pauseに関してはリアルタイム描画を抑制した際の負荷を見る為に設置。
  • tkinterはPython標準ライブラリのためインストール済み。
  • 動作確認。
    • 「python -m tkinter」でウィンドウが出てくればとりあえずOK。
  • メインウィンドウをrootとして定義し、そこに各ウィジットを生成&紐づけしていく。
    • matplotlib関連もtkinter連携用モジュールが存在する。
  • tkinterのimportはそのまんま。
    • 「import tkinter」
  • VisualStudioのリソースエディタのようなものは無い。
  • メインウィンドウ生成方法解説。
    • titleも指定可能。
  • メインウィンドウの大きさと位置指定。
    • 文字列で以下のような感じで指定。
      • ‘960×540+960+0’
  • matplotlibをtkinterに埋め込む際には以下のモジュールが必要。
    • matplotlib.pyplot。
      • プロット用。
    • FigureCanvasTkAgg。
      • プロット画面埋め込み用。
    • NavigationToolbar2Tk。
      • メニューバー。
  • 埋め込む前に図(figure)を用意しておく。
  • FigureCanvasTkAggを使用してtkinterへmatplotlibを埋め込み。
    • pack(side = tkinter.RIGHT)でメインウィンドウの右端に接するよう配置。
  • NavigationToolbar2Tkを使用してメニューバー配置。
    • place(x=0, y=h/2-40)を使用して座標指定で配置。
  • チェックボックスの配置。
    • チェック状態を確認するための変数を用意し、ウィジット生成時に渡しておく。
      • この変数を見ればチェック状態が分かる。
    • チェック状態をPython側から変更することも可能。
    • placeで座標指定して配置。
  • 同様の処理を必要なチェックボックス分実施。
    • GUI上でも設置されていることを確認。
  • 最後のウィジットとしてScaleを配置。
    • いままでのウィジットの中では最もパラメータが多い。
    • 縦置き、横置きの指定ができる。
    • commandという引数で値変更時のコールバックを設定できる。
    • to、from_で値の範囲を指定できるが大きさが逆転しても良い。
      • 0値の配置を逆にしたい場合は意図的に逆転させる。(公式もOK情報あり)
  • Scaleの配置確認。
    • 問題無く配置されているのを確認。
  • Scaleの挙動確認。
    • 値をprintしているのでそれを確認。
      • Scaleの値が出力されているの挙動もOK。
  • tkinterはmainloopに入ると、戻ってこない。
    • よって、タイマ処理を使ってうFMU関連、描画関連をうまくハンドリングする必要あり。
  • まずはtkinterのタイマ処理から確認。
    • タイマ処理に描画処理、FMU処理を組み込むためまずはタイマ。
  • タイマ処理はafterメソッドに指定時間とタイマハンドラを渡すことで実施できる。
    • しかし、一回コールバックするとそれで終了。
    • よって、周期的に処理したい場合はタイマハンドラ内で再度afterメソッドを呼び出す必要あり。
  • タイマ処理にmatplotlibの描画更新処理を入れてみる。
    • 現時刻をベースに過去8秒分の時間軸を生成し、それをsin関数に入れてsin波生成。
    • relimメソッドを使うと縦軸が自動調整される。
  • pauseのチェックボックスを設置していたので、チェック状態を確認して更新するかしないかの判定に使う。
  • いままでごちゃごちゃと追加してきたコードを再確認。
  • tkinterによるリアルタイム描画の動作確認。
    • sin波が流れるように描画されていることを確認。
    • pauseで一時停止もできることを確認。
  • FMU関連処理はまだ未実装だが、tkinterによるリアルタイム描画はできそう。
  • FMU関連処理追加がそこそこいろいろあるので情報整理。
    • 基本はいままでのFMU処理ではある。
  • 追加でdequeが必要そう。
    • シミュレーションデータ全てを保持すると、記憶する配列が膨大になる可能性が高い。
    • よって、描画に必要なデータだけを格納するためにdequeを使用。
    • dequeの使用方法を追々説明予定。
  • FMU関連処理前準備を一気に説明。
    • いままでやってきたものなので細かい説明は省略。
  • deque関連の初期化。
    • dequeはcollectionsライブラリの一部。
    • maxlenで最大要素数を指定できる。
      • 指定なしの場合は無限に入れられる。
        • 実際には物理的な限界はあるだろうが・・・。
  • 必要なタイマハンドラは2つ
    • FMU処理用とmatplotlib描画用。
    • 描画用側の負荷が高いので、こっちは長めの周期で実施。
  • FMU処理用タイマハンドラ内で時間関連の演算が入るため、タイマハンドラ外で下準備が必要。
  • Masterの初回呼び出し時のオプションと2回目以降で変える必要がある。
    • initializeオプション。
  • FMU_handlerことFMUシミュレーション用処理のコードを提示。
    • 外部の変数を使用するためにglobal定義している変数あり。
  • 実時間の経過を元にFMUシミュレーション時間を決定している。
    • 実時間にシミュレーション時間を追いつかせる方式。
    • ただし、開始と終了のstepが重複したり欠落したりするので微小時間の調整が必要。
  • plot_handlerことmatplotlibリアルタイム更新処理のコード提示。
    • 以前のリアルタイム描画実験との差は以下。
      • deque使用。
      • plot時にnp.arrayに変換しているが無くてもOK。
      • CPU負荷描画チェックボックス確認。
  • dequeのように要素数が分からない場合は-1による末尾指定が便利。
  • 現状のコードで動作確認をしてみた。
    • FMUの波形表示OK。
    • Scaleで指令値変更OK。
  • まだ以下の課題が残っている。
    • ソースコードがちょっとヤッツケ。
    • 負荷関連の評価が出来ていない。
      • 実際の負荷状態。
      • 描画処理の影響有無の評価。
      • なんとなく現状でも負荷の影響が見え隠れはしている。
  • 現状のソースコード確認。
    • 暗黙的なものも含めてグローバル変数が点在している。
      • GUIイベントとかタイマハンドラで関数が分離しているので仕方ない面もある。
  • 折角なのでクラスとしてまとめてみようと試みる。
    • 基本的なロジックは出来ているのでそれほど修正時間はかからない見込み。
  • 実験用Pythonコードのクラス化。
    • 基本、関数横断変数をメンバ変数にした。
    • つまり、メンバ変数になっているものが暗黙的なグローバル変数だった。
    • 「実はグローバル変数だったのかー!」ってのはあるある。
  • 前回と同様の動きはしていそうなので、このコードをベースに実験を継続する。
    • 特に負荷関連が残っている。
  • 修正コードでとりあえず動作させてみた。
    • 問題無く動作した。
  • 負荷確認実施。
    • カクついているが人間の操作のせいの可能性もある。
  • sin波の自動入力で確認。
    • やはりカクついている。
      • よって、操作の問題ではない。
  • CPU負荷を見て、さらにmatpotlibの波形拡大で詳細確認。
    • FMU処理以外の処理負荷大きそう。
  • 前回の負荷を安定化する方法を検討。
    • 描画に処理時間を持っていかれているっぽからpauseで描画だけ停止していた。
      • 案の定、負荷安定化。
  • 上記より、フェーズを分けた利用法が考えられる。
    • デバッグフェーズは波形を見ながら。
    • ある程度デバッグが完了したら波形無しの高精度状態で検証する。
  • 描画を止める以外で負荷低減方法を検討してみた。
  • スレッドの利用案が出たが以下の理由で不可。
    • GIL(Global Interpreter Lock)仕様の影響で期待するスレッドのコンテキストスイッチにならない。
    • よって、負荷はほぼ減らない。
      • 下手したら増える可能性もある。
      • というわけで対策としては見送り。
  • スレッド以外で処理分散を検討。
    • マルチプロセスでやれば分散できるかもしれない。
  • マルチプロセスを実現するにはプロセス間通信が必要。
    • Queue、Pipe、共有メモリとあるが、今回はPipeを使用してみる。
      • ただし、通常の文字列送信方式ではなく、バイナリ方式。
      • 文字列方式は型を維持できて便利だがオーバーヘッドが大きい。
  • Pythonでマルチプロセスをする上での手順確認。
    • Pipeを使ったプロセス間通信が面倒そう。
  • 追加コードについては今回は触り程度。
    • 要importのライブラリ。
      • multiprocessingのProcessとPipe。
    • Pipeの生成。
      • 送受信の2つが取得できる。
      • 引数自体で双方向、片方向が切り替わる
  • Pythonでサブプロセスの生成と開始について説明。
    • Process関数を呼ぶだけ。
    • 引数にサブプロセス開始時に呼び出す関数とそれに対する引数を渡せる。
      • 今回はPipeを渡しておく。
  • Pipeによる送信について説明。
    • send_bytesを呼ぶだけ。
    • dequeのままでは渡せないのでdouble型のarrayにする。
  • Pipeの受信処理のコードを提示。
    • 受信自体は一関数だが、byte配列からdoubleに直すのに手間がかかってる感じ。
  • Pipeの受信確認。
    • 受信関数自体は受信が来るまで戻ってこないので、無条件では呼びたくない。
    • よって、poll関数で受信有無を確認した後に実際の受信処理をする。
  • 次回から実際のコードと動作確認。
  • マルチプロセス化した実験コードを提示。
    • 基本的な流れは一緒だが、Pipe関連のコードが増えた。
    • deque毎にsend_bytesで送信。
    • Scaleの入力やsin波生成指示を描画のメインプロセスからFMUのサブプロセスに送ってる。
      • sin波をサブプロセス側にしないと、結局描画負荷でsin波が崩れる。
  • マルチプロセス化実験コードを実際に動作させてみた。
  • 一応負荷は下がったが、それでも50ms間の負荷がかかるポイントが散見される。
  • 結果としてはPipeのプロセス間通信のオーバーヘッド。
    • 他のプロセス間通信でもこのオーバーヘッドはそれほど変わらない。
  • 今後の実験は通常のシングルプロセス版のコードをベースとする。

スポンサーリンク

CAN連携

  • さらなるHILSっぽさを求めて外部入力を検討。
    • ECUのインターフェースを想定すると分かり易い。
  • インターフェースは様々ではあるが、とりあえずCANであれば融通が利きそう。
    • CANからADC、DAC、PWMへの変更はそれほど大変ではない。
    • よって、今後はCANをインターフェースの前提として話を進める。
  • CANをHILSのインターフェースにするには本来では専用のインターフェース装置が必要。
    • しかし、今回はVector社のXL Driver Library付属のVirtual CAN Busを使用。(無償)
  • 実験構成は指令値をCANで指定。
  • 前回までのtkinterに対して追加する。
    • 入力パターンが増えるイメージ。
  • CAN経由で渡したい情報として指令値を仮定義。
    • Ramp Up、Ramp Down、Stepなどの組み合わせ
  • Python-CANの復習。
    • インストールの話。
      • pip使えばOK。
    • importの話。
      • canをimportすればOK。
  • 以降はバス初期化、送受信などの話が続く予定。
  • Python-CAN関連は基本的には過去記事参照でOK。
  • バス初期化時に使用するデバイスやボーレートを設定。
    • 回線が複数ある場合は、回線毎にバス初期化をする。
    • そして戻り値のbusをもって通信先を識別する。
  • CANのメッセージはcan.Messageで作成。
    • CANID、標準or拡張フレームの指定ができる。
  • Python-CANで指令値送信のコードを提示。
    • CAN関連の前回までの復習の話のまんま。
  • 利便性を上げるためにコマンドライン引数を取り込む機能追加。
    • 送信周期変更と繰り返し処理切替を追加。
  • 動作確認はBusMasterでモニタすることで実施予定。
    • 実際のCANだと対抗機が必要だがVirtual CAN Busでは不要。

スポンサーリンク