feat(heater): heater-modul in cannode does now also publish pitot_temperature via raw-air-data,

pitot_temperature added to uORB-msg differential_pressure
This commit is contained in:
Marin Doetterer
2026-06-24 17:28:30 +02:00
committed by Alexander Lerach
parent 6849612e97
commit 4065b05930
30 changed files with 118 additions and 43 deletions

View File

@@ -25,6 +25,6 @@ param set-default HEATER1_TEMP 10.0
param set-default UAVCAN_ESC_IFACE 2
# IIM42653 on SPI1
param set-default HEATER1_IMU_ID 3014666
param set-default HEATER1_SENS_ID 3014666
safety_button start

View File

@@ -26,12 +26,12 @@ param set-default UAVCAN_ESC_IFACE 2
if ver hwtypecmp ARKV6X000
then
param set-default HEATER1_IMU_ID 2818058
param set-default HEATER1_SENS_ID 2818058
fi
if ver hwtypecmp ARKV6X001
then
param set-default HEATER1_IMU_ID 3014666
param set-default HEATER1_SENS_ID 3014666
fi
safety_button start

View File

@@ -22,7 +22,7 @@ param set-default HEATER1_TEMP 10.0
if ver hwtypecmp ARKFPV000
then
param set-default HEATER1_IMU_ID 3014666
param set-default HEATER1_SENS_ID 3014666
fi
param set-default BAT1_V_DIV 21.0

View File

@@ -31,7 +31,7 @@ param set-default UAVCAN_ESC_IFACE 1
if ver hwtypecmp ARKPI6X000
then
# TODO: Add the correct sensor ID
param set-default HEATER1_IMU_ID 2490378
param set-default HEATER1_SENS_ID 2490378
fi
param set-default EKF2_MULTI_IMU 0

View File

@@ -21,12 +21,12 @@ param set-default UAVCAN_SUB_GPS 1
param set-default UAVCAN_SUB_BAT 1
# IMU thermal control (multi-instance heater)
# HEATER1_IMU_ID: 2818058(IIM42652 SPI1)
# HEATER2_IMU_ID: 3014698(IIM42653 SPI5)
# HEATER1_SENS_ID: 2818058(IIM42652 SPI1)
# HEATER2_SENS_ID: 3014698(IIM42653 SPI5)
param set-default SENS_EN_THERMAL 1
param set-default HEATER1_IMU_ID 2818058
param set-default HEATER1_SENS_ID 2818058
param set-default HEATER1_TEMP 45
param set-default HEATER2_IMU_ID 3014698
param set-default HEATER2_SENS_ID 3014698
param set-default HEATER2_TEMP 45
# CUAV pwm voltage 3.3V/5V switch

View File

@@ -23,9 +23,9 @@ param set-default UAVCAN_SUB_BAT 1
# IMU thermal control (multi-instance heater)
param set-default SENS_EN_THERMAL 1
param set-default HEATER1_IMU_ID 2818066
param set-default HEATER1_SENS_ID 2818066
param set-default HEATER1_TEMP 45
param set-default HEATER2_IMU_ID 3014698
param set-default HEATER2_SENS_ID 3014698
param set-default HEATER2_TEMP 45
# CUAV pwm voltage 3.3V/5V switch

View File

@@ -12,7 +12,7 @@ param set-default BAT2_A_PER_V 24
# Enable IMU thermal control
param set-default SENS_EN_THERMAL 1
param set-default HEATER1_IMU_ID 6946850
param set-default HEATER1_SENS_ID 6946850
rgbled_pwm start
safety_button start

View File

@@ -12,6 +12,6 @@ param set-default BAT2_A_PER_V 17
# Disable IMU thermal control
param set-default SENS_EN_THERMAL 0
param set-default -s HEATER1_IMU_ID 2621474
param set-default -s HEATER1_SENS_ID 2621474
set IOFW "/etc/extras/cubepilot_io-v2_default.bin"

View File

@@ -18,7 +18,7 @@ ms5611 -s -b 4 start
if icm42688p -s -b 4 -R 10 -q start -c 15
then
# We need to use the temperature of the first isolated IMU for heater control.
param set-default HEATER1_IMU_ID 2490402
param set-default HEATER1_SENS_ID 2490402
if ! icm20948 -s -b 4 -R 10 -M -q start
then
@@ -28,7 +28,7 @@ else
icm45686 -s -b 4 -R 10 start -c 15
icm45686 -s -b 4 -R 6 start -c 13
param set-default HEATER1_IMU_ID 3407906
param set-default HEATER1_SENS_ID 3407906
fi
# SPI1, body-fixed

