1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-25 17:25:20 +03:00

Port iNav's rangefinder

This commit is contained in:
jflyper 2017-12-14 23:44:22 +09:00
parent 8a9fd29dd3
commit 11c47c631b
53 changed files with 898 additions and 492 deletions

View file

@ -35,24 +35,12 @@
#include "sensors/barometer.h"
#include "sensors/gyro.h"
#include "sensors/compass.h"
#include "sensors/sonar.h"
#include "sensors/rangefinder.h"
#include "sensors/initialisation.h"
uint8_t detectedSensors[SENSOR_INDEX_COUNT] = { GYRO_NONE, ACC_NONE, BARO_NONE, MAG_NONE };
#ifdef USE_SONAR
static bool sonarDetect(void)
{
if (feature(FEATURE_SONAR)) {
// the user has set the sonar feature, so assume they have an HC-SR04 plugged in,
// since there is no way to detect it
sensorsSet(SENSOR_SONAR);
return true;
}
return false;
}
#endif
// requestedSensors is not actually used
uint8_t requestedSensors[SENSOR_INDEX_COUNT] = { GYRO_NONE, ACC_NONE, BARO_NONE, MAG_NONE, RANGEFINDER_NONE };
uint8_t detectedSensors[SENSOR_INDEX_COUNT] = { GYRO_NONE, ACC_NONE, BARO_NONE, MAG_NONE, RANGEFINDER_NONE };
bool sensorsAutodetect(void)
{
@ -73,10 +61,8 @@ bool sensorsAutodetect(void)
baroDetect(&baro.dev, barometerConfig()->baro_hardware);
#endif
#ifdef USE_SONAR
if (sonarDetect()) {
sonarInit(sonarConfig());
}
#ifdef USE_RANGEFINDER
rangefinderInit();
#endif
return gyroDetected;

View file

@ -0,0 +1,370 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <platform.h>
#ifdef USE_RANGEFINDER
#include "build/build_config.h"
#include "build/debug.h"
#include "common/maths.h"
#include "common/utils.h"
#include "common/time.h"
#include "config/feature.h"
#include "pg/pg.h"
#include "pg/pg_ids.h"
#include "drivers/io.h"
#include "drivers/time.h"
#include "drivers/rangefinder/rangefinder_hcsr04.h"
#include "drivers/rangefinder/rangefinder.h"
#include "fc/config.h"
#include "fc/runtime_config.h"
#include "fc/fc_tasks.h"
#include "sensors/sensors.h"
#include "sensors/rangefinder.h"
#include "sensors/battery.h"
#include "scheduler/scheduler.h"
//#include "uav_interconnect/uav_interconnect.h"
// XXX Interface to CF/BF legacy(?) altitude estimation code.
// XXX Will be gone once iNav's estimator is ported.
int16_t rangefinderMaxRangeCm;
int16_t rangefinderMaxAltWithTiltCm;
int16_t rangefinderCfAltCm; // Complimentary Filter altitude
rangefinder_t rangefinder;
#define RANGEFINDER_HARDWARE_TIMEOUT_MS 500 // Accept 500ms of non-responsive sensor, report HW failure otherwise
#define RANGEFINDER_DYNAMIC_THRESHOLD 600 //Used to determine max. usable rangefinder disatance
#define RANGEFINDER_DYNAMIC_FACTOR 75
PG_REGISTER_WITH_RESET_TEMPLATE(rangefinderConfig_t, rangefinderConfig, PG_RANGEFINDER_CONFIG, 0);
PG_RESET_TEMPLATE(rangefinderConfig_t, rangefinderConfig,
.rangefinder_hardware = RANGEFINDER_NONE,
);
#ifdef USE_RANGEFINDER_HCSR04
PG_REGISTER_WITH_RESET_TEMPLATE(sonarConfig_t, sonarConfig, PG_SONAR_CONFIG, 1);
#ifndef RANGEFINDER_HCSR04_TRIGGER_PIN
#define RANGEFINDER_HCSR04_TRIGGER_PIN NONE
#endif
#ifndef RANGEFINDER_HCSR04_ECHO_PIN
#define RANGEFINDER_HCSR04_ECHO_PIN NONE
#endif
PG_RESET_TEMPLATE(sonarConfig_t, sonarConfig,
.triggerTag = IO_TAG(RANGEFINDER_HCSR04_TRIGGER_PIN),
.echoTag = IO_TAG(RANGEFINDER_HCSR04_ECHO_PIN),
);
#endif
/*
* Detect which rangefinder is present
*/
static bool rangefinderDetect(rangefinderDev_t * dev, uint8_t rangefinderHardwareToUse)
{
rangefinderType_e rangefinderHardware = RANGEFINDER_NONE;
requestedSensors[SENSOR_INDEX_RANGEFINDER] = rangefinderHardwareToUse;
switch (rangefinderHardwareToUse) {
case RANGEFINDER_HCSR04:
#ifdef USE_RANGEFINDER_HCSR04
{
if (hcsr04Detect(dev, sonarConfig())) { // FIXME: Do actual detection if HC-SR04 is plugged in
rangefinderHardware = RANGEFINDER_HCSR04;
rescheduleTask(TASK_RANGEFINDER, TASK_PERIOD_MS(RANGEFINDER_HCSR04_TASK_PERIOD_MS));
}
}
#endif
break;
case RANGEFINDER_SRF10:
#ifdef USE_RANGEFINDER_SRF10
if (srf10Detect(dev)) {
rangefinderHardware = RANGEFINDER_SRF10;
rescheduleTask(TASK_RANGEFINDER, TASK_PERIOD_MS(RANGEFINDER_SRF10_TASK_PERIOD_MS));
}
#endif
break;
case RANGEFINDER_HCSR04I2C:
#ifdef USE_RANGEFINDER_HCSR04_I2C
if (hcsr04i2c0Detect(dev)) {
rangefinderHardware = RANGEFINDER_HCSR04I2C;
rescheduleTask(TASK_RANGEFINDER, TASK_PERIOD_MS(RANGEFINDER_HCSR04_i2C_TASK_PERIOD_MS));
}
#endif
break;
case RANGEFINDER_VL53L0X:
#if defined(USE_RANGEFINDER_VL53L0X)
if (vl53l0xDetect(dev)) {
rangefinderHardware = RANGEFINDER_VL53L0X;
rescheduleTask(TASK_RANGEFINDER, TASK_PERIOD_MS(RANGEFINDER_VL53L0X_TASK_PERIOD_MS));
}
#endif
break;
case RANGEFINDER_UIB:
#if defined(USE_RANGEFINDER_UIB)
if (uibRangefinderDetect(dev)) {
rangefinderHardware = RANGEFINDER_UIB;
rescheduleTask(TASK_RANGEFINDER, TASK_PERIOD_MS(RANGEFINDER_UIB_TASK_PERIOD_MS));
}
#endif
break;
case RANGEFINDER_NONE:
rangefinderHardware = RANGEFINDER_NONE;
break;
}
if (rangefinderHardware == RANGEFINDER_NONE) {
sensorsClear(SENSOR_RANGEFINDER);
return false;
}
detectedSensors[SENSOR_INDEX_RANGEFINDER] = rangefinderHardware;
sensorsSet(SENSOR_RANGEFINDER);
return true;
}
void rangefinderResetDynamicThreshold(void)
{
rangefinder.snrThresholdReached = false;
rangefinder.dynamicDistanceThreshold = 0;
}
bool rangefinderInit(void)
{
if (!rangefinderDetect(&rangefinder.dev, rangefinderConfig()->rangefinder_hardware)) {
return false;
}
rangefinder.dev.init(&rangefinder.dev);
rangefinder.rawAltitude = RANGEFINDER_OUT_OF_RANGE;
rangefinder.calculatedAltitude = RANGEFINDER_OUT_OF_RANGE;
rangefinder.maxTiltCos = cos_approx(DECIDEGREES_TO_RADIANS(rangefinder.dev.detectionConeExtendedDeciDegrees / 2.0f));
rangefinder.lastValidResponseTimeMs = millis();
rangefinder.snr = 0;
rangefinderResetDynamicThreshold();
// XXX Interface to CF/BF legacy(?) altitude estimation code.
// XXX Will be gone once iNav's estimator is ported.
rangefinderMaxRangeCm = rangefinder.dev.maxRangeCm;
rangefinderMaxAltWithTiltCm = rangefinderMaxRangeCm * rangefinder.maxTiltCos;
rangefinderCfAltCm = rangefinder.dev.maxRangeCm / 2 ; // Complimentary Filter altitude
return true;
}
static int32_t applyMedianFilter(int32_t newReading)
{
#define DISTANCE_SAMPLES_MEDIAN 5
static int32_t filterSamples[DISTANCE_SAMPLES_MEDIAN];
static int filterSampleIndex = 0;
static bool medianFilterReady = false;
if (newReading > RANGEFINDER_OUT_OF_RANGE) {// only accept samples that are in range
filterSamples[filterSampleIndex] = newReading;
++filterSampleIndex;
if (filterSampleIndex == DISTANCE_SAMPLES_MEDIAN) {
filterSampleIndex = 0;
medianFilterReady = true;
}
}
return medianFilterReady ? quickMedianFilter5(filterSamples) : newReading;
}
static int16_t computePseudoSnr(int32_t newReading) {
#define SNR_SAMPLES 5
static int16_t snrSamples[SNR_SAMPLES];
static uint8_t snrSampleIndex = 0;
static int32_t previousReading = RANGEFINDER_OUT_OF_RANGE;
static bool snrReady = false;
int16_t pseudoSnr = 0;
snrSamples[snrSampleIndex] = constrain((int)(pow(newReading - previousReading, 2) / 10), 0, 6400);
++snrSampleIndex;
if (snrSampleIndex == SNR_SAMPLES) {
snrSampleIndex = 0;
snrReady = true;
}
previousReading = newReading;
if (snrReady) {
for (uint8_t i = 0; i < SNR_SAMPLES; i++) {
pseudoSnr += snrSamples[i];
}
return constrain(pseudoSnr, 0, 32000);
} else {
return RANGEFINDER_OUT_OF_RANGE;
}
}
/*
* This is called periodically by the scheduler
*/
// XXX Returns timeDelta_t for iNav for pseudo-RT scheduling.
void rangefinderUpdate(timeUs_t currentTimeUs)
{
UNUSED(currentTimeUs);
if (rangefinder.dev.update) {
rangefinder.dev.update(&rangefinder.dev);
}
// return rangefinder.dev.delayMs * 1000; // to microseconds XXX iNav only
}
bool isSurfaceAltitudeValid() {
/*
* Preconditions: raw and calculated altidude > 0
* SNR lower than threshold
*/
if (
rangefinder.calculatedAltitude > 0 &&
rangefinder.rawAltitude > 0 &&
rangefinder.snr < RANGEFINDER_DYNAMIC_THRESHOLD
) {
/*
* When critical altitude was determined, distance reported by rangefinder
* has to be lower than it to assume healthy readout
*/
if (rangefinder.snrThresholdReached) {
return (rangefinder.rawAltitude < rangefinder.dynamicDistanceThreshold);
} else {
return true;
}
} else {
return false;
}
}
/**
* Get the last distance measured by the sonar in centimeters. When the ground is too far away, RANGEFINDER_OUT_OF_RANGE is returned.
*/
bool rangefinderProcess(float cosTiltAngle)
{
if (rangefinder.dev.read) {
const int32_t distance = rangefinder.dev.read(&rangefinder.dev);
// If driver reported no new measurement - don't do anything
if (distance == RANGEFINDER_NO_NEW_DATA) {
return false;
}
if (distance >= 0) {
rangefinder.lastValidResponseTimeMs = millis();
rangefinder.rawAltitude = applyMedianFilter(distance);
}
else if (distance == RANGEFINDER_OUT_OF_RANGE) {
rangefinder.lastValidResponseTimeMs = millis();
rangefinder.rawAltitude = RANGEFINDER_OUT_OF_RANGE;
}
else {
// Invalid response / hardware failure
rangefinder.rawAltitude = RANGEFINDER_HARDWARE_FAILURE;
}
rangefinder.snr = computePseudoSnr(distance);
if (rangefinder.snrThresholdReached == false && rangefinder.rawAltitude > 0) {
if (rangefinder.snr < RANGEFINDER_DYNAMIC_THRESHOLD && rangefinder.dynamicDistanceThreshold < rangefinder.rawAltitude) {
rangefinder.dynamicDistanceThreshold = rangefinder.rawAltitude * RANGEFINDER_DYNAMIC_FACTOR / 100;
}
if (rangefinder.snr >= RANGEFINDER_DYNAMIC_THRESHOLD) {
rangefinder.snrThresholdReached = true;
}
}
DEBUG_SET(DEBUG_RANGEFINDER, 3, rangefinder.snr);
DEBUG_SET(DEBUG_RANGEFINDER_QUALITY, 0, rangefinder.rawAltitude);
DEBUG_SET(DEBUG_RANGEFINDER_QUALITY, 1, rangefinder.snrThresholdReached);
DEBUG_SET(DEBUG_RANGEFINDER_QUALITY, 2, rangefinder.dynamicDistanceThreshold);
DEBUG_SET(DEBUG_RANGEFINDER_QUALITY, 3, isSurfaceAltitudeValid());
}
else {
// Bad configuration
rangefinder.rawAltitude = RANGEFINDER_OUT_OF_RANGE;
}
/**
* Apply tilt correction to the given raw sonar reading in order to compensate for the tilt of the craft when estimating
* the altitude. Returns the computed altitude in centimeters.
*
* When the ground is too far away or the tilt is too large, RANGEFINDER_OUT_OF_RANGE is returned.
*/
if (cosTiltAngle < rangefinder.maxTiltCos || rangefinder.rawAltitude < 0) {
rangefinder.calculatedAltitude = RANGEFINDER_OUT_OF_RANGE;
} else {
rangefinder.calculatedAltitude = rangefinder.rawAltitude * cosTiltAngle;
}
DEBUG_SET(DEBUG_RANGEFINDER, 1, rangefinder.rawAltitude);
DEBUG_SET(DEBUG_RANGEFINDER, 2, rangefinder.calculatedAltitude);
return true;
}
/**
* Get the latest altitude that was computed, or RANGEFINDER_OUT_OF_RANGE if sonarCalculateAltitude
* has never been called.
*/
int32_t rangefinderGetLatestAltitude(void)
{
return rangefinder.calculatedAltitude;
}
int32_t rangefinderGetLatestRawAltitude(void) {
return rangefinder.rawAltitude;
}
bool rangefinderIsHealthy(void)
{
return (millis() - rangefinder.lastValidResponseTimeMs) < RANGEFINDER_HARDWARE_TIMEOUT_MS;
}
#endif

View file

@ -0,0 +1,61 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include "pg/pg.h"
#include "drivers/rangefinder/rangefinder.h"
typedef enum {
RANGEFINDER_NONE = 0,
RANGEFINDER_HCSR04 = 1,
RANGEFINDER_SRF10 = 2,
RANGEFINDER_HCSR04I2C = 3,
RANGEFINDER_VL53L0X = 4,
RANGEFINDER_UIB = 5,
} rangefinderType_e;
typedef struct rangefinderConfig_s {
uint8_t rangefinder_hardware;
} rangefinderConfig_t;
PG_DECLARE(rangefinderConfig_t, rangefinderConfig);
typedef struct rangefinder_s {
rangefinderDev_t dev;
float maxTiltCos;
int32_t rawAltitude;
int32_t calculatedAltitude;
timeMs_t lastValidResponseTimeMs;
bool snrThresholdReached;
int32_t dynamicDistanceThreshold;
int16_t snr;
} rangefinder_t;
extern rangefinder_t rangefinder;
void rangefinderResetDynamicThreshold(void);
bool rangefinderInit(void);
int32_t rangefinderGetLatestAltitude(void);
int32_t rangefinderGetLatestRawAltitude(void);
void rangefinderUpdate(timeUs_t currentTimeUs);
bool rangefinderProcess(float cosTiltAngle);
bool rangefinderIsHealthy(void);

View file

@ -22,9 +22,11 @@ typedef enum {
SENSOR_INDEX_ACC,
SENSOR_INDEX_BARO,
SENSOR_INDEX_MAG,
SENSOR_INDEX_RANGEFINDER,
SENSOR_INDEX_COUNT
} sensorIndex_e;
extern uint8_t requestedSensors[SENSOR_INDEX_COUNT];
extern uint8_t detectedSensors[SENSOR_INDEX_COUNT];
typedef struct int16_flightDynamicsTrims_s {
@ -48,6 +50,7 @@ typedef enum {
SENSOR_BARO = 1 << 2,
SENSOR_MAG = 1 << 3,
SENSOR_SONAR = 1 << 4,
SENSOR_RANGEFINDER = 1 << 4,
SENSOR_GPS = 1 << 5,
SENSOR_GPSMAG = 1 << 6
} sensors_e;

View file

@ -1,153 +0,0 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <stdint.h>
#include <math.h>
#include "platform.h"
#ifdef USE_SONAR
#include "build/build_config.h"
#include "common/maths.h"
#include "common/utils.h"
#include "config/feature.h"
#include "pg/pg.h"
#include "pg/pg_ids.h"
#include "drivers/io.h"
#include "fc/runtime_config.h"
#include "sensors/sensors.h"
#include "sensors/battery.h"
#include "sensors/sonar.h"
// Sonar measurements are in cm, a value of SONAR_OUT_OF_RANGE indicates sonar is not in range.
// Inclination is adjusted by imu
int16_t sonarMaxRangeCm;
int16_t sonarMaxAltWithTiltCm;
int16_t sonarCfAltCm; // Complimentary Filter altitude
STATIC_UNIT_TESTED int16_t sonarMaxTiltDeciDegrees;
float sonarMaxTiltCos;
static int32_t calculatedAltitude;
PG_REGISTER_WITH_RESET_TEMPLATE(sonarConfig_t, sonarConfig, PG_SONAR_CONFIG, 0);
#ifndef SONAR_TRIGGER_PIN
#define SONAR_TRIGGER_PIN NONE
#endif
#ifndef SONAR_ECHO_PIN
#define SONAR_ECHO_PIN NONE
#endif
PG_RESET_TEMPLATE(sonarConfig_t, sonarConfig,
.triggerTag = IO_TAG(SONAR_TRIGGER_PIN),
.echoTag = IO_TAG(SONAR_ECHO_PIN)
);
void sonarInit(const sonarConfig_t *sonarConfig)
{
sonarRange_t sonarRange;
hcsr04_init(sonarConfig, &sonarRange);
sensorsSet(SENSOR_SONAR);
sonarMaxRangeCm = sonarRange.maxRangeCm;
sonarCfAltCm = sonarMaxRangeCm / 2;
sonarMaxTiltDeciDegrees = sonarRange.detectionConeExtendedDeciDegrees / 2;
sonarMaxTiltCos = cos_approx(sonarMaxTiltDeciDegrees / 10.0f * RAD);
sonarMaxAltWithTiltCm = sonarMaxRangeCm * sonarMaxTiltCos;
calculatedAltitude = SONAR_OUT_OF_RANGE;
}
#define DISTANCE_SAMPLES_MEDIAN 5
static int32_t applySonarMedianFilter(int32_t newSonarReading)
{
static int32_t sonarFilterSamples[DISTANCE_SAMPLES_MEDIAN];
static int currentFilterSampleIndex = 0;
static bool medianFilterReady = false;
int nextSampleIndex;
if (newSonarReading > SONAR_OUT_OF_RANGE) // only accept samples that are in range
{
nextSampleIndex = (currentFilterSampleIndex + 1);
if (nextSampleIndex == DISTANCE_SAMPLES_MEDIAN) {
nextSampleIndex = 0;
medianFilterReady = true;
}
sonarFilterSamples[currentFilterSampleIndex] = newSonarReading;
currentFilterSampleIndex = nextSampleIndex;
}
if (medianFilterReady)
return quickMedianFilter5(sonarFilterSamples);
else
return newSonarReading;
}
void sonarUpdate(timeUs_t currentTimeUs)
{
UNUSED(currentTimeUs);
hcsr04_start_reading();
}
/**
* Get the last distance measured by the sonar in centimeters. When the ground is too far away, SONAR_OUT_OF_RANGE is returned.
*/
int32_t sonarRead(void)
{
int32_t distance = hcsr04_get_distance();
if (distance > HCSR04_MAX_RANGE_CM)
distance = SONAR_OUT_OF_RANGE;
return applySonarMedianFilter(distance);
}
/**
* Apply tilt correction to the given raw sonar reading in order to compensate for the tilt of the craft when estimating
* the altitude. Returns the computed altitude in centimeters.
*
* When the ground is too far away or the tilt is too large, SONAR_OUT_OF_RANGE is returned.
*/
int32_t sonarCalculateAltitude(int32_t sonarDistance, float cosTiltAngle)
{
// calculate sonar altitude only if the ground is in the sonar cone
if (cosTiltAngle <= sonarMaxTiltCos)
calculatedAltitude = SONAR_OUT_OF_RANGE;
else
// altitude = distance * cos(tiltAngle), use approximation
calculatedAltitude = sonarDistance * cosTiltAngle;
return calculatedAltitude;
}
/**
* Get the latest altitude that was computed by a call to sonarCalculateAltitude(), or SONAR_OUT_OF_RANGE if sonarCalculateAltitude
* has never been called.
*/
int32_t sonarGetLatestAltitude(void)
{
return calculatedAltitude;
}
#endif

View file

@ -1,37 +0,0 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "pg/pg.h"
#include "common/time.h"
#include "drivers/sonar_hcsr04.h"
#include "sensors/battery.h"
#define SONAR_OUT_OF_RANGE (-1)
extern int16_t sonarMaxRangeCm;
extern int16_t sonarCfAltCm;
extern int16_t sonarMaxAltWithTiltCm;
PG_DECLARE(sonarConfig_t, sonarConfig);
void sonarInit(const sonarConfig_t *sonarConfig);
void sonarUpdate(timeUs_t currentTimeUs);
int32_t sonarRead(void);
int32_t sonarCalculateAltitude(int32_t sonarDistance, float cosTiltAngle);
int32_t sonarGetLatestAltitude(void);