【上流検証】最小構成のモデルベース開発事例 その15【ACG】

【上流検証】最小構成のモデルベース開発事例 その15【ACG】 事例

バックナンバーはこちら
https://www.simulationroom999.com/blog/model-based-of-minimum-backnumber/

はじめに

前回、SimulinkによるMILSを実施。
アルゴリズムとしては一定の確かさが得られた状態。
そして今回はACG(AutoCodeGenerator:自動コード生成)に関わるお話。

登場人物

博識フクロウのフクさん

イラストACにて公開の「kino_k」さんのイラストを使用しています。
https://www.ac-illust.com/main/profile.php?id=iKciwKA9&area=1

エンジニア歴8年の太郎くん

イラストACにて公開の「しのみ」さんのイラストを使用しています。
https://www.ac-illust.com/main/profile.php?id=uCKphAW2&area=1

委託先からの相談

太郎くん
太郎くん

うーん。困った。

フクさん
フクさん

・・・。

太郎くん
太郎くん

あー。困ったなー。

フクさん
フクさん

・・・。

太郎くん
太郎くん

困ってるって言ってるでしょ!

フクさん
フクさん

(なぜキレられるのか・・・。)

フクさん
フクさん

どうしたの?

太郎くん
太郎くん

それがねぇ、
委託先に仕様書を出したのはいいんだけど、PIDとフィルタの実装がイマイチ分からないらしくて相談を受けてるんだよ。

フクさん
フクさん

教えてあげればいいじゃない。

太郎くん
太郎くん

そうなんだけど、
僕もPIDとフィルタをC言語として書いたときにどんな感じになるのかイメージが湧いてなくて・・・。

フクさん
フクさん

なるほど。
だったら、SimulinkCoderの力を借りれば良いと思うよ。

PIDの自動コード生成

太郎くん
太郎くん

あ、いわゆるACGってやつ?
本当にできるんだ!
てっきり都市伝説化と思ってたよ。

フクさん
フクさん

まぁ、

言うほど自動って感じでもなくて、

インターフェース等は調整しないといけないことも多いので、

世間で期待されているものとは違うかもね。

太郎くん
太郎くん

とりあえず、ACGをやってみてよ!

フクさん
フクさん

まずはPID。
元々のSimulinkモデルはこんなん。

PID制御器離散化ブロック線図サチュレーション付き
フクさん
フクさん

そして、これをACGしたものがこれ。

void controller_step(void)
{
	real_T rtb_Gain3;
	real_T rtb_Switch;
	real_T rtb_Add;

	rtb_Add = controller_U.Target - controller_U.input;
	rtb_Gain3 = (rtb_Add - controller_DWork.UnitDelay_DSTATE) *
	  controller_P.Gain3_Gain;
	if (controller_U.Mode > controller_P.Switch_Threshold) {
	  rtb_Switch = ((rtb_Gain3 - controller_DWork.UnitDelay1_DSTATE) *
	                controller_P.Gain4_Gain * controller_P.Kd_Gain +
	                (controller_P.Ki_Gain * rtb_Add + controller_P.Kp_Gain *
	                 rtb_Gain3)) * controller_P.Gain5_Gain +
	    controller_DWork.UnitDelay2_DSTATE;
	} else {
	  rtb_Switch = controller_U.Driver;
	}
	
	if (rtb_Switch >= controller_P.Saturation_UpperSat) {
	  controller_Y.output = controller_P.Saturation_UpperSat;
	} else if (rtb_Switch <= controller_P.Saturation_LowerSat) {
	  controller_Y.output = controller_P.Saturation_LowerSat;
	} else {
	  controller_Y.output = rtb_Switch;
	}
	controller_DWork.UnitDelay_DSTATE = rtb_Add;
	controller_DWork.UnitDelay1_DSTATE = rtb_Gain3;
	controller_DWork.UnitDelay2_DSTATE = controller_Y.output;
}
太郎くん
太郎くん

うーん、変数名とかはSimulinkモデルのブロック名が元になっているのかな?
でも、これはこれで合ってるんだよね?

フクさん
フクさん

当然、

絶対問題無しとは言えないかもしれないが、
まぁ、まずは問題無しとして話を進めてそのうち別の手段で品質を担保しよう。

太郎くん
太郎くん

あと、入力と出力はどれになるの?

フクさん
フクさん

controller_Uという構造体に入力。
controller_Yという構造体に出力。
controller_Pという構造体に各種係数。
controller_DWorkという構造体に前回値が保持されているようだね。
ここら辺はそのまま使うか、委託先にリコードしてもらうかは太郎くんの判断になるかな。

