My Project 1.0
Holistic Fusion: A Generic Framework for Multimodal Sensor Fusion
Loading...
Searching...
No Matches
TrajectoryAlignment.cpp
Go to the documentation of this file.
1/*
2Copyright 2022 by Julian Nubert, Robotic Systems Lab, ETH Zurich.
3All rights reserved.
4This file is released under the "BSD-3-Clause License".
5Please see the LICENSE file that has been included as part of this package.
6 */
7
8// C++
9#include <iostream>
10
11// Package
13
14namespace holistic_fusion {
15
16// Public -------------------------------------------------------------------
18 std::cout << YELLOW_START << "TrajectoryAlignment" << GREEN_START << " Created Trajectory Alignment instance." << COLOR_END << std::endl;
19}
20
21std::vector<std::pair<double, Eigen::Vector3d>> TrajectoryAlignment::getSe3Trajectory() {
22 std::vector<std::pair<double, Eigen::Vector3d>> trajectory;
23 std::pair<double, Eigen::Vector3d> onePose;
24 for (auto pose : se3Trajectory_.poses()) {
25 onePose.first = pose.time();
26 onePose.second = pose.position();
27 trajectory.push_back(onePose);
28 }
29 return trajectory;
30}
31
32std::vector<std::pair<double, Eigen::Vector3d>> TrajectoryAlignment::getR3Trajectory() {
33 std::vector<std::pair<double, Eigen::Vector3d>> trajectory;
34 std::pair<double, Eigen::Vector3d> onePose;
35 for (auto pose : r3Trajectory_.poses()) {
36 onePose.first = pose.time();
37 onePose.second = pose.position();
38 trajectory.push_back(onePose);
39 }
40 return trajectory;
41}
42
43void TrajectoryAlignment::addSe3Position(Eigen::Vector3d position, double time) {
44 const std::lock_guard<std::mutex> alignmentLock(alignmentMutex);
45 se3Trajectory_.addPose(position, time);
46}
47
48void TrajectoryAlignment::addR3Position(Eigen::Vector3d position, double time) {
49 const std::lock_guard<std::mutex> alignmentLock(alignmentMutex);
50 r3Trajectory_.addPose(position, time);
51}
52
53bool TrajectoryAlignment::associateTrajectories(Trajectory& trajectoryA, Trajectory& trajectoryB, Trajectory& newTrajectoryA,
54 Trajectory& newTrajectoryB) {
55 // matching trajectories with their timestamps
56 bool swapped = false;
57 if (trajectoryA.poses().size() < trajectoryB.poses().size()) {
58 std::swap(trajectoryA, trajectoryB);
59 swapped = true;
60 }
61
62 int indexB = 0;
63 int matchingIndexA = -1;
64 int lastMatchingIndexA = 0;
65 bool matched = false;
66 for (const auto& poseB : trajectoryB.poses()) { // shorter trajectory.
67 int indexA = 0;
68 double diff = std::numeric_limits<double>::max();
69 for (auto it = trajectoryA.poses().begin(); it != trajectoryA.poses().end(); ++it) {
70 if ((abs(poseB.time() - it->time())) < diff && abs((poseB.time() - it->time())) < 0.1) {
71 diff = abs(poseB.time() - it->time());
72 matchingIndexA = indexA;
73 // lastMatchingIndexA = matchingIndexA;
74 matched = true;
75 }
76 ++indexA;
77 }
78
79 if (matched) {
80 newTrajectoryA.addPose(trajectoryA.poses().at(matchingIndexA));
81 newTrajectoryB.addPose(trajectoryB.poses().at(indexB));
82 matched = false;
83 }
84 ++indexB;
85 }
86
87 if (swapped) {
88 std::swap(newTrajectoryA, newTrajectoryB);
89 }
90
91 return true;
92}
93
94bool TrajectoryAlignment::trajectoryAlignment(Trajectory& trajectoryA, Trajectory& trajectoryB, Eigen::Isometry3d& returnTransform) {
95 // Fill matrices to use Eigen Umeyama function
96 const int numberOfMeasurements = std::min(trajectoryA.poses().size(), trajectoryB.poses().size());
97 if (numberOfMeasurements < 2) return false;
98
99 Eigen::MatrixXd posesA;
100 Eigen::MatrixXd posesB;
101 posesA.resize(3, numberOfMeasurements);
102 posesB.resize(3, numberOfMeasurements);
103
104 for (unsigned i = 0; i < numberOfMeasurements; ++i) {
105 posesA.col(i) = trajectoryA.poses().at(i).position();
106 posesB.col(i) = trajectoryB.poses().at(i).position();
107 }
108
109 // Umeyama Alignment
110 Eigen::Matrix4d transform = umeyama(posesB, posesA, false);
111 returnTransform = Eigen::Isometry3d(transform);
112 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START << " Umeyama transform: " << std::endl
113 << COLOR_END << returnTransform.matrix() << std::endl;
114
115 return true;
116}
117
118bool TrajectoryAlignment::alignTrajectories(double& yaw, Eigen::Isometry3d& returnTransform) {
119 // Containers for running data processing
120 Trajectory copySe3Trajectory;
121 Trajectory copyR3Trajectory;
122 {
123 // Mutex for alignment Flag
124 const std::lock_guard<std::mutex> alignmentLock(alignmentMutex);
125 // Copy Trajectories
126 copySe3Trajectory = se3Trajectory_;
127 copyR3Trajectory = r3Trajectory_;
128 }
129
130 // Check whether we are standing still
131 if (copySe3Trajectory.isStanding(se3Rate_, noMovementTime_, noMovementDistance_) ||
133 // Mutex for alignment Flag
134 const std::lock_guard<std::mutex> alignmentLock(alignmentMutex);
135 se3Trajectory_.poses().clear();
136 r3Trajectory_.poses().clear();
137 if (r3Trajectory_.poses().size() % 10 == 0) {
138 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START << " No movement detected. Clearing trajectories." << COLOR_END
139 << std::endl;
140 }
142 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START << " First Alignment Try." << COLOR_END << std::endl;
144 }
145 return false;
146 }
147
148 // Status
149 if (copyR3Trajectory.poses().size() % 10 == 0) {
150 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START << " Current Distance of Se3 / R3 [m]: " << COLOR_END
151 << copySe3Trajectory.distance() << " / " << copyR3Trajectory.distance() << " of required [m] " << minDistanceHeadingInit_
152 << std::endl;
153 }
154
155 // Perform Checks
156 if (copySe3Trajectory.distance() < minDistanceHeadingInit_) {
157 return false;
158 }
159 if (copyR3Trajectory.distance() < minDistanceHeadingInit_) {
160 return false;
161 }
162
163 // Status that all checks passed
164 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START << " All checks passed. Trying to align." << COLOR_END << std::endl;
165
166 // Cutting of Timestamps Outside the Joint Time Window
167 double startTime = std::max(copySe3Trajectory.poses().front().time(), copyR3Trajectory.poses().front().time());
168 double endTime = std::min(copySe3Trajectory.poses().back().time(), copyR3Trajectory.poses().back().time());
169 // Print
170 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START << " Trajectories A and B before cutting of size: " << COLOR_END
171 << copySe3Trajectory.poses().size() << " / " << copyR3Trajectory.poses().size() << std::endl;
172 // Cut
173 copySe3Trajectory.cutTrajectory(startTime, endTime);
174 copyR3Trajectory.cutTrajectory(startTime, endTime);
175 // Print
176 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START
177 << " Trajectories A and B after cutting of size and time: " << COLOR_END << copySe3Trajectory.poses().size() << " / "
178 << copyR3Trajectory.poses().size() << std::endl;
179 // Downsample the longer trajectory to the shorter one
180 int downsampleFactor = 1;
181 if (copySe3Trajectory.poses().size() > copyR3Trajectory.poses().size()) {
182 downsampleFactor = std::floor(copySe3Trajectory.poses().size() / copyR3Trajectory.poses().size());
183 copySe3Trajectory.downsample(downsampleFactor);
184 } else {
185 downsampleFactor = std::floor(copyR3Trajectory.poses().size() / copySe3Trajectory.poses().size());
186 copyR3Trajectory.downsample(downsampleFactor);
187 }
188 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START << " Downsample Factor: " << COLOR_END << downsampleFactor
189 << std::endl;
190 // Print
191 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START << " Trajectories A and B after downsampling: " << COLOR_END
192 << copySe3Trajectory.poses().size() << " / " << copyR3Trajectory.poses().size() << std::endl;
193
194 // Align Trajectories
195 Trajectory newSe3Trajectory;
196 Trajectory newR3Trajectory;
197 // Associate Trajectories
198 // Difference of trajectory size should not be larger than 10%
199 bool sizeDeviatesTooMuch = (std::abs(static_cast<int>(copySe3Trajectory.poses().size() - copyR3Trajectory.poses().size())) >
200 0.1 * std::min(copySe3Trajectory.poses().size(), copyR3Trajectory.poses().size()));
201 // If the trajectories deviate too much, associate them
202 if (sizeDeviatesTooMuch) {
203 associateTrajectories(copySe3Trajectory, copyR3Trajectory, newSe3Trajectory, newR3Trajectory);
204 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START
205 << " Trajectories Associated, as after downsampling they were different in size." << COLOR_END << std::endl;
206 }
207 // If the trajectories do not deviate too much, just copy them
208 else {
209 newSe3Trajectory = copySe3Trajectory;
210 newR3Trajectory = copyR3Trajectory;
211 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START
212 << " Trajectories copied, as after downsampling they were similar in size." << COLOR_END << std::endl;
213 }
214
215 // Update Trajectories
216 copySe3Trajectory = newSe3Trajectory;
217 copyR3Trajectory = newR3Trajectory;
218 Eigen::Isometry3d alignmentTransform;
219 // Trajectory Alignment
220 if (!trajectoryAlignment(newR3Trajectory, newSe3Trajectory, alignmentTransform)) {
221 std::cout << "TrajectoryAlignment::initializeYaw trajectoryAlignment failed." << std::endl;
222 return false;
223 } else {
224 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START << " Trajectories Aligned." << COLOR_END << std::endl;
225 }
226 // Math --> Rotation to Yaw
227 Eigen::Matrix3d rotation = alignmentTransform.rotation();
228 double pitch = -asin(rotation(2, 0));
229 double roll = atan2(rotation(2, 1), rotation(2, 2));
230 yaw = atan2(rotation(1, 0) / cos(pitch), rotation(0, 0) / cos(pitch));
231 std::cout << YELLOW_START << "Trajectory Alignment" << GREEN_START << " Initial Roll/Pitch/Yaw(deg):" << COLOR_END << roll * 180 / M_PI
232 << "," << pitch * 180 / M_PI << "," << yaw * 180 / M_PI << std::endl;
233 // Return
234 returnTransform = alignmentTransform;
235 return true;
236}
237
238} // namespace holistic_fusion
#define COLOR_END
#define GREEN_START
#define YELLOW_START
void addR3Position(Eigen::Vector3d position, double time)
std::vector< std::pair< double, Eigen::Vector3d > > getSe3Trajectory()
std::vector< std::pair< double, Eigen::Vector3d > > getR3Trajectory()
void addSe3Position(Eigen::Vector3d position, double time)
bool associateTrajectories(Trajectory &trajectoryA, Trajectory &trajectoryB, Trajectory &newTrajectoryA, Trajectory &newTrajectoryB)
bool trajectoryAlignment(Trajectory &trajectoryA, Trajectory &trajectoryB, Eigen::Isometry3d &returnTransform)
bool alignTrajectories(double &yaw, Eigen::Isometry3d &returnTransform)
Class for holding a trajectory.
Definition Trajectory.h:12
void addPose(Eigen::Vector3d position, double time)
Definition Trajectory.h:18
void cutTrajectory(const double &startTime, const double &endTime)
Definition Trajectory.h:54
void downsample(const int &factor)
Definition Trajectory.h:65
std::vector< Pose > poses() const
Definition Trajectory.h:51
bool isStanding(const double rate, const double seconds, const double &noMovementThreshold)
Definition Trajectory.h:37