View File

@@ -12,7 +12,7 @@ param set-default BAT2_A_PER_V 24
# Enable IMU thermal control
param set-default SENS_EN_THERMAL 1
param set-default HEATER1_IMU_ID 6946850
param set-default HEATER1_SENS_ID 6946850
rgbled_pwm start
safety_button start

View File

@@ -10,7 +10,7 @@ param set-default SER_TEL2_BAUD 921600 # 921600
# Temperature stabilization
param set-default SENS_EN_THERMAL 1 # Enable heater
param set-default HEATER1_IMU_ID 2359314 # Heated IMU ID
param set-default HEATER1_SENS_ID 2359314 # Heated SENSOR ID
# Battery scaling
param set-default BAT1_N_CELLS 4

View File

@@ -9,4 +9,5 @@ uint64 timestamp_sample # [us] Time of raw data capture
uint32 device_id # [-] Unique device ID for the sensor that does not change between power cycles
float32 differential_pressure_pa # [Pa] Differential pressure reading (may be negative)
float32 temperature # [degC] [@invalid NaN if unknown] Temperature
float32 pitot_temperature # [degC] [@invalid NaN if unknown] Pitot temperature (if available)
uint32 error_count # [-] Number of errors detected by driver

View File

@@ -243,6 +243,7 @@ int ASP5033::collect()
differential_pressure.temperature = _temperature ;
differential_pressure.pitot_temperature = NAN;
differential_pressure.error_count = perf_event_count(_comms_errors);
differential_pressure.timestamp = timestamp_sample;
_differential_pressure_pub.publish(differential_pressure);

View File

@@ -88,6 +88,7 @@ void AUAV_Differential::publish_pressure(const float pressure_p, const float tem
}
differential_pressure.temperature = temperature_c;
differential_pressure.pitot_temperature = NAN;
differential_pressure.error_count = perf_event_count(_comms_errors);
_differential_pressure_pub.publish(differential_pressure);
}

View File

@@ -123,6 +123,7 @@ int ETSAirspeed::collect()
}
differential_pressure.temperature = NAN;
differential_pressure.pitot_temperature = NAN;
differential_pressure.error_count = perf_event_count(_comms_errors);
differential_pressure.timestamp = hrt_absolute_time();
_differential_pressure_pub.publish(differential_pressure);

View File

@@ -173,6 +173,7 @@ int MS4515::collect()
}
differential_pressure.temperature = temperature_c;
differential_pressure.pitot_temperature = NAN;
differential_pressure.error_count = perf_event_count(_comms_errors);
differential_pressure.timestamp = hrt_absolute_time();
_differential_pressure_pub.publish(differential_pressure);

View File

@@ -210,6 +210,7 @@ void MS4525DO::RunImpl()
}
differential_pressure.temperature = temperature_c;
differential_pressure.pitot_temperature = NAN;
differential_pressure.error_count = perf_event_count(_comms_errors);
differential_pressure.timestamp = hrt_absolute_time();
_differential_pressure_pub.publish(differential_pressure);

View File

@@ -318,6 +318,7 @@ int MS5525DSO::collect()
}
differential_pressure.temperature = temperature_c;
differential_pressure.pitot_temperature = NAN;
differential_pressure.error_count = perf_event_count(_comms_errors);
differential_pressure.timestamp = hrt_absolute_time();
_differential_pressure_pub.publish(differential_pressure);

View File

@@ -197,6 +197,7 @@ int SDP3X::collect()
}
differential_pressure.temperature = temperature_c;
differential_pressure.pitot_temperature = NAN;
differential_pressure.error_count = perf_event_count(_comms_errors);
differential_pressure.timestamp = hrt_absolute_time();
_differential_pressure_pub.publish(differential_pressure);

View File

