My Project 1.0
Holistic Fusion: A Generic Framework for Multimodal Sensor Fusion
Loading...
Searching...
No Matches
ImuBuffer.cpp
Go to the documentation of this file.
1/*
2Copyright 2023 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// Implementation
10
11// CPP
12#include <iomanip>
13#include <iterator>
14
15// Workspace
18
19namespace holistic_fusion {
20
21// Constructor ---------------------------------------------------
22// Constructor
23ImuBuffer::ImuBuffer(const std::shared_ptr<GraphConfig> graphConfigPtr) {
24 // Reset IMU Buffer
25 timeToImuBuffer_.clear();
26 // Parameters
27 useImuSignalLowPassFilter_ = graphConfigPtr->useImuSignalLowPassFilter_;
28 imuRate_ = graphConfigPtr->imuRate_;
29 imuBufferLength_ = graphConfigPtr->imuBufferLength_;
30 verboseLevel_ = graphConfigPtr->verboseLevel_;
31 // If low pass filter is used, initialize it
34 std::make_unique<ImuSignalLowPassFilter>(graphConfigPtr->imuLowPassFilterCutoffFreqHz_, 1.0 / graphConfigPtr->imuRate_);
35 }
36}
37
38// Public --------------------------------------------------------
39// Returns actually added IMU measurements
40Eigen::Matrix<double, 6, 1> ImuBuffer::addToImuBuffer(double ts, const Eigen::Vector3d& linearAcc, const Eigen::Vector3d& angularVel) {
41 // Check that imuBufferLength was set
42 if (imuBufferLength_ < 0) {
43 throw std::runtime_error("GMsfImuBuffer: imuBufferLength has to be set by the user.");
44 }
45
46 // Copy of IMU measurements
47 Eigen::Matrix<double, 6, 1> filteredImuMeas;
48
49 // Potentially low pass filter IMU measurements
51 filteredImuMeas = imuSignalLowPassFilterPtr_->filter(linearAcc, angularVel);
52 } else {
53 filteredImuMeas << linearAcc, angularVel;
54 }
55
56 // Convert to gtsam type
58 imuMeas.timestamp = ts;
59 imuMeas.acceleration = filteredImuMeas.head<3>();
60 imuMeas.angularVelocity = filteredImuMeas.tail<3>();
61
62 // Add to buffer
63 {
64 // Writing to IMU buffer --> acquire mutex
65 const std::lock_guard<std::mutex> writeInBufferLock(writeInBufferMutex_);
66 timeToImuBuffer_[ts] = imuMeas;
67 // Update latest timestamp in buffer
68 if (ts > tLatestInBuffer_) {
70 }
73 }
74
76 std::ostringstream errorStream;
77 errorStream << YELLOW_START << "GMsf-ImuBuffer" << COLOR_END << " IMU Buffer has grown too large. It contains "
78 << timeToImuBuffer_.size() << " measurements instead of " << imuBufferLength_ << ".";
79 throw std::runtime_error(errorStream.str());
80 }
81 }
82
83 return filteredImuMeas;
84}
85
87 // Reading from IMU buffer --> acquire mutex
88 const std::lock_guard<std::mutex> writeInBufferLock(writeInBufferMutex_);
89 return tLatestInBuffer_;
90}
91
92bool ImuBuffer::getImuMeasurementAtTime(holistic_fusion::ImuMeasurement& returnedImuMeasurement, const double ts) {
93 // Get IMU measurement
94 TimeToImuMap::iterator imuItr = timeToImuBuffer_.find(ts);
95 if (imuItr == timeToImuBuffer_.end()) {
96 std::ostringstream errorStream;
97 errorStream << YELLOW_START << "GMsf-ImuBuffer" << RED_START << " IMU Measurement at time " << std::fixed << ts
98 << " not found in buffer.";
99 return false;
100 }
101 returnedImuMeasurement = imuItr->second;
102 return true;
103}
104
106 TimeToImuMap::iterator endItr = --(timeToImuBuffer_.end());
107 TimeToImuMap::iterator previousItr = --(--(timeToImuBuffer_.end()));
108
109 // Write into IMU Map
110 imuMap[previousItr->first] = previousItr->second;
111 imuMap[endItr->first] = endItr->second;
112}
113
114bool ImuBuffer::estimateAttitudeFromImu(gtsam::Rot3& initAttitude, double& gravityMagnitude, Eigen::Vector3d& gyrBias) {
115 // Make sure that imuBuffer is long enough
117 throw std::runtime_error(
118 "ImuBufferLength is not large enough for "
119 "initialization. Must be at least 1 second.");
120 }
121
122 // Get timestamp of first message for lookup
124 return false;
125 } else {
126 // Accumulate Acceleration part of IMU Messages
127 Eigen::Vector3d initAccMean(0.0, 0.0, 0.0), initGyrMean(0.0, 0.0, 0.0);
128 for (auto& itr : timeToImuBuffer_) {
129 initAccMean += itr.second.acceleration;
130 initGyrMean += itr.second.angularVelocity;
131 }
132
133 // Average IMU measurements and set assumed gravity direction
134 initAccMean /= timeToImuBuffer_.size();
135 gravityMagnitude = initAccMean.norm();
136 Eigen::Vector3d gUnitVecInWorld = Eigen::Vector3d(0.0, 0.0, 1.0); // ROS convention
137
138 // Normalize gravity vectors to remove the affect of gravity magnitude from
139 // place-to-place
140 initAccMean.normalize();
141 initAttitude = gtsam::Rot3(Eigen::Quaterniond().setFromTwoVectors(initAccMean, gUnitVecInWorld));
142
143 // Gyro
144 initGyrMean /= timeToImuBuffer_.size();
145 gyrBias = initGyrMean;
146
147 // Calculate robot initial orientation using gravity vector.
148 std::cout << YELLOW_START << "GMsf-ImuBuffer" << COLOR_END << " Gravity Magnitude: " << gravityMagnitude << std::endl;
149 std::cout << YELLOW_START << "GMsf-ImuBuffer" << COLOR_END << " Mean IMU Acceleration Vector(x,y,z): " << initAccMean.transpose()
150 << " - Gravity Unit Vector(x,y,z): " << gUnitVecInWorld.transpose() << std::endl;
151 std::cout << YELLOW_START << "GMsf-ImuBuffer" << GREEN_START << " RPY (deg): " << initAttitude.rpy().transpose() * (180.0 / M_PI)
152 << COLOR_END << std::endl;
153 std::cout << YELLOW_START << "GMsf-ImuBuffer" << COLOR_END << " Gyro bias(x,y,z): " << initGyrMean.transpose() << std::endl;
154 }
155 return true;
156}
157
158bool ImuBuffer::getIMUBufferIteratorsInInterval(const double tsStart, const double tsEnd, TimeToImuMap::iterator& startIterator,
159 TimeToImuMap::iterator& endIterator) {
160 // Check if timestamps are in correct order
161 if (tsStart >= tsEnd) {
162 std::cerr << YELLOW_START << "GMsf-ImuBuffer" << RED_START << " IMU Lookup Timestamps are not correct ts_start(" << std::fixed
163 << tsStart << ") >= ts_end(" << tsEnd << ")\n";
164 return false;
165 }
166
167 // Get Iterator Belonging to ts_start
168 startIterator = timeToImuBuffer_.lower_bound(tsStart);
169 // Get Iterator Belonging to ts_end
170 endIterator = timeToImuBuffer_.lower_bound(tsEnd);
171
172 // Check if it is first value in the buffer which means there is no value
173 // before to interpolate with
174 if (startIterator == timeToImuBuffer_.begin()) {
175 std::cerr << YELLOW_START << "GMsf-ImuBuffer" << RED_START
176 << " Lookup requires first message of IMU buffer, cannot "
177 "Interpolate back, "
178 "Lookup Start/End: "
179 << std::fixed << tsStart << "/" << tsEnd << ", Buffer Start/End: " << timeToImuBuffer_.begin()->first << "/"
180 << timeToImuBuffer_.rbegin()->first << std::endl;
181 return false;
182 }
183
184 // Check if lookup start time is ahead of buffer start time
185 if (startIterator == timeToImuBuffer_.end()) {
186 std::cerr << YELLOW_START << "GMsf-ImuBuffer" << RED_START
187 << " IMU Lookup start time ahead latest IMU message in the "
188 "buffer, lookup: "
189 << tsStart << ", latest IMU: " << timeToImuBuffer_.rbegin()->first << std::endl;
190 return false;
191 }
192
193 // Check if last value is valid
194 if (endIterator == timeToImuBuffer_.end()) {
195 std::cerr << YELLOW_START << "GMsf-ImuBuffer" << RED_START << " Lookup is past IMU buffer, with lookup Start/End: " << std::fixed
196 << tsStart << "/" << tsEnd << " and latest IMU: " << timeToImuBuffer_.rbegin()->first << std::endl;
197 --endIterator;
198 }
199
200 // Check if two IMU messages are different
201 if (startIterator == endIterator) {
202 std::cerr << YELLOW_START << "GMsf-ImuBuffer" << RED_START
203 << " Not Enough IMU values between timestamps , with Start/End: " << std::fixed << tsStart << "/" << tsEnd
204 << ", with diff: " << tsEnd - tsStart << std::endl;
205 return false;
206 }
207
208 // If everything is good
209 return true;
210}
211
212bool ImuBuffer::getClosestImuMeasurement(double& returnedImuTimestamp, ImuMeasurement& returnedImuMeasurement,
213 const double maxSearchDeviation, const double tK) {
214 const std::lock_guard<std::mutex> writeInBufferLock(writeInBufferMutex_);
215 if (timeToImuBuffer_.empty()) {
216 std::cerr << YELLOW_START << "GMsf-ImuBuffer: Buffer is empty!" << COLOR_END << std::endl;
217 return false;
218 }
219
220 auto closestIterator = timeToImuBuffer_.lower_bound(tK);
221 if (closestIterator == timeToImuBuffer_.end()) {
222 --closestIterator;
223 } else if (closestIterator != timeToImuBuffer_.begin()) {
224 const auto lowerIterator = std::prev(closestIterator);
225 if (tK - lowerIterator->first < closestIterator->first - tK) {
226 closestIterator = lowerIterator;
227 }
228 }
229 returnedImuTimestamp = closestIterator->first;
230 returnedImuMeasurement = closestIterator->second;
231 const double timeDeviation = returnedImuTimestamp - tK;
232
233 if (verboseLevel_ >= 2) {
234 std::cout << YELLOW_START << "GMsf-ImuBuffer" << COLOR_END << " Searched time step: " << std::setprecision(14) << tK << std::endl;
235 std::cout << YELLOW_START << "GMsf-ImuBuffer" << COLOR_END << " Found time step: " << std::setprecision(14) << returnedImuTimestamp
236 << std::endl;
237 std::cout << YELLOW_START << "GMsf-ImuBuffer" << COLOR_END << " Time Deviation (t_graph-t_request): " << 1000 * timeDeviation << " ms"
238 << std::endl;
239 std::cout << YELLOW_START << "GMsf-TimeKeyBuffer" << COLOR_END << " Latest IMU timestamp: " << tLatestInBuffer_
240 << ", hence absolut delay of measurement is " << 1000 * (tLatestInBuffer_ - tK) << "ms." << std::endl;
241 }
242
243 // Check for error and warn user
244 if (std::abs(timeDeviation) > maxSearchDeviation) {
245 return false;
246 }
247
248 return true;
249}
250
251gtsam::NavState ImuBuffer::integrateNavStateFromTimestamp(const double& tsStart, const double& tsEnd, const gtsam::NavState& stateStart,
252 const gtsam::imuBias::ConstantBias& imuBias,
253 const Eigen::Vector3d& W_gravityVector) {
254 // Get iterators
255 TimeToImuMap::iterator startIterator, nextToStartIterator, endIterator;
256 // Get IMU iterators in interval
257 if (!getIMUBufferIteratorsInInterval(tsStart, tsEnd, startIterator, endIterator)) {
258 throw std::runtime_error("Could not get IMU iterators in interval");
259 }
260 nextToStartIterator = startIterator;
261 ++nextToStartIterator;
262
263 // Propagated state
264 gtsam::NavState propagatedState = stateStart;
265
266 // For Loop for IMU integration
267 Eigen::Vector3d i_measAcceleration, i_measAngularVelocity;
268 for (; startIterator != endIterator; ++startIterator, ++nextToStartIterator) {
269 // Get IMU measurement
270 i_measAcceleration = imuBias.correctAccelerometer(nextToStartIterator->second.acceleration);
271 i_measAngularVelocity = imuBias.correctGyroscope(nextToStartIterator->second.angularVelocity);
272
273 // Calculate dt
274 double dt = nextToStartIterator->first - startIterator->first;
275
276 // Update propagated state
277 Eigen::Vector3d i_gravityVector = propagatedState.R().transpose() * W_gravityVector;
278 propagatedState =
279 propagatedState.update(i_gravityVector + i_measAcceleration, i_measAngularVelocity, dt);
280 }
281 return propagatedState;
282}
283} // namespace holistic_fusion
#define COLOR_END
#define GREEN_START
#define RED_START
#define YELLOW_START
ImuBuffer(const std::shared_ptr< GraphConfig > graphConfigPtr)
Definition ImuBuffer.cpp:23
bool getClosestImuMeasurement(double &returnedImuTimestamp, ImuMeasurement &returnedImuMeasurement, const double maxSearchDeviation, const double tK)
TimeToImuMap timeToImuBuffer_
Definition ImuBuffer.hpp:76
gtsam::NavState integrateNavStateFromTimestamp(const double &tsStart, const double &tsEnd, const gtsam::NavState &stateStart, const gtsam::imuBias::ConstantBias &imuBias, const Eigen::Vector3d &W_gravityVector)
std::unique_ptr< ImuSignalLowPassFilter > imuSignalLowPassFilterPtr_
Definition ImuBuffer.hpp:86
Eigen::Matrix< double, 6, 1 > addToImuBuffer(double ts, const Eigen::Vector3d &linearAcc, const Eigen::Vector3d &angularVel)
Definition ImuBuffer.cpp:40
void getLastTwoMeasurements(TimeToImuMap &imuMap)
bool getImuMeasurementAtTime(holistic_fusion::ImuMeasurement &returnedImuMeasurement, const double ts)
Definition ImuBuffer.cpp:92
bool estimateAttitudeFromImu(gtsam::Rot3 &initAttitude, double &gravityMagnitude, Eigen::Vector3d &gyrBias)
Determine initial IMU pose w.r.t to gravity vector pointing up.
bool getIMUBufferIteratorsInInterval(const double tsStart, const double tsEnd, TimeToImuMap::iterator &startIterator, TimeToImuMap::iterator &endIterator)
std::map< double, holistic_fusion::ImuMeasurement, std::less< double >, Eigen::aligned_allocator< std::pair< const double, holistic_fusion::ImuMeasurement > > > TimeToImuMap
Definition ImuBuffer.hpp:35