diff --git a/CMakeLists.txt b/CMakeLists.txt index d15dc6a79549afa2c0ca3b3f8d29ab7c9e161f40..c71a9885f3622a706f27d03cc06424378af29c7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,19 +79,15 @@ ENDIF() option(_WOLF_TRACE "Enable wolf tracing macro" ON) -# option(BUILD_EXAMPLES "Build examples" OFF) set(BUILD_TESTS true) -set(BUILD_EXAMPLES false) # Does this has any other interest # but for the examples ? # yes, for the tests ! -IF(BUILD_EXAMPLES OR BUILD_TESTS) +IF(BUILD_DEMOS OR BUILD_TESTS) string(TOUPPER ${PROJECT_NAME} UPPER_NAME) set(_WOLF_ROOT_DIR ${CMAKE_SOURCE_DIR}) -ENDIF(BUILD_EXAMPLES OR BUILD_TESTS) - -message("UPPER_NAME ${UPPER_NAME}") +ENDIF(BUILD_DEMOS OR BUILD_TESTS) #find dependencies. @@ -212,7 +208,6 @@ TARGET_LINK_LIBRARIES(${PLUGIN_NAME} ${wolfcore_LIBRARIES}) # TARGET_LINK_LIBRARIES(${PLUGIN_NAME} wolfimu_INCLUDE_DIRS) IF(BUILD_DEMOS) - #Build examples MESSAGE("Building demos.") ADD_SUBDIRECTORY(demos) ENDIF(BUILD_DEMOS) diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt index e8896eccf78f6f4aef3138b1c6914646c788617c..e070121147a47b1e73fcd72e714ac3afa01e8f42 100644 --- a/demos/CMakeLists.txt +++ b/demos/CMakeLists.txt @@ -45,8 +45,8 @@ target_link_libraries(solo_real_povcdl_estimation z ) -add_executable(solo_real_pov_estimation solo_real_pov_estimation.cpp) -target_link_libraries(solo_real_pov_estimation +add_executable(solo_imu_kine solo_imu_kine.cpp) +target_link_libraries(solo_imu_kine mcapi_utils ${wolfcore_LIBRARIES} ${wolfimu_LIBRARIES} @@ -56,8 +56,19 @@ target_link_libraries(solo_real_pov_estimation z ) -add_executable(solo_mocap_imu solo_mocap_imu.cpp) -target_link_libraries(solo_mocap_imu +add_executable(solo_kine_mocap solo_kine_mocap.cpp) +target_link_libraries(solo_kine_mocap + mcapi_utils + ${wolfcore_LIBRARIES} + ${wolfimu_LIBRARIES} + ${PLUGIN_NAME} + ${pinocchio_LIBRARIES} + /usr/local/lib/libcnpy.so + z +) + +add_executable(solo_imu_mocap solo_imu_mocap.cpp) +target_link_libraries(solo_imu_mocap ${wolfcore_LIBRARIES} ${wolfimu_LIBRARIES} ${PLUGIN_NAME} diff --git a/demos/solo_real_pov_estimation.cpp b/demos/solo_imu_kine.cpp similarity index 98% rename from demos/solo_real_pov_estimation.cpp rename to demos/solo_imu_kine.cpp index efd455589bd1cbb5889630802a58b0099e8e11f0..2c1870e02af803af36326cd4f81280113d59d36a 100644 --- a/demos/solo_real_pov_estimation.cpp +++ b/demos/solo_imu_kine.cpp @@ -75,15 +75,17 @@ typedef pinocchio::ForceTpl<double> Force; int main (int argc, char **argv) { // retrieve parameters from yaml file YAML::Node config = YAML::LoadFile("/home/mfourmy/Documents/Phd_LAAS/wolf/bodydynamics/demos/solo_real_estimation.yaml"); - std::string robot_urdf = config["robot_urdf"].as<std::string>(); - int dimc = config["dimc"].as<int>(); - int nb_feet = config["nb_feet"].as<int>(); double dt = config["dt"].as<double>(); double min_t = config["min_t"].as<double>(); double max_t = config["max_t"].as<double>(); double solve_every_sec = config["solve_every_sec"].as<double>(); bool solve_end = config["solve_end"].as<bool>(); + // robot + std::string robot_urdf = config["robot_urdf"].as<std::string>(); + int dimc = config["dimc"].as<int>(); + int nb_feet = config["nb_feet"].as<int>(); + // Ceres setup int max_num_iterations = config["max_num_iterations"].as<int>(); @@ -106,12 +108,16 @@ int main (int argc, char **argv) { Eigen::Map<Vector6d> bias_imu_prior(bias_imu_prior_vec.data()); std::vector<double> b_p_bi_vec = config["b_p_bi"].as<std::vector<double>>(); std::vector<double> b_q_i_vec = config["b_q_i"].as<std::vector<double>>(); - std::vector<double> l_p_lg_vec = config["l_p_lg"].as<std::vector<double>>(); Eigen::Map<Vector3d> b_p_bi(b_p_bi_vec.data()); Eigen::Map<Vector4d> b_qvec_i(b_q_i_vec.data()); + + + // contacts + std::vector<double> l_p_lg_vec = config["l_p_lg"].as<std::vector<double>>(); Eigen::Map<Vector3d> l_p_lg(l_p_lg_vec.data()); double fz_thresh = config["fz_thresh"].as<double>(); + // MOCAP double std_pose_p = config["std_pose_p"].as<double>(); double std_pose_o_deg = config["std_pose_o_deg"].as<double>(); @@ -125,8 +131,6 @@ int main (int argc, char **argv) { std::string data_file_path = config["data_file_path"].as<std::string>(); std::string out_npz_file_path = config["out_npz_file_path"].as<std::string>(); - std::vector<std::string> ee_names = {"FL_ANKLE", "FR_ANKLE", "HL_ANKLE", "HR_ANKLE"}; - unsigned int nbc = ee_names.size(); // Base to IMU transform Quaterniond b_q_i(b_qvec_i); assert(b_q_i.normalized().isApprox(b_q_i)); @@ -185,12 +189,13 @@ int main (int argc, char **argv) { N = N < int(max_t/dt) ? N : int(max_t/dt); } - // Creating measurements from simulated trajectory // Load the urdf model Model model; pinocchio::urdf::buildModel(robot_urdf, JointModelFreeFlyer(), model); std::cout << "model name: " << model.name << std::endl; Data data(model); + std::vector<std::string> ee_names = {"FL_ANKLE", "FR_ANKLE", "HL_ANKLE", "HR_ANKLE"}; + unsigned int nbc = ee_names.size(); // Recover end effector frame ids std::vector<int> ee_ids; @@ -216,15 +221,15 @@ int main (int argc, char **argv) { ////////////////////// // COMPUTE INITIAL STATE TimeStamp t0(t_arr[0]); - Eigen::Map<Matrix<double,12, 1>> qa(qa_arr); - Eigen::Map<Matrix<double,12, 1>> dqa(dqa_arr); Eigen::Map<Vector4d> o_q_i(o_q_i_arr); // initialize with IMU orientation Eigen::Map<Vector3d> i_omg_oi(i_omg_oi_arr); // Hyp: b=i (not the case) Eigen::Map<Vector3d> w_p_wm(w_p_wm_arr); Eigen::Map<Vector4d> w_qvec_wm(w_q_m_arr); w_qvec_wm.normalize(); // not close enough to wolf eps sometimes - // initial state + // initial configuration + Eigen::Map<Matrix<double,12, 1>> qa(qa_arr); + Eigen::Map<Matrix<double,12, 1>> dqa(dqa_arr); VectorXd q(19); q << 0,0,0, o_q_i, qa; // TODO: put current base orientation estimate? YES VectorXd dq(18); dq << 0,0,0, i_omg_oi, dqa; // TODO: put current base velocity estimate? YES @@ -299,14 +304,6 @@ int main (int argc, char **argv) { // sen_imu->fixIntrinsics(); - /////////////////////////////////////////// - // process measurements in sequential order - double ts_since_last_kf = 0; - - std::vector<Vector3d> i_p_il_vec_prev; - std::vector<Vector4d> i_qvec_l_vec_prev; - std::vector<bool> feet_in_contact_prev; - ////////////////////////////////////////// // Vectors to store estimates at the time of data processing // fbk -> feedback: to check if the estimation is stable enough for feedback control diff --git a/demos/solo_mocap_imu.cpp b/demos/solo_imu_mocap.cpp similarity index 88% rename from demos/solo_mocap_imu.cpp rename to demos/solo_imu_mocap.cpp index a8eff287c62c87bdd8be1caf188efb53989ec4a8..e8989d6356d6b85a466c754fce59f22586c2f215 100644 --- a/demos/solo_mocap_imu.cpp +++ b/demos/solo_imu_mocap.cpp @@ -246,7 +246,11 @@ int main (int argc, char **argv) { double* extr_mocap_fbk_carr = new double[7*N]; // covariances computed on the fly + std::vector<Vector3d> Qp_fbk_v; Qp_fbk_v.push_back(Vector3d::Zero()); + std::vector<Vector3d> Qo_fbk_v; Qo_fbk_v.push_back(Vector3d::Zero()); + std::vector<Vector3d> Qv_fbk_v; Qv_fbk_v.push_back(Vector3d::Zero()); std::vector<Vector6d> Qbi_fbk_v; Qbi_fbk_v.push_back(Vector6d::Zero()); + std::vector<Vector6d> Qm_fbk_v; Qm_fbk_v.push_back(Vector6d::Zero()); // std::vector<Vector3d> i_omg_oi_v; @@ -256,35 +260,44 @@ int main (int argc, char **argv) { for (unsigned int i=0; i < N; i++){ TimeStamp ts(t_arr[i]); - //////////////////////////////////// - //////////////////////////////////// + //////////////// + // PROCESS MOCAP + //////////////// // Get measurements // retrieve traj data - Eigen::Map<Vector3d> imu_acc(imu_acc_arr+3*i); - Eigen::Map<Vector3d> i_omg_oi(i_omg_oi_arr+3*i); // Hyp: b=i (not the case) Eigen::Map<Vector3d> w_p_wm(w_p_wm_arr+3*i); Eigen::Map<Vector4d> w_qvec_wm(w_q_m_arr+4*i); w_qvec_wm.normalize(); // not close enough to wolf eps sometimes + // Vector7d pose_meas; pose_meas << w_p_wm, w_qvec_wm; + Vector7d pose_meas; pose_meas << w_p_wm, w_qvec_wm; + CapturePosePtr CP = std::make_shared<CapturePose>(ts+time_shift_mocap, sensor_pose, pose_meas, sensor_pose->getNoiseCov()); + CP->process(); + //////////////// + //////////////// + + ////////////// + // PROCESS IMU + ////////////// + // Get measurements + // retrieve traj data + Eigen::Map<Vector3d> imu_acc(imu_acc_arr+3*i); + Eigen::Map<Vector3d> i_omg_oi(i_omg_oi_arr+3*i); // Hyp: b=i (not the case) + // store i_omg_oi for later computation of o_v_ob from o_v_oi i_omg_oi_v.push_back(i_omg_oi); Vector6d imu_bias = sen_imu->getStateBlockDynamic('I', t_arr[i])->getState(); Vector3d i_omg_oi_unbiased = (i_omg_oi - imu_bias.tail<3>()); - - //////////////////////////////////// - // IMU Vector6d acc_gyr_meas; acc_gyr_meas << imu_acc, i_omg_oi; Matrix6d acc_gyr_cov = sen_imu->getNoiseCov(); CaptureImuPtr CImu = std::make_shared<CaptureImu>(ts, sen_imu, acc_gyr_meas, acc_gyr_cov); CImu->process(); - - // Vector7d pose_meas; pose_meas << w_p_wm, w_qvec_wm; - Vector7d pose_meas; pose_meas << w_p_wm, w_qvec_wm; - CapturePosePtr CP = std::make_shared<CapturePose>(ts+time_shift_mocap, sensor_pose, pose_meas, sensor_pose->getNoiseCov()); - CP->process(); + ////////////// + ////////////// + // solve every new KF if (problem->getTrajectory()->getFrameMap().size() > nb_kf ){ @@ -294,16 +307,43 @@ int main (int argc, char **argv) { // recover covariances at this point auto kf_last = problem->getTrajectory()->getFrameMap().rbegin()->second; - - CaptureBasePtr cap_imu = kf_last->getCaptureOf(sen_imu); + + // compute covariances of KF and capture stateblocks + Eigen::MatrixXd Qp_fbk = Eigen::Matrix3d::Identity(); + Eigen::MatrixXd Qo_fbk = Eigen::Matrix3d::Identity(); // global or local? + Eigen::MatrixXd Qv_fbk = Eigen::Matrix3d::Identity(); Eigen::MatrixXd Qbi_fbk = Eigen::Matrix6d::Identity(); + Eigen::MatrixXd Qmp_fbk = Eigen::Matrix3d::Identity(); // extr mocap + Eigen::MatrixXd Qmo_fbk = Eigen::Matrix3d::Identity(); // extr mocap + + solver->computeCovariances(SolverManager::CovarianceBlocksToBeComputed::ALL); // should not be computed every time !! see below + problem->getCovarianceBlock(kf_last->getStateBlock('P'), Qp_fbk); + problem->getCovarianceBlock(kf_last->getStateBlock('O'), Qo_fbk); + problem->getCovarianceBlock(kf_last->getStateBlock('V'), Qv_fbk); + + std::vector<StateBlockPtr> extr_sb_vec = {sensor_pose->getP(), sensor_pose->getO()}; + solver->computeCovariances(extr_sb_vec); // not computed by ALL and destroys other computed covs -> issue to raise + problem->getCovarianceBlock(sensor_pose->getP(), Qmp_fbk); + problem->getCovarianceBlock(sensor_pose->getO(), Qmo_fbk); + + CaptureBasePtr cap_imu = kf_last->getCaptureOf(sen_imu); solver->computeCovariances(cap_imu->getStateBlockVec()); // not computed by ALL and destroys other computed covs -> issue to raise problem->getCovarianceBlock(cap_imu->getSensorIntrinsic(), Qbi_fbk); - Vector6d Qbi_fbk_diag = Qbi_fbk.diagonal(); + // Retrieve diagonals + Vector3d Qp_fbk_diag = Qp_fbk.diagonal(); + Vector3d Qo_fbk_diag = Qo_fbk.diagonal(); + Vector3d Qv_fbk_diag = Qv_fbk.diagonal(); + Vector6d Qbi_fbk_diag = Qbi_fbk.diagonal(); + Vector6d Qm_fbk_diag; Qm_fbk_diag << Qmp_fbk.diagonal(), Qmo_fbk.diagonal(); + Vector3d Qmo_fbk_diag = Qmo_fbk.diagonal(); + Qp_fbk_v.push_back(Qp_fbk_diag); + Qo_fbk_v.push_back(Qo_fbk_diag); + Qv_fbk_v.push_back(Qv_fbk_diag); Qbi_fbk_v.push_back(Qbi_fbk_diag); + Qm_fbk_v.push_back(Qm_fbk_diag); // std::cout << "Qbi_fbk: \n" << Qbi_fbk.topLeftCorner<3,3>() << std::endl; // std::cout << "Qbi_fbk: \n" << Qbi_fbk << std::endl; @@ -395,7 +435,11 @@ int main (int argc, char **argv) { double* Qbi_carr = new double[6*Nkf]; double* Qm_carr = new double[6*Nkf]; // feedback covariances + double* Qp_fbk_carr = new double[3*Nkf]; + double* Qo_fbk_carr = new double[3*Nkf]; + double* Qv_fbk_carr = new double[3*Nkf]; double* Qbi_fbk_carr = new double[6*Nkf]; + double* Qm_fbk_carr = new double[6*Nkf]; // factor errors double* fac_imu_err_carr = new double[9*Nkf]; @@ -444,7 +488,11 @@ int main (int argc, char **argv) { memcpy(Qm_carr + 6*i, Qm_diag.data(), 6*sizeof(double)); // Recover feedback covariances + memcpy(Qp_fbk_carr + 3*i, Qp_fbk_v[i].data(), 3*sizeof(double)); + memcpy(Qo_fbk_carr + 3*i, Qo_fbk_v[i].data(), 3*sizeof(double)); + memcpy(Qv_fbk_carr + 3*i, Qv_fbk_v[i].data(), 3*sizeof(double)); memcpy(Qbi_fbk_carr + 6*i, Qbi_fbk_v[i].data(), 6*sizeof(double)); + memcpy(Qm_fbk_carr + 6*i, Qm_fbk_v[i].data(), 6*sizeof(double)); // Factor errors @@ -514,8 +562,12 @@ int main (int argc, char **argv) { cnpy::npz_save(out_npz_file_path, "Qo", Qo_carr, {Nkf,3}, "a"); cnpy::npz_save(out_npz_file_path, "Qv", Qv_carr, {Nkf,3}, "a"); cnpy::npz_save(out_npz_file_path, "Qbi", Qbi_carr, {Nkf,6}, "a"); - cnpy::npz_save(out_npz_file_path, "Qbi_fbk", Qbi_fbk_carr, {Nkf,6}, "a"); cnpy::npz_save(out_npz_file_path, "Qm", Qm_carr, {Nkf,6}, "a"); + cnpy::npz_save(out_npz_file_path, "Qp_fbk", Qp_fbk_carr, {Nkf,3}, "a"); + cnpy::npz_save(out_npz_file_path, "Qo_fbk", Qo_fbk_carr, {Nkf,3}, "a"); + cnpy::npz_save(out_npz_file_path, "Qv_fbk", Qv_fbk_carr, {Nkf,3}, "a"); + cnpy::npz_save(out_npz_file_path, "Qbi_fbk", Qbi_fbk_carr, {Nkf,6}, "a"); + cnpy::npz_save(out_npz_file_path, "Qm_fbk", Qm_fbk_carr, {Nkf,6}, "a"); cnpy::npz_save(out_npz_file_path, "fac_imu_err", fac_imu_err_carr, {Nkf,9}, "a"); cnpy::npz_save(out_npz_file_path, "fac_pose_err", fac_pose_err_carr, {Nkf,6}, "a"); diff --git a/demos/solo_kine_mocap.cpp b/demos/solo_kine_mocap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b44013c1db341c94451977b287c8a0b71a3ffea1 --- /dev/null +++ b/demos/solo_kine_mocap.cpp @@ -0,0 +1,607 @@ +#include <iostream> +#include <fstream> + +// #include <random> +#include <yaml-cpp/yaml.h> +#include <Eigen/Dense> +#include <ctime> +#include "eigenmvn.h" + +#include "pinocchio/parsers/urdf.hpp" +#include "pinocchio/algorithm/joint-configuration.hpp" +#include "pinocchio/algorithm/kinematics.hpp" +#include "pinocchio/algorithm/center-of-mass.hpp" +#include "pinocchio/algorithm/centroidal.hpp" +#include "pinocchio/algorithm/rnea.hpp" +#include "pinocchio/algorithm/crba.hpp" +#include "pinocchio/algorithm/frames.hpp" + +// CNPY +#include <cnpy.h> + +// WOLF +#include <core/problem/problem.h> +#include <core/ceres_wrapper/solver_ceres.h> +#include <core/math/rotations.h> +#include <core/capture/capture_base.h> +#include <core/capture/capture_pose.h> +#include <core/feature/feature_base.h> +#include <core/factor/factor_pose_3d_with_extrinsics.h> +#include <core/factor/factor_block_absolute.h> +#include <core/processor/processor_odom_3d.h> +#include <core/sensor/sensor_odom_3d.h> +#include "core/tree_manager/tree_manager_sliding_window_dual_rate.h" +#include "core/yaml/parser_yaml.h" + +#include <core/sensor/sensor_pose.h> +#include <core/processor/processor_pose.h> + +// IMU +#include "imu/sensor/sensor_imu.h" +#include "imu/capture/capture_imu.h" +#include "imu/processor/processor_imu.h" +#include "imu/factor/factor_imu.h" + +// BODYDYNAMICS +#include "bodydynamics/internal/config.h" +#include "bodydynamics/sensor/sensor_point_feet_nomove.h" +#include "bodydynamics/processor/processor_point_feet_nomove.h" +#include "bodydynamics/capture/capture_point_feet_nomove.h" + + +// UTILS +#include "mcapi_utils.h" + + +using namespace Eigen; +using namespace wolf; +using namespace pinocchio; + +typedef pinocchio::SE3Tpl<double> SE3; +typedef pinocchio::ForceTpl<double> Force; + + +int main (int argc, char **argv) { + // retrieve parameters from yaml file + YAML::Node config = YAML::LoadFile("/home/mfourmy/Documents/Phd_LAAS/wolf/bodydynamics/demos/solo_real_estimation.yaml"); + double dt = config["dt"].as<double>(); + double min_t = config["min_t"].as<double>(); + double max_t = config["max_t"].as<double>(); + double solve_every_sec = config["solve_every_sec"].as<double>(); + bool solve_end = config["solve_end"].as<bool>(); + + // robot + std::string robot_urdf = config["robot_urdf"].as<std::string>(); + int dimc = config["dimc"].as<int>(); + int nb_feet = config["nb_feet"].as<int>(); + + // Ceres setup + int max_num_iterations = config["max_num_iterations"].as<int>(); + + // estimator sensor noises + // double std_pbc_est = config["std_pbc_est"].as<double>(); + // double std_vbc_est = config["std_vbc_est"].as<double>(); + // double std_f_est = config["std_f_est"].as<double>(); + // double std_tau_est = config["std_tau_est"].as<double>(); + double std_odom3d_est = config["std_odom3d_est"].as<double>(); + + // priors + double std_prior_p = config["std_prior_p"].as<double>(); + double std_prior_o = config["std_prior_o"].as<double>(); + double std_prior_v = config["std_prior_v"].as<double>(); + + // IMU + double std_abs_bias_acc = config["std_abs_bias_acc"].as<double>(); + double std_abs_bias_gyro = config["std_abs_bias_gyro"].as<double>(); + std::vector<double> bias_imu_prior_vec = config["bias_imu_prior"].as<std::vector<double>>(); + Eigen::Map<Vector6d> bias_imu_prior(bias_imu_prior_vec.data()); + + // base extrinsics + std::vector<double> b_p_bi_vec = config["b_p_bi"].as<std::vector<double>>(); + std::vector<double> b_q_i_vec = config["b_q_i"].as<std::vector<double>>(); + Eigen::Map<Vector3d> b_p_bi(b_p_bi_vec.data()); + Eigen::Map<Vector4d> b_qvec_i(b_q_i_vec.data()); + + // contacts + std::vector<double> l_p_lg_vec = config["l_p_lg"].as<std::vector<double>>(); + Eigen::Map<Vector3d> l_p_lg(l_p_lg_vec.data()); + double fz_thresh = config["fz_thresh"].as<double>(); + + + // MOCAP + double std_pose_p = config["std_pose_p"].as<double>(); + double std_pose_o_deg = config["std_pose_o_deg"].as<double>(); + double std_mocap_extr_p = config["std_mocap_extr_p"].as<double>(); + double std_mocap_extr_o_deg = config["std_mocap_extr_o_deg"].as<double>(); + bool unfix_extr_sensor_pose = config["unfix_extr_sensor_pose"].as<bool>(); + bool time_tolerance_mocap = config["time_tolerance_mocap"].as<double>(); + double time_shift_mocap = config["time_shift_mocap"].as<double>(); + + + std::string data_file_path = config["data_file_path"].as<std::string>(); + std::string out_npz_file_path = config["out_npz_file_path"].as<std::string>(); + + // Base to IMU transform + Quaterniond b_q_i(b_qvec_i); + assert(b_q_i.normalized().isApprox(b_q_i)); + Quaterniond i_q_b = b_q_i.conjugate(); + SE3 b_T_i(b_q_i, b_p_bi); + SE3 i_T_b = b_T_i.inverse(); + Matrix3d b_R_i = b_T_i.rotation(); + Vector3d i_p_ib = i_T_b.translation(); + Matrix3d i_R_b = i_T_b.rotation(); + + // initialize some vectors and fill them with dicretized data + cnpy::npz_t arr_map = cnpy::npz_load(data_file_path); + + //load it into a new array + cnpy::NpyArray t_npyarr = arr_map["t"]; + cnpy::NpyArray imu_acc_npyarr = arr_map["imu_acc"]; + // cnpy::NpyArray o_a_oi_npyarr = arr_map["o_a_oi"]; + cnpy::NpyArray o_q_i_npyarr = arr_map["o_q_i"]; + cnpy::NpyArray i_omg_oi_npyarr = arr_map["i_omg_oi"]; + cnpy::NpyArray qa_npyarr = arr_map["qa"]; + cnpy::NpyArray dqa_npyarr = arr_map["dqa"]; + cnpy::NpyArray tau_npyarr = arr_map["tau"]; + cnpy::NpyArray l_forces_npyarr = arr_map["l_forces"]; + // cnpy::NpyArray w_pose_wm_npyarr = arr_map["w_pose_wm"]; + cnpy::NpyArray m_v_wm_npyarr = arr_map["m_v_wm"]; + cnpy::NpyArray w_v_wm_npyarr = arr_map["w_v_wm"]; + // cnpy::NpyArray o_R_i_npyarr = arr_map["o_R_i"]; + cnpy::NpyArray w_p_wm_npyarr = arr_map["w_p_wm"]; + cnpy::NpyArray w_q_m_npyarr = arr_map["w_q_m"]; + // cnpy::NpyArray contact_npyarr = arr_map["contact"]; + // cnpy::NpyArray b_p_bl_mocap_npyarr = arr_map["b_p_bl_mocap"]; + + // cnpy::NpyArray w_R_m_npyarr = arr_map["w_R_m"]; + double* t_arr = t_npyarr.data<double>(); + double* imu_acc_arr = imu_acc_npyarr.data<double>(); + // double* o_a_oi_arr = o_a_oi_npyarr.data<double>(); + double* i_omg_oi_arr = i_omg_oi_npyarr.data<double>(); + double* qa_arr = qa_npyarr.data<double>(); + double* dqa_arr = dqa_npyarr.data<double>(); + double* tau_arr = tau_npyarr.data<double>(); + double* l_forces_arr = l_forces_npyarr.data<double>(); + // double* w_pose_wm_arr = w_pose_wm_npyarr.data<double>(); + double* m_v_wm_arr = m_v_wm_npyarr.data<double>(); + double* w_v_wm_arr = w_v_wm_npyarr.data<double>(); + double* o_q_i_arr = o_q_i_npyarr.data<double>(); + // double* o_R_i_arr = o_R_i_npyarr.data<double>(); + double* w_p_wm_arr = w_p_wm_npyarr.data<double>(); + double* w_q_m_arr = w_q_m_npyarr.data<double>(); + + // double* contact_arr = contact_npyarr.data<double>(); + // double* b_p_bl_mocap_arr = b_p_bl_mocap_npyarr.data<double>(); + + // double* w_R_m_arr = w_R_m_npyarr.data<double>(); + unsigned int N = t_npyarr.shape[0]; + if (max_t > 0){ + N = N < int(max_t/dt) ? N : int(max_t/dt); + } + + // Load the urdf model + Model model; + pinocchio::urdf::buildModel(robot_urdf, JointModelFreeFlyer(), model); + std::cout << "model name: " << model.name << std::endl; + Data data(model); + std::vector<std::string> ee_names = {"FL_ANKLE", "FR_ANKLE", "HL_ANKLE", "HR_ANKLE"}; + unsigned int nbc = ee_names.size(); + + // Recover end effector frame ids + std::vector<int> ee_ids; + std::cout << "Frame ids" << std::endl; + for (auto ee_name: ee_names){ + ee_ids.push_back(model.getFrameId(ee_name)); + std::cout << ee_name << std::endl; + } + + ///////////////////////// + // WOLF enters the scene + // SETUP PROBLEM/SENSORS/PROCESSORS + std::string bodydynamics_root_dir = _WOLF_BODYDYNAMICS_ROOT_DIR; + + ProblemPtr problem = Problem::create("PO", 3); + // ProblemPtr problem = Problem::create("POV", 3); + + // add a tree manager for sliding window optimization + ParserYaml parser = ParserYaml("demos/tree_manager.yaml", bodydynamics_root_dir); + ParamsServer server = ParamsServer(parser.getParams()); + auto tree_manager = TreeManagerSlidingWindow::create("tree_manager", server); + problem->setTreeManager(tree_manager); + + ////////////////////// + // COMPUTE INITIAL STATE + TimeStamp t0(t_arr[0]); + Eigen::Map<Vector4d> o_q_i(o_q_i_arr); // initialize with IMU orientation + Eigen::Map<Vector3d> i_omg_oi(i_omg_oi_arr); // Hyp: b=i (not the case) + Eigen::Map<Vector3d> w_p_wm(w_p_wm_arr); + Eigen::Map<Vector4d> w_qvec_wm(w_q_m_arr); + w_qvec_wm.normalize(); // not close enough to wolf eps sometimes + + // initial configuration + Eigen::Map<Matrix<double,12, 1>> qa(qa_arr); + Eigen::Map<Matrix<double,12, 1>> dqa(dqa_arr); + VectorXd q(19); q << 0,0,0, o_q_i, qa; // TODO: put current base orientation estimate? YES + VectorXd dq(18); dq << 0,0,0, i_omg_oi, dqa; // TODO: put current base velocity estimate? YES + + ////////////////////// + + // CERES WRAPPER + SolverCeresPtr solver = std::make_shared<SolverCeres>(problem); + solver->getSolverOptions().max_num_iterations = max_num_iterations; + + //===================================================== INITIALIZATION + // SENSOR + PROCESSOR POINT FEET NOMOVE + ParamsSensorPointFeetNomovePtr intr_pfn = std::make_shared<ParamsSensorPointFeetNomove>(); + intr_pfn->std_foot_nomove_ = std_odom3d_est; + VectorXd extr; // default size for dynamic vector is 0-0 -> what we need + SensorBasePtr sen_pfnm_base = problem->installSensor("SensorPointFeetNomove", "SenPfnm", extr, intr_pfn); + SensorPointFeetNomovePtr sen_pfnm = std::static_pointer_cast<SensorPointFeetNomove>(sen_pfnm_base); + ParamsProcessorPointFeetNomovePtr params_pfnm = std::make_shared<ParamsProcessorPointFeetNomove>(); + ProcessorBasePtr proc_pfnm_base = problem->installProcessor("ProcessorPointFeetNomove", "PPointFeetNomove", sen_pfnm, params_pfnm); + + + // SENSOR + PROCESSOR POSE (for mocap) + // pose sensor and proc (to get extrinsics in the prb) + auto intr_sp = std::make_shared<ParamsSensorPose>(); + intr_sp->std_p = std_pose_p; + intr_sp->std_o = toRad(std_pose_o_deg); + Vector7d extr_pose; extr_pose << i_p_ib, i_q_b.coeffs(); + auto sensor_pose = problem->installSensor("SensorPose", "pose", extr_pose, intr_sp); + auto params_proc = std::make_shared<ParamsProcessorPose>(); + params_proc->time_tolerance = time_tolerance_mocap; + auto proc_pose = problem->installProcessor("ProcessorPose", "pose", sensor_pose, params_proc); + // somehow by default the sensor extrinsics is fixed + Matrix3d cov_sp_p = pow(std_mocap_extr_p, 2)*Matrix3d::Identity(); + Matrix3d cov_sp_o = pow(std_mocap_extr_o_deg/60, 2)*Matrix3d::Identity(); + sensor_pose->addPriorParameter('P', extr_pose.head<3>(), cov_sp_p); + sensor_pose->addPriorParameter('O', extr_pose.tail<4>(), cov_sp_o); + if (unfix_extr_sensor_pose){ + sensor_pose->unfixExtrinsics(); + } + else{ + sensor_pose->fixExtrinsics(); + } + + Matrix3d w_R_m_init = q2R(w_qvec_wm); + Vector3d w_p_wi_init = w_p_wm + w_R_m_init*b_p_bi; + + VectorComposite x_origin("PO", {w_p_wi_init, w_qvec_wm}); + VectorComposite std_origin("PO", {std_prior_p*Vector3d::Ones(), std_prior_o*Vector3d::Ones()}); + // FrameBasePtr KF1 = problem->setPriorFactor(x_origin, std_origin, t0, 0.0005); + FrameBasePtr KF1 = problem->setPriorInitialGuess(x_origin, t0, time_tolerance_mocap); // if mocap used + + + ////////////////////////////////////////// + // Vectors to store estimates at the time of data processing + // fbk -> feedback: to check if the estimation is stable enough for feedback control + std::vector<Vector3d> p_ob_fbk_v; + std::vector<Vector4d> q_ob_fbk_v; + std::vector<Vector3d> v_ob_fbk_v; + // Allocate on the heap to prevent stack overflow for large datasets + double* o_p_ob_fbk_carr = new double[3*N]; + double* o_q_b_fbk_carr = new double[4*N]; + double* o_v_ob_fbk_carr = new double[3*N]; + double* imu_bias_fbk_carr = new double[6*N]; + double* extr_mocap_fbk_carr = new double[7*N]; + + // covariances computed on the fly + std::vector<Vector3d> Qp_fbk_v; Qp_fbk_v.push_back(Vector3d::Zero()); + std::vector<Vector3d> Qo_fbk_v; Qo_fbk_v.push_back(Vector3d::Zero()); + std::vector<Vector3d> Qv_fbk_v; Qv_fbk_v.push_back(Vector3d::Zero()); + std::vector<Vector6d> Qbi_fbk_v; Qbi_fbk_v.push_back(Vector6d::Zero()); + std::vector<Vector6d> Qm_fbk_v; Qm_fbk_v.push_back(Vector6d::Zero()); + + // + std::vector<Vector3d> i_omg_oi_v; + ////////////////////////////////////////// + + unsigned int nb_kf = 1; + for (unsigned int i=0; i < N; i++){ + TimeStamp ts(t_arr[i]); + std::cout << ts << std::endl; + + //////////////// + // PROCESS MOCAP + //////////////// + // Get measurements + // retrieve traj data + Eigen::Map<Vector3d> w_p_wm(w_p_wm_arr+3*i); + Eigen::Map<Vector4d> w_qvec_wm(w_q_m_arr+4*i); + w_qvec_wm.normalize(); // not close enough to wolf eps sometimes + + // Vector7d pose_meas; pose_meas << w_p_wm, w_qvec_wm; + Vector7d pose_meas; pose_meas << w_p_wm, w_qvec_wm; + CapturePosePtr CP = std::make_shared<CapturePose>(ts+time_shift_mocap, sensor_pose, pose_meas, sensor_pose->getNoiseCov()); + CP->process(); + //////////////// + //////////////// + + ///////////////////// + // PROCESS KINEMATICS + ///////////////////// + Eigen::Map<Matrix<double,12, 1>> tau(tau_arr+12*i); + Eigen::Map<Matrix<double,12, 1>> qa(qa_arr+12*i); + Eigen::Map<Matrix<double,12, 1>> dqa(dqa_arr+12*i); + + + // Or else, retrieve from preprocessed dataset + Eigen::Map<Matrix<double,12, 1>> l_forces(l_forces_arr+12*i); + + Vector3d o_f_tot = Vector3d::Zero(); + std::vector<Vector3d> l_f_l_vec; + // std::vector<Vector3d> o_f_l_vec; + std::vector<Vector3d> i_p_il_vec; + std::vector<Vector4d> i_qvec_l_vec; + // needing relative kinematics measurements + VectorXd q_static = q; q_static.head<7>() << 0,0,0, 0,0,0,1; + VectorXd dq_static = dq; dq_static.head<6>() << 0,0,0, 0,0,0; + forwardKinematics(model, data, q_static, dq_static); + updateFramePlacements(model, data); + + for (unsigned int i_ee=0; i_ee < nbc; i_ee++){ + auto b_T_l = data.oMf[ee_ids[i_ee]]; + + + auto i_T_l = i_T_b * b_T_l; + Vector3d i_p_il = i_T_l.translation(); // meas + Matrix3d i_R_l = i_T_l.rotation(); + Vector4d i_qvec_l = Quaterniond(i_R_l).coeffs(); // meas + i_p_il = i_p_il + i_R_l*l_p_lg; + + Vector3d l_f_l = l_forces.segment(3*i_ee, 3); // meas + // Vector3d o_f_l = o_R_i*i_R_l * l_f_l; // for contact test + + l_f_l_vec.push_back(l_f_l); + // o_f_l_vec.push_back(o_f_l); + i_p_il_vec.push_back(i_p_il); + i_qvec_l_vec.push_back(i_qvec_l); + } + + // 1) detect feet in contact + int nb_feet_in_contact = 0; + std::unordered_map<int, Vector3d> kin_incontact; + for (unsigned int i_ee=0; i_ee<nbc; i_ee++){ + // basic contact detection based on normal foot vel, things break if no foot is in contact + // std::cout << "F" << ee_names[i_ee] << " norm: " << wrench_meas_v[i_ee][i].norm() << std::endl; + // nb_feet: just for debugging/testing kinematic factor + + if ((true)){ // !!! ASSUME ALWAYS IN CONTACT + // if ((o_f_l_vec[i_ee](2) > fz_thresh) && (nb_feet_in_contact < nb_feet)){ + // if (contact_gtr(i_ee)){ + nb_feet_in_contact++; + kin_incontact.insert({i_ee, i_p_il_vec[i_ee]}); + } + } + + CapturePointFeetNomovePtr CPF = std::make_shared<CapturePointFeetNomove>(ts, sen_pfnm, kin_incontact); + CPF->process(); + + + // solve every new KF + if (problem->getTrajectory()->getFrameMap().size() > nb_kf ){ + std::string report = solver->solve(SolverManager::ReportVerbosity::BRIEF); + std::cout << ts << " "; + std::cout << report << std::endl; + + // recover covariances at this point + auto kf_last = problem->getTrajectory()->getFrameMap().rbegin()->second; + + + // compute covariances of KF and capture stateblocks + Eigen::MatrixXd Qp_fbk = Eigen::Matrix3d::Identity(); + Eigen::MatrixXd Qo_fbk = Eigen::Matrix3d::Identity(); // global or local? + Eigen::MatrixXd Qv_fbk = Eigen::Matrix3d::Identity(); + Eigen::MatrixXd Qbi_fbk = Eigen::Matrix6d::Identity(); + Eigen::MatrixXd Qmp_fbk = Eigen::Matrix3d::Identity(); // extr mocap + Eigen::MatrixXd Qmo_fbk = Eigen::Matrix3d::Identity(); // extr mocap + + solver->computeCovariances(SolverManager::CovarianceBlocksToBeComputed::ALL); // should not be computed every time !! see below + problem->getCovarianceBlock(kf_last->getStateBlock('P'), Qp_fbk); + problem->getCovarianceBlock(kf_last->getStateBlock('O'), Qo_fbk); + problem->getCovarianceBlock(kf_last->getStateBlock('V'), Qv_fbk); + + std::vector<StateBlockPtr> extr_sb_vec = {sensor_pose->getP(), sensor_pose->getO()}; + solver->computeCovariances(extr_sb_vec); // not computed by ALL and destroys other computed covs -> issue to raise + problem->getCovarianceBlock(sensor_pose->getP(), Qmp_fbk); + problem->getCovarianceBlock(sensor_pose->getO(), Qmo_fbk); + + + // Retrieve diagonals + Vector3d Qp_fbk_diag = Qp_fbk.diagonal(); + Vector3d Qo_fbk_diag = Qo_fbk.diagonal(); + Vector3d Qv_fbk_diag = Qv_fbk.diagonal(); + Vector6d Qbi_fbk_diag = Qbi_fbk.diagonal(); + Vector6d Qm_fbk_diag; Qm_fbk_diag << Qmp_fbk.diagonal(), Qmo_fbk.diagonal(); + Vector3d Qmo_fbk_diag = Qmo_fbk.diagonal(); + + Qp_fbk_v.push_back(Qp_fbk_diag); + Qo_fbk_v.push_back(Qo_fbk_diag); + Qv_fbk_v.push_back(Qv_fbk_diag); + Qbi_fbk_v.push_back(Qbi_fbk_diag); + Qm_fbk_v.push_back(Qm_fbk_diag); + + // std::cout << "Qbi_fbk: \n" << Qbi_fbk.topLeftCorner<3,3>() << std::endl; + // std::cout << "Qbi_fbk: \n" << Qbi_fbk << std::endl; + + + nb_kf++; + } + + // Store current state estimation + VectorComposite state_fbk = problem->getState(); + // VectorComposite state_fbk = problem->getState(ts); + Vector3d o_p_oi = state_fbk['P']; + Vector4d o_qvec_i = state_fbk['O']; + // Vector3d o_v_oi = state_fbk['V']; + Vector3d o_v_oi = Vector3d::Zero(); + + // IMU to base frame + Matrix3d o_R_i = Quaterniond(o_qvec_i).toRotationMatrix(); + // o_R_i = Quaterniond(o_qvec_i).toRotationMatrix(); + Matrix3d o_R_b = o_R_i * i_R_b; + Vector4d o_qvec_b = Quaterniond(o_R_b).coeffs(); + Vector3d o_p_ob = o_p_oi + o_R_i * i_p_ib; + // Vector3d o_v_ob = o_v_oi + o_R_i * i_omg_oi_unbiased.cross(o_R_i*i_R_b*b_p_bi); + // Vector3d o_p_ob = o_p_oi; + Vector3d o_v_ob = o_v_oi; + + Vector3d imu_bias = Vector3d::Zero(); + // imu_bias = sen_imu->getIntrinsic()->getState(); + // imu_bias = sen_imu->getIntrinsic(ts)->getState(); + Vector7d extr_mocap_est; extr_mocap_est << sensor_pose->getP()->getState(), sensor_pose->getO()->getState(); + + mempcpy(o_p_ob_fbk_carr+3*i, o_p_ob.data(), 3*sizeof(double)); + mempcpy(o_q_b_fbk_carr +4*i, o_qvec_b.data(), 4*sizeof(double)); + mempcpy(o_v_ob_fbk_carr+3*i, o_v_ob.data(), 3*sizeof(double)); + mempcpy(imu_bias_fbk_carr+6*i, imu_bias.data(), 6*sizeof(double)); + mempcpy(extr_mocap_fbk_carr+7*i, extr_mocap_est.data(), 7*sizeof(double)); + + p_ob_fbk_v.push_back(o_p_ob); + q_ob_fbk_v.push_back(o_qvec_b); + v_ob_fbk_v.push_back(o_v_ob); + } + + + + /////////////////////////////////////////// + //////////////// SOLVING ////////////////// + /////////////////////////////////////////// + if (solve_end){ + std::string report = solver->solve(SolverManager::ReportVerbosity::BRIEF); + problem->print(4,true,true,true); + std::cout << report << std::endl; + } + + double* o_p_ob_carr = new double[3*N]; + double* o_q_b_carr = new double[4*N]; + double* imu_bias_carr = new double[6*N]; + double* o_v_ob_carr = new double[6*N]; + for (unsigned int i=0; i < N; i++) { + VectorComposite state_est = problem->getState(t_arr[i]); + Vector3d i_omg_oi = i_omg_oi_v[i]; + Vector3d o_p_oi = state_est['P']; + Vector4d o_qvec_i = state_est['O']; + Vector3d o_v_oi = Vector3d::Zero(); + + Matrix3d o_R_i = Quaterniond(o_qvec_i).toRotationMatrix(); + Matrix3d o_R_b = o_R_i * i_R_b; + Vector4d o_qvec_b = Quaterniond(o_R_b).coeffs(); + Vector3d o_p_ob = o_p_oi + o_R_i * i_p_ib; + // Vector3d o_v_ob = o_v_oi + o_R_i * i_omg_oi_unbiased.cross(i_R_b*b_p_bi); + // Vector3d o_p_ob = o_p_oi; + Vector3d o_v_ob = o_v_oi; + + mempcpy(o_p_ob_carr+3*i, o_p_ob.data(), 3*sizeof(double)); + mempcpy(o_q_b_carr +4*i, o_qvec_b.data(), 4*sizeof(double)); + mempcpy(o_v_ob_carr+3*i, o_v_ob.data(), 3*sizeof(double)); + // mempcpy(imu_bias_carr+6*i, imu_bias.data(), 6*sizeof(double)); + } + + + // Compute Covariances + unsigned int Nkf = problem->getTrajectory()->getFrameMap().size(); + double* tkf_carr = new double[1*Nkf]; + double* Qp_carr = new double[3*Nkf]; + double* Qo_carr = new double[3*Nkf]; + double* Qv_carr = new double[3*Nkf]; + double* Qbi_carr = new double[6*Nkf]; + double* Qm_carr = new double[6*Nkf]; + // feedback covariances + double* Qp_fbk_carr = new double[3*Nkf]; + double* Qo_fbk_carr = new double[3*Nkf]; + double* Qv_fbk_carr = new double[3*Nkf]; + double* Qbi_fbk_carr = new double[6*Nkf]; + double* Qm_fbk_carr = new double[6*Nkf]; + + // factor errors + double* fac_imu_err_carr = new double[9*Nkf]; + double* fac_pose_err_carr = new double[6*Nkf]; + int i = 0; + for (auto& elt: problem->getTrajectory()->getFrameMap()){ + std::cout << "Traj " << i << "/" << problem->getTrajectory()->getFrameMap().size() << std::endl; + tkf_carr[i] = elt.first.get(); + auto kf = elt.second; + VectorComposite kf_state = kf->getState(); + + // compute covariances of KF and capture stateblocks + Eigen::MatrixXd Qp = Eigen::Matrix3d::Identity(); + Eigen::MatrixXd Qo = Eigen::Matrix3d::Identity(); // global or local? + Eigen::MatrixXd Qv = Eigen::Matrix3d::Identity(); + Eigen::MatrixXd Qbi = Eigen::Matrix6d::Identity(); + Eigen::MatrixXd Qmp = Eigen::Matrix3d::Identity(); // extr mocap + Eigen::MatrixXd Qmo = Eigen::Matrix3d::Identity(); // extr mocap + + solver->computeCovariances(SolverManager::CovarianceBlocksToBeComputed::ALL); // should not be computed every time !! see below + problem->getCovarianceBlock(kf->getStateBlock('P'), Qp); + problem->getCovarianceBlock(kf->getStateBlock('O'), Qo); + problem->getCovarianceBlock(kf->getStateBlock('V'), Qv); + + std::vector<StateBlockPtr> extr_sb_vec = {sensor_pose->getP(), sensor_pose->getO()}; + solver->computeCovariances(extr_sb_vec); // not computed by ALL and destroys other computed covs -> issue to raise + problem->getCovarianceBlock(sensor_pose->getP(), Qmp); + problem->getCovarianceBlock(sensor_pose->getO(), Qmo); + + // diagonal components + Vector3d Qp_diag = Qp.diagonal(); + Vector3d Qo_diag = Qo.diagonal(); + Vector3d Qv_diag = Qv.diagonal(); + Vector6d Qbi_diag = Qbi.diagonal(); + Vector6d Qm_diag; Qm_diag << Qmp.diagonal(), Qmo.diagonal(); + + memcpy(Qp_carr + 3*i, Qp_diag.data(), 3*sizeof(double)); + memcpy(Qo_carr + 3*i, Qo_diag.data(), 3*sizeof(double)); + memcpy(Qv_carr + 3*i, Qv_diag.data(), 3*sizeof(double)); + memcpy(Qbi_carr + 6*i, Qbi_diag.data(), 6*sizeof(double)); + memcpy(Qm_carr + 6*i, Qm_diag.data(), 6*sizeof(double)); + + // Recover feedback covariances + memcpy(Qp_fbk_carr + 3*i, Qp_fbk_v[i].data(), 3*sizeof(double)); + memcpy(Qo_fbk_carr + 3*i, Qo_fbk_v[i].data(), 3*sizeof(double)); + memcpy(Qv_fbk_carr + 3*i, Qv_fbk_v[i].data(), 3*sizeof(double)); + memcpy(Qbi_fbk_carr + 6*i, Qbi_fbk_v[i].data(), 6*sizeof(double)); + memcpy(Qm_fbk_carr + 6*i, Qm_fbk_v[i].data(), 6*sizeof(double)); + + i++; + } + + // Save trajectories in npz file + // save ground truth + cnpy::npz_save(out_npz_file_path, "t", t_arr, {N}, "w"); // "w" overwrites any file with same name + cnpy::npz_save(out_npz_file_path, "w_p_wm", w_p_wm_arr, {N,3}, "a"); // "a" appends to the file we created above + cnpy::npz_save(out_npz_file_path, "w_q_m", w_q_m_arr, {N,4}, "a"); + cnpy::npz_save(out_npz_file_path, "w_v_wm", w_v_wm_arr, {N,3}, "a"); + cnpy::npz_save(out_npz_file_path, "m_v_wm", m_v_wm_arr, {N,3}, "a"); + cnpy::npz_save(out_npz_file_path, "o_q_i", o_q_i_arr, {N,3}, "a"); + cnpy::npz_save(out_npz_file_path, "qa", qa_arr, {N,12}, "a"); + + // save estimates + cnpy::npz_save(out_npz_file_path, "o_p_ob_fbk", o_p_ob_fbk_carr, {N,3}, "a"); + cnpy::npz_save(out_npz_file_path, "o_q_b_fbk", o_q_b_fbk_carr, {N,4}, "a"); + cnpy::npz_save(out_npz_file_path, "o_v_ob_fbk", o_v_ob_fbk_carr, {N,3}, "a"); + + cnpy::npz_save(out_npz_file_path, "o_p_ob", o_p_ob_carr, {N,3}, "a"); + cnpy::npz_save(out_npz_file_path, "o_q_b", o_q_b_carr, {N,4}, "a"); + cnpy::npz_save(out_npz_file_path, "o_v_ob", o_v_ob_carr, {N,3}, "a"); + + // and biases/extrinsics + cnpy::npz_save(out_npz_file_path, "imu_bias_fbk", imu_bias_fbk_carr, {N,6}, "a"); + cnpy::npz_save(out_npz_file_path, "imu_bias", imu_bias_carr, {N,6}, "a"); + cnpy::npz_save(out_npz_file_path, "extr_mocap_fbk", extr_mocap_fbk_carr, {N,7}, "a"); + + // covariances + cnpy::npz_save(out_npz_file_path, "tkf", tkf_carr, {Nkf}, "a"); + cnpy::npz_save(out_npz_file_path, "Qp", Qp_carr, {Nkf,3}, "a"); + cnpy::npz_save(out_npz_file_path, "Qo", Qo_carr, {Nkf,3}, "a"); + cnpy::npz_save(out_npz_file_path, "Qv", Qv_carr, {Nkf,3}, "a"); + cnpy::npz_save(out_npz_file_path, "Qbi", Qbi_carr, {Nkf,6}, "a"); + cnpy::npz_save(out_npz_file_path, "Qm", Qm_carr, {Nkf,6}, "a"); + cnpy::npz_save(out_npz_file_path, "Qp_fbk", Qp_fbk_carr, {Nkf,3}, "a"); + cnpy::npz_save(out_npz_file_path, "Qo_fbk", Qo_fbk_carr, {Nkf,3}, "a"); + cnpy::npz_save(out_npz_file_path, "Qv_fbk", Qv_fbk_carr, {Nkf,3}, "a"); + cnpy::npz_save(out_npz_file_path, "Qbi_fbk", Qbi_fbk_carr, {Nkf,6}, "a"); + cnpy::npz_save(out_npz_file_path, "Qm_fbk", Qm_fbk_carr, {Nkf,6}, "a"); + cnpy::npz_save(out_npz_file_path, "fac_imu_err", fac_imu_err_carr, {Nkf,9}, "a"); + cnpy::npz_save(out_npz_file_path, "fac_pose_err", fac_pose_err_carr, {Nkf,6}, "a"); + +}