Master Thesis Code
by Simon Moser
Loading...
Searching...
No Matches
Trajectory.m
Go to the documentation of this file.
1% ==================================================================
2%> @brief Trajectory Class for generating and manipulating trajectories
3%> from waypoints.
4%>
5%> The Trajectory class is used to generate and manipulate trajectories
6%> from waypoints. The waypoints are defined by their start time, stay
7%> time, position, and orientation. The trajectory is generated by
8%> interpolating the position and orientation between the waypoints.
9%>
10%> @code
11%> % Example usage:
12%> % Create a Trajectory object
13%> traj = Trajectory();
14%>
15%> % Add waypoints to the trajectory
16%> t_start = seconds([0, 5, 10, 15]);
17%> t_stay = seconds([5, 5, 5, 5]);
18%> pos = [0,0,0; 1,1,1; 2,2,2; 3,3,3];
19%> ori = quaternion([1,0,0,0; 0.7071,0,0.7071,0; 0.7071,0,0,0.7071; 0,0,0,1]);
20%> traj.addWaypoints(t_start, t_stay, pos, ori);
21%>
22%> % Plot the waypoints
23%> traj.plotWaypoints();
24%>
25%> % Generate the trajectory
26%> traj.generateTrajectory();
27%>
28%> % Get the trajectory
29%> traj.getTrajectory();
30%> @endcode
31%>
32%> see also the more detailed example in @ref trajectoryExample.m
33%>
34%> @copyright see the file @ref LICENSE in the root directory of the repository
35% ==================================================================
37
38 properties (Constant, Access = private)
39 %> names of the variables in the waypoints table
40 tabVarNames = ["t_start", "t_stay","pos","ori"];
41
42 %> types of the variables in the waypoints table
43 tabVarTypes = ["duration", "duration","cell","quaternion"];
44
45 %> available interpolation methods for position
46 interpMethodsPos = ["minJerk","minSnap","linear","spline","pchip","makima"];
48 %> available interpolation methods for orientation
49 interpMethodsOri = ["linear","spline","pchip","makima"];
50
51 %> names of the variables in the trajectory table
52 trajVarNames = ["pos","vel","acc","jerk","ori","gyr"];
53
54 %> types of the variables in the trajectory table
55 trajVarTypes = ["cell","cell","cell","cell","quaternion", "cell"];
56
57 %> units of the variables in the trajectory table
58 trajVarUnits = ["m","m/s","m/s^2","m/s^3","quaternion", "deg/s"];
59 end
60
61 properties (Access = private)
62 %> sampling frequency in Hz
63 fs = 200;
64
65 %> waypoints table
66 tab
68 %> trajectory table
69 traj
70
71 %> interpolation method of the position
72 interpMethodPos
74 %> interpolation method of the orientation
75 interpMethodOri
76
77 %> flag if the trajectory is generated and up to date
78 isGenerated = false;
79 end
80
81 methods (Access = public)
82 % ==================================================================
83 %> @brief Trajectory Class constructor
84 %>
85 %> @param options (optional) - Structure specifying custom settings for the Trajectory object.
86 %> - options.fs (optional) - Sampling frequency in Hz (default: 200)
87 %>
88 %> @retval obj - The created Trajectory object.
89 % ==================================================================
90 function obj = Trajectory(options)
91
92 arguments
93 options.fs (1,1) double = 200
94 end
95
96 obj.fs = options.fs;
97 obj.tab = table('Size',[0,numel(obj.tabVarNames)],'VariableNames', obj.tabVarNames,'VariableTypes', obj.tabVarTypes); % waypoints table
98
99 obj.interpMethodPos = obj.interpMethodsPos(1);
100 obj.interpMethodOri = obj.interpMethodsOri(1);
101 end
102
103 % ==================================================================
104 %> @brief get a copy of the Trajectory object
105 %>
106 %> @param None
107 %>
108 %> @retval obj - Copy of the Trajectory object
109 %> ==================================================================
110 function obj = copy(obj)
111 obj = Trajectory();
112 obj.fs = obj.fs;
113 obj.tab = obj.tab;
114 obj.traj = obj.traj;
115 obj.interpMethodPos = obj.interpMethodPos;
116 obj.interpMethodOri = obj.interpMethodOri;
117 obj.isGenerated = obj.isGenerated;
118 end
119
120 % ==================================================================
121 %> @brief Add waypoints to the trajectory
122 %>
123 %> addWaypoints(obj,t_start,t_stay,pos,ori) adds waypoints to the
124 %> trajectory from the inputs. The waypoints are defined by their
125 %> start time, stay time, position, and orientation.
126 %>
127 %> @param t_start - Start time of each waypoint (duration array)
128 %> @param t_stay - Duration of each waypoint (duration array)
129 %> @param pos - Position of each waypoint (Nx3 double array)
130 %> @param ori - Orientation of each waypoint (quaternion array)
131 %>
132 %> @retval None
133 %>
134 %> @note The inputs must have the same height.
135 % ==================================================================
136 function addWaypoints(obj,t_start,t_stay,pos,ori)
137
138 arguments
139 obj
140 t_start (:,1) duration
141 t_stay (:,1) duration
142 pos (:,3) double
143 ori (:,1) quaternion
144 end
145
146 % make sure the height of the inputs is the same
147 n = height(t_start);
148 assert(all([height(t_stay),height(pos),height(ori)] == n), "The inputs must have the same height");
149
150 % try to add the waypoints to the table
151 try
152 if isempty(obj.tab)
153 obj.tab = table(t_start, t_stay, pos, ori, 'VariableNames', obj.tabVarNames);
154 else
155 obj.tab = [obj.tab; table(t_start, t_stay, pos, ori, 'VariableNames', obj.tabVarNames)];
156 end
157 catch
158 error("The inputs are not compatible with the waypoints table");
159 end
160
161 % flag the trajectory as not generated
162 obj.isGenerated = false;
163 end
164
165 % ==================================================================
166 %> @brief set the sampling frequency
167 %>
168 %> @param fs - Sampling frequency in Hz (positive integer)
169 %>
170 %> @retval None
171 %>
172 %> @note The Sampling frequency is used to generate the trajectory
173 % ==================================================================
174 function setSamplingFrequency(obj,fs)
175
176 arguments
177 obj
178 fs (1,1) double {mustBePositive,mustBeInteger}
179 end
180
181 % update the sampling frequency
182 obj.fs = fs;
183
184 % flag the trajectory as not generated
185 obj.isGenerated = false;
186 end
187
188 % ==================================================================
189 %> @brief get the sampling frequency
190 %>
191 %> @param None
192 %>
193 %> @retval fs - Sampling frequency in Hz
194 % ==================================================================
195 function fs = getSamplingFrequency(obj)
196 fs = obj.fs;
197 end
198
199 % ==================================================================
200 %> @brief plot the waypoints
201 %>
202 %> plotWaypoints(obj,ax,options) plots the waypoints on the
203 %> specified axes using the custom settings specified by the options
204 %> structure. The resulting plot is a 3D plot with the waypoints
205 %> represented by a model of the JUMP sensor and a line connecting the
206 %> waypoints.
207 %>
208 %> @param ax (optional) - Axes to plot the waypoints on
209 %> @param options (optional) - Structure specifying custom settings for the plot
210 %> - options.scale (optional) - Scale factor for the waypoints (default: 10)
211 %> - options.numbers (optional) - Flag to display the waypoint numbers (default: true)
212 %> - options.line (optional) - Flag to display a line between the waypoints (default: true)
213 %>
214 %> @retval fig - Figure handle of the plot
215 % ==================================================================
216 function fig = plotWaypoints(obj, ax, options)
217
218 arguments
219 obj
220 ax = gca
221 options.scale (1,1) double = 10
222 options.numbers (1,1) logical = true
223 options.line (1,1) logical = true
224 end
225
226 % Plot each waypoint
227 for i = 1:height(obj.tab)
228 poseplot(ax, obj.tab.ori(i), obj.tab.pos(i,:), "NED",...
229 MeshFileName = "jumpSimplified.stl", PatchFaceAlpha = 0.8, ...
230 PatchFaceColor = [0,0.57,0.82], ScaleFactor = options.scale);
231 hold on
232
233 % plot the waypoint numbers above the waypoints
234 if options.numbers
235 offset = 0.02*options.scale; % make sure the numbers are not hidden by the mesh
236 text(obj.tab.pos(i,1)-offset,obj.tab.pos(i,2)+offset,obj.tab.pos(i,3)-offset,...
237 num2str(i),'FontSize',12, 'Color', 'white','FontWeight','bold','BackgroundColor','black');
238 end
239 end
240 xlabel('X [m]');
241 ylabel('Y [m]');
242 zlabel('Z [m]');
243
244 % display a line between the waypoints with changing color
245 if options.line
246 colors = hsv(height(obj.tab)-1); % generate a colormap based on the number of waypoints
247 for i = 1:height(obj.tab)-1
248 plot3(ax, obj.tab.pos(i:i+1,1), obj.tab.pos(i:i+1,2), obj.tab.pos(i:i+1,3), 'Color', colors(i,:), 'LineWidth', 2);
249 hold on;
250 end
251
252 % add a colorbar to the figure
253 colormap(colors);
254 clim([1,height(obj.tab)]);
255 c = colorbar("Direction","reverse");
256 c.Label.String = 'Waypoint number';
257 c.Label.FontSize = 14;
258 c.Ticks = 1:height(obj.tab);
259 c.Limits = [1,height(obj.tab)];
260 end
261
262 hold off;
263
264 fig = ax.Parent;
265 end
266
267 % ==================================================================
268 %> @brief return the waypoints table in a reduced format
269 %>
270 %> @param None
271 %>
272 %> @retval tab - Waypoints table in a reduced format
273 %>
274 %> @note This format is used in the trajectoryGenerationApp.
275 % ==================================================================
276 function tab = reduce(obj)
277
278 if height(obj.tab) == 0
279 tab = table('Size',[0,9],'VariableNames',["t_start","t_stay","x","y","z","q","i","j","k"],'VariableTypes',["string","string","double","double","double","double","double","double","double"]);
280 return
281 end
282
283 t_start = string(obj.tab.t_start,"mm:ss.SSS");
284 dur = string(obj.tab.t_stay,"mm:ss.SSS");
285
286 x = obj.tab.pos(:,1);
287 y = obj.tab.pos(:,2);
288 z = obj.tab.pos(:,3);
289 quat = obj.tab.ori.compact;
290 q= quat(:,1);
291 i= quat(:,2);
292 j= quat(:,3);
293 k= quat(:,4);
294
295 tab = table(t_start,dur,x,y,z,q,i,j,k,'VariableNames',["t_start","t_stay","x","y","z","q","i","j","k"]);
296 end
297
298 % ==================================================================
299 %> @brief import a waypoints table
300 %>
301 %> @param tab - Waypoints table
302 %>
303 %> @retval None
304 %>
305 %> @note this is used in the trajectoryGenerationApp. The table must
306 %> have the same format as the one returned by the reduce method.
307 % ==================================================================
308 function importTab(obj,tab)
309
310 t_start = duration(tab.t_start,"InputFormat","mm:ss.SSS");
311 t_stay = duration(tab.t_stay,"InputFormat","mm:ss.SSS");
312
313 pos = [tab.x,tab.y,tab.z];
314 ori = quaternion(tab.q,tab.i,tab.j,tab.k);
315
316 obj.tab = table(t_start, t_stay, pos, ori, 'VariableNames', obj.tabVarNames);
317
318 % flag the trajectory as not generated
319 obj.isGenerated = false;
320 end
321
322 % ==================================================================
323 %> @brief export the waypoints table
324 %>
325 %> @param None
326 %>
327 %> @retval tab - Waypoints table
328 % ==================================================================
329 function tab = getTab(obj)
330 tab = obj.tab;
331 end
332
333 % ==================================================================
334 %> @brief set the waypoints table
335 %>
336 %> @param tab - Waypoints table
337 %>
338 %> @retval None
339 % ==================================================================
340 function setTab(obj,tab)
341 obj.tab = tab;
342
343 % flag the trajectory as not generated
344 obj.isGenerated = false;
345 end
346
347 % ==================================================================
348 %> @brief sort the waypoints table in ascending order of start time
349 %>
350 %> @param None
351 %>
352 %> @retval None
353 % ==================================================================
354 function sortTab(obj)
355 % SORTTAB Sort the waypoints table by start time
356
357 obj.tab = sortrows(obj.tab,1);
358 end
359
360 % ==================================================================
361 %> @brief clean the waypoints table
362 %>
363 %> cleanTab(obj) removes duplicate waypoints, rounds the start and
364 %> stay times to the nearest multiple of the sampling time, and checks
365 %> for time overlapping. The resulting table is sorted by start time.
366 %>
367 %> @param None
368 %>
369 %> @retval None
370 % ==================================================================
371 function cleanTab(obj)
372
373 % backup the table
374 tab_backup = obj.tab;
375
376 % make sure the table is sorted
377 obj.sortTab();
378
379 % if the smallest t_start is not 0, add a waypoint at the origin
380 if obj.tab.t_start(1) > seconds(0)
381 obj.addWaypoints(seconds(0),seconds(0),[0,0,0],quaternion(1,0,0,0));
382 obj.sortTab(); % make sure the new waypoint is at the top
383 end
384
385 % remove duplicate waypoints (same t_start)
386 [~,idx] = unique(obj.tab.t_start);
387 if isempty(idx) && height(obj.tab) > 0
388 idx = 1;
389 end
390 obj.tab = obj.tab(idx,:);
391
392 % round the start time to the nearest multiple of the sampling time
393 obj.tab.t_start = round(obj.tab.t_start/seconds(1/obj.fs))*seconds(1/obj.fs);
394
395 % round the stay time to the nearest multiple of the sampling time
396 obj.tab.t_stay = round(obj.tab.t_stay/seconds(1/obj.fs))*seconds(1/obj.fs);
397
398 % check for time overlapping (t_start + t_stay must be < t_start of the next waypoint)
399 for i = 1:height(obj.tab)-1
400 if obj.tab.t_start(i) + obj.tab.t_stay(i) > obj.tab.t_start(i+1) - seconds(2/obj.fs)
401 % reduce the t_stay of the current waypoint
402 obj.tab.t_stay(i) = obj.tab.t_start(i+1) - obj.tab.t_start(i) - seconds(2/obj.fs);
403 end
404 end
405
406 % flag the trajectory as not generated if the table has changed
407 if ~isequal(tab_backup,obj.tab)
408 obj.isGenerated = false;
409 end
410 end
411
412 % ==================================================================
413 %> @brief get the interpolation methods
414 %>
415 %> @param None
416 %>
417 %> @retval methods - Structure containing the available interpolation
418 %> methods for position and orientation, as well as the currently
419 %> selected methods.
420 % ==================================================================
421 function methods = getInterpMethods(obj)
422
423 methods.pos = obj.interpMethodsPos;
424 methods.ori = obj.interpMethodsOri;
425 methods.posCurrent = obj.interpMethodPos;
426 methods.oriCurrent = obj.interpMethodOri;
427 end
428
429 % ==================================================================
430 %> @brief set the interpolation methods
431 %>
432 %> setInterpMethods(obj,methods) sets the interpolation methods for
433 %> position and orientation.
434 %>
435 %> @param methods.pos (optional) - Interpolation method for position
436 %> @param methods.ori (optional) - Interpolation method for orientation
437 %>
438 %> @retval None
439 %>
440 %> @note The interpolation methods must be part of the available
441 %> interpolation methods. The available interpolation methods can be
442 %> obtained by calling the getInterpMethods() method.
443 % ==================================================================
444 function setInterpMethods(obj,methods)
445
446 if isfield(methods,"pos")
447 if ismember(methods.pos,obj.interpMethodsPos)
448 obj.interpMethodPos = methods.pos;
449 end
450 end
451 if isfield(methods,"ori")
452 if ismember(methods.ori,obj.interpMethodsOri)
453 obj.interpMethodOri = methods.ori;
454 end
455 end
456
457 % flag the trajectory as not generated
458 obj.isGenerated = false;
459 end
460
461 % ==================================================================
462 %> @brief Generate the trajectory from the waypoints table
463 %>
464 %> generateTrajectory(obj) generates the trajectory from the waypoints
465 %> table using the default settings. For the generation of the
466 %> trajectory, the position is interpolated to get a trajectory with
467 %> the selected sampling frequency matching the waypoints.
468 %>
469 %> generateTrajectory(obj, options) generates the trajectory from the
470 %> waypoints table using custom settings specified by the options
471 %> structure.
472 %>
473 %> @param options (optional) - Custom settings for the trajectory
474 %> generation
475 %> @param options.pos (optional) - Interpolation method for
476 %> position
477 %> @param options.ori (optional) - Interpolation method for
478 %> orientation
479 %> @param options.fs (optional) - Sampling frequency in Hz
480 % ==================================================================
481 function generateTrajectory(obj, options)
482
483 arguments
484 obj
485 options.pos (1,1) string = obj.interpMethodPos
486 options.ori (1,1) string = obj.interpMethodOri
487 options.fs (1,1) double = obj.fs
488 end
489
490 % update the settings of the obj from the options
491 obj.setSamplingFrequency(options.fs);
492 obj.setInterpMethods(options);
493
494 % make sure the table is clean
495 obj.cleanTab();
496
497 % get the time limits of the trajectory
498 traj_t_start = obj.tab.t_start(1);
499 traj_t_end = obj.tab.t_start(end) + obj.tab.t_stay(end);
500
501 % generate the time vector
502 t = (traj_t_start:seconds(1/obj.fs):traj_t_end)';
503
504 % generate the trajectory table
505 obj.traj = timetable(...
506 t,... % time vector
507 nan(height(t),3),... % position
508 nan(height(t),3),... % velocity
509 nan(height(t),3),... % acceleration
510 nan(height(t),3),... % jerk
511 quaternion.zeros(height(t),1),... % orientation
512 nan(height(t),3),... % angular velocity
513 'VariableNames',obj.trajVarNames);
514
515 obj.traj.Properties.VariableUnits = obj.trajVarUnits;
516
517 % fill the known values and save the start and end indices of each waypoint
518 idx_start = zeros(height(obj.tab),1);
519 idx_end = zeros(height(obj.tab),1);
520
521 for i = 1:height(obj.tab)
522 % find the indices of the current waypoint in the trajectory table
523 idx_start(i) = find(t >= obj.tab.t_start(i),1);
524 idx_end(i) = find(t >= obj.tab.t_start(i) + obj.tab.t_stay(i),1);
525
526 % fill the position
527 obj.traj.pos(idx_start(i):idx_end(i),:) = repmat(obj.tab.pos(i,:),idx_end(i)-idx_start(i)+1,1);
528
529 % fill the orientation
530 obj.traj.ori(idx_start(i):idx_end(i)) = repmat(obj.tab.ori(i),idx_end(i)-idx_start(i)+1,1);
531 end
532
533 % interpolate the position, velocity, acceleration and jerk
534 switch obj.interpMethodPos
535 case {"minJerk","minSnap"}
536 assert(numel(idx_start) == numel(idx_end), "The number of start and end indices must be the same");
537
538
539 % explanation of the usage of a while loop and the iExtra variable:
540 % if the waypoint has no stay time, the position should not be
541 % slowed down to 0 at the end of the waypoint. Therefore, we
542 % interpolate trough the waypoints with no stay time and
543 % increment the index by the number of waypoints with no stay time
544 i = 1;
545 while (i < numel(idx_start))
546
547 % handle waypoints with no stay time
548 iExtra = 0;
549 while i+iExtra < numel(idx_start)-1
550 if idx_start(i+iExtra+1) == idx_end(i+iExtra+1)
551 iExtra = iExtra + 1;
552 else
553 break;
554 end
555 end
556 idx = zeros(1,iExtra+2);
557 idx(1) = idx_end(i);
558 for j=1:iExtra+1
559 idx(j+1) = idx_start(j+i);
560 end
561
562 switch obj.interpMethodPos
563 case "minJerk"
564 [pos, vel, acc, jerk, ~, ~, t] = minjerkpolytraj(obj.traj.pos(idx,:)', seconds(obj.traj.t(idx))', idx(end)-idx(1));
565
566 case "minSnap"
567 [pos, vel, acc, jerk, ~, ~, ~, t] = minsnappolytraj(obj.traj.pos(idx,:)', seconds(obj.traj.t(idx))', idx(end)-idx(1));
568
569 end
570
571 % since the functions do not sample at the exact wanted time, we need to interpolate the results
572 tt = timetable(seconds(t'),pos',vel',acc',jerk','VariableNames',["pos","vel","acc","jerk"]);
573 tt = retime(tt, obj.traj.t(idx(1):idx(end)), 'linear');
574
575 % fill the trajectory table
576 assert(all(tt.Time == obj.traj.t(idx(1):idx(end))), "The time vectors do not match");
577 obj.traj.pos(idx(1):idx(end),:) = tt.pos;
578 obj.traj.vel(idx(1):idx(end),:) = tt.vel;
579 obj.traj.acc(idx(1):idx(end),:) = tt.acc;
580 obj.traj.jerk(idx(1):idx(end),:) = tt.jerk;
581
582 % increment the index
583 i = i + iExtra + 1;
584
585 end
586
587 case {"linear","spline","pchip","makima"}
588 % interpolate the actual position
589 obj.traj.pos = fillmissing(obj.traj.pos,obj.interpMethodPos,1);
590
591 % find the velocity, acceleration, and jerk from the position
592 obj.traj.vel = gradient(obj.traj.pos',1/obj.fs)';
593 obj.traj.acc = gradient(obj.traj.vel',1/obj.fs)';
594 obj.traj.jerk = gradient(obj.traj.acc',1/obj.fs)';
595
596 % Suppress extremely small values
597 threshold = 1e-10;
598 obj.traj.vel(abs(obj.traj.vel) < threshold) = 0;
599 obj.traj.acc(abs(obj.traj.acc) < threshold) = 0;
600 obj.traj.jerk(abs(obj.traj.jerk) < threshold) = 0;
601
602
603 end
604
605 % interpolate the orientation
606 f = nan(height(obj.traj),1);
607 f(idx_end) = 0:numel(idx_end)-1; %end of the waypoint = start of the orientation change
608 f(idx_start) = 0:numel(idx_end)-1; %start of the waypoint = end of the orientation change
609
610 % fill the missing values during a stay time
611 for i=1:numel(idx_start)
612 f(idx_start(i):idx_end(i)) = fillmissing(f(idx_start(i):idx_end(i)),"nearest");
613 end
614
615 % add values to start and end to smooth beginning and stop
616 f = [zeros(10,1); f; ones(10,1)*max(f)];
617
618 % fill the missing values with the interpolation method
619 f = fillmissing(f,obj.interpMethodOri,1);
620
621 % remove the added ones and zeros
622 f = f(11:end-10);
623
624 % restrict to numbers greater than one
625 f(f > 1) = f(f > 1) - floor(f(f > 1));
626
627 for i = 1: numel(idx_start)-1
628 idx = [idx_end(i), idx_start(i+1)];
629
630 % define the normalized amount of the transformation
631 this_f = f(idx(1):idx(2));
632
633 % make sure, beginning and end ar correct (problem occurs
634 % with zero stay waypoints)
635 this_f(1) = 0;
636 this_f(end) = 1;
637
638 % interpolate
639 ori = nan(numel(this_f),4);
640 % NOTE : can also be a parfor loop
641 for j = 1:numel(this_f)
642 ori(j,:) = quatinterp(obj.traj.ori(idx(1)).normalize.compact,obj.traj.ori(idx(2)).normalize.compact,this_f(j),"slerp");
643 end
644
645 % fill the trajectory table
646 obj.traj.ori(idx(1):idx(2)) = quaternion(ori);
647
648 end
649
650 % fill the missing values
651 obj.traj.ori = obj.quaternionFillMissing(obj.traj.ori);
652 obj.traj.gyr = angvel(obj.traj.ori,1/obj.fs,"frame") * 180/pi;
653 obj.traj(:,1:end-2) = fillmissing(obj.traj(:,1:end-2),"nearest");
654
655 % flag the trajectory as generated
656 obj.isGenerated = true;
657
658 end
659
660 % ==================================================================
661 %> @brief get the trajectory timetable
662 %>
663 %> @param None
664 %>
665 %> @retval traj - Trajectory timetable
666 % ==================================================================
667 function traj = getTrajectory(obj)
668 if ~obj.isGenerated
669 obj.generateTrajectory();
670 end
671 traj = obj.traj;
672 end
673
674 function isGenerated = getIsGenerated(obj)
675 isGenerated = obj.isGenerated;
676 end
677 end
678
679 methods (Access = private, Static)
680
681 % ==================================================================
682 %> @brief fill missing values in a quaternion array
683 %>
684 %> @param q - Quaternion array
685 %>
686 %> @retval qFilled - Quaternion array with filled missing values
687 % ==================================================================
688 function qFilled = quaternionFillMissing(q)
689 arguments
690 q (:,1) quaternion
691 end
692
693 n = length(q);
694 qFilled = q; % Initialize filled quaternion vector with original values
695
696 % Identify zero quaternions
697 isZero = arrayfun(@(x) isequal(x, quaternion.zeros(1)), q);
698 isZeroIdx = find(isZero);
699
700 for i = 1:numel(isZeroIdx) % Iterate over indices of zero quaternions
701 for j = 1:n/2
702
703 % check previous values
704 prevIdx = isZeroIdx(i) - j;
705 if prevIdx > 0
706 if ~isZero(prevIdx)
707 qFilled(isZeroIdx(i)) = q(prevIdx);
708 break;
709 end
710 end
711
712 % check next values
713 nextIdx = isZeroIdx(i) + j;
714 if nextIdx <= n
715 if ~isZero(nextIdx)
716 qFilled(isZeroIdx(i)) = q(nextIdx);
717 break;
718 end
719 end
720 end
721 end
722 end
723
724 end
725end
726
Trajectory Class for generating and manipulating trajectories from waypoints.
Definition Trajectory.m:37
Constant Property interpMethodsOri
available interpolation methods for orientation
Definition Trajectory.m:55
Property fs
sampling frequency in Hz
Definition Trajectory.m:73
function generateTrajectory(in obj, in options)
Generate the trajectory from the waypoints table.
function importTab(in obj, in tab)
import a waypoints table
function getTrajectory(in obj)
get the trajectory timetable
Property isGenerated
flag if the trajectory is generated and up to date
Definition Trajectory.m:93
Constant Property tabVarTypes
types of the variables in the waypoints table
Definition Trajectory.m:47
Constant Property trajVarTypes
types of the variables in the trajectory table
Definition Trajectory.m:63
function copy(in obj)
get a copy of the Trajectory object
function setSamplingFrequency(in obj, in fs)
set the sampling frequency
function cleanTab(in obj)
clean the waypoints table
Constant Property trajVarUnits
units of the variables in the trajectory table
Definition Trajectory.m:67
Constant Property interpMethodsPos
available interpolation methods for position
Definition Trajectory.m:51
Property tab
waypoints table
Definition Trajectory.m:77
Property interpMethodOri
interpolation method of the orientation
Definition Trajectory.m:89
function sortTab(in obj)
sort the waypoints table in ascending order of start time
Constant Property trajVarNames
names of the variables in the trajectory table
Definition Trajectory.m:59
function plotWaypoints(in obj, in ax, in options)
plot the waypoints
function Trajectory(in options)
Trajectory Class constructor.
function getInterpMethods(in obj)
get the interpolation methods
function getIsGenerated(in obj)
function setInterpMethods(in obj, in methods)
set the interpolation methods
function setTab(in obj, in tab)
set the waypoints table
Property traj
trajectory table
Definition Trajectory.m:81
function addWaypoints(in obj, in t_start, in t_stay, in pos, in ori)
Add waypoints to the trajectory.
Constant Property tabVarNames
names of the variables in the waypoints table
Definition Trajectory.m:43
function getTab(in obj)
export the waypoints table
static function quaternionFillMissing(in q)
fill missing values in a quaternion array
function getSamplingFrequency(in obj)
get the sampling frequency
function reduce(in obj)
return the waypoints table in a reduced format
Property interpMethodPos
interpolation method of the position
Definition Trajectory.m:85
handle class from MATLAB.
app that helps to generate a trajectory.