バックナンバーはこちら
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モデルはこんなん。
そして、これを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モデルはこれだったね。
そして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されたコードは可読性が良くない場合がある。
- プロタイプの段階では、一旦可読性は無視するという選択もあり。
- リコードする場合は別の手段で品質担保することを考える必要がある。
バックナンバーはこちら
コメント