太郎くん
太郎くん

今回はリコードしてもらおうかな。
プロトタイプとして進めるなら、まずはこのままでも良いとは思うんだけど、
今後の検証とかのスケジュールからすると、Cコードをベースに話が進みそうなんで、Cコードの可読性が高くないと危ないかも。

フクさん
フクさん

その判断は妥当だと思うよ。
プロトタイプで試行錯誤できる段階であれば、ACGの出力そのままを利用するのはいいだろう。
今回の場合はそのフェーズはもう過ぎてしまっているのでCコードの可読性を引き上げる方に工数を割いた方が良い。

シグマフィルタの自動コード生成

フクさん
フクさん

シグマフィルタのSimulinkモデルはこれだったね。

標準偏差σフィルタのSimulinkモデル、サンプリングバッファ、平均値μ算出、分散σ^2算出、平均値μ±標準偏差σのサンプリングデータ特定と平滑
フクさん
フクさん

そしてACG。

void Filter0_step(void)
{
  boolean_T b[10];
  int32_T k;
  real_T rtb_Gain;
  real_T rtb_Product[10];
  real_T rtb_Gain1;
  int32_T i;
  int32_T y_data[10];
  int32_T y_sizes;
  boolean_T b_0;

  rtb_Gain = Filter0_DWork.UnitDelay_DSTATE[0];
  for (i = 0; i < 9; i++) {
    rtb_Gain += Filter0_DWork.UnitDelay_DSTATE[i + 1];
  }
  rtb_Gain *= Filter0_P.Gain_Gain;
  for (i = 0; i < 10; i++) {
    rtb_Gain1 = rtb_Gain - Filter0_DWork.UnitDelay_DSTATE[i];

    rtb_Gain1 *= rtb_Gain1;
    rtb_Product[i] = rtb_Gain1;
  }
  rtb_Gain1 = rtb_Product[0];
  for (i = 0; i < 9; i++) {
    rtb_Gain1 += rtb_Product[i + 1];
  }
  rtb_Gain1 *= Filter0_P.Gain1_Gain;

  k = 0;
  for (i = 0; i < 10; i++) {
    b_0 = (rtb_Product[i] - rtb_Gain1 <= 0.0);
    if (b_0) {
      k++;
    }

    b[i] = b_0;
  }
 y_sizes = k;
  k = 0;
  for (i = 0; i < 10; i++) {
    if (b[i]) {
      y_data[k] = i + 1;
      k++;
    }
  }

  if (y_sizes == 0) {
    rtb_Gain1 = 0.0;
  } else {
    rtb_Gain1 = Filter0_DWork.UnitDelay_DSTATE[y_data[0] - 1];
    for (k = 2; k <= y_sizes; k++) {
      rtb_Gain1 += Filter0_DWork.UnitDelay_DSTATE[y_data[k - 1] - 1];
    }
  }

  Filter0_Y.Out1 = rtb_Gain1 / (real_T)y_sizes;

  Filter0_Y.u = rtb_Gain;

  memcpy(&rtb_Product[0], &Filter0_DWork.UnitDelay_DSTATE[1], 9U * sizeof(real_T));
  rtb_Product[9] = Filter0_U.In;
  memcpy(&Filter0_DWork.UnitDelay_DSTATE[0], &rtb_Product[0], 10U * sizeof
         (real_T));
}
太郎くん
太郎くん

これはまた思ったよりも規模が大きいコードになったような・・・。

フクさん
フクさん

まぁ数列をシフトしたり抜き出したりしているからね。

太郎くん
太郎くん

これは、
Filter0_Uが入力。
Filter0_Yが出力。
Filter0_DWorkに前回値。
Filter0_Pに係数。
って感じかな。

フクさん
フクさん

そうだね。

太郎くん
太郎くん

これも別の手段で品質担保って可能?

フクさん
フクさん

うん。可能だよ。

太郎くん
太郎くん

よし。これもサンプルコードという位置づけで送ってしまおう。

まとめ

フクさん
フクさん

まとめだよ。

  • SimulinkCoderを使ってSimulinkモデルをACGできる。
    • SimulinkCoderを使用するためにはMATLAB Coderのライセンスも必要。
  • ACGされたコードは可読性が良くない場合がある。
    • プロタイプの段階では、一旦可読性は無視するという選択もあり。
    • リコードする場合は別の手段で品質担保することを考える必要がある。

バックナンバーはこちら

コメント

タイトルとURLをコピーしました