BsplineSE3.cpp
Go to the documentation of this file.
1 /*
2  * OpenVINS: An Open Platform for Visual-Inertial Research
3  * Copyright (C) 2018-2023 Patrick Geneva
4  * Copyright (C) 2018-2023 Guoquan Huang
5  * Copyright (C) 2018-2023 OpenVINS Contributors
6  * Copyright (C) 2018-2019 Kevin Eckenhoff
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include "BsplineSE3.h"
23 
24 using namespace ov_core;
25 
26 void BsplineSE3::feed_trajectory(std::vector<Eigen::VectorXd> traj_points) {
27 
28  // Find the average frequency to use as our uniform timesteps
29  double sumdt = 0;
30  for (size_t i = 0; i < traj_points.size() - 1; i++) {
31  sumdt += traj_points.at(i + 1)(0) - traj_points.at(i)(0);
32  }
33  dt = sumdt / (traj_points.size() - 1);
34  dt = (dt < 0.05) ? 0.05 : dt;
35  PRINT_DEBUG("[B-SPLINE]: control point dt = %.3f (original dt of %.3f)\n", dt, sumdt / (traj_points.size() - 1));
36 
37  // convert all our trajectory points into SE(3) matrices
38  // we are given [timestamp, p_IinG, q_GtoI]
39  AlignedEigenMat4d trajectory_points;
40  for (size_t i = 0; i < traj_points.size() - 1; i++) {
41  Eigen::Matrix4d T_IinG = Eigen::Matrix4d::Identity();
42  T_IinG.block(0, 0, 3, 3) = quat_2_Rot(traj_points.at(i).block(4, 0, 4, 1)).transpose();
43  T_IinG.block(0, 3, 3, 1) = traj_points.at(i).block(1, 0, 3, 1);
44  trajectory_points.insert({traj_points.at(i)(0), T_IinG});
45  }
46 
47  // Get the oldest timestamp
48  double timestamp_min = INFINITY;
49  double timestamp_max = -INFINITY;
50  for (const auto &pose : trajectory_points) {
51  if (pose.first <= timestamp_min) {
52  timestamp_min = pose.first;
53  }
54  if (pose.first >= timestamp_max) {
55  timestamp_max = pose.first;
56  }
57  }
58  PRINT_DEBUG("[B-SPLINE]: trajectory start time = %.6f\n", timestamp_min);
59  PRINT_DEBUG("[B-SPLINE]: trajectory end time = %.6f\n", timestamp_max);
60 
61  // then create spline control points
62  double timestamp_curr = timestamp_min;
63  while (true) {
64 
65  // Get bounding posed for the current time
66  double t0, t1;
67  Eigen::Matrix4d pose0, pose1;
68  bool success = find_bounding_poses(timestamp_curr, trajectory_points, t0, pose0, t1, pose1);
69  // PRINT_DEBUG("[SIM]: time curr = %.6f | lambda = %.3f | dt = %.3f | dtmeas =
70  // %.3f\n",timestamp_curr,(timestamp_curr-t0)/(t1-t0),dt,(t1-t0));
71 
72  // If we didn't find a bounding pose, then that means we are at the end of the dataset
73  // Thus break out of this loop since we have created our max number of control points
74  if (!success)
75  break;
76 
77  // Linear interpolation and append to our control points
78  double lambda = (timestamp_curr - t0) / (t1 - t0);
79  Eigen::Matrix4d pose_interp = exp_se3(lambda * log_se3(pose1 * Inv_se3(pose0))) * pose0;
80  control_points.insert({timestamp_curr, pose_interp});
81  timestamp_curr += dt;
82  // std::stringstream ss;
83  // ss << pose_interp(0,3) << "," << pose_interp(1,3) << "," << pose_interp(2,3) << std::endl;
84  // PRINT_DEBUG(ss.str().c_str());
85  }
86 
87  // The start time of the system is two dt in since we need at least two older control points
88  timestamp_start = timestamp_min + 2 * dt;
89  PRINT_DEBUG("[B-SPLINE]: start trajectory time of %.6f\n", timestamp_start);
90 }
91 
92 bool BsplineSE3::get_pose(double timestamp, Eigen::Matrix3d &R_GtoI, Eigen::Vector3d &p_IinG) {
93 
94  // Get the bounding poses for the desired timestamp
95  double t0, t1, t2, t3;
96  Eigen::Matrix4d pose0, pose1, pose2, pose3;
97  bool success = find_bounding_control_points(timestamp, control_points, t0, pose0, t1, pose1, t2, pose2, t3, pose3);
98  // PRINT_DEBUG("[SIM]: time curr = %.6f | dt1 = %.3f | dt2 = %.3f | dt3 = %.3f | dt4 = %.3f | success =
99  // %d\n",timestamp,t0-timestamp,t1-timestamp,t2-timestamp,t3-timestamp,(int)success);
100 
101  // Return failure if we can't get bounding poses
102  if (!success) {
103  R_GtoI.setIdentity();
104  p_IinG.setZero();
105  return false;
106  }
107 
108  // Our De Boor-Cox matrix scalars
109  double DT = (t2 - t1);
110  double u = (timestamp - t1) / DT;
111  double b0 = 1.0 / 6.0 * (5 + 3 * u - 3 * u * u + u * u * u);
112  double b1 = 1.0 / 6.0 * (1 + 3 * u + 3 * u * u - 2 * u * u * u);
113  double b2 = 1.0 / 6.0 * (u * u * u);
114 
115  // Calculate interpolated poses
116  Eigen::Matrix4d A0 = exp_se3(b0 * log_se3(Inv_se3(pose0) * pose1));
117  Eigen::Matrix4d A1 = exp_se3(b1 * log_se3(Inv_se3(pose1) * pose2));
118  Eigen::Matrix4d A2 = exp_se3(b2 * log_se3(Inv_se3(pose2) * pose3));
119 
120  // Finally get the interpolated pose
121  Eigen::Matrix4d pose_interp = pose0 * A0 * A1 * A2;
122  R_GtoI = pose_interp.block(0, 0, 3, 3).transpose();
123  p_IinG = pose_interp.block(0, 3, 3, 1);
124  return true;
125 }
126 
127 bool BsplineSE3::get_velocity(double timestamp, Eigen::Matrix3d &R_GtoI, Eigen::Vector3d &p_IinG, Eigen::Vector3d &w_IinI,
128  Eigen::Vector3d &v_IinG) {
129 
130  // Get the bounding poses for the desired timestamp
131  double t0, t1, t2, t3;
132  Eigen::Matrix4d pose0, pose1, pose2, pose3;
133  bool success = find_bounding_control_points(timestamp, control_points, t0, pose0, t1, pose1, t2, pose2, t3, pose3);
134  // PRINT_DEBUG("[SIM]: time curr = %.6f | dt1 = %.3f | dt2 = %.3f | dt3 = %.3f | dt4 = %.3f | success =
135  // %d\n",timestamp,t0-timestamp,t1-timestamp,t2-timestamp,t3-timestamp,(int)success);
136 
137  // Return failure if we can't get bounding poses
138  if (!success) {
139  w_IinI.setZero();
140  v_IinG.setZero();
141  return false;
142  }
143 
144  // Our De Boor-Cox matrix scalars
145  double DT = (t2 - t1);
146  double u = (timestamp - t1) / DT;
147  double b0 = 1.0 / 6.0 * (5 + 3 * u - 3 * u * u + u * u * u);
148  double b1 = 1.0 / 6.0 * (1 + 3 * u + 3 * u * u - 2 * u * u * u);
149  double b2 = 1.0 / 6.0 * (u * u * u);
150  double b0dot = 1.0 / (6.0 * DT) * (3 - 6 * u + 3 * u * u);
151  double b1dot = 1.0 / (6.0 * DT) * (3 + 6 * u - 6 * u * u);
152  double b2dot = 1.0 / (6.0 * DT) * (3 * u * u);
153 
154  // Cache some values we use alot
155  Eigen::Matrix<double, 6, 1> omega_10 = log_se3(Inv_se3(pose0) * pose1);
156  Eigen::Matrix<double, 6, 1> omega_21 = log_se3(Inv_se3(pose1) * pose2);
157  Eigen::Matrix<double, 6, 1> omega_32 = log_se3(Inv_se3(pose2) * pose3);
158 
159  // Calculate interpolated poses
160  Eigen::Matrix4d A0 = exp_se3(b0 * omega_10);
161  Eigen::Matrix4d A1 = exp_se3(b1 * omega_21);
162  Eigen::Matrix4d A2 = exp_se3(b2 * omega_32);
163  Eigen::Matrix4d A0dot = b0dot * hat_se3(omega_10) * A0;
164  Eigen::Matrix4d A1dot = b1dot * hat_se3(omega_21) * A1;
165  Eigen::Matrix4d A2dot = b2dot * hat_se3(omega_32) * A2;
166 
167  // Get the interpolated pose
168  Eigen::Matrix4d pose_interp = pose0 * A0 * A1 * A2;
169  R_GtoI = pose_interp.block(0, 0, 3, 3).transpose();
170  p_IinG = pose_interp.block(0, 3, 3, 1);
171 
172  // Finally get the interpolated velocities
173  // NOTE: Rdot = R*skew(omega) => R^T*Rdot = skew(omega)
174  Eigen::Matrix4d vel_interp = pose0 * (A0dot * A1 * A2 + A0 * A1dot * A2 + A0 * A1 * A2dot);
175  w_IinI = vee(pose_interp.block(0, 0, 3, 3).transpose() * vel_interp.block(0, 0, 3, 3));
176  v_IinG = vel_interp.block(0, 3, 3, 1);
177  return true;
178 }
179 
180 bool BsplineSE3::get_acceleration(double timestamp, Eigen::Matrix3d &R_GtoI, Eigen::Vector3d &p_IinG, Eigen::Vector3d &w_IinI,
181  Eigen::Vector3d &v_IinG, Eigen::Vector3d &alpha_IinI, Eigen::Vector3d &a_IinG) {
182 
183  // Get the bounding poses for the desired timestamp
184  double t0, t1, t2, t3;
185  Eigen::Matrix4d pose0, pose1, pose2, pose3;
186  bool success = find_bounding_control_points(timestamp, control_points, t0, pose0, t1, pose1, t2, pose2, t3, pose3);
187 
188  // Return failure if we can't get bounding poses
189  if (!success) {
190  alpha_IinI.setZero();
191  a_IinG.setZero();
192  return false;
193  }
194 
195  // Our De Boor-Cox matrix scalars
196  double DT = (t2 - t1);
197  double u = (timestamp - t1) / DT;
198  double b0 = 1.0 / 6.0 * (5 + 3 * u - 3 * u * u + u * u * u);
199  double b1 = 1.0 / 6.0 * (1 + 3 * u + 3 * u * u - 2 * u * u * u);
200  double b2 = 1.0 / 6.0 * (u * u * u);
201  double b0dot = 1.0 / (6.0 * DT) * (3 - 6 * u + 3 * u * u);
202  double b1dot = 1.0 / (6.0 * DT) * (3 + 6 * u - 6 * u * u);
203  double b2dot = 1.0 / (6.0 * DT) * (3 * u * u);
204  double b0dotdot = 1.0 / (6.0 * DT * DT) * (-6 + 6 * u);
205  double b1dotdot = 1.0 / (6.0 * DT * DT) * (6 - 12 * u);
206  double b2dotdot = 1.0 / (6.0 * DT * DT) * (6 * u);
207 
208  // Cache some values we use alot
209  Eigen::Matrix<double, 6, 1> omega_10 = log_se3(Inv_se3(pose0) * pose1);
210  Eigen::Matrix<double, 6, 1> omega_21 = log_se3(Inv_se3(pose1) * pose2);
211  Eigen::Matrix<double, 6, 1> omega_32 = log_se3(Inv_se3(pose2) * pose3);
212  Eigen::Matrix4d omega_10_hat = hat_se3(omega_10);
213  Eigen::Matrix4d omega_21_hat = hat_se3(omega_21);
214  Eigen::Matrix4d omega_32_hat = hat_se3(omega_32);
215 
216  // Calculate interpolated poses
217  Eigen::Matrix4d A0 = exp_se3(b0 * omega_10);
218  Eigen::Matrix4d A1 = exp_se3(b1 * omega_21);
219  Eigen::Matrix4d A2 = exp_se3(b2 * omega_32);
220  Eigen::Matrix4d A0dot = b0dot * omega_10_hat * A0;
221  Eigen::Matrix4d A1dot = b1dot * omega_21_hat * A1;
222  Eigen::Matrix4d A2dot = b2dot * omega_32_hat * A2;
223  Eigen::Matrix4d A0dotdot = b0dot * omega_10_hat * A0dot + b0dotdot * omega_10_hat * A0;
224  Eigen::Matrix4d A1dotdot = b1dot * omega_21_hat * A1dot + b1dotdot * omega_21_hat * A1;
225  Eigen::Matrix4d A2dotdot = b2dot * omega_32_hat * A2dot + b2dotdot * omega_32_hat * A2;
226 
227  // Get the interpolated pose
228  Eigen::Matrix4d pose_interp = pose0 * A0 * A1 * A2;
229  R_GtoI = pose_interp.block(0, 0, 3, 3).transpose();
230  p_IinG = pose_interp.block(0, 3, 3, 1);
231 
232  // Get the interpolated velocities
233  // NOTE: Rdot = R*skew(omega) => R^T*Rdot = skew(omega)
234  Eigen::Matrix4d vel_interp = pose0 * (A0dot * A1 * A2 + A0 * A1dot * A2 + A0 * A1 * A2dot);
235  w_IinI = vee(pose_interp.block(0, 0, 3, 3).transpose() * vel_interp.block(0, 0, 3, 3));
236  v_IinG = vel_interp.block(0, 3, 3, 1);
237 
238  // Finally get the interpolated velocities
239  // NOTE: Rdot = R*skew(omega)
240  // NOTE: Rdotdot = Rdot*skew(omega) + R*skew(alpha) => R^T*(Rdotdot-Rdot*skew(omega))=skew(alpha)
241  Eigen::Matrix4d acc_interp = pose0 * (A0dotdot * A1 * A2 + A0 * A1dotdot * A2 + A0 * A1 * A2dotdot + 2 * A0dot * A1dot * A2 +
242  2 * A0 * A1dot * A2dot + 2 * A0dot * A1 * A2dot);
243  Eigen::Matrix3d omegaskew = pose_interp.block(0, 0, 3, 3).transpose() * vel_interp.block(0, 0, 3, 3);
244  alpha_IinI = vee(pose_interp.block(0, 0, 3, 3).transpose() * (acc_interp.block(0, 0, 3, 3) - vel_interp.block(0, 0, 3, 3) * omegaskew));
245  a_IinG = acc_interp.block(0, 3, 3, 1);
246  return true;
247 }
248 
249 bool BsplineSE3::find_bounding_poses(const double timestamp, const AlignedEigenMat4d &poses, double &t0, Eigen::Matrix4d &pose0, double &t1,
250  Eigen::Matrix4d &pose1) {
251 
252  // Set the default values
253  t0 = -1;
254  t1 = -1;
255  pose0 = Eigen::Matrix4d::Identity();
256  pose1 = Eigen::Matrix4d::Identity();
257 
258  // Find the bounding poses
259  bool found_older = false;
260  bool found_newer = false;
261 
262  // Find the bounding poses for interpolation.
263  auto lower_bound = poses.lower_bound(timestamp); // Finds timestamp or next(timestamp) if not available
264  auto upper_bound = poses.upper_bound(timestamp); // Finds next(timestamp)
265 
266  if (lower_bound != poses.end()) {
267  // Check that the lower bound is the timestamp.
268  // If not then we move iterator to previous timestamp so that the timestamp is bounded
269  if (lower_bound->first == timestamp) {
270  found_older = true;
271  } else if (lower_bound != poses.begin()) {
272  --lower_bound;
273  found_older = true;
274  }
275  }
276 
277  if (upper_bound != poses.end()) {
278  found_newer = true;
279  }
280 
281  // If we found the older one, set it
282  if (found_older) {
283  t0 = lower_bound->first;
284  pose0 = lower_bound->second;
285  }
286 
287  // If we found the newest one, set it
288  if (found_newer) {
289  t1 = upper_bound->first;
290  pose1 = upper_bound->second;
291  }
292 
293  // Assert the timestamps
294  if (found_older && found_newer)
295  assert(t0 < t1);
296 
297  // Return true if we found both bounds
298  return (found_older && found_newer);
299 }
300 
301 bool BsplineSE3::find_bounding_control_points(const double timestamp, const AlignedEigenMat4d &poses, double &t0, Eigen::Matrix4d &pose0,
302  double &t1, Eigen::Matrix4d &pose1, double &t2, Eigen::Matrix4d &pose2, double &t3,
303  Eigen::Matrix4d &pose3) {
304 
305  // Set the default values
306  t0 = -1;
307  t1 = -1;
308  t2 = -1;
309  t3 = -1;
310  pose0 = Eigen::Matrix4d::Identity();
311  pose1 = Eigen::Matrix4d::Identity();
312  pose2 = Eigen::Matrix4d::Identity();
313  pose3 = Eigen::Matrix4d::Identity();
314 
315  // Get the two bounding poses
316  bool success = find_bounding_poses(timestamp, poses, t1, pose1, t2, pose2);
317 
318  // Return false if this was a failure
319  if (!success)
320  return false;
321 
322  // Now find the poses that are below and above
323  auto iter_t1 = poses.find(t1);
324  auto iter_t2 = poses.find(t2);
325 
326  // Check that t1 is not the first timestamp
327  if (iter_t1 == poses.begin()) {
328  return false;
329  }
330 
331  // Move the older pose backwards in time
332  // Move the newer one forwards in time
333  auto iter_t0 = --iter_t1;
334  auto iter_t3 = ++iter_t2;
335 
336  // Check that it is valid
337  if (iter_t3 == poses.end()) {
338  return false;
339  }
340 
341  // Set the oldest one
342  t0 = iter_t0->first;
343  pose0 = iter_t0->second;
344 
345  // Set the newest one
346  t3 = iter_t3->first;
347  pose3 = iter_t3->second;
348 
349  // Assert the timestamps
350  if (success) {
351  assert(t0 < t1);
352  assert(t1 < t2);
353  assert(t2 < t3);
354  }
355 
356  // Return true if we found all four bounding poses
357  return success;
358 }
ov_core::BsplineSE3::control_points
AlignedEigenMat4d control_points
Our control SE3 control poses (R_ItoG, p_IinG)
Definition: BsplineSE3.h:171
ov_core::BsplineSE3::feed_trajectory
void feed_trajectory(std::vector< Eigen::VectorXd > traj_points)
Will feed in a series of poses that we will then convert into control points.
Definition: BsplineSE3.cpp:26
PRINT_DEBUG
#define PRINT_DEBUG(x...)
Definition: print.h:97
ov_core::BsplineSE3::AlignedEigenMat4d
std::map< double, Eigen::Matrix4d, std::less< double >, Eigen::aligned_allocator< std::pair< const double, Eigen::Matrix4d > > > AlignedEigenMat4d
Type defintion of our aligned eigen4d matrix: https://eigen.tuxfamily.org/dox/group__TopicStlContaine...
Definition: BsplineSE3.h:168
ov_core::log_se3
Eigen::Matrix< double, 6, 1 > log_se3(Eigen::Matrix4d mat)
SE(3) matrix logarithm.
Definition: quat_ops.h:388
ov_core::exp_se3
Eigen::Matrix4d exp_se3(Eigen::Matrix< double, 6, 1 > vec)
SE(3) matrix exponential function.
Definition: quat_ops.h:332
ov_core::BsplineSE3::timestamp_start
double timestamp_start
Start time of the system.
Definition: BsplineSE3.h:164
ov_core::vee
Eigen::Matrix< double, 3, 1 > vee(const Eigen::Matrix< double, 3, 3 > &w_x)
Returns vector portion of skew-symmetric.
Definition: quat_ops.h:205
ov_core::BsplineSE3::get_velocity
bool get_velocity(double timestamp, Eigen::Matrix3d &R_GtoI, Eigen::Vector3d &p_IinG, Eigen::Vector3d &w_IinI, Eigen::Vector3d &v_IinG)
Gets the angular and linear velocity at a given timestamp.
Definition: BsplineSE3.cpp:127
ov_core::hat_se3
Eigen::Matrix4d hat_se3(const Eigen::Matrix< double, 6, 1 > &vec)
Hat operator for R^6 -> Lie Algebra se(3)
Definition: quat_ops.h:419
BsplineSE3.h
ov_core::BsplineSE3::get_acceleration
bool get_acceleration(double timestamp, Eigen::Matrix3d &R_GtoI, Eigen::Vector3d &p_IinG, Eigen::Vector3d &w_IinI, Eigen::Vector3d &v_IinG, Eigen::Vector3d &alpha_IinI, Eigen::Vector3d &a_IinG)
Gets the angular and linear acceleration at a given timestamp.
Definition: BsplineSE3.cpp:180
ov_core::BsplineSE3::get_pose
bool get_pose(double timestamp, Eigen::Matrix3d &R_GtoI, Eigen::Vector3d &p_IinG)
Gets the orientation and position at a given timestamp.
Definition: BsplineSE3.cpp:92
ov_core::BsplineSE3::find_bounding_poses
static bool find_bounding_poses(const double timestamp, const AlignedEigenMat4d &poses, double &t0, Eigen::Matrix4d &pose0, double &t1, Eigen::Matrix4d &pose1)
Will find the two bounding poses for a given timestamp.
Definition: BsplineSE3.cpp:249
ov_core::quat_2_Rot
Eigen::Matrix< double, 3, 3 > quat_2_Rot(const Eigen::Matrix< double, 4, 1 > &q)
Converts JPL quaterion to SO(3) rotation matrix.
Definition: quat_ops.h:152
ov_core::BsplineSE3::find_bounding_control_points
static bool find_bounding_control_points(const double timestamp, const AlignedEigenMat4d &poses, double &t0, Eigen::Matrix4d &pose0, double &t1, Eigen::Matrix4d &pose1, double &t2, Eigen::Matrix4d &pose2, double &t3, Eigen::Matrix4d &pose3)
Will find two older poses and two newer poses for the current timestamp.
Definition: BsplineSE3.cpp:301
ov_core::BsplineSE3::dt
double dt
Uniform sampling time for our control points.
Definition: BsplineSE3.h:161
ov_core::Inv_se3
Eigen::Matrix4d Inv_se3(const Eigen::Matrix4d &T)
SE(3) matrix analytical inverse.
Definition: quat_ops.h:439
ov_core
Core algorithms for OpenVINS.
Definition: CamBase.h:30


ov_core
Author(s): Patrick Geneva , Kevin Eckenhoff , Guoquan Huang
autogenerated on Mon Jan 22 2024 03:08:17