mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-05-27 10:17:45 +08:00
sensors: sensor_preflight_imu -> sensors_status_imu and run continuously
- inconsistency checks now run continuously instead of only preflight - keep inconsistencies for all sensors - add per sensor data validator state as overall health flag
This commit is contained in:
@@ -862,6 +862,7 @@ void statusFTDI() {
|
|||||||
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener sensor_gyro"'
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener sensor_gyro"'
|
||||||
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener sensor_gyro_fifo"'
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener sensor_gyro_fifo"'
|
||||||
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener sensor_mag"'
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener sensor_mag"'
|
||||||
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener sensors_status_imu"'
|
||||||
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener px4io_status"'
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener px4io_status"'
|
||||||
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener system_power"'
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener system_power"'
|
||||||
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener vehicle_air_data"'
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-FTDI_*` --cmd "listener vehicle_air_data"'
|
||||||
@@ -934,6 +935,7 @@ void statusSEGGER() {
|
|||||||
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener sensor_gyro"'
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener sensor_gyro"'
|
||||||
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener sensor_gyro_fifo"'
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener sensor_gyro_fifo"'
|
||||||
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener sensor_mag"'
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener sensor_mag"'
|
||||||
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener sensors_status_imu"'
|
||||||
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener px4io_status"'
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener px4io_status"'
|
||||||
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener system_power"'
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener system_power"'
|
||||||
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener vehicle_air_data"'
|
sh './Tools/HIL/run_nsh_cmd.py --device `find /dev/serial -name *usb-SEGGER_*` --cmd "listener vehicle_air_data"'
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ def get_gps_check_fail_flags(estimator_status: dict) -> dict:
|
|||||||
return gps_fail_flags
|
return gps_fail_flags
|
||||||
|
|
||||||
|
|
||||||
def magnetic_field_estimates_from_status(estimator_states: dict) -> Tuple[float, float, float]:
|
def magnetic_field_estimates_from_states(estimator_states: dict) -> Tuple[float, float, float]:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:param estimator_states:
|
:param estimator_states:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import numpy as np
|
|||||||
from matplotlib.backends.backend_pdf import PdfPages
|
from matplotlib.backends.backend_pdf import PdfPages
|
||||||
from pyulog import ULog
|
from pyulog import ULog
|
||||||
|
|
||||||
from analysis.post_processing import magnetic_field_estimates_from_status, get_estimator_check_flags
|
from analysis.post_processing import magnetic_field_estimates_from_states, get_estimator_check_flags
|
||||||
from plotting.data_plots import TimeSeriesPlot, InnovationPlot, ControlModeSummaryPlot, \
|
from plotting.data_plots import TimeSeriesPlot, InnovationPlot, ControlModeSummaryPlot, \
|
||||||
CheckFlagsPlot
|
CheckFlagsPlot
|
||||||
from analysis.detectors import PreconditionError
|
from analysis.detectors import PreconditionError
|
||||||
@@ -34,6 +34,12 @@ def create_pdf_report(ulog: ULog, output_plot_filename: str) -> None:
|
|||||||
except:
|
except:
|
||||||
raise PreconditionError('could not find estimator_status data')
|
raise PreconditionError('could not find estimator_status data')
|
||||||
|
|
||||||
|
try:
|
||||||
|
estimator_states = ulog.get_dataset('estimator_states').data
|
||||||
|
print('found estimator_states data')
|
||||||
|
except:
|
||||||
|
raise PreconditionError('could not find estimator_states data')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
estimator_innovations = ulog.get_dataset('estimator_innovations').data
|
estimator_innovations = ulog.get_dataset('estimator_innovations').data
|
||||||
estimator_innovation_variances = ulog.get_dataset('estimator_innovation_variances').data
|
estimator_innovation_variances = ulog.get_dataset('estimator_innovation_variances').data
|
||||||
@@ -45,12 +51,6 @@ def create_pdf_report(ulog: ULog, output_plot_filename: str) -> None:
|
|||||||
except:
|
except:
|
||||||
raise PreconditionError('could not find innovation data')
|
raise PreconditionError('could not find innovation data')
|
||||||
|
|
||||||
try:
|
|
||||||
sensor_preflight_imu = ulog.get_dataset('sensor_preflight_imu').data
|
|
||||||
print('found sensor_preflight data')
|
|
||||||
except:
|
|
||||||
raise PreconditionError('could not find sensor_preflight_imu data')
|
|
||||||
|
|
||||||
control_mode, innov_flags, gps_fail_flags = get_estimator_check_flags(estimator_status)
|
control_mode, innov_flags, gps_fail_flags = get_estimator_check_flags(estimator_status)
|
||||||
|
|
||||||
status_time = 1e-6 * estimator_status['timestamp']
|
status_time = 1e-6 * estimator_status['timestamp']
|
||||||
@@ -60,17 +60,6 @@ def create_pdf_report(ulog: ULog, output_plot_filename: str) -> None:
|
|||||||
|
|
||||||
with PdfPages(output_plot_filename) as pdf_pages:
|
with PdfPages(output_plot_filename) as pdf_pages:
|
||||||
|
|
||||||
# plot IMU consistency data
|
|
||||||
if ('accel_inconsistency_m_s_s' in sensor_preflight_imu.keys()) and (
|
|
||||||
'gyro_inconsistency_rad_s' in sensor_preflight_imu.keys()):
|
|
||||||
data_plot = TimeSeriesPlot(
|
|
||||||
sensor_preflight_imu, [['accel_inconsistency_m_s_s'], ['gyro_inconsistency_rad_s']],
|
|
||||||
x_labels=['data index', 'data index'],
|
|
||||||
y_labels=['acceleration (m/s/s)', 'angular rate (rad/s)'],
|
|
||||||
plot_title='IMU Consistency Check Levels', pdf_handle=pdf_pages)
|
|
||||||
data_plot.save()
|
|
||||||
data_plot.close()
|
|
||||||
|
|
||||||
# vertical velocity and position innovations
|
# vertical velocity and position innovations
|
||||||
data_plot = InnovationPlot(
|
data_plot = InnovationPlot(
|
||||||
innovation_data, [('gps_vpos', 'var_gps_vpos'),
|
innovation_data, [('gps_vpos', 'var_gps_vpos'),
|
||||||
@@ -292,10 +281,10 @@ def create_pdf_report(ulog: ULog, output_plot_filename: str) -> None:
|
|||||||
data_plot.close()
|
data_plot.close()
|
||||||
|
|
||||||
# Plot the earth frame magnetic field estimates
|
# Plot the earth frame magnetic field estimates
|
||||||
declination, field_strength, inclination = magnetic_field_estimates_from_status(
|
declination, field_strength, inclination = magnetic_field_estimates_from_states(
|
||||||
estimator_status)
|
estimator_states)
|
||||||
data_plot = CheckFlagsPlot(
|
data_plot = CheckFlagsPlot(
|
||||||
1e-6 * estimator_status['timestamp'],
|
1e-6 * estimator_states['timestamp'],
|
||||||
{'strength': field_strength, 'declination': declination, 'inclination': inclination},
|
{'strength': field_strength, 'declination': declination, 'inclination': inclination},
|
||||||
[['declination'], ['inclination'], ['strength']],
|
[['declination'], ['inclination'], ['strength']],
|
||||||
x_label='time (sec)', y_labels=['declination (deg)', 'inclination (deg)',
|
x_label='time (sec)', y_labels=['declination (deg)', 'inclination (deg)',
|
||||||
@@ -307,7 +296,7 @@ def create_pdf_report(ulog: ULog, output_plot_filename: str) -> None:
|
|||||||
|
|
||||||
# Plot the body frame magnetic field estimates
|
# Plot the body frame magnetic field estimates
|
||||||
data_plot = CheckFlagsPlot(
|
data_plot = CheckFlagsPlot(
|
||||||
1e-6 * estimator_status['timestamp'], estimator_status,
|
1e-6 * estimator_states['timestamp'], estimator_states,
|
||||||
[['states[19]'], ['states[20]'], ['states[21]']],
|
[['states[19]'], ['states[20]'], ['states[21]']],
|
||||||
x_label='time (sec)', y_labels=['X (Gauss)', 'Y (Gauss)', 'Z (Gauss)'],
|
x_label='time (sec)', y_labels=['X (Gauss)', 'Y (Gauss)', 'Z (Gauss)'],
|
||||||
plot_title='Magnetometer Bias Estimates', annotate=False, pdf_handle=pdf_pages)
|
plot_title='Magnetometer Bias Estimates', annotate=False, pdf_handle=pdf_pages)
|
||||||
@@ -316,7 +305,7 @@ def create_pdf_report(ulog: ULog, output_plot_filename: str) -> None:
|
|||||||
|
|
||||||
# Plot the EKF wind estimates
|
# Plot the EKF wind estimates
|
||||||
data_plot = CheckFlagsPlot(
|
data_plot = CheckFlagsPlot(
|
||||||
1e-6 * estimator_status['timestamp'], estimator_status,
|
1e-6 * estimator_states['timestamp'], estimator_states,
|
||||||
[['states[22]'], ['states[23]']], x_label='time (sec)',
|
[['states[22]'], ['states[23]']], x_label='time (sec)',
|
||||||
y_labels=['North (m/s)', 'East (m/s)'], plot_title='Wind Velocity Estimates',
|
y_labels=['North (m/s)', 'East (m/s)'], plot_title='Wind Velocity Estimates',
|
||||||
annotate=False, pdf_handle=pdf_pages)
|
annotate=False, pdf_handle=pdf_pages)
|
||||||
|
|||||||
+1
-1
@@ -120,9 +120,9 @@ set(msg_files
|
|||||||
sensor_gyro.msg
|
sensor_gyro.msg
|
||||||
sensor_gyro_fifo.msg
|
sensor_gyro_fifo.msg
|
||||||
sensor_mag.msg
|
sensor_mag.msg
|
||||||
sensor_preflight_imu.msg
|
|
||||||
sensor_preflight_mag.msg
|
sensor_preflight_mag.msg
|
||||||
sensor_selection.msg
|
sensor_selection.msg
|
||||||
|
sensors_status_imu.msg
|
||||||
subsystem_info.msg
|
subsystem_info.msg
|
||||||
system_power.msg
|
system_power.msg
|
||||||
task_stack_info.msg
|
task_stack_info.msg
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
#
|
|
||||||
# Pre-flight sensor check metrics. These will be zero if the vehicle only has one sensor.
|
|
||||||
# The topic will not be updated when the vehicle is armed
|
|
||||||
#
|
|
||||||
uint64 timestamp # time since system start (microseconds)
|
|
||||||
float32 accel_inconsistency_m_s_s # magnitude of maximum acceleration difference between IMU instances in (m/s/s).
|
|
||||||
float32 gyro_inconsistency_rad_s # magnitude of maximum angular rate difference between IMU instances in (rad/s).
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#
|
||||||
|
# Sensor check metrics. This will be zero for a sensor that's primary or unpopulated.
|
||||||
|
#
|
||||||
|
uint64 timestamp # time since system start (microseconds)
|
||||||
|
|
||||||
|
uint32 accel_device_id_primary # current primary accel device id for reference
|
||||||
|
|
||||||
|
uint32[3] accel_device_ids
|
||||||
|
float32[3] accel_inconsistency_m_s_s # magnitude of acceleration difference between IMU instance and primary in (m/s/s).
|
||||||
|
bool[3] accel_healthy
|
||||||
|
|
||||||
|
|
||||||
|
uint32 gyro_device_id_primary # current primary gyro device id for reference
|
||||||
|
|
||||||
|
uint32[3] gyro_device_ids
|
||||||
|
float32[3] gyro_inconsistency_rad_s # magnitude of angular rate difference between IMU instance and primary in (rad/s).
|
||||||
|
bool[3] gyro_healthy
|
||||||
@@ -144,7 +144,7 @@ rtps:
|
|||||||
id: 67
|
id: 67
|
||||||
- msg: sensor_mag
|
- msg: sensor_mag
|
||||||
id: 68
|
id: 68
|
||||||
- msg: sensor_preflight_imu
|
- msg: sensors_status_imu
|
||||||
id: 69
|
id: 69
|
||||||
- msg: sensor_selection
|
- msg: sensor_selection
|
||||||
id: 70
|
id: 70
|
||||||
|
|||||||
@@ -38,53 +38,88 @@
|
|||||||
#include <lib/parameters/param.h>
|
#include <lib/parameters/param.h>
|
||||||
#include <systemlib/mavlink_log.h>
|
#include <systemlib/mavlink_log.h>
|
||||||
#include <uORB/Subscription.hpp>
|
#include <uORB/Subscription.hpp>
|
||||||
#include <uORB/topics/sensor_preflight_imu.h>
|
#include <uORB/topics/sensors_status_imu.h>
|
||||||
#include <uORB/topics/subsystem_info.h>
|
#include <uORB/topics/subsystem_info.h>
|
||||||
|
|
||||||
bool PreFlightCheck::imuConsistencyCheck(orb_advert_t *mavlink_log_pub, vehicle_status_s &status,
|
bool PreFlightCheck::imuConsistencyCheck(orb_advert_t *mavlink_log_pub, vehicle_status_s &status,
|
||||||
const bool report_status)
|
const bool report_status)
|
||||||
{
|
{
|
||||||
float test_limit = 1.0f; // pass limit re-used for each test
|
|
||||||
|
|
||||||
// Get sensor_preflight data if available and exit with a fail recorded if not
|
// Get sensor_preflight data if available and exit with a fail recorded if not
|
||||||
uORB::SubscriptionData<sensor_preflight_imu_s> sensors_sub{ORB_ID(sensor_preflight_imu)};
|
uORB::SubscriptionData<sensors_status_imu_s> sensors_status_imu_sub{ORB_ID(sensors_status_imu)};
|
||||||
sensors_sub.update();
|
const sensors_status_imu_s &imu = sensors_status_imu_sub.get();
|
||||||
const sensor_preflight_imu_s &sensors = sensors_sub.get();
|
|
||||||
|
|
||||||
// Use the difference between IMU's to detect a bad calibration.
|
// Use the difference between IMU's to detect a bad calibration.
|
||||||
// If a single IMU is fitted, the value being checked will be zero so this check will always pass.
|
// If a single IMU is fitted, the value being checked will be zero so this check will always pass.
|
||||||
param_get(param_find("COM_ARM_IMU_ACC"), &test_limit);
|
float accel_test_limit = 1.f;
|
||||||
|
param_get(param_find("COM_ARM_IMU_ACC"), &accel_test_limit);
|
||||||
|
|
||||||
if (sensors.accel_inconsistency_m_s_s > test_limit) {
|
for (unsigned i = 0; i < (sizeof(imu.accel_inconsistency_m_s_s) / sizeof(imu.accel_inconsistency_m_s_s[0])); i++) {
|
||||||
if (report_status) {
|
if (imu.accel_device_ids[i] != 0) {
|
||||||
mavlink_log_critical(mavlink_log_pub, "Preflight Fail: Accels inconsistent - Check Cal");
|
if (imu.accel_device_ids[i] == imu.accel_device_id_primary) {
|
||||||
set_health_flags_healthy(subsystem_info_s::SUBSYSTEM_TYPE_ACC, false, status);
|
set_health_flags_healthy(subsystem_info_s::SUBSYSTEM_TYPE_ACC, imu.accel_healthy[i], status);
|
||||||
set_health_flags_healthy(subsystem_info_s::SUBSYSTEM_TYPE_ACC2, false, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
} else {
|
||||||
|
set_health_flags_healthy(subsystem_info_s::SUBSYSTEM_TYPE_ACC2, imu.accel_healthy[i], status);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (sensors.accel_inconsistency_m_s_s > test_limit * 0.8f) {
|
const float accel_inconsistency_m_s_s = imu.accel_inconsistency_m_s_s[i];
|
||||||
if (report_status) {
|
|
||||||
mavlink_log_info(mavlink_log_pub, "Preflight Advice: Accels inconsistent - Check Cal");
|
if (accel_inconsistency_m_s_s > accel_test_limit) {
|
||||||
|
if (report_status) {
|
||||||
|
mavlink_log_critical(mavlink_log_pub, "Preflight Fail: Accel %u inconsistent - Check Cal", i);
|
||||||
|
|
||||||
|
if (imu.accel_device_ids[i] == imu.accel_device_id_primary) {
|
||||||
|
set_health_flags_healthy(subsystem_info_s::SUBSYSTEM_TYPE_ACC, false, status);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
set_health_flags_healthy(subsystem_info_s::SUBSYSTEM_TYPE_ACC2, false, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} else if (accel_inconsistency_m_s_s > accel_test_limit * 0.8f) {
|
||||||
|
if (report_status) {
|
||||||
|
mavlink_log_info(mavlink_log_pub, "Preflight Advice: Accel %u inconsistent - Check Cal", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail if gyro difference greater than 5 deg/sec and notify if greater than 2.5 deg/sec
|
// Fail if gyro difference greater than 5 deg/sec and notify if greater than 2.5 deg/sec
|
||||||
param_get(param_find("COM_ARM_IMU_GYR"), &test_limit);
|
float gyro_test_limit = 1.f;
|
||||||
|
param_get(param_find("COM_ARM_IMU_GYR"), &gyro_test_limit);
|
||||||
|
|
||||||
if (sensors.gyro_inconsistency_rad_s > test_limit) {
|
for (unsigned i = 0; i < (sizeof(imu.gyro_inconsistency_rad_s) / sizeof(imu.gyro_inconsistency_rad_s[0])); i++) {
|
||||||
if (report_status) {
|
if (imu.gyro_device_ids[i] != 0) {
|
||||||
mavlink_log_critical(mavlink_log_pub, "Preflight Fail: Gyros inconsistent - Check Cal");
|
if (imu.gyro_device_ids[i] == imu.gyro_device_id_primary) {
|
||||||
set_health_flags_healthy(subsystem_info_s::SUBSYSTEM_TYPE_GYRO, false, status);
|
set_health_flags_healthy(subsystem_info_s::SUBSYSTEM_TYPE_GYRO, imu.accel_healthy[i], status);
|
||||||
set_health_flags_healthy(subsystem_info_s::SUBSYSTEM_TYPE_GYRO2, false, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
} else {
|
||||||
|
set_health_flags_healthy(subsystem_info_s::SUBSYSTEM_TYPE_GYRO2, imu.accel_healthy[i], status);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (sensors.gyro_inconsistency_rad_s > test_limit * 0.5f) {
|
const float gyro_inconsistency_rad_s = imu.gyro_inconsistency_rad_s[i];
|
||||||
if (report_status) {
|
|
||||||
mavlink_log_info(mavlink_log_pub, "Preflight Advice: Gyros inconsistent - Check Cal");
|
if (gyro_inconsistency_rad_s > gyro_test_limit) {
|
||||||
|
if (report_status) {
|
||||||
|
mavlink_log_critical(mavlink_log_pub, "Preflight Fail: Gyro %u inconsistent - Check Cal", i);
|
||||||
|
|
||||||
|
if (imu.gyro_device_ids[i] == imu.gyro_device_id_primary) {
|
||||||
|
set_health_flags_healthy(subsystem_info_s::SUBSYSTEM_TYPE_GYRO, false, status);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
set_health_flags_healthy(subsystem_info_s::SUBSYSTEM_TYPE_GYRO2, false, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} else if (gyro_inconsistency_rad_s > gyro_test_limit * 0.5f) {
|
||||||
|
if (report_status) {
|
||||||
|
mavlink_log_info(mavlink_log_pub, "Preflight Advice: Gyro %u inconsistent - Check Cal", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ void LoggedTopics::add_default_topics()
|
|||||||
add_topic("safety");
|
add_topic("safety");
|
||||||
add_topic("sensor_combined");
|
add_topic("sensor_combined");
|
||||||
add_topic("sensor_correction");
|
add_topic("sensor_correction");
|
||||||
add_topic("sensor_preflight_imu", 200);
|
add_topic("sensors_status_imu", 200);
|
||||||
add_topic("sensor_preflight_mag", 500);
|
add_topic("sensor_preflight_mag", 500);
|
||||||
add_topic("sensor_selection");
|
add_topic("sensor_selection");
|
||||||
add_topic("system_power", 500);
|
add_topic("system_power", 500);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
#include <uORB/topics/airspeed.h>
|
#include <uORB/topics/airspeed.h>
|
||||||
#include <uORB/topics/differential_pressure.h>
|
#include <uORB/topics/differential_pressure.h>
|
||||||
#include <uORB/topics/parameter_update.h>
|
#include <uORB/topics/parameter_update.h>
|
||||||
#include <uORB/topics/sensor_preflight_imu.h>
|
#include <uORB/topics/sensors_status_imu.h>
|
||||||
#include <uORB/topics/vehicle_air_data.h>
|
#include <uORB/topics/vehicle_air_data.h>
|
||||||
#include <uORB/topics/vehicle_control_mode.h>
|
#include <uORB/topics/vehicle_control_mode.h>
|
||||||
#include <uORB/topics/vehicle_imu.h>
|
#include <uORB/topics/vehicle_imu.h>
|
||||||
@@ -115,7 +115,6 @@ private:
|
|||||||
hrt_abstime _sensor_combined_prev_timestamp{0};
|
hrt_abstime _sensor_combined_prev_timestamp{0};
|
||||||
|
|
||||||
sensor_combined_s _sensor_combined{};
|
sensor_combined_s _sensor_combined{};
|
||||||
sensor_preflight_imu_s _sensor_preflight_imu{};
|
|
||||||
|
|
||||||
uORB::SubscriptionCallbackWorkItem _vehicle_imu_sub[3] {
|
uORB::SubscriptionCallbackWorkItem _vehicle_imu_sub[3] {
|
||||||
{this, ORB_ID(vehicle_imu), 0},
|
{this, ORB_ID(vehicle_imu), 0},
|
||||||
@@ -130,7 +129,6 @@ private:
|
|||||||
|
|
||||||
uORB::Publication<airspeed_s> _airspeed_pub{ORB_ID(airspeed)};
|
uORB::Publication<airspeed_s> _airspeed_pub{ORB_ID(airspeed)};
|
||||||
uORB::Publication<sensor_combined_s> _sensor_pub{ORB_ID(sensor_combined)};
|
uORB::Publication<sensor_combined_s> _sensor_pub{ORB_ID(sensor_combined)};
|
||||||
uORB::Publication<sensor_preflight_imu_s> _sensor_preflight_imu_pub{ORB_ID(sensor_preflight_imu)};
|
|
||||||
|
|
||||||
perf_counter_t _loop_perf; /**< loop performance counter */
|
perf_counter_t _loop_perf; /**< loop performance counter */
|
||||||
|
|
||||||
@@ -584,16 +582,6 @@ void Sensors::Run()
|
|||||||
_voted_sensors_update.setRelativeTimestamps(_sensor_combined);
|
_voted_sensors_update.setRelativeTimestamps(_sensor_combined);
|
||||||
_sensor_pub.publish(_sensor_combined);
|
_sensor_pub.publish(_sensor_combined);
|
||||||
_sensor_combined_prev_timestamp = _sensor_combined.timestamp;
|
_sensor_combined_prev_timestamp = _sensor_combined.timestamp;
|
||||||
|
|
||||||
// If the the vehicle is disarmed calculate the length of the maximum difference between
|
|
||||||
// IMU units as a consistency metric and publish to the sensor preflight topic
|
|
||||||
if (!_armed) {
|
|
||||||
_voted_sensors_update.calcAccelInconsistency(_sensor_preflight_imu);
|
|
||||||
_voted_sensors_update.calcGyroInconsistency(_sensor_preflight_imu);
|
|
||||||
|
|
||||||
_sensor_preflight_imu.timestamp = hrt_absolute_time();
|
|
||||||
_sensor_preflight_imu_pub.publish(_sensor_preflight_imu);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep adding sensors as long as we are not armed,
|
// keep adding sensors as long as we are not armed,
|
||||||
@@ -725,7 +713,7 @@ The provided functionality includes:
|
|||||||
- Make sure the sensor drivers get the updated calibration parameters (scale & offset) when the parameters change or
|
- Make sure the sensor drivers get the updated calibration parameters (scale & offset) when the parameters change or
|
||||||
on startup. The sensor drivers use the ioctl interface for parameter updates. For this to work properly, the
|
on startup. The sensor drivers use the ioctl interface for parameter updates. For this to work properly, the
|
||||||
sensor drivers must already be running when `sensors` is started.
|
sensor drivers must already be running when `sensors` is started.
|
||||||
- Do preflight sensor consistency checks and publish the `sensor_preflight_imu` topic.
|
- Do sensor consistency checks and publish the `sensors_status_imu` topic.
|
||||||
|
|
||||||
### Implementation
|
### Implementation
|
||||||
It runs in its own thread and polls on the currently selected gyro topic.
|
It runs in its own thread and polls on the currently selected gyro topic.
|
||||||
|
|||||||
@@ -187,8 +187,8 @@ void VotedSensorsUpdate::imuPoll(struct sensor_combined_s &raw)
|
|||||||
_accel.voter.get_best(hrt_absolute_time(), &accel_best_index);
|
_accel.voter.get_best(hrt_absolute_time(), &accel_best_index);
|
||||||
_gyro.voter.get_best(hrt_absolute_time(), &gyro_best_index);
|
_gyro.voter.get_best(hrt_absolute_time(), &gyro_best_index);
|
||||||
|
|
||||||
checkFailover(_accel, "Accel", subsystem_info_s::SUBSYSTEM_TYPE_ACC);
|
checkFailover(_accel, "Accel");
|
||||||
checkFailover(_gyro, "Gyro", subsystem_info_s::SUBSYSTEM_TYPE_GYRO);
|
checkFailover(_gyro, "Gyro");
|
||||||
|
|
||||||
// write data for the best sensor to output variables
|
// write data for the best sensor to output variables
|
||||||
if ((accel_best_index >= 0) && (gyro_best_index >= 0)) {
|
if ((accel_best_index >= 0) && (gyro_best_index >= 0)) {
|
||||||
@@ -229,7 +229,7 @@ void VotedSensorsUpdate::imuPoll(struct sensor_combined_s &raw)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VotedSensorsUpdate::checkFailover(SensorData &sensor, const char *sensor_name, const uint64_t type)
|
bool VotedSensorsUpdate::checkFailover(SensorData &sensor, const char *sensor_name)
|
||||||
{
|
{
|
||||||
if (sensor.last_failover_count != sensor.voter.failover_count() && !_hil_enabled) {
|
if (sensor.last_failover_count != sensor.voter.failover_count() && !_hil_enabled) {
|
||||||
|
|
||||||
@@ -261,34 +261,6 @@ bool VotedSensorsUpdate::checkFailover(SensorData &sensor, const char *sensor_na
|
|||||||
|
|
||||||
// reduce priority of failed sensor to the minimum
|
// reduce priority of failed sensor to the minimum
|
||||||
sensor.priority[failover_index] = 1;
|
sensor.priority[failover_index] = 1;
|
||||||
|
|
||||||
int ctr_valid = 0;
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < sensor.subscription_count; i++) {
|
|
||||||
if (sensor.priority[i] > 1) {
|
|
||||||
ctr_valid++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctr_valid < 2) {
|
|
||||||
if (ctr_valid == 0) {
|
|
||||||
// Zero valid sensors remain! Set even the primary sensor health to false
|
|
||||||
_info.subsystem_type = type;
|
|
||||||
|
|
||||||
} else if (ctr_valid == 1) {
|
|
||||||
// One valid sensor remains, set secondary sensor health to false
|
|
||||||
if (type == subsystem_info_s::SUBSYSTEM_TYPE_GYRO) { _info.subsystem_type = subsystem_info_s::SUBSYSTEM_TYPE_GYRO2; }
|
|
||||||
|
|
||||||
if (type == subsystem_info_s::SUBSYSTEM_TYPE_ACC) { _info.subsystem_type = subsystem_info_s::SUBSYSTEM_TYPE_ACC2; }
|
|
||||||
}
|
|
||||||
|
|
||||||
_info.timestamp = hrt_absolute_time();
|
|
||||||
_info.present = true;
|
|
||||||
_info.enabled = true;
|
|
||||||
_info.ok = false;
|
|
||||||
|
|
||||||
_info_pub.publish(_info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,8 +329,41 @@ void VotedSensorsUpdate::sensorsPoll(sensor_combined_s &raw)
|
|||||||
_selection.timestamp = hrt_absolute_time();
|
_selection.timestamp = hrt_absolute_time();
|
||||||
_sensor_selection_pub.publish(_selection);
|
_sensor_selection_pub.publish(_selection);
|
||||||
_selection_changed = false;
|
_selection_changed = false;
|
||||||
|
|
||||||
|
for (int sensor_index = 0; sensor_index < ACCEL_COUNT_MAX; sensor_index++) {
|
||||||
|
_accel_diff[sensor_index].zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int sensor_index = 0; sensor_index < GYRO_COUNT_MAX; sensor_index++) {
|
||||||
|
_gyro_diff[sensor_index].zero();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calcAccelInconsistency();
|
||||||
|
calcGyroInconsistency();
|
||||||
|
|
||||||
|
sensors_status_imu_s status{};
|
||||||
|
status.accel_device_id_primary = _selection.accel_device_id;
|
||||||
|
status.gyro_device_id_primary = _selection.gyro_device_id;
|
||||||
|
|
||||||
|
for (int i = 0; i < SENSOR_COUNT_MAX; i++) {
|
||||||
|
if (_accel_device_id[i] != 0) {
|
||||||
|
status.accel_device_ids[i] = _accel_device_id[i];
|
||||||
|
status.accel_inconsistency_m_s_s[i] = _accel_diff[i].norm();
|
||||||
|
status.accel_healthy[i] = (_accel.voter.get_sensor_state(i) == DataValidator::ERROR_FLAG_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_gyro_device_id[i] != 0) {
|
||||||
|
status.gyro_device_ids[i] = _gyro_device_id[i];
|
||||||
|
status.gyro_inconsistency_rad_s[i] = _gyro_diff[i].norm();
|
||||||
|
status.gyro_healthy[i] = (_gyro.voter.get_sensor_state(i) == DataValidator::ERROR_FLAG_NO_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status.timestamp = hrt_absolute_time();
|
||||||
|
_sensors_status_imu_pub.publish(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VotedSensorsUpdate::setRelativeTimestamps(sensor_combined_s &raw)
|
void VotedSensorsUpdate::setRelativeTimestamps(sensor_combined_s &raw)
|
||||||
@@ -369,100 +374,34 @@ void VotedSensorsUpdate::setRelativeTimestamps(sensor_combined_s &raw)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VotedSensorsUpdate::calcAccelInconsistency(sensor_preflight_imu_s &preflt)
|
void VotedSensorsUpdate::calcAccelInconsistency()
|
||||||
{
|
{
|
||||||
float accel_diff_sum_max_sq = 0.0f; // the maximum sum of axis differences squared
|
const Vector3f primary_accel{_last_sensor_data[_accel.last_best_vote].accelerometer_m_s2};
|
||||||
unsigned check_index = 0; // the number of sensors the primary has been checked against
|
|
||||||
|
|
||||||
// Check each sensor against the primary
|
// Check each sensor against the primary
|
||||||
for (int sensor_index = 0; sensor_index < _accel.subscription_count; sensor_index++) {
|
for (int sensor_index = 0; sensor_index < ACCEL_COUNT_MAX; sensor_index++) {
|
||||||
|
|
||||||
// check that the sensor we are checking against is not the same as the primary
|
// check that the sensor we are checking against is not the same as the primary
|
||||||
if (_accel.advertised[sensor_index] && (_accel.priority[sensor_index] > 0) && (sensor_index != _accel.last_best_vote)) {
|
if (_accel.advertised[sensor_index] && (_accel.priority[sensor_index] > 0)
|
||||||
|
&& (_accel_device_id[sensor_index] != _selection.accel_device_id)) {
|
||||||
|
|
||||||
float accel_diff_sum_sq = 0.0f; // sum of differences squared for a single sensor comparison agains the primary
|
const Vector3f current_accel{_last_sensor_data[sensor_index].accelerometer_m_s2};
|
||||||
|
_accel_diff[sensor_index] = 0.95f * _accel_diff[sensor_index] + 0.05f * (primary_accel - current_accel);
|
||||||
// calculate accel_diff_sum_sq for the specified sensor against the primary
|
|
||||||
for (unsigned axis_index = 0; axis_index < 3; axis_index++) {
|
|
||||||
_accel_diff[axis_index][check_index] = 0.95f * _accel_diff[axis_index][check_index] + 0.05f *
|
|
||||||
(_last_sensor_data[_accel.last_best_vote].accelerometer_m_s2[axis_index] -
|
|
||||||
_last_sensor_data[sensor_index].accelerometer_m_s2[axis_index]);
|
|
||||||
accel_diff_sum_sq += _accel_diff[axis_index][check_index] * _accel_diff[axis_index][check_index];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// capture the largest sum value
|
|
||||||
if (accel_diff_sum_sq > accel_diff_sum_max_sq) {
|
|
||||||
accel_diff_sum_max_sq = accel_diff_sum_sq;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// increment the check index
|
|
||||||
check_index++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check to see if the maximum number of checks has been reached and break
|
|
||||||
if (check_index >= 2) {
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip check if less than 2 sensors
|
|
||||||
if (check_index < 1) {
|
|
||||||
preflt.accel_inconsistency_m_s_s = 0.0f;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// get the vector length of the largest difference and write to the combined sensor struct
|
|
||||||
preflt.accel_inconsistency_m_s_s = sqrtf(accel_diff_sum_max_sq);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VotedSensorsUpdate::calcGyroInconsistency(sensor_preflight_imu_s &preflt)
|
void VotedSensorsUpdate::calcGyroInconsistency()
|
||||||
{
|
{
|
||||||
float gyro_diff_sum_max_sq = 0.0f; // the maximum sum of axis differences squared
|
const Vector3f primary_gyro{_last_sensor_data[_gyro.last_best_vote].gyro_rad};
|
||||||
unsigned check_index = 0; // the number of sensors the primary has been checked against
|
|
||||||
|
|
||||||
// Check each sensor against the primary
|
// Check each sensor against the primary
|
||||||
for (int sensor_index = 0; sensor_index < _gyro.subscription_count; sensor_index++) {
|
for (int sensor_index = 0; sensor_index < GYRO_COUNT_MAX; sensor_index++) {
|
||||||
|
|
||||||
// check that the sensor we are checking against is not the same as the primary
|
// check that the sensor we are checking against is not the same as the primary
|
||||||
if (_gyro.advertised[sensor_index] && (_gyro.priority[sensor_index] > 0) && (sensor_index != _gyro.last_best_vote)) {
|
if (_gyro.advertised[sensor_index] && (_gyro.priority[sensor_index] > 0)
|
||||||
|
&& (_gyro_device_id[sensor_index] != _selection.gyro_device_id)) {
|
||||||
|
|
||||||
float gyro_diff_sum_sq = 0.0f; // sum of differences squared for a single sensor comparison against the primary
|
const Vector3f current_gyro{_last_sensor_data[sensor_index].gyro_rad};
|
||||||
|
_gyro_diff[sensor_index] = 0.95f * _gyro_diff[sensor_index] + 0.05f * (primary_gyro - current_gyro);
|
||||||
// calculate gyro_diff_sum_sq for the specified sensor against the primary
|
|
||||||
for (unsigned axis_index = 0; axis_index < 3; axis_index++) {
|
|
||||||
_gyro_diff[axis_index][check_index] = 0.95f * _gyro_diff[axis_index][check_index] + 0.05f *
|
|
||||||
(_last_sensor_data[_gyro.last_best_vote].gyro_rad[axis_index] -
|
|
||||||
_last_sensor_data[sensor_index].gyro_rad[axis_index]);
|
|
||||||
gyro_diff_sum_sq += _gyro_diff[axis_index][check_index] * _gyro_diff[axis_index][check_index];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// capture the largest sum value
|
|
||||||
if (gyro_diff_sum_sq > gyro_diff_sum_max_sq) {
|
|
||||||
gyro_diff_sum_max_sq = gyro_diff_sum_sq;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// increment the check index
|
|
||||||
check_index++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check to see if the maximum number of checks has been reached and break
|
|
||||||
if (check_index >= 2) {
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip check if less than 2 sensors
|
|
||||||
if (check_index < 1) {
|
|
||||||
preflt.gyro_inconsistency_rad_s = 0.0f;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// get the vector length of the largest difference and write to the combined sensor struct
|
|
||||||
preflt.gyro_inconsistency_rad_s = sqrtf(gyro_diff_sum_max_sq);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,11 +53,10 @@
|
|||||||
#include <uORB/topics/sensor_accel.h>
|
#include <uORB/topics/sensor_accel.h>
|
||||||
#include <uORB/topics/sensor_gyro.h>
|
#include <uORB/topics/sensor_gyro.h>
|
||||||
#include <uORB/topics/sensor_combined.h>
|
#include <uORB/topics/sensor_combined.h>
|
||||||
#include <uORB/topics/sensor_preflight_imu.h>
|
#include <uORB/topics/sensors_status_imu.h>
|
||||||
#include <uORB/topics/sensor_selection.h>
|
#include <uORB/topics/sensor_selection.h>
|
||||||
#include <uORB/topics/vehicle_imu.h>
|
#include <uORB/topics/vehicle_imu.h>
|
||||||
#include <uORB/topics/vehicle_imu_status.h>
|
#include <uORB/topics/vehicle_imu_status.h>
|
||||||
#include <uORB/topics/subsystem_info.h>
|
|
||||||
|
|
||||||
namespace sensors
|
namespace sensors
|
||||||
{
|
{
|
||||||
@@ -111,16 +110,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setRelativeTimestamps(sensor_combined_s &raw);
|
void setRelativeTimestamps(sensor_combined_s &raw);
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the magnitude in m/s/s of the largest difference between the primary and any other accel sensor
|
|
||||||
*/
|
|
||||||
void calcAccelInconsistency(sensor_preflight_imu_s &preflt);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the magnitude in rad/s of the largest difference between the primary and any other gyro sensor
|
|
||||||
*/
|
|
||||||
void calcGyroInconsistency(sensor_preflight_imu_s &preflt);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static constexpr uint8_t ACCEL_COUNT_MAX = 3;
|
static constexpr uint8_t ACCEL_COUNT_MAX = 3;
|
||||||
@@ -157,7 +146,17 @@ private:
|
|||||||
* Check & handle failover of a sensor
|
* Check & handle failover of a sensor
|
||||||
* @return true if a switch occured (could be for a non-critical reason)
|
* @return true if a switch occured (could be for a non-critical reason)
|
||||||
*/
|
*/
|
||||||
bool checkFailover(SensorData &sensor, const char *sensor_name, const uint64_t type);
|
bool checkFailover(SensorData &sensor, const char *sensor_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the magnitude in m/s/s of the largest difference between the primary and any other accel sensor
|
||||||
|
*/
|
||||||
|
void calcAccelInconsistency();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the magnitude in rad/s of the largest difference between the primary and any other gyro sensor
|
||||||
|
*/
|
||||||
|
void calcGyroInconsistency();
|
||||||
|
|
||||||
SensorData _accel{ORB_ID::sensor_accel};
|
SensorData _accel{ORB_ID::sensor_accel};
|
||||||
SensorData _gyro{ORB_ID::sensor_gyro};
|
SensorData _gyro{ORB_ID::sensor_gyro};
|
||||||
@@ -166,7 +165,7 @@ private:
|
|||||||
orb_advert_t _mavlink_log_pub{nullptr};
|
orb_advert_t _mavlink_log_pub{nullptr};
|
||||||
|
|
||||||
uORB::Publication<sensor_selection_s> _sensor_selection_pub{ORB_ID(sensor_selection)}; /**< handle to the sensor selection uORB topic */
|
uORB::Publication<sensor_selection_s> _sensor_selection_pub{ORB_ID(sensor_selection)}; /**< handle to the sensor selection uORB topic */
|
||||||
uORB::PublicationQueued<subsystem_info_s> _info_pub{ORB_ID(subsystem_info)}; /* subsystem info publication */
|
uORB::Publication<sensors_status_imu_s> _sensors_status_imu_pub{ORB_ID(sensors_status_imu)};
|
||||||
|
|
||||||
uORB::SubscriptionCallbackWorkItem(&_vehicle_imu_sub)[3];
|
uORB::SubscriptionCallbackWorkItem(&_vehicle_imu_sub)[3];
|
||||||
uORB::SubscriptionMultiArray<vehicle_imu_status_s, ACCEL_COUNT_MAX> _vehicle_imu_status_subs{ORB_ID::vehicle_imu_status};
|
uORB::SubscriptionMultiArray<vehicle_imu_status_s, ACCEL_COUNT_MAX> _vehicle_imu_status_subs{ORB_ID::vehicle_imu_status};
|
||||||
@@ -177,8 +176,8 @@ private:
|
|||||||
|
|
||||||
bool _selection_changed{true}; /**< true when a sensor selection has changed and not been published */
|
bool _selection_changed{true}; /**< true when a sensor selection has changed and not been published */
|
||||||
|
|
||||||
float _accel_diff[3][2] {}; /**< filtered accel differences between IMU units (m/s/s) */
|
matrix::Vector3f _accel_diff[ACCEL_COUNT_MAX] {}; /**< filtered accel differences between IMU units (m/s/s) */
|
||||||
float _gyro_diff[3][2] {}; /**< filtered gyro differences between IMU uinits (rad/s) */
|
matrix::Vector3f _gyro_diff[GYRO_COUNT_MAX] {}; /**< filtered gyro differences between IMU uinits (rad/s) */
|
||||||
|
|
||||||
uint32_t _accel_device_id[SENSOR_COUNT_MAX] {}; /**< accel driver device id for each uorb instance */
|
uint32_t _accel_device_id[SENSOR_COUNT_MAX] {}; /**< accel driver device id for each uorb instance */
|
||||||
uint32_t _gyro_device_id[SENSOR_COUNT_MAX] {}; /**< gyro driver device id for each uorb instance */
|
uint32_t _gyro_device_id[SENSOR_COUNT_MAX] {}; /**< gyro driver device id for each uorb instance */
|
||||||
@@ -186,7 +185,6 @@ private:
|
|||||||
uint64_t _last_accel_timestamp[ACCEL_COUNT_MAX] {}; /**< latest full timestamp */
|
uint64_t _last_accel_timestamp[ACCEL_COUNT_MAX] {}; /**< latest full timestamp */
|
||||||
|
|
||||||
sensor_selection_s _selection {}; /**< struct containing the sensor selection to be published to the uORB */
|
sensor_selection_s _selection {}; /**< struct containing the sensor selection to be published to the uORB */
|
||||||
subsystem_info_s _info {}; /**< subsystem info publication */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace sensors */
|
} /* namespace sensors */
|
||||||
|
|||||||
Reference in New Issue
Block a user