見出し画像

Simulinkで高速bar描画

plot系をSimulinkで

Simulinkではタイムスコープ表示が標準であり、スクリプトで使えるplot系(barやscatter等)に相当するブロックがありません。

Scopeでもフレーム単位の表示とすることでplotっぽい表示もできますが、plotの方が見やすい場合があります。

MATLABとSimulinkと私 (思考を止めない開発ツール)
にも書きましたが、実は、MATLAB Functionブロック内に書けばplotも使えます。

コード生成には対応していませんが、barも使えます。

コード生成に対応していない関数をMATLAB Functionブロック内で使うには、coder.extrinsicを使って外部関数として定義します。

 ​coder.extrinsic('bar')

また、毎回描画し直すとちらついたり遅くなったりするので、オブジェクトのプロパティを直接書き換えます。

例えば

> p=plot(rand(10,1))

とすると、構造体である "Line のプロパティ:"
が表示されます。

この内、

p.YData = rand(10,1);

とすればyの各値が、

p.XData =

 とすればxの各値が更新されます。


barの場合はコード生成に対応していないので、構造体による指定ができません。

その場合、データの更新には setfield (データ取得はgetfield)を使います。

f1 = setfield(f1,'YData', u1c);


barが遅い!?

今までplotやscatterはよく使っていたのですが、barは使っていませんでした。

最近barを使ってみたら、plotに比べて「非常に遅い」ことに気付きました。


私の環境では、単純比較で4,5倍掛かるようです。

y=rand(1000,1);
x=1:length(y);
n=100;
figure
tic
for k=1:n
   bar(y)
end
bt = toc
tic
for k=1:n
   plot(y)
end
pt = toc
tic
for k=1:n
   fill(x,y,'b')
end
ft = toc
bt/pt
bt/ft
bt = 1.8005
pt = 0.3421
ft = 0.3777
ans = 5.2638
ans = 4.7673

plotなら早いのですが、やはり中が塗れてないと寂しいです。

fillでbarを

Twitterで@michio_MWJさんが別の件でfillを勧めていて、「そういやそんな関数あったな」と使ってみたらplot並に速かったので、fillでbarみたいな見た目なのを作ってみました。


barはどんな感じかというと、

y = [-1 0 -1 -3 0 2];
bar(y,1)

画像1

見た目をbarっぽくするには、
-同じx座標で前の値から次の値へラインを引く
-次のx座標までyの値をホールド
-一旦 y=0 に戻る
と言う動作が必要です。

なので、y座標は、
同じ値 → 同じ値 → 0 → 次の値
xは
n → n+1 → n+1 → n+1
とすれば描けそうです。

MATLABで書くとこんな感じでしょうか。

x2 = [x;x;x];
x2 = reshape(x2,[],1)';
y2 = [y; y; zeros(size(y))];
y2 = reshape(y2,[],1)';
x3 = [x2(2:end) 0];
y3 = [0 y2 0 0];

比べてみましょう。

C = colororder;
y = [-1 0 -1 -3 0 2];
x = 0.5:length(y)+0.5;
x2 = [x;x;x];
x2 = reshape(x2,[],1)';
y2 = [y; y; zeros(size(y))];
y2 = reshape(y2,[],1)';
x3 = [x2(2:end) 0];
y3 = [0 y2 0 0];
subplot(3,1,1)
bar(y,1)
xlim([0 7])
grid on
title('bar')
subplot(3,1,2)
plot(x3,y3)
grid on
title('plot')
subplot(3,1,3)
fill(x3,y3,C(1,:))
grid on
title('fill')

画像2

良さそうですね。


これでfunctionにして、ちゃんとデータ更新のみにしてbarとの実行速度を添付のコードで比較したところ、データ量を3倍に増やす処理も入っているのに

rand(1,100)を
10回ループ plot: 25、fill: 25倍
100回ループ plot: 70、fill: 96倍
1000回ループ plot:156、fill:134倍

という結果になりました!?
(実行する度に結構変わりますが)

なぜそんなに bar が遅いのかは謎ですが、これで Simulink でも bar っぽい表示が高速にできるようになったので、そこには踏み込まないようにします。(u_u)

----> 2021/06/26 追記
理由が分かりました。
bar はコード生成非対応であり、実行の度に外部呼び出しされるためのようです。coder.extrinsic を使用するコードが MATLAB Function ブロックにある場合はかなり遅くなります。
<---- 2021/06/26追記

最後に、Simulink(R2020b)での実例を添付しておきます。

画像7

画像4

バージョン違いでとかで読めない方のために一応設定も。

画像5

画像6

画像7

function fill_bar(u)
coder.extrinsic('bar')
coder.extrinsic('setfield')
coder.extrinsic('fill')
persistent f1
c1 = [1 156/255 70/255];
x=0.5:64.5;
x=[x;x;x];
x=reshape(x,[],1)';
a=[u;u; zeros(size(u))];
u1b=reshape(a,[],1)';
x2 = [x(2:end), 0];
u1c = [0, u1b, 0, 0];
if isempty(f1)
   h = figure(10);
   ax1 = subplot(1,1,1);
   f1 = fill(ax1, x2, u1c, c1);
   grid(ax1,'on')
   grid(ax1,'minor')
   xlim(ax1,[1 64])
else
   f1 = setfield(f1,'YData', u1c);
end

ではまた。




この記事が気に入ったらサポートをしてみませんか?