/* * Copyright 2016 The Cartographer Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CARTOGRAPHER_MAPPING_INTERNAL_3D_IMU_INTEGRATION_H_ #define CARTOGRAPHER_MAPPING_INTERNAL_3D_IMU_INTEGRATION_H_ #include #include #include "Eigen/Core" #include "Eigen/Geometry" #include "cartographer/common/time.h" #include "cartographer/sensor/imu_data.h" #include "cartographer/transform/rigid_transform.h" #include "cartographer/transform/transform.h" #include "glog/logging.h" namespace cartographer { namespace mapping { template struct IntegrateImuResult { Eigen::Matrix delta_velocity; Eigen::Matrix delta_translation; Eigen::Quaternion delta_rotation; }; template IntegrateImuResult IntegrateImu( const RangeType& imu_data, const Eigen::Transform& linear_acceleration_calibration, const Eigen::Transform& angular_velocity_calibration, const common::Time start_time, const common::Time end_time, IteratorType* const it) { CHECK_LE(start_time, end_time); CHECK(*it != imu_data.end()); CHECK_LE((*it)->time, start_time); if (std::next(*it) != imu_data.end()) { CHECK_GT(std::next(*it)->time, start_time); } common::Time current_time = start_time; IntegrateImuResult result = {Eigen::Matrix::Zero(), Eigen::Matrix::Zero(), Eigen::Quaterniond::Identity().cast()}; while (current_time < end_time) { common::Time next_imu_data = common::Time::max(); if (std::next(*it) != imu_data.end()) { next_imu_data = std::next(*it)->time; } common::Time next_time = std::min(next_imu_data, end_time); const T delta_t(common::ToSeconds(next_time - current_time)); const Eigen::Matrix delta_angle = (angular_velocity_calibration * (*it)->angular_velocity.template cast()) * delta_t; result.delta_rotation *= transform::AngleAxisVectorToRotationQuaternion(delta_angle); result.delta_velocity += result.delta_rotation * ((linear_acceleration_calibration * (*it)->linear_acceleration.template cast()) * delta_t); result.delta_translation += delta_t * result.delta_velocity; current_time = next_time; if (current_time == next_imu_data) { ++(*it); } } return result; } // Returns velocity delta in map frame. template IntegrateImuResult IntegrateImu(const RangeType& imu_data, const common::Time start_time, const common::Time end_time, IteratorType* const it) { return IntegrateImu( imu_data, Eigen::Affine3d::Identity(), Eigen::Affine3d::Identity(), start_time, end_time, it); } template struct ExtrapolatePoseResult { transform::Rigid3 pose; Eigen::Matrix velocity; }; // Returns pose and linear velocity at 'time' which is equal to // 'prev_from_tracking' extrapolated using IMU data. template ExtrapolatePoseResult ExtrapolatePoseWithImu( const transform::Rigid3& prev_from_tracking, const Eigen::Matrix& prev_velocity_in_tracking, const common::Time prev_time, const Eigen::Matrix& gravity, const common::Time time, const RangeType& imu_data, IteratorType* const imu_it) { const IntegrateImuResult result = IntegrateImu(imu_data, Eigen::Transform::Identity(), Eigen::Transform::Identity(), prev_time, time, imu_it); const T delta_t = static_cast(common::ToSeconds(time - prev_time)); const Eigen::Matrix translation = prev_from_tracking.translation() + prev_from_tracking.rotation() * (delta_t * prev_velocity_in_tracking + result.delta_translation) - static_cast(.5) * delta_t * delta_t * gravity; const Eigen::Quaternion rotation = prev_from_tracking.rotation() * result.delta_rotation; const Eigen::Matrix velocity = prev_from_tracking.rotation() * (prev_velocity_in_tracking + result.delta_velocity) - delta_t * gravity; return ExtrapolatePoseResult{transform::Rigid3(translation, rotation), velocity}; } // Same as above but given the last two poses. template ExtrapolatePoseResult ExtrapolatePoseWithImu( const transform::Rigid3& prev_from_tracking, const common::Time prev_time, const transform::Rigid3& prev_prev_from_tracking, const common::Time prev_prev_time, const Eigen::Matrix& gravity, const common::Time time, const RangeType& imu_data, IteratorType* const imu_it) { // TODO(danielsievers): Really we should integrate velocities starting from // the midpoint in between two poses, since this is how we fit them to poses // in the optimization. const T prev_delta_t = static_cast(common::ToSeconds(prev_time - prev_prev_time)); const Eigen::Matrix prev_velocity_in_tracking = prev_from_tracking.inverse().rotation() * (prev_from_tracking.translation() - prev_prev_from_tracking.translation()) / prev_delta_t; return ExtrapolatePoseWithImu(prev_from_tracking, prev_velocity_in_tracking, prev_time, gravity, time, imu_data, imu_it); } } // namespace mapping } // namespace cartographer #endif // CARTOGRAPHER_MAPPING_INTERNAL_3D_IMU_INTEGRATION_H_