95 int n_cams = vcc_read_vio_cam_conf_file(vio_cams,
MAX_CAM_CNT, 1);
100 Eigen::Matrix4d T_imu_body = Eigen::Matrix4d::Identity();
103 vcc_extrinsic_t body_imu_ext;
105 if (vcc_fetch_extrinsic(
"body", vio_cams[0].imu, &body_imu_ext) != 0)
107 fprintf(stderr,
"Failed to fetch body->%s extrinsic. IMU body mode requires this extrinsic to be defined.\n", vio_cams[0].imu);
112 printf(
"\n[INFO] IMU body mode enabled - loading body->%s transformation\n", vio_cams[0].imu);
113 printExtrinsic(body_imu_ext);
117 Eigen::Vector3d t_imu_wrt_body(
118 body_imu_ext.T_child_wrt_parent[0],
119 body_imu_ext.T_child_wrt_parent[1],
120 body_imu_ext.T_child_wrt_parent[2]
122 const Eigen::Matrix<double,3,3,Eigen::RowMajor> R_imu_to_body(&body_imu_ext.R_child_to_parent[0][0]);
123 T_imu_body.block<3,3>(0,0) = R_imu_to_body.transpose();
124 T_imu_body.block<3,1>(0,3) = -R_imu_to_body.transpose() * t_imu_wrt_body;
127 printf(
"\n[INFO] T_imu_body transformation matrix (body->imu):\n");
128 std::cout << T_imu_body << std::endl;
133 for (
int i = 0; i < n_cams; i++)
135 if (!vio_cams[i].is_extrinsic_present)
137 fprintf(stderr,
"failed to find extrinsic config for vio cam %s\n", vio_cams[i].name);
140 if (!vio_cams[i].is_cal_present)
142 fprintf(stderr,
"failed to find cam cal for vio cam %s\n", vio_cams[i].name);
155 for (
int i = 1; i < n_cams; i++)
157 if (strcmp(vio_cams[i].imu,
imu_name) != 0)
159 fprintf(stderr,
"vio cam %s has a different imu than vio cam %s\n", vio_cams[i].name, vio_cams[0].name);
168 size_t current_len = strlen(
imu_name);
169 const char* suffix =
"_body";
170 size_t suffix_len = strlen(suffix);
172 if (current_len + suffix_len >=
sizeof(
imu_name))
174 fprintf(stderr,
"Error: IMU name '%s' is too long to append '_body' suffix\n",
imu_name);
181 printf(
"[INFO] IMU body mode: subscribing to '%s' instead of '%s'\n",
188 cJSON *imu_json = pipe_get_info_json(vio_cams[0].imu);
190 cJSON *model = cJSON_GetObjectItem(imu_json,
"imu_model");
191 if (model && cJSON_IsString(model) && model->valuestring) {
192 if (strcmp(model->valuestring,
"ICM42688") == 0) {
194 }
else if (strcmp(model->valuestring,
"BMI270") == 0) {
198 fprintf(stderr,
"WARNING: unrecognized IMU model: %s\n", model->valuestring);
200 printf(
"IMU model detected: %s\n", model->valuestring);
203 fprintf(stderr,
"WARNING: imu_model field not found in pipe info\n");
205 cJSON_Delete(imu_json);
208 fprintf(stderr,
"WARNING: could not read IMU pipe info JSON for '%s'\n", vio_cams[0].imu);
213 std::string yaml_estimator_path = std::string(
folder_base) +
"/estimator_config.yaml";
217 config = YAML::LoadFile(yaml_estimator_path);
219 catch (
const std::exception &e)
221 fprintf(stderr,
"Failed to load estimator YAML: %s\n", e.what());
222 fprintf(stderr,
"Creating new estimator config file\n");
224 config[
"max_cameras"] = n_cams;
229 case IMU_MODEL_ICM42688:
230 config[
"prop_window"] = 0.1;
231 config[
"zupt_prop_window"] = 0.1;
233 case IMU_MODEL_BMI270:
234 config[
"prop_window"] = 0.075;
235 config[
"zupt_prop_window"] = 0.1;
238 config[
"prop_window"] = 0.075;
239 config[
"zupt_prop_window"] = 0.1;
244 config[
"max_cameras"] = n_cams;
250 std::ofstream fout(yaml_estimator_path);
251 fout <<
"%YAML:1.0" << std::endl << std::endl;
254 catch (
const std::exception &e)
256 std::cerr <<
"Failed to write estimator YAML: " << e.what() << std::endl;
263 std::string yaml_camchain_path = std::string(
folder_base) +
"/kalibr_imucam_chain.yaml";
264 YAML::Node camchain_config;
267 camchain_config = YAML::LoadFile(yaml_camchain_path);
269 catch (
const std::exception &e)
271 fprintf(stderr,
"Failed to load YAML: %s\n", e.what());
275 std::vector<Eigen::Matrix4d> T_final_per_cam(n_cams, Eigen::Matrix4d::Identity());
276 for (
int i = 0; i < n_cams; i++)
279 std::string cam_key =
"cam" + std::to_string(i);
283 strncpy(cam.
name, vio_cams[i].name,
sizeof(cam.
name) - 1);
284 cam.
name[
sizeof(cam.
name) - 1] =
'\0';
292 printf(
"vio cam: %d is occluded: %d\n", i, vio_cams[i].is_occluded_on_ground);
299 YAML::Node intrinsics;
300 intrinsics.push_back(vio_cams[i].cal.fx);
301 intrinsics.push_back(vio_cams[i].cal.fy);
302 intrinsics.push_back(vio_cams[i].cal.cx);
303 intrinsics.push_back(vio_cams[i].cal.cy);
304 camchain_config[cam_key][
"intrinsics"] = intrinsics;
307 YAML::Node distortion_coeffs;
308 distortion_coeffs.push_back(vio_cams[i].cal.D[0]);
309 distortion_coeffs.push_back(vio_cams[i].cal.D[1]);
310 distortion_coeffs.push_back(vio_cams[i].cal.D[2]);
311 distortion_coeffs.push_back(vio_cams[i].cal.D[3]);
312 camchain_config[cam_key][
"distortion_coeffs"] = distortion_coeffs;
315 YAML::Node resolution;
316 resolution.push_back(vio_cams[i].cal.width);
317 resolution.push_back(vio_cams[i].cal.height);
318 camchain_config[cam_key][
"resolution"] = resolution;
320 camchain_config[cam_key][
"distortion_model"] = vio_cams[i].cal.is_fisheye ?
"equidistant" :
"radtan";
323 YAML::Node extrinsics = YAML::Node(YAML::NodeType::Sequence);
325 vcc_extrinsic_t ext = vio_cams[i].extrinsic;
326 Eigen::Vector3d t_pc_p(
327 ext.T_child_wrt_parent[0],
328 ext.T_child_wrt_parent[1],
329 ext.T_child_wrt_parent[2]
331 const Eigen::Matrix<double,3,3,Eigen::RowMajor> R_cp(&ext.R_child_to_parent[0][0]);
334 Eigen::Matrix4d T_cam_imu = Eigen::Matrix4d::Identity();
335 T_cam_imu.block<3,3>(0,0) = R_cp.transpose();
336 T_cam_imu.block<3,1>(0,3) = -R_cp.transpose() * t_pc_p;
339 Eigen::Matrix4d T_final =
en_imu_body ? (T_cam_imu * T_imu_body) : T_cam_imu;
341 printf(
"\n[INFO] Camera %d (%s) - Applied body transformation\n", i, vio_cams[i].name);
342 printf(
" T_cam_body (body->cam):\n");
343 std::cout << T_final << std::endl;
345 T_final_per_cam[i] = T_final;
347 for (
int row = 0; row < 4; ++row) {
348 YAML::Node row_node = YAML::Node(YAML::NodeType::Sequence);
349 for (
int col = 0; col < 4; ++col) {
350 row_node.push_back(T_final(row, col));
352 extrinsics.push_back(row_node);
356 camchain_config[cam_key][
"T_cam_imu"] = extrinsics;
368 g_stereo_calib_valid =
false;
369 printf(
"[stereo_calib] loaded conf: n_cams=%d using_stereo=%d z=[%.2f,%.1f]m\n",
375 sc.left = (modal_flow::CameraId)0;
376 sc.right = (modal_flow::CameraId)1;
378 sc.K_left [0] = (float)vio_cams[0].cal.fx; sc.K_left [1] = (float)vio_cams[0].cal.fy;
379 sc.K_left [2] = (float)vio_cams[0].cal.cx; sc.K_left [3] = (float)vio_cams[0].cal.cy;
380 sc.K_right[0] = (float)vio_cams[1].cal.fx; sc.K_right[1] = (float)vio_cams[1].cal.fy;
381 sc.K_right[2] = (float)vio_cams[1].cal.cx; sc.K_right[3] = (float)vio_cams[1].cal.cy;
382 for (
int j = 0; j < 4; j++) {
383 sc.D_left [j] = (float)vio_cams[0].cal.D[j];
384 sc.D_right[j] = (float)vio_cams[1].cal.D[j];
387 Eigen::Matrix4d T_cam1_cam0 = Eigen::Matrix4d::Identity();
399 Eigen::Matrix4d Tf0_inv = Eigen::Matrix4d::Identity();
401 Eigen::Matrix3d R0 = T_final_per_cam[0].block<3,3>(0,0);
402 Eigen::Vector3d t0 = T_final_per_cam[0].block<3,1>(0,3);
403 Tf0_inv.block<3,3>(0,0) = R0.transpose();
404 Tf0_inv.block<3,1>(0,3) = -R0.transpose() * t0;
406 T_cam1_cam0 = T_final_per_cam[1] * Tf0_inv;
412 YAML::Node t_node = camchain_config[
"cam1"][
"T_cn_cnm1"];
413 if (t_node && t_node.IsSequence() && t_node.size() == 4) {
414 for (
int r = 0; r < 4; r++) {
415 YAML::Node row = t_node[r];
416 if (!row || !row.IsSequence() || row.size() != 4) {
417 throw std::runtime_error(
"malformed row");
419 for (
int c = 0; c < 4; c++) T_cam1_cam0(r, c) = row[c].as<
double>();
422 throw std::runtime_error(
"missing or malformed T_cn_cnm1");
424 }
catch (
const std::exception &e) {
425 fprintf(stderr,
"[stereo_calib] sync_config=false but T_cn_cnm1 not parseable: %s\n", e.what());
426 T_cam1_cam0 = Eigen::Matrix4d::Identity();
429 Eigen::Matrix3d R = T_cam1_cam0.block<3,3>(0,0);
430 Eigen::Vector3d t = T_cam1_cam0.block<3,1>(0,3);
431 for (
int r = 0; r < 3; r++)
432 for (
int c = 0; c < 3; c++)
433 sc.R_lr[r*3+c] = (
float)R(r,c);
434 sc.t_lr[0] = (float)t(0); sc.t_lr[1] = (float)t(1); sc.t_lr[2] = (float)t(2);
439 double baseline_mm = 1000.0 * t.norm();
440 printf(
"[stereo_calib] composed t_cam1_cam0 = [%.4f, %.4f, %.4f] m (|t|=%.1f mm)\n",
441 t(0), t(1), t(2), baseline_mm);
442 if (baseline_mm > 5.0 && baseline_mm < 500.0) {
443 g_stereo_calib_valid =
true;
444 printf(
"[stereo_calib] packed for ZNCC matcher: baseline=%.1f mm (sync_config=%d)\n",
447 fprintf(stderr,
"[stereo_calib] composed baseline=%.1f mm out of plausible range; refusing\n",
454 double det_R = R.determinant();
455 printf(
"[stereo_calib] R_lr = [% .4f % .4f % .4f]\n",
456 R(0,0), R(0,1), R(0,2));
457 printf(
"[stereo_calib] [% .4f % .4f % .4f]\n",
458 R(1,0), R(1,1), R(1,2));
459 printf(
"[stereo_calib] [% .4f % .4f % .4f] det=% .4f\n",
460 R(2,0), R(2,1), R(2,2), det_R);
461 for (
int i = 0; i < 2 && i < (int)n_cams; i++) {
462 printf(
"[stereo_calib] cam%d: is_fisheye=%d fxy=(%.1f,%.1f) c=(%.1f,%.1f) "
463 "D=[%.4f,%.4f,%.4f,%.4f]\n",
464 i, (
int)vio_cams[i].cal.is_fisheye,
465 vio_cams[i].cal.fx, vio_cams[i].cal.fy,
466 vio_cams[i].cal.cx, vio_cams[i].cal.cy,
467 vio_cams[i].cal.D[0], vio_cams[i].cal.D[1],
468 vio_cams[i].cal.D[2], vio_cams[i].cal.D[3]);
477 printf(
"Writing synced camera chain YAML to %s\n", yaml_camchain_path.c_str());
479 printf(
"[INFO] IMU body mode enabled\n");
481 std::ofstream fout(yaml_camchain_path);
483 fout <<
"%YAML:1.0" << std::endl
485 fout << camchain_config;
487 catch (
const std::exception &e)
489 std::cerr <<
"Failed to write YAML: " << e.what() << std::endl;
528 fprintf(stderr,
"Creating new OV server config file: %s\n",
CONFIG_FILE);
536 json_fetch_bool_with_default(parent,
"en_auto_reset", &
en_auto_reset, 1);
542 json_fetch_float_with_default(parent,
"fast_yaw_thresh", &
fast_yaw_thresh, 5.0f);
543 json_fetch_float_with_default(parent,
"fast_yaw_timeout_s", &
fast_yaw_timeout_s, 1.75f);
544 json_fetch_string_with_default(parent,
"yaml_folder",
folder_base,
CHAR_BUF_SIZE,
"/usr/share/modalai/voxl-open-vins/VoxlConfig/starling2");
545 json_fetch_int_with_default(parent,
"using_stereo", &
using_stereo, 0);
547 json_fetch_bool_with_default(parent,
"takeoff_occlude_stereo_left", (
int *)&
occlude_stereo_left, 0);
548 json_fetch_bool_with_default(parent,
"takeoff_occlude_stereo_right", (
int *)&
occlude_stereo_right, 0);
549 json_fetch_bool_with_default(parent,
"sync_config", (
int *)&
sync_config, 1);
554 json_fetch_float_with_default(parent,
"stereo_z_min", &
stereo_z_min, 0.10f);
555 json_fetch_float_with_default(parent,
"stereo_z_max", &
stereo_z_max, 100.0f);
556 json_fetch_bool_with_default(parent,
"imu_body_frame_mode", (
int *)&
en_imu_body, 1);
567 if (json_get_parse_error_flag())
569 fprintf(stderr,
"failed to parse config file %s\n",
CONFIG_FILE);
570 cJSON_Delete(parent);
575 if (json_get_modified_flag())
577 printf(
"The config file was modified during parsing, saving the changes to disk\n");
580 cJSON_Delete(parent);