mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-05-19 10:57:46 +08:00
UAVCAN: Overhaul SensorBridge drivers
Supposedly multiple sensor callbacks were supported; in reality, this was not the case, as the mag SensorBridge in particular can only calibrate one compass, leading to a race condition on which compass appears first on the bus to get published and calibrated (with no warning to the user that the 'wrong' compass is being used). For sensors with existing generic driver classes (baro and mag) the sensor bridges use these classes for the driver registration, and uORB publication, and calibration interface (ioctl) handling.
This commit is contained in:
committed by
Daniel Agar
parent
79dc313260
commit
4c8cfa140a
@@ -39,10 +39,14 @@
|
||||
#include "baro.hpp"
|
||||
#include <math.h>
|
||||
|
||||
#include <lib/drivers/barometer/PX4Barometer.hpp>
|
||||
|
||||
const char *const UavcanBarometerBridge::NAME = "baro";
|
||||
|
||||
#define UAVCAN_BARO_BASE_DEVICE_PATH "/dev/uavcan/baro"
|
||||
|
||||
UavcanBarometerBridge::UavcanBarometerBridge(uavcan::INode &node) :
|
||||
UavcanCDevSensorBridgeBase("uavcan_baro", "/dev/uavcan/baro", BARO_BASE_DEVICE_PATH, ORB_ID(sensor_baro)),
|
||||
UavcanCDevSensorBridgeBase("uavcan_baro", "/dev/uavcan/baro", UAVCAN_BARO_BASE_DEVICE_PATH, ORB_ID(sensor_baro)),
|
||||
_sub_air_pressure_data(node),
|
||||
_sub_air_temperature_data(node)
|
||||
{ }
|
||||
@@ -83,24 +87,40 @@ void
|
||||
UavcanBarometerBridge::air_pressure_sub_cb(const
|
||||
uavcan::ReceivedDataStructure<uavcan::equipment::air_data::StaticPressure> &msg)
|
||||
{
|
||||
sensor_baro_s report{};
|
||||
lock();
|
||||
uavcan_bridge::Channel *channel = get_channel_for_node(msg.getSrcNodeID().get());
|
||||
unlock();
|
||||
|
||||
// Set the devid address to the UAVCAN node ID (so we get a unique address)
|
||||
_device_id.devid_s.address = (uint8_t)(msg.getSrcNodeID().get() & 0xFF);
|
||||
if (channel == nullptr) {
|
||||
// Something went wrong - no channel to publish on; return
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME HACK
|
||||
* This code used to rely on msg.getMonotonicTimestamp().toUSec() instead of HRT.
|
||||
* It stopped working when the time sync feature has been introduced, because it caused libuavcan
|
||||
* to use an independent time source (based on hardware TIM5) instead of HRT.
|
||||
* The proper solution is to be developed.
|
||||
*/
|
||||
report.timestamp = hrt_absolute_time();
|
||||
report.temperature = last_temperature_kelvin - 273.15F;
|
||||
report.pressure = msg.static_pressure / 100.0F; // Convert to millibar
|
||||
report.error_count = 0;
|
||||
// Cast our generic CDev pointer to the sensor-specific driver class
|
||||
PX4Barometer *_baro = (PX4Barometer *)channel->h_driver;
|
||||
|
||||
report.device_id = _device_id.devid;
|
||||
|
||||
publish(msg.getSrcNodeID().get(), &report);
|
||||
_baro->set_temperature(last_temperature_kelvin - 273.15f);
|
||||
_baro->update(hrt_absolute_time(), msg.static_pressure / 100.0f); // Convert pressure to millibar
|
||||
}
|
||||
|
||||
int UavcanBarometerBridge::init_driver(uavcan_bridge::Channel *channel)
|
||||
{
|
||||
// update device id as we now know our device node_id
|
||||
DeviceId device_id{_device_id};
|
||||
|
||||
// No sensor info is included int he StaticPressure msg; use some generic baro type
|
||||
device_id.devid_s.devtype = DRV_BARO_DEVTYPE_MS5611;
|
||||
device_id.devid_s.address = static_cast<uint8_t>(channel->node_id);
|
||||
|
||||
channel->h_driver = new PX4Barometer(device_id.devid, ORB_PRIO_HIGH);
|
||||
|
||||
if (channel->h_driver == nullptr) {
|
||||
return PX4_ERROR;
|
||||
}
|
||||
|
||||
PX4Barometer *_baro = (PX4Barometer *)channel->h_driver;
|
||||
|
||||
channel->class_instance = _baro->get_class_instance();
|
||||
|
||||
return PX4_OK;
|
||||
}
|
||||
|
||||
@@ -59,6 +59,8 @@ private:
|
||||
void air_pressure_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::air_data::StaticPressure> &msg);
|
||||
void air_temperature_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::air_data::StaticTemperature> &msg);
|
||||
|
||||
int init_driver(uavcan_bridge::Channel *channel) override;
|
||||
|
||||
typedef uavcan::MethodBinder < UavcanBarometerBridge *,
|
||||
void (UavcanBarometerBridge::*)
|
||||
(const uavcan::ReceivedDataStructure<uavcan::equipment::air_data::StaticPressure> &) >
|
||||
|
||||
@@ -40,18 +40,17 @@
|
||||
#include <drivers/drv_hrt.h>
|
||||
#include <systemlib/err.h>
|
||||
|
||||
#include <lib/drivers/magnetometer/PX4Magnetometer.hpp>
|
||||
|
||||
const char *const UavcanMagnetometerBridge::NAME = "mag";
|
||||
|
||||
#define UAVCAN_MAG_BASE_DEVICE_PATH "/dev/uavcan/mag"
|
||||
|
||||
UavcanMagnetometerBridge::UavcanMagnetometerBridge(uavcan::INode &node) :
|
||||
UavcanCDevSensorBridgeBase("uavcan_mag", "/dev/uavcan/mag", MAG_BASE_DEVICE_PATH, ORB_ID(sensor_mag)),
|
||||
UavcanCDevSensorBridgeBase("uavcan_mag", "/dev/uavcan/mag", UAVCAN_MAG_BASE_DEVICE_PATH, ORB_ID(sensor_mag)),
|
||||
_sub_mag(node),
|
||||
_sub_mag2(node)
|
||||
{
|
||||
_device_id.devid_s.devtype = DRV_MAG_DEVTYPE_HMC5883; // <-- Why?
|
||||
|
||||
_scale.x_scale = 1.0F;
|
||||
_scale.y_scale = 1.0F;
|
||||
_scale.z_scale = 1.0F;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -85,26 +84,6 @@ UavcanMagnetometerBridge::ioctl(struct file *filp, int cmd, unsigned long arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
|
||||
case MAGIOCSSCALE: {
|
||||
std::memcpy(&_scale, reinterpret_cast<const void *>(arg), sizeof(_scale));
|
||||
return 0;
|
||||
}
|
||||
|
||||
case MAGIOCGSCALE: {
|
||||
std::memcpy(reinterpret_cast<void *>(arg), &_scale, sizeof(_scale));
|
||||
return 0;
|
||||
}
|
||||
|
||||
case MAGIOCGEXTERNAL: {
|
||||
return 1; // declare it external rise it's priority and to allow for correct orientation compensation
|
||||
}
|
||||
|
||||
case MAGIOCCALIBRATE:
|
||||
case MAGIOCSRANGE:
|
||||
case MAGIOCEXSTRAP: {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
default: {
|
||||
return CDev::ioctl(filp, cmd, arg);
|
||||
}
|
||||
@@ -115,46 +94,62 @@ void
|
||||
UavcanMagnetometerBridge::mag_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::ahrs::MagneticFieldStrength>
|
||||
&msg)
|
||||
{
|
||||
lock();
|
||||
/*
|
||||
* FIXME HACK
|
||||
* This code used to rely on msg.getMonotonicTimestamp().toUSec() instead of HRT.
|
||||
* It stopped working when the time sync feature has been introduced, because it caused libuavcan
|
||||
* to use an independent time source (based on hardware TIM5) instead of HRT.
|
||||
* The proper solution is to be developed.
|
||||
*/
|
||||
_report.timestamp = hrt_absolute_time();
|
||||
_report.device_id = _device_id.devid;
|
||||
_report.is_external = true;
|
||||
uavcan_bridge::Channel *channel = get_channel_for_node(msg.getSrcNodeID().get());
|
||||
|
||||
_report.x = (msg.magnetic_field_ga[0] - _scale.x_offset) * _scale.x_scale;
|
||||
_report.y = (msg.magnetic_field_ga[1] - _scale.y_offset) * _scale.y_scale;
|
||||
_report.z = (msg.magnetic_field_ga[2] - _scale.z_offset) * _scale.z_scale;
|
||||
unlock();
|
||||
if (channel == nullptr) {
|
||||
// Something went wrong - no channel to publish on; return
|
||||
return;
|
||||
}
|
||||
|
||||
publish(msg.getSrcNodeID().get(), &_report);
|
||||
// Cast our generic CDev pointer to the sensor-specific driver class
|
||||
PX4Magnetometer *_mag = (PX4Magnetometer *)channel->h_driver;
|
||||
|
||||
const float x = msg.magnetic_field_ga[0];
|
||||
const float y = msg.magnetic_field_ga[1];
|
||||
const float z = msg.magnetic_field_ga[2];
|
||||
|
||||
_mag->update(hrt_absolute_time(), x, y, z);
|
||||
}
|
||||
|
||||
void
|
||||
UavcanMagnetometerBridge::mag2_sub_cb(const
|
||||
uavcan::ReceivedDataStructure<uavcan::equipment::ahrs::MagneticFieldStrength2> &msg)
|
||||
{
|
||||
lock();
|
||||
/*
|
||||
* FIXME HACK
|
||||
* This code used to rely on msg.getMonotonicTimestamp().toUSec() instead of HRT.
|
||||
* It stopped working when the time sync feature has been introduced, because it caused libuavcan
|
||||
* to use an independent time source (based on hardware TIM5) instead of HRT.
|
||||
* The proper solution is to be developed.
|
||||
*/
|
||||
_report.timestamp = hrt_absolute_time();
|
||||
_report.device_id = _device_id.devid;
|
||||
_report.is_external = true;
|
||||
uavcan_bridge::Channel *channel = get_channel_for_node(msg.getSrcNodeID().get());
|
||||
|
||||
_report.x = (msg.magnetic_field_ga[0] - _scale.x_offset) * _scale.x_scale;
|
||||
_report.y = (msg.magnetic_field_ga[1] - _scale.y_offset) * _scale.y_scale;
|
||||
_report.z = (msg.magnetic_field_ga[2] - _scale.z_offset) * _scale.z_scale;
|
||||
unlock();
|
||||
if (channel == nullptr) {
|
||||
// Something went wrong - no channel to publish on; return
|
||||
return;
|
||||
}
|
||||
|
||||
publish(msg.getSrcNodeID().get(), &_report);
|
||||
// Cast our generic CDev pointer to the sensor-specific driver class
|
||||
PX4Magnetometer *_mag = (PX4Magnetometer *)channel->h_driver;
|
||||
|
||||
const float x = msg.magnetic_field_ga[0];
|
||||
const float y = msg.magnetic_field_ga[1];
|
||||
const float z = msg.magnetic_field_ga[2];
|
||||
|
||||
_mag->update(hrt_absolute_time(), x, y, z);
|
||||
}
|
||||
|
||||
int UavcanMagnetometerBridge::init_driver(uavcan_bridge::Channel *channel)
|
||||
{
|
||||
// update device id as we now know our device node_id
|
||||
DeviceId device_id{_device_id};
|
||||
|
||||
// No sensor info is included in the MagneticFieldStrength msg; use some generic mag type
|
||||
device_id.devid_s.devtype = DRV_MAG_DEVTYPE_MPU9250;
|
||||
device_id.devid_s.address = static_cast<uint8_t>(channel->node_id);
|
||||
|
||||
channel->h_driver = new PX4Magnetometer(device_id.devid, ORB_PRIO_HIGH, ROTATION_NONE);
|
||||
|
||||
if (channel->h_driver == nullptr) {
|
||||
return PX4_ERROR;
|
||||
}
|
||||
|
||||
PX4Magnetometer *_mag = (PX4Magnetometer *)channel->h_driver;
|
||||
_mag->set_external(true);
|
||||
channel->class_instance = _mag->get_class_instance();
|
||||
|
||||
return PX4_OK;
|
||||
}
|
||||
|
||||
@@ -59,6 +59,8 @@ private:
|
||||
|
||||
int ioctl(struct file *filp, int cmd, unsigned long arg) override;
|
||||
|
||||
int init_driver(uavcan_bridge::Channel *channel) override;
|
||||
|
||||
void mag_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::ahrs::MagneticFieldStrength> &msg);
|
||||
void mag2_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::ahrs::MagneticFieldStrength2> &msg);
|
||||
|
||||
@@ -74,7 +76,4 @@ private:
|
||||
|
||||
uavcan::Subscriber<uavcan::equipment::ahrs::MagneticFieldStrength, MagCbBinder> _sub_mag;
|
||||
uavcan::Subscriber<uavcan::equipment::ahrs::MagneticFieldStrength2, Mag2CbBinder> _sub_mag2;
|
||||
|
||||
mag_calibration_s _scale{};
|
||||
sensor_mag_s _report{};
|
||||
};
|
||||
|
||||
@@ -73,7 +73,9 @@ UavcanCDevSensorBridgeBase::~UavcanCDevSensorBridgeBase()
|
||||
void
|
||||
UavcanCDevSensorBridgeBase::publish(const int node_id, const void *report)
|
||||
{
|
||||
Channel *channel = nullptr;
|
||||
assert(report != nullptr);
|
||||
|
||||
uavcan_bridge::Channel *channel = nullptr;
|
||||
|
||||
// Checking if such channel already exists
|
||||
for (unsigned i = 0; i < _max_channels; i++) {
|
||||
@@ -109,6 +111,9 @@ UavcanCDevSensorBridgeBase::publish(const int node_id, const void *report)
|
||||
// update device id as we now know our device node_id
|
||||
_device_id.devid_s.address = static_cast<uint8_t>(node_id);
|
||||
|
||||
// initialize the driver, which registers the class device name and uORB publisher
|
||||
// int ret = init_driver(channel);
|
||||
|
||||
// Ask the CDev helper which class instance we can take
|
||||
const int class_instance = register_class_devname(_class_devname);
|
||||
|
||||
@@ -119,29 +124,86 @@ UavcanCDevSensorBridgeBase::publish(const int node_id, const void *report)
|
||||
return;
|
||||
}
|
||||
|
||||
// if (ret != PX4_OK) {
|
||||
// DEVICE_LOG("INIT ERROR node %d errno %d", channel->node_id, ret);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Publish to the appropriate topic, abort on failure
|
||||
channel->node_id = node_id;
|
||||
channel->class_instance = class_instance;
|
||||
DEVICE_LOG("channel %d class instance %d ok", channel->node_id, channel->class_instance);
|
||||
|
||||
channel->orb_advert = orb_advertise_multi(_orb_topic, report, &channel->orb_instance, ORB_PRIO_VERY_HIGH);
|
||||
|
||||
if (channel->orb_advert == nullptr) {
|
||||
DEVICE_LOG("ADVERTISE FAILED");
|
||||
(void)unregister_class_devname(_class_devname, class_instance);
|
||||
*channel = Channel();
|
||||
*channel = uavcan_bridge::Channel();
|
||||
return;
|
||||
}
|
||||
|
||||
DEVICE_LOG("channel %d class instance %d ok", channel->node_id, channel->class_instance);
|
||||
}
|
||||
|
||||
//publish_sensor()
|
||||
|
||||
assert(channel != nullptr);
|
||||
|
||||
(void)orb_publish(_orb_topic, channel->orb_advert, report);
|
||||
}
|
||||
|
||||
unsigned
|
||||
UavcanCDevSensorBridgeBase::get_num_redundant_channels() const
|
||||
uavcan_bridge::Channel *UavcanCDevSensorBridgeBase::get_channel_for_node(int node_id)
|
||||
{
|
||||
uavcan_bridge::Channel *channel = nullptr;
|
||||
|
||||
// Checking if such channel already exists
|
||||
for (unsigned i = 0; i < _max_channels; i++) {
|
||||
if (_channels[i].node_id == node_id) {
|
||||
channel = _channels + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No such channel - try to create one
|
||||
if (channel == nullptr) {
|
||||
if (_out_of_channels) {
|
||||
return channel; // Give up immediately - saves some CPU time
|
||||
}
|
||||
|
||||
DEVICE_LOG("adding channel %d...", node_id);
|
||||
|
||||
// Search for the first free channel
|
||||
for (unsigned i = 0; i < _max_channels; i++) {
|
||||
if (_channels[i].node_id < 0) {
|
||||
channel = _channels + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No free channels left
|
||||
if (channel == nullptr) {
|
||||
_out_of_channels = true;
|
||||
DEVICE_LOG("out of channels");
|
||||
return channel;
|
||||
}
|
||||
|
||||
// initialize the driver, which registers the class device name and uORB publisher
|
||||
channel->node_id = node_id;
|
||||
int ret = init_driver(channel);
|
||||
|
||||
if (ret != PX4_OK) {
|
||||
DEVICE_LOG("INIT ERROR node %d errno %d", channel->node_id, ret);
|
||||
return channel;
|
||||
}
|
||||
|
||||
DEVICE_LOG("channel %d class instance %d ok", channel->node_id, channel->class_instance);
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
unsigned UavcanCDevSensorBridgeBase::get_num_redundant_channels() const
|
||||
{
|
||||
unsigned out = 0;
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <uavcan/uavcan.hpp>
|
||||
#include <drivers/device/device.h>
|
||||
#include <drivers/drv_orb_dev.h>
|
||||
#include <lib/cdev/CDev.hpp>
|
||||
#include <uORB/uORB.h>
|
||||
|
||||
/**
|
||||
@@ -81,26 +82,30 @@ public:
|
||||
static void make_all(uavcan::INode &node, List<IUavcanSensorBridge *> &list);
|
||||
};
|
||||
|
||||
namespace uavcan_bridge
|
||||
{
|
||||
struct Channel {
|
||||
int node_id = -1;
|
||||
orb_advert_t orb_advert = nullptr;
|
||||
int class_instance = -1;
|
||||
int orb_instance = -1;
|
||||
cdev::CDev *h_driver = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the base class for redundant sensors with an independent ORB topic per each redundancy channel.
|
||||
* For example, sensor_mag0, sensor_mag1, etc.
|
||||
*/
|
||||
class UavcanCDevSensorBridgeBase : public IUavcanSensorBridge, public device::CDev
|
||||
{
|
||||
struct Channel {
|
||||
int node_id = -1;
|
||||
orb_advert_t orb_advert = nullptr;
|
||||
int class_instance = -1;
|
||||
int orb_instance = -1;
|
||||
};
|
||||
|
||||
const char *const _class_devname;
|
||||
const orb_id_t _orb_topic;
|
||||
Channel *const _channels;
|
||||
uavcan_bridge::Channel *const _channels;
|
||||
bool _out_of_channels = false;
|
||||
|
||||
protected:
|
||||
static constexpr unsigned DEFAULT_MAX_CHANNELS = 5;
|
||||
static constexpr unsigned DEFAULT_MAX_CHANNELS = ORB_MULTI_MAX_INSTANCES;
|
||||
const unsigned _max_channels;
|
||||
|
||||
UavcanCDevSensorBridgeBase(const char *name, const char *devname, const char *class_devname,
|
||||
@@ -109,7 +114,7 @@ protected:
|
||||
device::CDev(name, devname),
|
||||
_class_devname(class_devname),
|
||||
_orb_topic(orb_topic_sensor),
|
||||
_channels(new Channel[max_channels]),
|
||||
_channels(new uavcan_bridge::Channel[max_channels]),
|
||||
_max_channels(max_channels)
|
||||
{
|
||||
_device_id.devid_s.bus_type = DeviceBusType_UAVCAN;
|
||||
@@ -124,6 +129,15 @@ protected:
|
||||
*/
|
||||
void publish(const int node_id, const void *report);
|
||||
|
||||
/**
|
||||
* Init the sensor driver for this channel.
|
||||
* Implementation depends on sensor type being constructed.
|
||||
* @param channel Channel pointer for which h_driver should be initialized.
|
||||
*/
|
||||
virtual int init_driver(uavcan_bridge::Channel *channel) { return PX4_OK; };
|
||||
|
||||
uavcan_bridge::Channel *get_channel_for_node(int node_id);
|
||||
|
||||
public:
|
||||
virtual ~UavcanCDevSensorBridgeBase();
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
|
||||
void update(hrt_abstime timestamp, float pressure);
|
||||
|
||||
int get_class_instance(void) { return _class_device_instance; };
|
||||
|
||||
void print_status();
|
||||
|
||||
private:
|
||||
|
||||
@@ -60,6 +60,8 @@ public:
|
||||
|
||||
void update(hrt_abstime timestamp_sample, float x, float y, float z);
|
||||
|
||||
int get_class_instance(void) { return _class_device_instance; };
|
||||
|
||||
void print_status();
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user