アニメーションの原理と、MBDynの出力データから剛体の運動のアニメーションを作成する方法について説明します。
まず、1次元の簡単なケースでアニメーションの原理を説明します。
ある点が時間とともにx軸上を移動するとします。一定時間間隔の点位置 x のデータが x = x1, x2, x3, ... と与えられている時、点の動きを表すアニメーションは次の1〜3を循環(ループ)させることによって作成することができます。ただし、点は小さな円で表示することとします。(図1参照)
コード1に、移動する点のアニメーションを作成するMATLABスクリプトの例を示します。また、このスクリプトを実行して得られるアニメーションを動画1に示します。
% animation_point.m clear; close all; % Create data t = 0:0.001:1; % Time data x = sin(2*pi*t); % Position data % Draw initial figure figure(1); set(gcf,'Renderer','OpenGL'); h = plot(x(1),0,'o','MarkerSize',20,'MarkerFaceColor','b'); set(h,'EraseMode','normal'); xlim([-1.5,1.5]); ylim([-1.5,1.5]); % Animation Loop i = 1; while i<=length(x) set(h,'XData',x(i)); drawnow; i = i+1; end
剛体のアニメーションも、点のアニメーションと原理は全く同じです。剛体の場合、描画と消去の対象が3次元形状になるという違いがあるだけです。ただし、MBDynは剛体の運動をnodeという代表点の運動で記述するので、nodeのデータを3次元形状のデータに変換する手続きが必要になります。
例として、図2のように剛体の形状がブロックの場合を考えてみましょう。まず、初期時刻におけるブロックの頂点を定義します(図2左)。ブロックの形状はnodeに張り付いて動くので、任意の時刻における頂点の座標は、nodeの位置と姿勢から図2右のように求めることができます。頂点の座標が求まれば、ブロックの形状を描画できます。このようにして、nodeの位置と姿勢のデータから各時刻におけるブロックの頂点の座標を求め、ブロックの形状に対して描画と消去のループを回せば、ブロックのアニメーションを表示することができます。
MATLABでは、一般の3次元形状を作成するのに“パッチ(patch)”が利用できます。パッチとは、3つ以上の点で定義される多角形の面です。パッチを組み合わせれば、任意の多面体を作ることができます。例えばブロック形状は、6個の四角形パッチを組み合わせて作ることができます。
さて、下のコード2は、例題2の自由回転するブロックの解析で出力されるmovファイルを読み込んで、ブロックの最上面のパッチのみをアニメーション表示するMATLABスクリプトです。動画2にこのスクリプトを実行して得られるアニメーションを示します。このようなパッチのアニメーションをブロックの6面全部について組み合わせれば、ブロックのアニメーションが完成します。
% animation_patch.m clear; close all; % Define Geometry Points (Vertices for the top surface of the block) Lx = 0.15; %[m] Ly = 0.05; %[m] Lz = 0.30; %[m] p1 = [ Lx/2, Ly/2, Lz/2 ]; p2 = [ Lx/2, -Ly/2, Lz/2 ]; p3 = [ -Lx/2, -Ly/2, Lz/2 ]; p4 = [ -Lx/2, Ly/2, Lz/2 ]; % Load Data [LABEL,DATA] = MBDynLoad('free_rotating_block.mov'); r = DATA(:,[2:4],LABEL==1); % Node Position [m] A = DATA(:,[5:7],LABEL==1)*pi/180; % Node Orientation [rad] (x-y-z Euler angle) n = size(r,1); % Data Length % Euler Angle -> Orientation Matrix for i=1:n a1 = A(i,1); a2 = A(i,2); a3 = A(i,3); R1 = [1, 0, 0; 0, cos(a1), -sin(a1); 0, sin(a1), cos(a1)]; R2 = [cos(a2), 0, sin(a2); 0, 1, 0; -sin(a2), 0, cos(a2)]; R3 = [cos(a3), -sin(a3), 0; sin(a3), cos(a3), 0; 0, 0, 1]; R(:,:,i) = R1*R2*R3; end % Compute Propagation of Geometry Points for i=1:n r1(i,:) = r(i,:) + p1*R(:,:,1)*R(:,:,i)'; r2(i,:) = r(i,:) + p2*R(:,:,1)*R(:,:,i)'; r3(i,:) = r(i,:) + p3*R(:,:,1)*R(:,:,i)'; r4(i,:) = r(i,:) + p4*R(:,:,1)*R(:,:,i)'; end % Compile Data for Drawing Patch PatchData_X = [r1(:,1),r2(:,1),r3(:,1),r4(:,1)]; PatchData_Y = [r1(:,2),r2(:,2),r3(:,2),r4(:,2)]; PatchData_Z = [r1(:,3),r2(:,3),r3(:,3),r4(:,3)]; % Draw Initial Figure figure(1); set(gcf,'Renderer','OpenGL'); h = patch(PatchData_X(1,:),PatchData_Y(1,:),PatchData_Z(1,:),'y'); set(h,'EraseMode','normal'); axis vis3d equal; view([-37.5,30]); camlight; grid on; xlim([-0.2,0.2]); ylim([-0.2,0.2]); zlim([-0.2,0.2]); % Animation Loop for i=1:n set(h,'XData',PatchData_X(i,:)'); set(h,'YData',PatchData_Y(i,:)'); set(h,'ZData',PatchData_Z(i,:)'); drawnow; end