こんな感じのプロット描きたい
と @motorcontrolman さんとの話の中で挙がったのがこちら。
引用:「モデル予測制御(MPC)による軌道追従制御」 by @taka_horibe さん
引用元では GIF 動画になっているのですが、簡潔+綺麗な可視化ですね。動いている小さな長方形はどうやって描いているんだろう・・。
ということで必要な要素の解説を入れながらちょっとづつやってみましょう。@motorcontrolman さんならサクッとできてしまいそうですが(笑
まずは subplot1
大枠から攻めていきます。Figure 上にプロットを複数作る定番は subplot
(公式 doc: subplot) ですね。
例えば subplot(3,1,2)
とすると Figure 画面上を 3 x 1 に分割して 2 個目(左から右、上から下に数えます)の位置に axes (座標軸) を作ります(下図左側)。2 つ以上の枠にまたがることも OK なので、例えば subplot(3,1,[1,2])
(下図右側)として大きめの図を作ります。
こんな感じ。組み合わせればとりあえずの形ができそう。
subplot(5,1,1:3) % subplot(5,1,[1,2,3]) と同じ
subplot(5,1,4)
subplot(5,1,5)
データは適当なもので描いてみました。
実際のコードはこちら
% ダミーデータ
Ndata = 200;
t = linspace(0,4*pi, Ndata);
x = sin(3*t);
y = cos(t);
% プロット
subplot(5,1,1:3)
plot(x,y), title('x vs y')
subplot(5,1,4)
plot(t,x), title('x')
subplot(5,1,5)
plot(t,y), title('y')
動きを加えてみる:アニメーション作成
あと欠かせない要素は動く部分。MATLAB のドキュメンテーション:アニメーション手法 によると
- 関数 animatedline を使用してストリーミング データのライン アニメーションを作成する。
- 新規のグラフィックス オブジェクトを作成する代わりに、既存のオブジェクトのプロパティを更新する。
という 2 択のようです。[公式 doc :アニメーション、プロットのアニメーション化]
(https://jp.mathworks.com/help/matlab/animation-1.html?s_eid=PSM_29435) には他にも情報がまとまっています。ちなみに 1 を使った方法は Qiita: グラフ背景色をデータの追加とともに変化させるアニメーション作成 でも紹介しました。
ラインに沿ったマーカーのトレース
ここではとりあえず 2 つ目の「既存のオブジェクトのプロパティを更新する」方法を使ってみます。
このサンプル使ってみましょう。先ほど描いた線上をマーカーを動かします。ポイントは drawnow
。一番重要なことなのでもう一度言います。ポイントは drawnow
です。
コードの実行中はこのコマンドを実行しないと描画を更新しません(処理速度を下げないため)ので、ループの内で drawnow
を実行して、逐次描画が更新されるようにします。
実際のコードはこちら
Ndata = 200;
t = linspace(0,4*pi, Ndata);
x = sin(3*t);
y = cos(t);
subplot(5,1,1:3)
plot(x,y), title('x vs y')
hold on
p1 = plot(x(1),y(1),'o','MarkerFaceColor','red'); % 逐次更新するオブジェクト1
hold off
subplot(5,1,4)
plot(t,x), title('x')
hold on
p2 = plot(t(1),x(1),'o','MarkerFaceColor','red'); % 逐次更新するオブジェクト2
hold off
subplot(5,1,5)
plot(t,y), title('y')
hold on
p3 = plot(t(1),y(1),'o','MarkerFaceColor','red'); % 逐次更新するオブジェクト3
hold off
filename = 'animation_sample.gif'; % Specify the output file name
frame = getframe(gcf); % Figure 画面をムービーフレーム(構造体)としてキャプチャ
tmp = frame2im(frame); % 画像に変更
[A,map] = rgb2ind(tmp,256); % RGB -> インデックス画像に
imwrite(A,map,filename,'gif','LoopCount',Inf,'DelayTime',0.1);
for k = 2:length(x)
% オブジェクト1のデータ更新
p1.XData = x(k);
p1.YData = y(k);
% オブジェクト2のデータ更新
p2.XData = t(k);
p2.YData = x(k);
% オブジェクト3のデータ更新
p3.XData = t(k);
p3.YData = y(k);
drawnow % 描画更新
frame = getframe(gcf); % Figure 画面をムービーフレーム(構造体)としてキャプチャ
tmp = frame2im(frame); % 画像に変更
[A,map] = rgb2ind(tmp,256); % RGB -> インデックス画像に
imwrite(A,map,filename,'gif','WriteMode','append','DelayTime',0.1);% 画像をアペンド
end
imwrite
を使って gif ファイルに出力するコードも入れています。結果は
こんな感じ。
ひとまずまとめ
基本的なところを実装してみました。
他にも追加したい要素あればやってみますのでなんなりとコメントください。