Master Thesis Code
by Simon Moser
Loading...
Searching...
No Matches
animateQuatPos.m
Go to the documentation of this file.
1% =========================================================================== %
2%> @brief animates a quaternion vector with position data
3%>
4%> animateQuat(quat, pos) animates the orientation data in the quaternion vector
5%> with the position data in the position vector.
6%>
7%> animateQuat(quat, pos, options) allows to specify additional options for the
8%> animation.
9%>
10%> animateQuat(quat1, pos1, quat2, pos2, ...) animates multiple quaternion and
11%> position vectors in the same figure.
12%>
13%> @param quat - Nx4 numeric vector or Nx1 quaternion vector
14%> @param pos - Nx3 numeric vector with position data
15%> @param options (optional) - struct with optional input arguments
16%> - names - cell array with names for the orientation data (default: 1:N)
17%> - freq - Sampling frequency of the data [Hz] (default: 200)
18%> - fps - Desired Frames per second [Hz] (default: 30)
19%> - speedup - Speedup factor of the animation (default: 1)
20%> - title - Title of the figure (default: "Orientation and Position Animation")
21%> - position - Position of the figure window (default: [0,0,0.5,0.5])
22%>
23%> @retval none
24%>
25%> @file animateQuatPos.m
26%>
27%> @copyright see the file @ref LICENSE in the root directory of the repository
28% =========================================================================== %
29
30function animateQuatPos(quat, pos, options)
31
32%% argument checking
33arguments (Repeating)
34 quat (:,:) {mustBeNonmissing}
35 pos (:,3) {mustBeNonmissing}
36end
37
38arguments
39 options.names (:,1) string = string.empty(1,0)
40 options.freq (1,1) double = 200;
41 options.fps (1,1) double = 30;
42 options.speedup (1,1) double = 1;
43 options.title (1,1) string = "Orientation and Position Animation"
44 options.position = [0,0,0.5,0.5]
45end
46N_quat = numel(quat);
47N_samples = 0;
48
49% check if quaternion vector is provided
50% define max sample size
51for i=1:N_quat
52 if ~isa(quat{i}, 'quaternion')
53 if size(quat{i}, 2) ~= 4
54 error('Quaternion vector must be Nx4');
55 else
56 quat{i} = quaternion(quat{i});
57 end
58 end
59
60 % max sample size
61 N_samples = max(N_samples, numel(quat{i}));
62end
63
64% check if position vector is the same size as the quaternion vector
65for i=1:N_quat
66 if size(pos{i}, 1) ~= size(quat{i}, 1)
67 error('Position vector must be the same size as the quaternion vector');
68 end
69end
70
71% check if names are provided
72if isempty(options.names) || numel(options.names) ~= N_quat
73 options.names = string(1:N_quat);
74end
75
76%% set up parameters
77% smoothed fps
78smoothed_fps = options.fps;
79
80% set up timer
81t = timer('ExecutionMode', 'fixedRate', 'Period', round(1/options.fps,3), 'TimerFcn', @timer_callback);
82t.UserData = 1;
83
84%% create figure
85fig = figure('Name', options.title, 'NumberTitle', 'off', 'MenuBar', 'none', 'ToolBar', 'figure', 'Units','normalized',"Position",options.position);
86for i=1:N_quat
87 subplot(1,N_quat,i);
88 pp(i) = poseplot();
89 title(options.names(i));
90 hold on
91 plot3(pos{i}(:,1), pos{i}(:,2), pos{i}(:,3), 'k', 'LineWidth', 2);
92 hold off
93end
94
95% add a close request function to the figure
96fig.CloseRequestFcn = @my_closereq;
97
98% add a time passed indicator to the bottom left of the figure
99text_passed = uicontrol('Style', 'text', 'String', sprintf('Time passed: %.2f s', 0), 'Units', 'normalized', 'Position', [0.01 0.01 0.3 0.05], 'FontSize', 12, 'FontWeight', 'bold');
100
101% add a fps indicator to the bottom right of the figure
102text_fps = uicontrol('Style', 'text', 'String', sprintf('FPS: %d', options.fps), 'Units', 'normalized', 'Position', [0.69 0.01 0.3 0.05], 'FontSize', 12, 'FontWeight', 'bold');
103
104% add a line to the top of the figure that indicates the current time
105line_curr_time = annotation('line', [0 0], [1,1], 'Units', 'normalized', 'LineWidth', 5, 'Color', 'r');
106
107% set up animation
108start(t);
109
110%% functions
111% timer callback function
112 function timer_callback(~, ~)
113 if t.UserData > N_samples
114 stop(t);
115 else
116 % update poseplot
117 for i=1:N_quat
118 if t.UserData < numel(quat{i})
119 pp(i).Orientation = quat{i}(t.UserData);
120 pp(i).Position = pos{i}(t.UserData, :);
121 else
122 pp(i).PatchFaceColor = 'r';
123 end
124 end
125
126 % update time passed indicator
127 text_passed.String = sprintf('Time passed: %.2f s', t.UserData/options.freq);
128
129 % update frequency indicator
130 curr_fps = 1/(t.InstantPeriod);
131 if isnan(curr_fps)
132 curr_fps = options.fps;
133 end
134
135 % Smooth the FPS value using an IIR filter
136 alpha = 0.05; % Smoothing factor
137 smoothed_fps = alpha * round(curr_fps) + (1 - alpha) * smoothed_fps;
138 text_fps.String = sprintf('FPS: %d', round(smoothed_fps));
139
140 % calculate steps to skip to keep up with the desired frequency
141 skip = round(options.speedup * options.freq/curr_fps);
142
143 % update current time indicator
144 line_curr_time.X = [0, t.UserData/N_samples];
145
146 % update counter
147 t.UserData = t.UserData + skip;
148 end
149 end
150
151% close request function
152 function my_closereq(~,~)
153 stop(t);
154 delete(t);
155 delete(fig);
156 end
157
158end
function animateQuatPos(in quat, in pos, in options)
function timer_callback(in ignoredArg, in ignoredArg)
function my_closereq(in ignoredArg, in ignoredArg)
function animateQuat(in quat, in options)