Add IMU orientation correction to the pose graph optimization. (#361)
Orientation of the IMU, most importantly pitch, is crucial to correctly track changes in altitude. Each trajectory gets optimization variables to correct for small errors in the IMU orientation. Also adds separate gravity constants for each trajectory. The idea is that different trajectories might use different hardware and slightly disagree about the strength of gravity. PAIR=damonkohlermaster
parent
19da0ab7c8
commit
955e190166
|
@ -41,9 +41,14 @@ class AccelerationCostFunction {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool operator()(const T* const middle_rotation, const T* const start_position,
|
bool operator()(const T* const middle_rotation, const T* const start_position,
|
||||||
const T* const middle_position, const T* const end_position,
|
const T* const middle_position, const T* const end_position,
|
||||||
const T* const gravity_constant, T* residual) const {
|
const T* const gravity_constant,
|
||||||
|
const T* const imu_calibration, T* residual) const {
|
||||||
|
const Eigen::Quaternion<T> eigen_imu_calibration(
|
||||||
|
imu_calibration[0], imu_calibration[1], imu_calibration[2],
|
||||||
|
imu_calibration[3]);
|
||||||
const Eigen::Matrix<T, 3, 1> imu_delta_velocity =
|
const Eigen::Matrix<T, 3, 1> imu_delta_velocity =
|
||||||
ToEigen(middle_rotation) * delta_velocity_imu_frame_.cast<T>() -
|
ToEigen(middle_rotation) * eigen_imu_calibration *
|
||||||
|
delta_velocity_imu_frame_.cast<T>() -
|
||||||
*gravity_constant *
|
*gravity_constant *
|
||||||
(0.5 * (first_delta_time_seconds_ + second_delta_time_seconds_) *
|
(0.5 * (first_delta_time_seconds_ + second_delta_time_seconds_) *
|
||||||
Eigen::Vector3d::UnitZ())
|
Eigen::Vector3d::UnitZ())
|
||||||
|
|
|
@ -36,13 +36,17 @@ class RotationCostFunction {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool operator()(const T* const start_rotation, const T* const end_rotation,
|
bool operator()(const T* const start_rotation, const T* const end_rotation,
|
||||||
T* residual) const {
|
const T* const imu_calibration, T* residual) const {
|
||||||
const Eigen::Quaternion<T> start(start_rotation[0], start_rotation[1],
|
const Eigen::Quaternion<T> start(start_rotation[0], start_rotation[1],
|
||||||
start_rotation[2], start_rotation[3]);
|
start_rotation[2], start_rotation[3]);
|
||||||
const Eigen::Quaternion<T> end(end_rotation[0], end_rotation[1],
|
const Eigen::Quaternion<T> end(end_rotation[0], end_rotation[1],
|
||||||
end_rotation[2], end_rotation[3]);
|
end_rotation[2], end_rotation[3]);
|
||||||
|
const Eigen::Quaternion<T> eigen_imu_calibration(
|
||||||
|
imu_calibration[0], imu_calibration[1], imu_calibration[2],
|
||||||
|
imu_calibration[3]);
|
||||||
const Eigen::Quaternion<T> error =
|
const Eigen::Quaternion<T> error =
|
||||||
end.conjugate() * start * delta_rotation_imu_frame_.cast<T>();
|
end.conjugate() * start * eigen_imu_calibration *
|
||||||
|
delta_rotation_imu_frame_.cast<T>() * eigen_imu_calibration.conjugate();
|
||||||
residual[0] = scaling_factor_ * error.x();
|
residual[0] = scaling_factor_ * error.x();
|
||||||
residual[1] = scaling_factor_ * error.y();
|
residual[1] = scaling_factor_ * error.y();
|
||||||
residual[2] = scaling_factor_ * error.z();
|
residual[2] = scaling_factor_ * error.z();
|
||||||
|
|
|
@ -191,8 +191,12 @@ void OptimizationProblem::Solve(const std::vector<Constraint>& constraints) {
|
||||||
|
|
||||||
// Add constraints based on IMU observations of angular velocities and
|
// Add constraints based on IMU observations of angular velocities and
|
||||||
// linear acceleration.
|
// linear acceleration.
|
||||||
|
trajectory_data_.resize(imu_data_.size());
|
||||||
for (size_t trajectory_id = 0; trajectory_id != node_data_.size();
|
for (size_t trajectory_id = 0; trajectory_id != node_data_.size();
|
||||||
++trajectory_id) {
|
++trajectory_id) {
|
||||||
|
TrajectoryData& trajectory_data = trajectory_data_.at(trajectory_id);
|
||||||
|
problem.AddParameterBlock(trajectory_data.imu_calibration.data(), 4,
|
||||||
|
new ceres::QuaternionParameterization());
|
||||||
const std::deque<ImuData>& imu_data = imu_data_.at(trajectory_id);
|
const std::deque<ImuData>& imu_data = imu_data_.at(trajectory_id);
|
||||||
CHECK(!imu_data.empty());
|
CHECK(!imu_data.empty());
|
||||||
// TODO(whess): Add support for empty trajectories.
|
// TODO(whess): Add support for empty trajectories.
|
||||||
|
@ -233,7 +237,7 @@ void OptimizationProblem::Solve(const std::vector<Constraint>& constraints) {
|
||||||
result_center_to_center.delta_velocity;
|
result_center_to_center.delta_velocity;
|
||||||
problem.AddResidualBlock(
|
problem.AddResidualBlock(
|
||||||
new ceres::AutoDiffCostFunction<AccelerationCostFunction, 3, 4, 3,
|
new ceres::AutoDiffCostFunction<AccelerationCostFunction, 3, 4, 3,
|
||||||
3, 3, 1>(
|
3, 3, 1, 4>(
|
||||||
new AccelerationCostFunction(
|
new AccelerationCostFunction(
|
||||||
options_.acceleration_weight(), delta_velocity,
|
options_.acceleration_weight(), delta_velocity,
|
||||||
common::ToSeconds(first_duration),
|
common::ToSeconds(first_duration),
|
||||||
|
@ -242,14 +246,16 @@ void OptimizationProblem::Solve(const std::vector<Constraint>& constraints) {
|
||||||
C_nodes[trajectory_id].at(node_index - 1).translation(),
|
C_nodes[trajectory_id].at(node_index - 1).translation(),
|
||||||
C_nodes[trajectory_id].at(node_index).translation(),
|
C_nodes[trajectory_id].at(node_index).translation(),
|
||||||
C_nodes[trajectory_id].at(node_index + 1).translation(),
|
C_nodes[trajectory_id].at(node_index + 1).translation(),
|
||||||
&gravity_constant_);
|
&trajectory_data.gravity_constant,
|
||||||
|
trajectory_data.imu_calibration.data());
|
||||||
}
|
}
|
||||||
problem.AddResidualBlock(
|
problem.AddResidualBlock(
|
||||||
new ceres::AutoDiffCostFunction<RotationCostFunction, 3, 4, 4>(
|
new ceres::AutoDiffCostFunction<RotationCostFunction, 3, 4, 4, 4>(
|
||||||
new RotationCostFunction(options_.rotation_weight(),
|
new RotationCostFunction(options_.rotation_weight(),
|
||||||
result.delta_rotation)),
|
result.delta_rotation)),
|
||||||
nullptr, C_nodes[trajectory_id].at(node_index - 1).rotation(),
|
nullptr, C_nodes[trajectory_id].at(node_index - 1).rotation(),
|
||||||
C_nodes[trajectory_id].at(node_index).rotation());
|
C_nodes[trajectory_id].at(node_index).rotation(),
|
||||||
|
trajectory_data.imu_calibration.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +267,20 @@ void OptimizationProblem::Solve(const std::vector<Constraint>& constraints) {
|
||||||
|
|
||||||
if (options_.log_solver_summary()) {
|
if (options_.log_solver_summary()) {
|
||||||
LOG(INFO) << summary.FullReport();
|
LOG(INFO) << summary.FullReport();
|
||||||
LOG(INFO) << "Gravity was: " << gravity_constant_;
|
for (size_t trajectory_id = 0; trajectory_id != trajectory_data_.size();
|
||||||
|
++trajectory_id) {
|
||||||
|
if (trajectory_id != 0) {
|
||||||
|
LOG(INFO) << "Trajectory " << trajectory_id << ":";
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Gravity was: "
|
||||||
|
<< trajectory_data_[trajectory_id].gravity_constant;
|
||||||
|
LOG(INFO) << "IMU correction was: "
|
||||||
|
<< common::RadToDeg(
|
||||||
|
2. *
|
||||||
|
std::acos(
|
||||||
|
trajectory_data_[trajectory_id].imu_calibration[0]))
|
||||||
|
<< " deg";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the result.
|
// Store the result.
|
||||||
|
|
|
@ -75,12 +75,17 @@ class OptimizationProblem {
|
||||||
const std::vector<std::vector<SubmapData>>& submap_data() const;
|
const std::vector<std::vector<SubmapData>>& submap_data() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct TrajectoryData {
|
||||||
|
double gravity_constant = 9.8;
|
||||||
|
std::array<double, 4> imu_calibration{{1., 0., 0., 0.}};
|
||||||
|
};
|
||||||
|
|
||||||
mapping::sparse_pose_graph::proto::OptimizationProblemOptions options_;
|
mapping::sparse_pose_graph::proto::OptimizationProblemOptions options_;
|
||||||
FixZ fix_z_;
|
FixZ fix_z_;
|
||||||
std::vector<std::deque<ImuData>> imu_data_;
|
std::vector<std::deque<ImuData>> imu_data_;
|
||||||
std::vector<std::vector<NodeData>> node_data_;
|
std::vector<std::vector<NodeData>> node_data_;
|
||||||
std::vector<std::vector<SubmapData>> submap_data_;
|
std::vector<std::vector<SubmapData>> submap_data_;
|
||||||
double gravity_constant_ = 9.8;
|
std::vector<TrajectoryData> trajectory_data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sparse_pose_graph
|
} // namespace sparse_pose_graph
|
||||||
|
|
Loading…
Reference in New Issue