VOXL OpenVINS Server 0.7.0
Visual Inertial Odometry Server for VOXL Platform
Loading...
Searching...
No Matches
VoxlHK.h
Go to the documentation of this file.
1/**
2 * @file VoxlHK.h
3 * @brief Housekeeping and data publishing for VOXL OpenVINS
4 * @author Joao Leonardo Silva Cotta (@zauberflote1)
5 * @date 2025
6 * @version 1.0
7 *
8 * This header defines the housekeeping system for the VOXL OpenVINS server,
9 * including data publishing, angular velocity calculations, and coordinate
10 * frame transformations. It provides the interface for outputting VIO data
11 * to external systems.
12 */
13
14#ifndef VOXL_HK_H
15#define VOXL_HK_H
16#pragma once
17
18// Standard includes
19#include <atomic>
20#include <memory>
21
22// Third-party includes
23#include <modal_pipe.h>
24#include <modal_json.h>
25#include <voxl_common_config.h>
26#include <core/VioManager.h>
27#include <core/VioManagerOptions.h>
28#include <types/LandmarkRepresentation.h>
29#include <cstring>
30#include <state/State.h>
31#include <state/StateHelper.h>
32#include <types/Landmark.h>
33#include <types/Type.h>
34#include <feat/Feature.h>
35#include <cmath>
36#include <algorithm>
37#include <thread>
38#include <chrono>
39#include <Eigen/Eigenvalues>
40// Local includes
41#include "VoxlVars.h"
42#include "VoxlCommon.h"
43
44namespace voxl
45{
46
47 // ============================================================================
48 // UTILITY FUNCTIONS
49 // ============================================================================
50
51 /**
52 * @brief Calculate angular velocity from consecutive quaternions
53 *
54 * Computes angular velocity via axis-angle extraction from the relative
55 * rotation dq = q1 * inv(q0). Falls back to a small-angle approximation
56 * when the rotation is near-identity (degenerate case).
57 *
58 * @param q0 Previous quaternion (4x1, JPL convention)
59 * @param q1 Current quaternion (4x1, JPL convention)
60 * @param dt Time difference in **nanoseconds**
61 * @return Angular velocity vector (3x1) in rad/s
62 */
63 inline Eigen::Matrix<double, 3, 1> dirtyOmega(const Eigen::Matrix<double, 4, 1> &q0,
64 const Eigen::Matrix<double, 4, 1> &q1,
65 double dt)
66 {
67 // Convert nanoseconds to seconds
68 dt *= 1e-9;
69
70 if (dt <= 0.0) return Eigen::Vector3d::Zero();
71 if (q0.isZero(0) || q1.isZero(0)) return Eigen::Vector3d::Zero();
72
73 // Relative rotation: dq = q1 * inv(q0)
74 Eigen::Matrix<double, 4, 1> dq = ov_core::quat_multiply(q1, ov_core::Inv(q0));
75 dq /= dq.norm();
76
77 // Shortest path
78 if (dq.w() < 0.0)
79 dq = -dq;
80
81 double w = std::clamp(dq.w(), -1.0, 1.0);
82 double sin_half = std::sqrt(std::max(1.0 - w * w, 0.0));
83
84 // Degenerate (small-angle) case: fall back to simple approximation
85 if (sin_half < 1e-8)
86 return (2.0 / dt) * dq.head<3>();
87
88 // General case: proper axis-angle extraction
89 double theta = 2.0 * std::acos(w);
90 Eigen::Vector3d axis = dq.head<3>() / sin_half;
91 return axis * (theta / dt);
92 }
93
94 /**
95 * @brief Get OpenVINS to FRD coordinate frame transformation matrix
96 *
97 * This function returns the rotation matrix that transforms from the
98 * OpenVINS coordinate frame to the Front-Right-Down (FRD) coordinate frame
99 * commonly used in aerospace applications.
100 *
101 * TODO: Port this for greater possibility of orientations --> case by case etc
102 *
103 * @return 3x3 rotation matrix from OpenVINS to FRD frame
104 */
105 inline const Eigen::Matrix3d &R_OV_FRD()
106 {
107 static const Eigen::Matrix3d R{
108 (Eigen::Matrix3d() << 1, 0, 0, 0, -1, 0, 0, 0, -1).finished()};
109 return R;
110 }
111
112 // ============================================================================
113 // PUBLISHER CLASS
114 // ============================================================================
115
116 /**
117 * @class Publisher
118 * @brief Singleton class for publishing VIO data
119 *
120 * This class manages the publication of VIO data to external systems through
121 * pipe interfaces. It implements the singleton pattern to ensure only one
122 * instance exists throughout the application lifecycle.
123 *
124 * The publisher handles:
125 * - VIO state data formatting
126 * - Track base information
127 * - Coordinate frame transformations
128 * - Data packet generation and transmission
129 */
131 {
132 public:
133 /**
134 * @brief Get singleton instance
135 * @return Reference to the singleton Publisher instance
136 */
138 {
139 static Publisher instance;
140 return instance;
141 }
142
143 // Delete copy constructor and assignment operator for singleton
144 Publisher(const Publisher &) = delete;
145 Publisher &operator=(const Publisher &) = delete;
146
147 /**
148 * @brief Start the publisher
149 *
150 * Initializes the publisher and prepares it for data transmission.
151 */
152 void start();
153
154 /**
155 * @brief Control pipe callback function
156 *
157 * Callback function to handle control pipe messages
158 */
159 static void ov_vio_control_pipe_cb(int ch, char *string, int bytes, void *context);
160
161 /**
162 * @brief Publish VIO data
163 *
164 * Publishes the current VIO state and tracking information to external
165 * systems through the configured pipe interfaces.
166 *
167 * @param state Current VIO state
168 * @param used_features_map Used-features map, by reference (no copy). The caller runs on
169 * the VIO thread synchronously after the update, so the reference stays valid for
170 * the whole call; publish must never re-enter feed/drain.
171 */
172 void publish(std::shared_ptr<ov_msckf::State> state,
173 const std::map<double, std::vector<std::shared_ptr<ov_core::Feature>>> &used_features_map = {});
174
175 /**
176 * @brief Stop the publisher
177 *
178 * Stops the publisher and cleans up resources.
179 */
180 void stop();
181
182 /**
183 * @brief Check if auto-reset should be triggered
184 *
185 * Evaluates current VIO state and error conditions to determine
186 * if an automatic reset should be triggered.
187 *
188 * @param state Current VIO state
189 * @param quality Current quality value
190 * @param n_features Number of tracked features
191 * @param yawrate Calculated yaw rate from angular velocity
192 * @param current_velocity Current velocity magnitude
193 * @param vel_x X-component of velocity
194 * @param vel_y Y-component of velocity
195 * @return true if auto-reset should be triggered, false otherwise
196 */
197 bool should_auto_reset(std::shared_ptr<ov_msckf::State> state,
198 int quality,
199 int n_features,
200 double yawrate,
201 double current_velocity,
202 double vel_x,
203 double vel_y);
204
205 /**
206 * @brief Calculate Quality of the VIO state (async-aware per-camera freshness ledger)
207 *
208 * Pools all used-feature entries from the last QUAL_UNION_WINDOW_S (dedup by featid) and
209 * scores each camera over the pooled set with the unchanged synced-path grid metric
210 * (5x5 grid, SLAM features weighted by covariance largest eigenvalue + quality field,
211 * MSCKF by quality field + track length), sum-then-clamp as sync always fused. The union
212 * gives each camera its full recent track set -- one update's used list is a sparse,
213 * noisy sample -- so async ticks score like synced batches; a time-constant EMA smooths
214 * the published value against single weak updates. Floor is 10 while running (the metric
215 * alone must not trip the sustained quality<1 auto-reset timers); resets/FAILED force
216 * 0/-1 in publish() itself.
217 *
218 * Must only be called from the VIO thread (publish); uses the qual_ema state with no
219 * locks by design.
220 *
221 * @param used_features_map Map of used features keyed by update (epoch) timestamp
222 * @param slam_features Map of SLAM features from the state
223 * @param state Current VIO state for covariance access
224 * @return Quality score (10-100 while running, higher is better)
225 */
226 double calcQuality(const std::map<double, std::vector<std::shared_ptr<ov_core::Feature>>> &used_features_map,
227 std::unordered_map<size_t, std::shared_ptr<ov_type::Landmark>> &slam_features,
228 std::shared_ptr<ov_msckf::State> state);
229
230 /**
231 * @brief Set the first packet flag
232 *
233 * This function sets the first packet flag to the provided value.
234 *
235 * @param first_packet The value to set the first packet flag to
236 */
237 void set_first_packet(bool first_packet_)
238 {
239 first_packet = first_packet_;
240 };
241 // ADD BLANK PUBLISHER TO INDICATE MISSING SENSORS
242 void publishBlank();
243
244 private:
245 /**
246 * @brief Private constructor for singleton pattern
247 */
248 Publisher();
249
250 /**
251 * @brief Private destructor for singleton pattern
252 */
253 ~Publisher();
254
255 // ============================================================================
256 // PRIVATE MEMBER VARIABLES
257 // ============================================================================
258
259 /** @brief Flag indicating if this is the first packet */
260 bool first_packet = true;
261
262 /** @brief Tracks the rising edge of vio_manager->initialized() so the
263 * origin snapshot is re-captured on every (re)init, including in-flight
264 * resets where the Publisher singleton persists. */
265 bool prev_initialized = false;
266
267 /** @brief Yaw-only origin rotation captured at each init. Zeros the
268 * (unobservable) heading at start while preserving gravity-true
269 * roll/pitch. Identity until the first init. */
270 Eigen::Matrix3d ned_rot_zero = Eigen::Matrix3d::Identity();
271
272 /** @brief VIO data packet structure */
273 vio_data_t vio_packet;
274
275 /** @brief Previous quaternion for angular velocity calculation */
276 Eigen::Matrix<double, 4, 1> past_q_I_G;
277
278 /** @brief State for the async-aware quality metric (see calcQuality). Written only
279 * inside publish() on the VIO/ingest thread -- single-writer, single-reader, no locks
280 * by design. Invalidated by publish() on the same condition that resets the quality
281 * hysteresis machine (reset counter change / FAILED), which fires for BOTH hard and
282 * soft resets, so stale pre-reset values can never pre-charge a new episode (sensor
283 * time is continuous across resets -- timestamps alone cannot tell). */
284 static constexpr int QUAL_MAX_CAMS = 4;
285 double qual_ema = -1.0; ///< smoothed fused quality; <0 = unseeded/invalid
286 double qual_ema_t = 0.0; ///< sensor time of the last EMA update
287 double qual_src_key = 0.0; ///< newest map key last scored (rescore fingerprint)
288 size_t qual_src_size = 0; ///< size of that entry when scored (appends re-dirty it)
289 size_t qual_src_map_size = 0; ///< map size when scored
290 };
291
292 // ============================================================================
293 // HEALTH CHECK CLASS
294 // ============================================================================
295
296 /**
297 * @class HealthCheck
298 * @brief Health monitoring system for VOXL OpenVINS
299 *
300 * This class provides comprehensive health monitoring capabilities for
301 * the VIO system, including error code monitoring, system state checks,
302 * and performance monitoring. It runs at 30Hz and continuously monitors
303 * the system health.
304 */
306 {
307 public:
308 /**
309 * @brief Get singleton instance
310 * @return Reference to the singleton HealthCheck instance
311 */
313 {
314 static HealthCheck instance;
315 return instance;
316 }
317
318 // Delete copy constructor and assignment operator for singleton
319 HealthCheck(const HealthCheck &) = delete;
320 HealthCheck &operator=(const HealthCheck &) = delete;
321
322 /**
323 * @brief Start the health check system
324 *
325 * Initializes and starts the health monitoring thread that runs at 30Hz.
326 * The thread continuously monitors system health and error conditions.
327 */
328 void start();
329
330 /**
331 * @brief Stop the health check system
332 *
333 * Stops the health monitoring thread and performs cleanup.
334 */
335 void stop();
336
337 /**
338 * @brief Check if health monitoring is running
339 * @return true if health check is active, false otherwise
340 */
341 bool isRunning() const { return running_.load(); }
342
343 /**
344 * @brief Clear specific error codes
345 *
346 * Clears the specified error codes from the global error state.
347 * This is useful when errors are resolved and should no longer
348 * be reported.
349 *
350 * @param error_mask Bit mask of error codes to clear
351 * @param clear_all If true, clear all error codes
352 */
353 static void clearErrorCodes(uint32_t error_mask)
354 {
355 clearErrorCodes(error_mask, false);
356 }
357 static void clearErrorCodes(uint32_t error_mask, bool clear_all);
358
359 private:
360 /**
361 * @brief Private constructor for singleton pattern
362 */
363 HealthCheck();
364
365 /**
366 * @brief Private destructor for singleton pattern
367 */
368 ~HealthCheck();
369
370 /**
371 * @brief Main health check loop
372 *
373 * Runs at 30Hz and performs comprehensive health monitoring including:
374 * - Error code analysis and logging
375 * - System state validation
376 * - Performance monitoring
377 * - Auto-reset condition checking
378 */
379 void healthCheckLoop();
380
381 /**
382 * @brief Analyze and log error codes
383 *
384 * Examines the current error codes and logs detailed information
385 * about any active errors or warnings.
386 */
387 void analyzeErrorCodes();
388
389 /**
390 * @brief Check system connectivity
391 *
392 * Monitors the connection status of cameras and IMU, logging
393 * any disconnection events or connectivity issues.
394 */
395 void checkSystemConnectivity();
396
397 /**
398 * @brief Monitor system performance
399 *
400 * Tracks system performance metrics including processing rates,
401 * memory usage, and timing statistics.
402 */
403 void monitorSystemPerformance();
404
405 /**
406 * @brief Check auto-reset conditions
407 *
408 * Evaluates whether auto-reset conditions are met based on
409 * current system state and error conditions.
410 */
411 void checkAutoResetConditions();
412
413 /**
414 * @brief Handle VINS reset command
415 *
416 * This function is invoked when a reset command is received.
417 * It performs the necessary actions to reset the VIO system.
418 */
419 void checkVINSResetRequest();
420
421 /**
422 * @brief Perform a hard reset of the VIO system
423 *
424 * This function creates a fresh instance of the
425 * VIO manager and reinitializes the system.,
426 */
427 int doHardReset();
428
429 /**
430 * @brief Perform a front-end-preserving SOFT reset.
431 *
432 * Resets the navigation EKF via VioManager::soft_reset() while keeping the feature DB and
433 * IMU history so re-init fires fast (no full-window re-collection). Escalates to
434 * doHardReset() on failure or if there is no live VIO manager.
435 */
436 int doSoftReset();
437
438 // ============================================================================
439 // PRIVATE MEMBER VARIABLES
440 // ============================================================================
441
442 /** @brief Flag indicating if health check is running */
443 std::atomic<bool> running_{false};
444
445 /** @brief Health check thread */
446 std::thread health_thread_;
447
448 /** @brief Last error code state for change detection */
449 uint32_t last_error_codes_{0};
450
451 /** @brief Last VIO state for change detection */
452 uint8_t last_vio_state_{0};
453
454 /** @brief Last IMU connection state for change detection */
455 bool last_imu_connected_{false};
456
457 /** @brief Last camera connection state for change detection */
458 bool last_cam_connected_{false};
459
460 /** @brief Used to track the first camera connection */
461 bool first_camera_connection_seen_{false};
462
463 /** @brief Timestamp of last health check */
464 int64_t last_health_check_ns_{0};
465
466 /** @brief Counter for health check iterations */
467 uint64_t health_check_count_{0};
468
469 /** @brief Mutex for thread safety */
470 mutable std::mutex health_mutex_;
471
472 /** @brief Timeout interval before we can reset again */
473 const uint64_t INIT_FAILURE_TIMEOUT_NS = 2000000000; // 2 seconds
474
475 /** @brief Timestamp of last reset */
476 uint64_t time_of_last_reset = 0;
477 };
478
479} // namespace voxl
480
481#endif // VOXL_HK_H
Common definitions and utilities for the VOXL OpenVINS server.
Global variable declarations and constants for VOXL OpenVINS server.
Health monitoring system for VOXL OpenVINS.
Definition VoxlHK.h:306
void start()
Start the health check system.
bool isRunning() const
Check if health monitoring is running.
Definition VoxlHK.h:341
static void clearErrorCodes(uint32_t error_mask)
Clear specific error codes.
Definition VoxlHK.h:353
void stop()
Stop the health check system.
static HealthCheck & getInstance()
Get singleton instance.
Definition VoxlHK.h:312
Singleton class for publishing VIO data.
Definition VoxlHK.h:131
void set_first_packet(bool first_packet_)
Set the first packet flag.
Definition VoxlHK.h:237
static Publisher & getInstance()
Get singleton instance.
Definition VoxlHK.h:137
static void ov_vio_control_pipe_cb(int ch, char *string, int bytes, void *context)
Control pipe callback function.
void stop()
Stop the publisher.
void start()
Start the publisher.
bool should_auto_reset(std::shared_ptr< ov_msckf::State > state, int quality, int n_features, double yawrate, double current_velocity, double vel_x, double vel_y)
Check if auto-reset should be triggered.
void publish(std::shared_ptr< ov_msckf::State > state, const std::map< double, std::vector< std::shared_ptr< ov_core::Feature > > > &used_features_map={})
Publish VIO data.
double calcQuality(const std::map< double, std::vector< std::shared_ptr< ov_core::Feature > > > &used_features_map, std::unordered_map< size_t, std::shared_ptr< ov_type::Landmark > > &slam_features, std::shared_ptr< ov_msckf::State > state)
Calculate Quality of the VIO state (async-aware per-camera freshness ledger)
Main namespace for VOXL OpenVINS server components.
Eigen::Matrix< double, 3, 1 > dirtyOmega(const Eigen::Matrix< double, 4, 1 > &q0, const Eigen::Matrix< double, 4, 1 > &q1, double dt)
Calculate angular velocity from consecutive quaternions.
Definition VoxlHK.h:63
const Eigen::Matrix3d & R_OV_FRD()
Get OpenVINS to FRD coordinate frame transformation matrix.
Definition VoxlHK.h:105