blob: 19b82e51774812eb626e880b237ccac5198cae57 [file] [log] [blame]
James Kuszmaula60aee12024-02-02 22:57:47 -08001#include "frc971/imu/imu_calibrator_solver.h"
2namespace frc971::imu {
3
4struct ImuCalibratorCostFunctor {
5 ImuCalibratorCostFunctor(
6 const std::vector<std::vector<RawImuReading>> &readings,
7 const std::vector<ImuConfig<double>> &nominal_config,
8 const size_t num_residuals)
9 : readings_(readings),
10 nominal_config_(nominal_config),
11 num_residuals_(num_residuals) {}
12 template <typename S>
13 bool operator()(const S *const *const parameters_ptr, S *residual) const {
14 AllParameters<S> params = AllParameters<S>::PopulateFromParameters(
15 nominal_config_, parameters_ptr);
16 std::vector<ImuConfig<S>> imu_configs;
17 for (const auto &param : params.imus) {
18 imu_configs.push_back(param);
19 }
20 ImuCalibrator<S> calibrator(imu_configs);
21 for (size_t imu_index = 0; imu_index < readings_.size(); ++imu_index) {
22 const auto imu_readings = readings_[imu_index];
23 for (size_t reading_index = 0; reading_index < imu_readings.size();
24 ++reading_index) {
25 calibrator.InsertImu(imu_index, imu_readings[reading_index]);
26 }
27 }
28 calibrator.CalculateResiduals({residual, num_residuals_});
29 return true;
30 }
31 const std::vector<std::vector<RawImuReading>> readings_;
32 const std::vector<ImuConfig<double>> nominal_config_;
33 const size_t num_residuals_;
34};
35
36AllParameters<double> Solve(
37 const std::vector<std::vector<RawImuReading>> &readings,
38 const std::vector<ImuConfig<double>> &nominal_config) {
39 ceres::Problem problem;
40
41 ceres::EigenQuaternionParameterization *quaternion_local_parameterization =
42 new ceres::EigenQuaternionParameterization();
43 AllParameters<double> parameters;
44 std::vector<size_t> num_readings;
45 CHECK_EQ(nominal_config.size(), readings.size());
46 for (size_t imu_index = 0; imu_index < nominal_config.size(); ++imu_index) {
47 const size_t num_params = readings[imu_index].size();
48 parameters.imus.emplace_back(nominal_config[imu_index]);
49 num_readings.push_back(num_params);
50 }
51 // Set up the only cost function (also known as residual). This uses
52 // auto-differentiation to obtain the derivative (jacobian).
53
54 {
55 const size_t num_residuals =
56 ImuCalibrator<double>::CalculateNumResiduals(num_readings);
57 ceres::DynamicCostFunction *cost_function =
58 new ceres::DynamicAutoDiffCostFunction<ImuCalibratorCostFunctor>(
59 new ImuCalibratorCostFunctor(readings, nominal_config,
60 num_residuals));
61
62 auto [vector_parameters, post_populate_methods] =
63 parameters.PopulateParameters(quaternion_local_parameterization,
64 &problem, cost_function);
65
66 cost_function->SetNumResiduals(num_residuals);
67
68 problem.AddResidualBlock(cost_function, new ceres::HuberLoss(1.0),
69 vector_parameters);
70 for (auto &method : post_populate_methods) {
71 method();
72 }
73 }
74
75 // Run the solver!
76 ceres::Solver::Options options;
77 options.minimizer_progress_to_stdout = true;
78 options.gradient_tolerance = 1e-12;
79 options.function_tolerance = 1e-6;
80 options.parameter_tolerance = 1e-6;
81 ceres::Solver::Summary summary;
82 Solve(options, &problem, &summary);
83 LOG(INFO) << summary.FullReport();
84 LOG(INFO) << "Solution is " << (summary.IsSolutionUsable() ? "" : "NOT ")
85 << "usable";
86 LOG(INFO) << "Solution:\n" << parameters.ToString();
87 return parameters;
88}
89} // namespace frc971::imu