1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-16 04:45:24 +03:00
betaflight/src/main/sensors/opticalflow.c
Mark Haslinghuis bde342c8cb
Fix PG ID and corruption in MSP_SET_SENSOR_CONFIG (#14230)
* Fix PG ID

* Fix corruption in MSP_SET_SENSOR_CONFIG

* No need to specify ammount of bytes remaining

* Update after review from @ledvinap

* Remove unused PG_BETAFLIGHT_END
2025-02-03 12:07:44 +01:00

196 lines
6 KiB
C

/*
* This file is part of Betaflight.
*
* Betaflight is free software. You can redistribute this software
* and/or modify this software 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.
*
* Betaflight 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 this software.
*
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <math.h>
#include "platform.h"
#ifdef USE_OPTICALFLOW
#include "build/build_config.h"
#include "build/debug.h"
#include "common/maths.h"
#include "common/time.h"
#include "common/utils.h"
#include "common/filter.h"
#include "config/config.h"
#include "config/feature.h"
#include "fc/runtime_config.h"
#include "scheduler/scheduler.h"
#include "pg/pg.h"
#include "pg/pg_ids.h"
#include "drivers/time.h"
#include "drivers/rangefinder/rangefinder.h"
#include "drivers/rangefinder/rangefinder_lidarmt.h"
#include "io/beeper.h"
#include "sensors/sensors.h"
#include "sensors/gyro.h"
#include "sensors/acceleration.h"
#include "sensors/opticalflow.h"
#define OPTICALFLOW_CALIBRATION_DURATION_MS 30000
#define RATE_SCALE_RESOLUTION (1000.0f)
// static prototypes
static void applySensorRotation(vector2_t * dst, vector2_t * src);
static void applyLPF(vector2_t * flowRates);
PG_REGISTER_WITH_RESET_TEMPLATE(opticalflowConfig_t, opticalflowConfig, PG_OPTICALFLOW_CONFIG, 0);
PG_RESET_TEMPLATE(opticalflowConfig_t, opticalflowConfig,
.opticalflow_hardware = OPTICALFLOW_NONE,
.rotation = 0,
.flip_x = 0,
.flow_lpf = 0
);
static opticalflow_t opticalflow;
float cosRotAngle = 1.0f;
float sinRotAngle = 0.0f;
static pt2Filter_t xFlowLpf, yFlowLpf;
// ======================================================================
// =================== Opticalflow Main Functions =======================
// ======================================================================
static bool opticalflowDetect(opticalflowDev_t * dev, uint8_t opticalflowHardwareToUse) {
UNUSED(dev);
opticalflowType_e opticalflowHardware = OPTICALFLOW_NONE;
requestedSensors[SENSOR_INDEX_OPTICALFLOW] = opticalflowHardwareToUse;
switch (opticalflowHardwareToUse) {
case OPTICALFLOW_MT:
#ifdef USE_RANGEFINDER_MT
if (mtOpticalflowDetect(dev, rangefinderConfig()->rangefinder_hardware)) {
opticalflowHardware = OPTICALFLOW_MT;
rescheduleTask(TASK_OPTICALFLOW, TASK_PERIOD_MS(dev->delayMs));
}
#endif
break;
case OPTICALFLOW_NONE:
opticalflowHardware = OPTICALFLOW_NONE;
break;
}
if (opticalflowHardware == OPTICALFLOW_NONE) {
sensorsClear(SENSOR_OPTICALFLOW);
return false;
}
detectedSensors[SENSOR_INDEX_OPTICALFLOW] = opticalflowHardware;
sensorsSet(SENSOR_OPTICALFLOW);
return true;
}
bool opticalflowInit(void) {
if (!opticalflowDetect(&opticalflow.dev, opticalflowConfig()->opticalflow_hardware)) {
return false;
}
opticalflow.dev.init(&opticalflow.dev);
opticalflow.quality = OPTICALFLOW_NO_NEW_DATA;
opticalflow.rawFlowRates.x = 0;
opticalflow.rawFlowRates.y = 0;
opticalflow.processedFlowRates.x = 0;
opticalflow.processedFlowRates.y = 0;
opticalflow.timeStampUs = micros();
cosRotAngle = cosf(DEGREES_TO_RADIANS(opticalflowConfig()->rotation));
sinRotAngle = sinf(DEGREES_TO_RADIANS(opticalflowConfig()->rotation));
//low pass filter
if (opticalflowConfig()->flow_lpf != 0) {
const float flowCutoffHz = (float)opticalflowConfig()->flow_lpf / 100.0f;
const float flowGain = pt2FilterGain(flowCutoffHz, opticalflow.dev.delayMs / 1000.0f);
pt2FilterInit(&xFlowLpf, flowGain);
pt2FilterInit(&yFlowLpf, flowGain);
}
return true;
}
void opticalflowUpdate(void) {
if (opticalflow.dev.update) {
opticalflow.dev.update(&opticalflow.dev);
}
}
void opticalflowProcess(void) {
opticalflowData_t data = {0};
uint32_t deltaTimeUs = 0;
opticalflow.dev.read(&opticalflow.dev, &data);
opticalflow.quality = data.quality;
deltaTimeUs = cmp32(data.timeStampUs, opticalflow.timeStampUs);
if (deltaTimeUs != 0) { // New data
vector2_t raw = data.flowRate;
vector2_t processed;
applySensorRotation(&processed, &raw);
applyLPF(&processed);
opticalflow.rawFlowRates = raw;
opticalflow.processedFlowRates = processed;
opticalflow.timeStampUs = data.timeStampUs;
// DEBUG SECTION
DEBUG_SET(DEBUG_OPTICALFLOW, 0, opticalflow.quality);
DEBUG_SET(DEBUG_OPTICALFLOW, 1, lrintf(opticalflow.rawFlowRates.x * 1000));
DEBUG_SET(DEBUG_OPTICALFLOW, 2, lrintf(opticalflow.rawFlowRates.y * 1000));
DEBUG_SET(DEBUG_OPTICALFLOW, 3, lrintf(opticalflow.processedFlowRates.x * 1000));
DEBUG_SET(DEBUG_OPTICALFLOW, 4, lrintf(opticalflow.processedFlowRates.y * 1000));
DEBUG_SET(DEBUG_OPTICALFLOW, 5, deltaTimeUs);
}
}
static void applySensorRotation(vector2_t * dst, vector2_t * src) {
dst->x = (opticalflowConfig()->flip_x ? -1.0f : 1.0f) * (src->x * cosRotAngle - src->y * sinRotAngle);
dst->y = src->x * sinRotAngle + src->y * cosRotAngle;
}
static void applyLPF(vector2_t * flowRates) {
if (opticalflowConfig()->flow_lpf == 0) {
return;
}
flowRates->x = pt2FilterApply(&xFlowLpf, flowRates->x);
flowRates->y = pt2FilterApply(&yFlowLpf, flowRates->y);
}
LOCAL_UNUSED_FUNCTION static const opticalflow_t * getLatestFlowOpticalflowData(void) {
return &opticalflow;
}
bool isOpticalflowHealthy(void) {
return cmp32(micros(), opticalflow.timeStampUs) < OPTICALFLOW_HARDWARE_TIMEOUT_US;
}
#endif // USE_OPTICALFLOW