VOXL OpenVINS Server 0.7.0
Visual Inertial Odometry Server for VOXL Platform
Loading...
Searching...
No Matches
VoxlConfigure.cpp
Go to the documentation of this file.
1/**
2 * @file VoxlConfigure.cpp
3 * @brief Configuration management implementation for VOXL OpenVINS server
4 * @author Joao Leonardo Silva Cotta (@zauberflote1)
5 * @date 2025
6 * @version 1.0
7 *
8 * This file implements the configuration management system for the VOXL OpenVINS
9 * server. It provides functions for reading server configuration files and
10 * synchronizing camera configurations with the system.
11 *
12 * The implementation handles:
13 * - Camera configuration synchronization with system services
14 * - Server configuration file parsing and validation
15 * - YAML file management for OpenVINS parameters
16 * - Camera calibration and extrinsic parameter handling
17 * - Blind takeoff feature configuration
18 */
19
20#include "VoxlConfigure.h"
21#include <modal_flow/StereoMatcher.hpp>
22
23namespace voxl
24{
25
26 static void printExtrinsic(const vcc_extrinsic_t& ext)
27 {
28 /* guard: empty parent means this slot wasn’t filled in */
29 if (ext.parent[0] == '\0') {
30 std::cout << "Extrinsic has no parent frame assigned.\n";
31 return;
32 }
33
34 std::cout << "========== Extrinsic calibration ==========\n";
35 std::cout << " Parent frame : " << ext.parent << '\n';
36 std::cout << " Child frame : " << ext.child << '\n';
37
38 // Translation vector (m)
39 std::cout << " Translation T_child←parent [m] : "
40 << std::fixed << std::setprecision(3)
41 << ext.T_child_wrt_parent[0] << ", "
42 << ext.T_child_wrt_parent[1] << ", "
43 << ext.T_child_wrt_parent[2] << '\n';
44
45 // Euler angles (deg)
46 std::cout << " RPY parent→child [deg] : "
47 << ext.RPY_parent_to_child[0] << ", "
48 << ext.RPY_parent_to_child[1] << ", "
49 << ext.RPY_parent_to_child[2] << '\n';
50
51 // 3×3 rotation matrix
52 std::cout << " Rotation matrix child←parent :\n";
53 for (int r = 0; r < 3; ++r) {
54 std::cout << " [ ";
55 for (int c = 0; c < 3; ++c)
56 std::cout << std::setw(10) << std::setprecision(6)
57 << ext.R_child_to_parent[r][c] << (c < 2 ? ' ' : '\0');
58 std::cout << " ]\n";
59 }
60 std::cout << "=============================================\n";
61 }
62
63 /**
64 * @brief Synchronize camera configuration with system services
65 *
66 * This function reads camera configuration from system services and
67 * synchronizes the lens intrinsics and distortion model parameters
68 * with the VIO system. It ensures that the camera calibration data
69 * used by the VIO system matches the current system configuration.
70 *
71 * The function performs the following operations:
72 * - Reads camera configuration from VIO camera configuration file
73 * - Validates extrinsic and calibration data presence
74 * - Ensures all cameras use the same IMU
75 * - Updates OpenVINS estimator YAML with camera count
76 * - Updates camera chain YAML with intrinsic parameters
77 * - Configures blind takeoff feature based on camera occlusion
78 * - Populates global camera information vector
79 *
80 * The function updates several YAML files:
81 * - estimator_config.yaml
82 * - kalibr_imucam_chain.yaml
83 *
84 *
85 * @return 0 on success, -1 on failure
86 * @see read_server_config()
87 */
89 {
90 // Initialize global variables
91 cam_info_vec.clear();
92
93 // GRAB CAMERA VIO CONF FILE
94 vio_cam_t vio_cams[MAX_CAM_CNT];
95 int n_cams = vcc_read_vio_cam_conf_file(vio_cams, MAX_CAM_CNT, 1);
96 if (n_cams < 1)
97 return -1;
98
99 // Load body to IMU transformation if IMU body mode is enabled
100 Eigen::Matrix4d T_imu_body = Eigen::Matrix4d::Identity();
101 if (en_imu_body)
102 {
103 vcc_extrinsic_t body_imu_ext;
104 // Fetch the body->imu extrinsic relation
105 if (vcc_fetch_extrinsic("body", vio_cams[0].imu, &body_imu_ext) != 0)
106 {
107 fprintf(stderr, "Failed to fetch body->%s extrinsic. IMU body mode requires this extrinsic to be defined.\n", vio_cams[0].imu);
108 return -1;
109 }
110
111 if (en_verbose) {
112 printf("\n[INFO] IMU body mode enabled - loading body->%s transformation\n", vio_cams[0].imu);
113 printExtrinsic(body_imu_ext);
114 }
115
116 // Build T_imu_body (body -> imu) from the vcc extrinsic.
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]
121 );
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(); // R_body_to_imu
124 T_imu_body.block<3,1>(0,3) = -R_imu_to_body.transpose() * t_imu_wrt_body;
125
126 if (en_verbose) {
127 printf("\n[INFO] T_imu_body transformation matrix (body->imu):\n");
128 std::cout << T_imu_body << std::endl;
129 }
130 }
131
132 // CHECK IF WE HAVE EXTRINSICS AND CAMERA CALIBRATION
133 for (int i = 0; i < n_cams; i++)
134 {
135 if (!vio_cams[i].is_extrinsic_present)
136 {
137 fprintf(stderr, "failed to find extrinsic config for vio cam %s\n", vio_cams[i].name);
138 return -1;
139 }
140 if (!vio_cams[i].is_cal_present)
141 {
142 fprintf(stderr, "failed to find cam cal for vio cam %s\n", vio_cams[i].name);
143 return -1;
144 }
145 }
146
147 // FOR NOW ALL CAMERAS SHOULD BE BASED OFF THE SAME IMU
148 // EXAMPLE: IF CAMERA 0 IS ATTACHED TO A DIFFERENT IMU THAN CAMERA 1, FLAG ERROR
149
150 // CHECK IF WE HAVE A VALID IMU FOR ALL CAMERAS AND IF THEY ARE THE SAME
151 // Security: Use strncpy with bounds checking and ensure null termination
152 strncpy(imu_name, vio_cams[0].imu, sizeof(imu_name) - 1);
153 imu_name[sizeof(imu_name) - 1] = '\0'; // Ensure null termination
154
155 for (int i = 1; i < n_cams; i++)
156 {
157 if (strcmp(vio_cams[i].imu, imu_name) != 0)
158 {
159 fprintf(stderr, "vio cam %s has a different imu than vio cam %s\n", vio_cams[i].name, vio_cams[0].name);
160 return -1;
161 }
162 }
163
164 // If IMU body mode is enabled, append "_body" to the IMU name
165 if (en_imu_body)
166 {
167 // Ensure we have enough space for "_body" suffix
168 size_t current_len = strlen(imu_name);
169 const char* suffix = "_body";
170 size_t suffix_len = strlen(suffix);
171
172 if (current_len + suffix_len >= sizeof(imu_name))
173 {
174 fprintf(stderr, "Error: IMU name '%s' is too long to append '_body' suffix\n", imu_name);
175 return -1;
176 }
177
178 strncat(imu_name, suffix, sizeof(imu_name) - current_len - 1);
179
180 if (en_verbose) {
181 printf("[INFO] IMU body mode: subscribing to '%s' instead of '%s'\n",
182 imu_name, vio_cams[0].imu);
183 }
184 }
185
186 // Detect IMU model from pipe info JSON
187 // Use the original IMU name (without _body suffix) to query pipe info
188 cJSON *imu_json = pipe_get_info_json(vio_cams[0].imu);
189 if (imu_json) {
190 cJSON *model = cJSON_GetObjectItem(imu_json, "imu_model");
191 if (model && cJSON_IsString(model) && model->valuestring) {
192 if (strcmp(model->valuestring, "ICM42688") == 0) {
193 imu_model = IMU_MODEL_ICM42688;
194 } else if (strcmp(model->valuestring, "BMI270") == 0) {
195 imu_model = IMU_MODEL_BMI270;
196 } else {
197 imu_model = IMU_MODEL_UNKNOWN;
198 fprintf(stderr, "WARNING: unrecognized IMU model: %s\n", model->valuestring);
199 }
200 printf("IMU model detected: %s\n", model->valuestring);
201 } else {
202 imu_model = IMU_MODEL_UNKNOWN;
203 fprintf(stderr, "WARNING: imu_model field not found in pipe info\n");
204 }
205 cJSON_Delete(imu_json);
206 } else {
207 imu_model = IMU_MODEL_UNKNOWN;
208 fprintf(stderr, "WARNING: could not read IMU pipe info JSON for '%s'\n", vio_cams[0].imu);
209 }
210
211 // NOW SYNC ESTIMATOR YAML WITH CONF FILE
212 // OPEN ESTIMATOR YAML FILE
213 std::string yaml_estimator_path = std::string(folder_base) + "/estimator_config.yaml";
214 YAML::Node config;
215 try
216 {
217 config = YAML::LoadFile(yaml_estimator_path);
218 }
219 catch (const std::exception &e)
220 {
221 fprintf(stderr, "Failed to load estimator YAML: %s\n", e.what());
222 fprintf(stderr, "Creating new estimator config file\n");
223 // Create a basic config if file doesn't exist
224 config["max_cameras"] = n_cams;
225 }
226
227 // Set prop_window and zupt_prop_window based on IMU model
228 switch (imu_model) {
229 case IMU_MODEL_ICM42688:
230 config["prop_window"] = 0.1;
231 config["zupt_prop_window"] = 0.1;
232 break;
233 case IMU_MODEL_BMI270:
234 config["prop_window"] = 0.075;
235 config["zupt_prop_window"] = 0.1;
236 break;
237 default:
238 config["prop_window"] = 0.075; // default fallback
239 config["zupt_prop_window"] = 0.1; // default fallback
240 break;
241 }
242
243 // SYNC MAX CAMERAS
244 config["max_cameras"] = n_cams;
245
246 // Write estimator config with IMU-specific and camera settings
247 if (sync_config) {
248 try
249 {
250 std::ofstream fout(yaml_estimator_path);
251 fout << "%YAML:1.0" << std::endl << std::endl;
252 fout << config;
253 }
254 catch (const std::exception &e)
255 {
256 std::cerr << "Failed to write estimator YAML: " << e.what() << std::endl;
257 return -1;
258 }
259 }
260 // NOW SYNC INTRINSICS AND DISTORTION MODEL
261
262 // LOAD CAMCHAIN YAML FILE
263 std::string yaml_camchain_path = std::string(folder_base) + "/kalibr_imucam_chain.yaml";
264 YAML::Node camchain_config;
265 try
266 {
267 camchain_config = YAML::LoadFile(yaml_camchain_path);
268 }
269 catch (const std::exception &e)
270 {
271 fprintf(stderr, "Failed to load YAML: %s\n", e.what());
272 return -1;
273 }
274 // Per-cam body/IMU->cam transform; composed into the stereo extrinsic below.
275 std::vector<Eigen::Matrix4d> T_final_per_cam(n_cams, Eigen::Matrix4d::Identity());
276 for (int i = 0; i < n_cams; i++)
277 {
278
279 std::string cam_key = "cam" + std::to_string(i);
280
281 cam_info cam;
282 // Security: Use strncpy with bounds checking and ensure null termination
283 strncpy(cam.name, vio_cams[i].name, sizeof(cam.name) - 1);
284 cam.name[sizeof(cam.name) - 1] = '\0';
285
286 strncpy(cam.tracking_name, vio_cams[i].pipe_for_tracking, sizeof(cam.tracking_name) - 1);
287 cam.tracking_name[sizeof(cam.tracking_name) - 1] = '\0';
288
289 cam.mode = using_stereo ? STEREO : MONO;
290 cam.cam_id = i;
291
292 printf("vio cam: %d is occluded: %d\n", i, vio_cams[i].is_occluded_on_ground);
293 // Only the occlusion flag is consumed downstream (takeoff masking in MonoCamera);
294 // the estimator gets intrinsics/extrinsics from the YAML chain, not cam_info
295 cam.is_occluded_on_takeoff = vio_cams[i].is_occluded_on_ground;
296
297 // INTRINSICS, DISTORTION MODEL, AND RESOLUTION
298 // Set intrinsics as array [fx, fy, cx, cy]
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;
305
306 // Set distortion coefficients as array [D[0], D[1], D[2], D[3]]
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;
313
314 // Set resolution as array [width, height]
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;
319
320 camchain_config[cam_key]["distortion_model"] = vio_cams[i].cal.is_fisheye ? "equidistant" : "radtan";
321
322 // Set extrinsics as a 4x4 matrix
323 YAML::Node extrinsics = YAML::Node(YAML::NodeType::Sequence);
324
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]
330 );
331 const Eigen::Matrix<double,3,3,Eigen::RowMajor> R_cp(&ext.R_child_to_parent[0][0]);
332
333 // T_cam_imu (imu -> cam).
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;
337
338 // T_final is body->cam under en_imu_body, otherwise imu->cam.
339 Eigen::Matrix4d T_final = en_imu_body ? (T_cam_imu * T_imu_body) : T_cam_imu;
340 if (en_imu_body && en_verbose) {
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;
344 }
345 T_final_per_cam[i] = T_final;
346
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));
351 }
352 extrinsics.push_back(row_node);
353 }
354 // Under en_imu_body the YAML "T_cam_imu" key carries body->cam, so the
355 // VIO state lives in body frame instead of IMU frame.
356 camchain_config[cam_key]["T_cam_imu"] = extrinsics;
357
358 cam_info_vec.push_back(cam);
359 }
360
361 // Pack the stereo calibration for the ZNCC epipolar-band matcher.
362 // Source for the cam0->cam1 extrinsic depends on sync_config:
363 // true -> compose from per-cam T_cam_imu (authoritative when sync)
364 // false -> read T_cn_cnm1 from the yaml directly
365 // We persist into VoxlVars globals here -- VoxlSpinner resets
366 // vio_manager_options to defaults after this runs and then copies the
367 // globals back in.
368 g_stereo_calib_valid = false;
369 printf("[stereo_calib] loaded conf: n_cams=%d using_stereo=%d z=[%.2f,%.1f]m\n",
370 n_cams, (int)using_stereo,
371 (double)stereo_z_min, (double)stereo_z_max);
372 if (n_cams >= 2)
373 {
374 modal_flow::StereoCalib &sc = g_stereo_calib;
375 sc.left = (modal_flow::CameraId)0;
376 sc.right = (modal_flow::CameraId)1;
377 // Intrinsics + equidistant fisheye distortion from vio_cams.cal.
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];
385 }
386 // Compose T_cam1_cam0 = T_final[1] * T_final[0]^-1; the body/IMU frame cancels.
387 Eigen::Matrix4d T_cam1_cam0 = Eigen::Matrix4d::Identity();
388 if (sync_config) {
389 // NOTE: do NOT use Eigen's general .inverse() here. On this build
390 // chain (Eigen 3.x linked into voxl-open-vins-server) it has been
391 // observed to return a wrong matrix for proper SE(3) inputs:
392 // given T = [R | t; 0 | 1] with orthogonal R (det=+1), .inverse()
393 // returned [R^T * R_z(180) | wrong t; 0 | 1] instead of the SE(3)
394 // closed form, corrupting T_cam1_cam0 with a phantom 180-deg
395 // rotation around the optical axis and doubling ||t|| (silent ZNCC
396 // matcher failure). Compose the inverse explicitly using the SE(3)
397 // closed form ([R t; 0 1]^-1 = [R^T -R^T t; 0 1]); also faster
398 // than general 4x4 cofactor inversion.
399 Eigen::Matrix4d Tf0_inv = Eigen::Matrix4d::Identity();
400 {
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;
405 }
406 T_cam1_cam0 = T_final_per_cam[1] * Tf0_inv;
407 } else {
408 // Parse the in-memory yaml (loaded by camchain_config). Falls
409 // back to identity if the entry isn't there, in which case we
410 // refuse to mark the calib valid below.
411 try {
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");
418 }
419 for (int c = 0; c < 4; c++) T_cam1_cam0(r, c) = row[c].as<double>();
420 }
421 } else {
422 throw std::runtime_error("missing or malformed T_cn_cnm1");
423 }
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(); // sentinel
427 }
428 }
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);
435 sc.z_min = stereo_z_min;
436 sc.z_max = stereo_z_max;
437
438 // Sanity: reject pathological compositions (zero baseline, non-rotation R).
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",
445 baseline_mm, (int)sync_config);
446 } else {
447 fprintf(stderr, "[stereo_calib] composed baseline=%.1f mm out of plausible range; refusing\n",
448 baseline_mm);
449 }
450
451 // Startup dump: R_lr should be ~I (or a known mount rotation), det=+1,
452 // and is_fisheye=1 on both cams (matcher kernel is equidistant-only).
453 {
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]);
469 }
470 }
471 }
472
473 // NOW SAVE CAMCHAIN YAML FILE
474 if(sync_config){
475 try
476 {
477 printf("Writing synced camera chain YAML to %s\n", yaml_camchain_path.c_str());
478 if (en_imu_body) {
479 printf("[INFO] IMU body mode enabled\n");
480 }
481 std::ofstream fout(yaml_camchain_path);
482 // Write YAML header first
483 fout << "%YAML:1.0" << std::endl
484 << std::endl;
485 fout << camchain_config;
486 }
487 catch (const std::exception &e)
488 {
489 std::cerr << "Failed to write YAML: " << e.what() << std::endl;
490 return -1;
491 }
492 }
493
494 return 0;
495 }
496
497 /**
498 * @brief Read and parse server configuration file
499 *
500 * This function reads the main server configuration file and parses
501 * all the parameters needed for VIO operation. It handles JSON format
502 * configuration files and validates the parameters.
503 *
504 * The function reads configuration for:
505 * - Auto-reset parameters and thresholds
506 * - Velocity covariance limits and timeouts
507 * - Feature count requirements and timeouts
508 * - Auto-fallback mode settings
509 * - Yaw monitoring and fast yaw detection
510 * - Debug output settings
511 *
512 * The function creates a new configuration file with default values
513 * if one doesn't exist, and saves any modifications made during
514 * parsing back to disk.
515 *
516 * Configuration file location: /etc/modalai/voxl-open-vins-server.conf
517 *
518 * @return 0 on success, -1 on failure
519 * @see sync_cam_config()
520 */
522 {
523 // THESE ARE HIGHER LEVEL CONFIGS FOR THE OV SERVER NOT OV ITSELF!
524 int ret = json_make_empty_file_with_header_if_missing(CONFIG_FILE, CONFIG_FILE_HEADER);
525 if (ret < 0)
526 return -1;
527 else if (ret > 0)
528 fprintf(stderr, "Creating new OV server config file: %s\n", CONFIG_FILE);
529
530 cJSON *parent = json_read_file(CONFIG_FILE);
531 if (parent == NULL)
532 return -1;
533
534 char string_holder[CHAR_BUF_SIZE];
535 memset(string_holder, '\0', CHAR_BUF_SIZE);
536 json_fetch_bool_with_default(parent, "en_auto_reset", &en_auto_reset, 1);
537 json_fetch_float_with_default(parent, "auto_reset_max_velocity", &auto_reset_max_velocity, 20.0f);
538 json_fetch_float_with_default(parent, "auto_reset_max_v_cov_timeout_s", &auto_reset_max_v_cov_timeout_s, 0.5f);
539 json_fetch_int_with_default(parent, "auto_reset_min_features", &auto_reset_min_features, 1);
540 json_fetch_float_with_default(parent, "auto_reset_min_feature_timeout_s", &auto_reset_min_feature_timeout_s, 10.0f);
541 json_fetch_float_with_default(parent, "ok_state_grace_timeout_s", &ok_state_grace_timeout_s, 2.0f);
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);
546 json_fetch_float_with_default(parent, "takeoff_alt_threshold", &takeoff_alt_threshold, 0.5f);
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);
550 // Server-side IMU frame-transform bring-up gate (deg). Default -1 => any attitude (feed the
551 // estimator immediately so the ceres-free S2 dynamic init can (re)init/reset at any attitude).
552 json_fetch_float_with_default(parent, "imu_init_max_gravity_angle_deg", &imu_init_max_gravity_angle_deg, -1.0f);
553
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);
557
558 // Quality hysteresis threshold configuration
559 json_fetch_int_with_default(parent, "quality_low_thresh_initial", &quality_low_thresh_initial, 15);
560 json_fetch_int_with_default(parent, "quality_low_thresh_good", &quality_low_thresh_good, 14);
561 json_fetch_int_with_default(parent, "quality_high_thresh", &quality_high_thresh, 35);
562 json_fetch_int_with_default(parent, "quality_initial_to_bad_count", &quality_initial_to_bad_count, 20);
563 json_fetch_int_with_default(parent, "quality_initial_to_good_count", &quality_initial_to_good_count, 50);
564 json_fetch_int_with_default(parent, "quality_bad_to_good_count", &quality_bad_to_good_count, 60);
565 json_fetch_int_with_default(parent, "quality_good_to_bad_count", &quality_good_to_bad_count, 45);
566
567 if (json_get_parse_error_flag())
568 {
569 fprintf(stderr, "failed to parse config file %s\n", CONFIG_FILE);
570 cJSON_Delete(parent);
571 return -1;
572 }
573
574 // write modified data to disk if neccessary
575 if (json_get_modified_flag())
576 {
577 printf("The config file was modified during parsing, saving the changes to disk\n");
578 json_write_to_file_with_header(CONFIG_FILE, parent, CONFIG_FILE_HEADER);
579 }
580 cJSON_Delete(parent);
581 return 0;
582 }
583
584} // namespace voxl
@ MONO
Single camera mode.
Definition VoxlCommon.h:92
@ STEREO
Stereo camera mode (both cameras active)
Definition VoxlCommon.h:93
Configuration management for VOXL OpenVINS server.
#define CONFIG_FILE
Default configuration file path.
#define CONFIG_FILE_HEADER
Configuration file header comment.
int en_verbose
Enable verbose output.
Definition VoxlVars.cpp:152
bool occlude_stereo_right
Occlude stereo right.
Definition VoxlVars.cpp:203
char folder_base[CHAR_BUF_SIZE]
Base folder for yaml configuration files.
Definition VoxlVars.cpp:146
int quality_initial_to_bad_count
Consecutive samples for INITIAL→BAD transition.
Definition VoxlVars.cpp:131
int quality_low_thresh_initial
Quality low threshold for INITIAL state.
Definition VoxlVars.cpp:122
int quality_bad_to_good_count
Consecutive samples for BAD→GOOD transition.
Definition VoxlVars.cpp:137
float fast_yaw_thresh
Auto fallback timeout (seconds)
Definition VoxlVars.cpp:112
int en_auto_reset
Enable automatic reset functionality.
Definition VoxlVars.cpp:84
float auto_reset_max_velocity
Maximum velocity threshold for auto reset.
Definition VoxlVars.cpp:87
float stereo_z_max
far depth bound for epipolar search (m)
Definition VoxlVars.cpp:222
char imu_name[64]
IMU device name.
Definition VoxlVars.cpp:165
modal_flow::StereoCalib g_stereo_calib
composed by VoxlConfigure
Definition VoxlVars.cpp:223
bool occlude_stereo_left
Occlude stereo left.
Definition VoxlVars.cpp:200
int quality_good_to_bad_count
Consecutive samples for GOOD→BAD transition.
Definition VoxlVars.cpp:140
float auto_reset_max_v_cov_timeout_s
Maximum velocity covariance for instant reset.
Definition VoxlVars.cpp:94
float stereo_z_min
near depth bound for epipolar search (m)
Definition VoxlVars.cpp:221
int using_stereo
using_stereo
Definition VoxlVars.cpp:143
float imu_init_max_gravity_angle_deg
Server-side IMU frame-transform bring-up gravity gate (degrees)
Definition VoxlVars.cpp:230
float ok_state_grace_timeout_s
Minimum amount of time after initialization that quality is held low (CEP)
Definition VoxlVars.cpp:103
bool en_imu_body
Enable IMU body measurements.
Definition VoxlVars.cpp:158
float auto_reset_min_feature_timeout_s
Minimum feature timeout for auto reset (seconds)
Definition VoxlVars.cpp:100
float takeoff_alt_threshold
Takeoff altitude threshold.
Definition VoxlVars.cpp:197
imu_model_t imu_model
Active IMU model.
Definition VoxlVars.cpp:168
float fast_yaw_timeout_s
Fast yaw timeout (seconds)
Definition VoxlVars.cpp:115
int auto_reset_min_features
Minimum number of features for auto reset.
Definition VoxlVars.cpp:97
int quality_high_thresh
Quality high threshold for recovery.
Definition VoxlVars.cpp:128
std::vector< cam_info > cam_info_vec
Vector of camera configuration information.
Definition VoxlVars.cpp:171
int quality_low_thresh_good
Quality low threshold for GOOD state.
Definition VoxlVars.cpp:125
int quality_initial_to_good_count
Consecutive samples for INITIAL→GOOD transition.
Definition VoxlVars.cpp:134
bool sync_config
Flag to indicate if configuration synchronization is enabled.
Definition VoxlVars.cpp:218
#define CHAR_BUF_SIZE
Standard character buffer size.
Definition VoxlVars.h:256
#define MAX_CAM_CNT
Maximum number of cameras supported by VOXL2.
Definition VoxlVars.h:249
Main namespace for VOXL OpenVINS server components.
int sync_cam_config(void)
Synchronize camera configuration with system services.
int read_server_config(void)
Read and parse server configuration file.
Camera information and calibration data.
Definition VoxlCommon.h:146
char name[128]
Camera name identifier.
Definition VoxlCommon.h:147
bool is_occluded_on_takeoff
Flag indicating if camera is occluded on takeoff.
Definition VoxlCommon.h:150
char tracking_name[128]
Name used for tracking operations.
Definition VoxlCommon.h:148
camera_mode mode
Camera operation mode.
Definition VoxlCommon.h:149
size_t cam_id
Unique camera identifier.
Definition VoxlCommon.h:151