@@ -112,8 +112,8 @@ Heater::Heater(uint8_t instance) :
char name[32];
// Dynamically locate the parameter handle corresponding to the current instance
snprintf(name, sizeof(name), "HEATER%u_IMU_ID", (unsigned)_instance);
_param_handles.imu_id = param_find(name); //
snprintf(name, sizeof(name), "HEATER%u_SENS_ID", (unsigned)_instance);
_param_handles.sens_id = param_find(name);
snprintf(name, sizeof(name), "HEATER%u_TEMP", (unsigned)_instance);
_param_handles.temp = param_find(name); //
@@ -316,7 +316,7 @@ void Heater::heater_on()
bool Heater::initialize_topics()
{
// Force a single read of the parameters to ensure _params.imu_id is already up to date.
// Force a single read of the parameters to ensure _params.sens_id is up to date.
update_params(true);
if (!_heater_initialized) {
@@ -324,7 +324,7 @@ bool Heater::initialize_topics()
return false;
}
const int32_t target = _params.imu_id;
const int32_t target = _params.sens_id;
const bool use_hygro = (_params.temp_src == heater_status_s::TEMPERATURE_SOURCE_HYGRO);
int8_t selected_instance = -1;
@@ -540,18 +540,18 @@ int Heater::start()
{
update_params(true);
const int32_t target = _params.imu_id;
const int32_t target = _params.sens_id;
// Disabled instance
if (target < 0) {
PX4_INFO("heater %u disabled (HEATER%u_IMU_ID=%ld)",
PX4_INFO("heater %u disabled (HEATER%u_SENS_ID=%ld)",
(unsigned)_instance, (unsigned)_instance, (long)target);
return PX4_OK;
}
// Auto-select only allowed for legacy single-heater setups
if ((target == 0) && (HEATER_NUM > 1)) {
PX4_INFO("heater %u disabled (HEATER%u_IMU_ID=0 not allowed when HEATER_NUM>1)",
PX4_INFO("heater %u disabled (HEATER%u_SENS_ID=0 not allowed when HEATER_NUM>1)",
(unsigned)_instance, (unsigned)_instance);
return PX4_OK;
}
@@ -677,8 +677,8 @@ void Heater::update_params(const bool force)
_parameter_update_sub.copy(&param_update);
// update parameters from storage
if (_param_handles.imu_id != PARAM_INVALID) {
param_get(_param_handles.imu_id, &_params.imu_id);
if (_param_handles.sens_id != PARAM_INVALID) {
param_get(_param_handles.sens_id, &_params.sens_id);
}
if (_param_handles.temp != PARAM_INVALID) {

View File

@@ -176,7 +176,7 @@ private:
volatile bool _should_exit{false};
struct {
param_t imu_id;
param_t sens_id;
param_t temp;
param_t temp_p;
param_t temp_i;
@@ -187,7 +187,7 @@ private:
} _param_handles;
struct {
int32_t imu_id; // HEATER<i>_IMU_ID: <0 disable, 0 auto, >0 match device_id
int32_t sens_id; // HEATER<i>_SENS_ID: <0 disable, 0 auto, >0 match device_id
float temp; // target temperature
float temp_p;
float temp_i;

View File

@@ -5,14 +5,15 @@ module_name: heater
parameters:
- group: Sensors
definitions:
HEATER${i}_IMU_ID:
HEATER${i}_SENS_ID:
description:
short: The ID of the IMU controlled by heater ${i}
short: Device ID of the temperature sensor read by heater ${i}
long: |
Specifies the sensor device ID (DEVID) that this heater instance controls.
Specifies the device ID of the temperature sensor whose readings
heater ${i} uses for thermal control.
-1 disables this heater instance.
If set to 0, auto-select is only supported when HEATER_NUM == 1. On boards with multiple heater outputs,
a valid DEVID must be configured for each heater to ensure a 1:1 mapping between heater output and IMU.
a valid DEVID must be configured for each heater to ensure a 1:1 mapping between heater output and temperature sensor.
type: int32
@@ -25,7 +26,7 @@ parameters:
description:
short: Target temperature for heater ${i}
long: |
Specify the target stable temperature (in degrees Celsius) for the IMU.
Specify the target stable temperature (in degrees Celsius) for the sensor controlled by heater ${i}.
It is generally recommended to set this between 40°C and 60°C,
which must be higher than the maximum ambient temperature.
@@ -41,7 +42,7 @@ parameters:
HEATER${i}_TEMP_FF:
description:
short: IMU heater controller ${i} feedforward value
short: Heater ${i} controller feedforward value
long: |
Used to predict the baseline power consumption required to maintain temperature,
helping to reduce adjustment time.
@@ -58,7 +59,7 @@ parameters:
HEATER${i}_TEMP_I:
description:
short: IMU heater controller ${i} integrator gain value
short: Heater ${i} controller integrator gain value
long: |
Integral gain is used to eliminate steady-state error,
ensuring that the temperature ultimately reaches the setpoint target.
@@ -75,7 +76,7 @@ parameters:
HEATER${i}_TEMP_P:
description:
short: IMU heater controller ${i} proportional gain value
short: Heater ${i} controller proportional gain value
long: |
The proportional gain determines how quickly the controller responds to temperature deviations.
@@ -109,11 +110,11 @@ parameters:
short: Temperature source for heater ${i}
long: |
Selects the sensor used as the temperature input for heater control.
0 = IMU (sensor_accel temperature), 1 = Hygrometer (sensor_hygrometer temperature).
0 = Accel (sensor_accel temperature), 1 = Hygrometer (sensor_hygrometer temperature).
type: int32
values:
0: IMU
0: Accel
1: Hygrometer
reboot_required: true
num_instances: *max_num_config_instances

View File

@@ -78,14 +78,13 @@ void UavcanDifferentialPressureBridge::air_sub_cb(const
diff_press_pa = -1.0f * msg.differential_pressure;
}
float temperature_c = msg.static_air_temperature + atmosphere::kAbsoluteNullCelsius;
differential_pressure_s report{};
report.timestamp_sample = timestamp_sample;
report.device_id = make_uavcan_device_id(msg);
report.differential_pressure_pa = diff_press_pa;
report.temperature = temperature_c;
report.timestamp = hrt_absolute_time();
report.differential_pressure_pa = diff_press_pa;
report.temperature = msg.static_air_temperature + atmosphere::kAbsoluteNullCelsius;
report.pitot_temperature = msg.pitot_temperature + atmosphere::kAbsoluteNullCelsius;
publish(msg.getSrcNodeID().get(), &report);

View File

@@ -40,8 +40,11 @@
#include <uavcan/equipment/air_data/RawAirData.hpp>
#include <uORB/SubscriptionCallback.hpp>
#include <uORB/SubscriptionMultiArray.hpp>
#include <uORB/topics/differential_pressure.h>
#include <uORB/topics/heater_status.h>
#include <lib/atmosphere/atmosphere.h>
#include <lib/parameters/param.h>
namespace uavcannode
{
@@ -58,6 +61,11 @@ public:
uavcan::Publisher<uavcan::equipment::air_data::RawAirData>(node)
{
this->setPriority(uavcan::TransferPriority::Default);
param_t h = param_find("CANNODE_PT_SENS");
if (h != PARAM_INVALID) {
param_get(h, &_heater_sensor_id);
}
}
void PrintInfo() override
@@ -72,6 +80,27 @@ public:
void BroadcastAnyUpdates() override
{
_heater_sensor_temp_current_k = NAN;
if (_heater_sensor_id > 0) {
for (auto &heater_sub : _heater_subs) {
heater_status_s heater_status{};
if (heater_sub.update(&heater_status) && (heater_status.device_id == (uint32_t)_heater_sensor_id)) {
_heater_sensor_temp_current_k = heater_status.temperature_sensor - atmosphere::kAbsoluteNullCelsius;
break;
}
}
}
if (PX4_ISFINITE(_heater_sensor_temp_current_k)) {
_heater_sensor_temp_last_k = _heater_sensor_temp_current_k;
_heater_sensor_temp_last_update_time = hrt_absolute_time();
} else if (hrt_elapsed_time(&_heater_sensor_temp_last_update_time) < 500_ms) {
_heater_sensor_temp_current_k = _heater_sensor_temp_last_k;
}
// differential_pressure -> uavcan::equipment::air_data::RawAirData
differential_pressure_s diff_press;
@@ -83,7 +112,7 @@ public:
raw_air_data.differential_pressure = diff_press.differential_pressure_pa;
raw_air_data.differential_pressure_sensor_temperature = diff_press.temperature - atmosphere::kAbsoluteNullCelsius;
raw_air_data.static_air_temperature = diff_press.temperature - atmosphere::kAbsoluteNullCelsius;
raw_air_data.pitot_temperature = NAN;
raw_air_data.pitot_temperature = _heater_sensor_temp_current_k;
// raw_air_data.covariance
uavcan::Publisher<uavcan::equipment::air_data::RawAirData>::broadcast(raw_air_data);
@@ -91,5 +120,13 @@ public:
uORB::SubscriptionCallbackWorkItem::registerCallback();
}
}
private:
uORB::SubscriptionMultiArray<heater_status_s> _heater_subs{ORB_ID::heater_status};
int32_t _heater_sensor_id{0};
float _heater_sensor_temp_current_k{NAN};
float _heater_sensor_temp_last_k{NAN};
hrt_abstime _heater_sensor_temp_last_update_time{0};
};
} // namespace uavcannode

View File

@@ -67,3 +67,14 @@ parameters:
default: 1
max: 1
reboot_required: true
CANNODE_PT_SENS:
description:
short: Temperature sensor device ID for pitot temperature
long: |-
Device ID of the temperature sensor (HEATER*_SENS_ID) whose value
is published as pitot_temperature in uavcan::equipment::air_data::RawAirData.
Set to 0 to disable (RawAirData.pitot_temperature set to NaN).
type: int32
default: 0
reboot_required: true

View File

@@ -244,5 +244,20 @@ param_modify_on_import_ret param_modify_on_import(bson_node_t node)
}
}
// 2026-06-22: translate HEATER*_IMU_ID to HEATER*_SENS_ID
// HEATER_MAX_INSTANCES == 3 (in heater.h)
{
if ((node->type == bson_type_t::BSON_INT32) && (strncmp("HEATER", node->name, 6) == 0)
&& strstr(node->name, "_IMU_ID") != nullptr) {
char old_name[BSON_MAXNAME];
strncpy(old_name, node->name, sizeof(old_name));
char new_name[16];
snprintf(new_name, sizeof(new_name), "HEATER%c_SENS_ID", node->name[6]);
strcpy(node->name, new_name);
PX4_INFO("migrating %s -> %s", old_name, new_name);
return param_modify_on_import_ret::PARAM_MODIFIED;
}
}
return param_modify_on_import_ret::PARAM_NOT_MODIFIED;
}

View File

@@ -2580,6 +2580,7 @@ MavlinkReceiver::handle_message_hil_sensor(mavlink_message_t *msg)
report.timestamp_sample = timestamp;
report.device_id = 1377548; // 1377548: DRV_DIFF_PRESS_DEVTYPE_SIM, BUS: 1, ADDR: 5, TYPE: SIMULATION
report.temperature = hil_sensor.temperature;
report.pitot_temperature = NAN;
report.differential_pressure_pa = hil_sensor.diff_pressure * 100.0f; // hPa to Pa
report.timestamp = hrt_absolute_time();
_differential_pressure_pub.publish(report);

View File

@@ -425,6 +425,7 @@ void GZBridge::airspeedCallback(const gz::msgs::AirSpeed &msg)
report.differential_pressure_pa = msg.diff_pressure(); // hPa to Pa;
_temperature = static_cast<float>(msg.temperature()) + atmosphere::kAbsoluteNullCelsius; // K to C
report.temperature = _temperature;
report.pitot_temperature = NAN;
_differential_pressure_pub.publish(report);
}

View File

@@ -164,6 +164,7 @@ void SensorAirspeedSim::Run()
differential_pressure.device_id = 1377548; // 1377548: DRV_DIFF_PRESS_DEVTYPE_SIM, BUS: 1, ADDR: 5, TYPE: SIMULATION
differential_pressure.differential_pressure_pa = _last_differential_pressure_pa;
differential_pressure.temperature = temperature_local + ABSOLUTE_ZERO_C; // K to C
differential_pressure.pitot_temperature = NAN;
differential_pressure.timestamp = hrt_absolute_time();
_differential_pressure_pub.publish(differential_pressure);
}

View File

@@ -342,6 +342,7 @@ void SimulatorMavlink::update_sensors(const hrt_abstime &time, const mavlink_hil
report.device_id = 1377548; // 1377548: DRV_DIFF_PRESS_DEVTYPE_SIM, BUS: 1, ADDR: 5, TYPE: SIMULATION
report.differential_pressure_pa = sensors.diff_pressure * 100.f * airspeed_blockage_scale; // hPa to Pa;
report.temperature = _sensors_temperature;
report.pitot_temperature = NAN;
report.timestamp = hrt_absolute_time();
_differential_pressure_pub.publish(report);
}