[Rotwing] Changes to ground detect and add hx711 based strain gauges (#3616)

* changes to ground detect and hx711
* keep reverse thrust even if ground_detect cuts out e.g. during tip over
* force sensor struct
* update pprzlink
This commit is contained in:
NoahWe
2026-04-03 10:08:58 +02:00
committed by GitHub
parent 846989b2d2
commit 219363e102
21 changed files with 627 additions and 67 deletions
+6
View File
@@ -275,6 +275,12 @@
<field name="actuators_t4_extra_data_in_ptr" type="float *">Pointer pointing at the actuators_t4_extra_data_in array</field>
</message>
<message name="FORCE_SENSOR" id="42">
<field name="stamp" type="uint32_t" unit="us">Timestamp in micro-seconds</field>
<field name="count" type="uint8_t">Number of force sensor values in the array</field>
<field name="values" type="int32_t *">Pointer to force sensor values (driver raw units)</field>
</message>
</msg_class>
</protocol>
@@ -263,8 +263,8 @@
</section>
<section name="GROUND_DETECT">
<define name="THRESHOLD_GROUND_DETECT" value="40"/>
<define name="USE_GROUND_DETECT_INDI_THRUST" value="FALSE"/>
<define name="USE_GROUND_DETECT_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_USE_INDI_THRUST" value="FALSE"/>
<define name="GROUND_DETECT_USE_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_SENSOR_AGL_MIN_VALUE" value="0.24"/>
<define name="AGL_DIST_MIN_DISTANCE_CHECK" value="0.20"/>
<define name="AGL_DIST_MAX_DISTANCE_CHECK" value="0.25"/>
@@ -51,7 +51,7 @@
<!-- Ground detect -->
<define name="GROUND_DETECT_SENSOR_AGL_MIN_VALUE" value="0.24"/>
<define name="USE_GROUND_DETECT_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_USE_AGL_DIST" value="TRUE"/>
<!-- AGL distance -->
<define name="AGL_DIST_FILTER" value="0.07"/>
@@ -53,10 +53,11 @@
<define name="DEFAULT_CIRCLE_RADIUS" value="100.0"/>
<!-- Ground detect -->
<define name="USE_GROUND_DETECT_INDI_THRUST" value="TRUE"/>
<define name="USE_GROUND_DETECT_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_SPECIFIC_THRUST_THRESHOLD" value="-12.0"/>
<define name="GROUND_DETECT_SENSOR_AGL_MIN_VALUE" value="0.24"/>
<define name="GROUND_DETECT_USE_INDI_THRUST" value="TRUE"/>
<define name="GROUND_DETECT_USE_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_SPECIFIC_THRUST_THRESHOLD" value="-5.0"/>
<define name="GROUND_DETECT_AGL_MIN_VALUE" value="0.24"/>
<define name="GROUND_DETECT_VERTICAL_SPEED_THRESHOLD" value="0.1"/>
<!-- Flight plan defines -->
<define name="FLARE_HEIGHT" value="12"/>
@@ -265,8 +265,8 @@
<section name="GROUND_DETECT">
<define name="THRESHOLD_GROUND_DETECT" value="40"/>
<define name="USE_GROUND_DETECT_INDI_THRUST" value="FALSE"/>
<define name="USE_GROUND_DETECT_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_USE_INDI_THRUST" value="FALSE"/>
<define name="GROUND_DETECT_USE_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_AGL_MIN_VALUE" value="0.24"/>
<define name="AGL_DIST_MIN_DISTANCE_CHECK" value="0.20"/>
<define name="AGL_DIST_MAX_DISTANCE_CHECK" value="0.25"/>
@@ -267,8 +267,8 @@
<section name="GROUND_DETECT">
<define name="THRESHOLD_GROUND_DETECT" value="40"/>
<define name="USE_GROUND_DETECT_INDI_THRUST" value="FALSE"/>
<define name="USE_GROUND_DETECT_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_USE_INDI_THRUST" value="FALSE"/>
<define name="GROUND_DETECT_USE_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_AGL_MIN_VALUE" value="0.24"/>
<define name="AGL_DIST_MIN_DISTANCE_CHECK" value="0.20"/>
<define name="AGL_DIST_MAX_DISTANCE_CHECK" value="0.25"/>
@@ -256,8 +256,8 @@
</section>
<section name="GROUND_DETECT">
<define name="THRESHOLD_GROUND_DETECT" value="40"/>
<define name="USE_GROUND_DETECT_INDI_THRUST" value="FALSE"/>
<define name="USE_GROUND_DETECT_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_USE_INDI_THRUST" value="FALSE"/>
<define name="GROUND_DETECT_USE_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_AGL_MIN_VALUE" value="0.24"/>
<define name="AGL_DIST_MIN_DISTANCE_CHECK" value="0.20"/>
<define name="AGL_DIST_MAX_DISTANCE_CHECK" value="0.25"/>
@@ -224,8 +224,8 @@
</section>
<section name="GROUND_DETECT">
<define name="THRESHOLD_GROUND_DETECT" value="40"/>
<define name="USE_GROUND_DETECT_INDI_THRUST" value="FALSE"/>
<define name="USE_GROUND_DETECT_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_USE_INDI_THRUST" value="FALSE"/>
<define name="GROUND_DETECT_USE_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_AGL_MIN_VALUE" value="0.24"/>
<define name="AGL_DIST_MIN_DISTANCE_CHECK" value="0.20"/>
<define name="AGL_DIST_MAX_DISTANCE_CHECK" value="0.25"/>
@@ -261,8 +261,8 @@
</section>
<section name="GROUND_DETECT">
<define name="THRESHOLD_GROUND_DETECT" value="40"/>
<define name="USE_GROUND_DETECT_INDI_THRUST" value="FALSE"/>
<define name="USE_GROUND_DETECT_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_USE_INDI_THRUST" value="FALSE"/>
<define name="GROUND_DETECT_USE_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_AGL_MIN_VALUE" value="0.24"/>
<define name="AGL_DIST_MIN_DISTANCE_CHECK" value="0.20"/>
<define name="AGL_DIST_MAX_DISTANCE_CHECK" value="0.25"/>
@@ -261,8 +261,8 @@
</section>
<section name="GROUND_DETECT">
<define name="THRESHOLD_GROUND_DETECT" value="40"/>
<define name="USE_GROUND_DETECT_INDI_THRUST" value="FALSE"/>
<define name="USE_GROUND_DETECT_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_USE_INDI_THRUST" value="FALSE"/>
<define name="GROUND_DETECT_USE_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_AGL_MIN_VALUE" value="0.24"/>
<define name="AGL_DIST_MIN_DISTANCE_CHECK" value="0.20"/>
<define name="AGL_DIST_MAX_DISTANCE_CHECK" value="0.25"/>
@@ -255,8 +255,8 @@
</section>
<section name="GROUND_DETECT">
<define name="THRESHOLD_GROUND_DETECT" value="40"/>
<define name="USE_GROUND_DETECT_INDI_THRUST" value="FALSE"/>
<define name="USE_GROUND_DETECT_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_USE_INDI_THRUST" value="FALSE"/>
<define name="GROUND_DETECT_USE_AGL_DIST" value="TRUE"/>
<define name="GROUND_DETECT_AGL_MIN_VALUE" value="0.24"/>
<define name="AGL_DIST_MIN_DISTANCE_CHECK" value="0.20"/>
<define name="AGL_DIST_MAX_DISTANCE_CHECK" value="0.25"/>
+11
View File
@@ -42,6 +42,17 @@
<!-- <define name="IMU_LOG_HIGHSPEED" value="TRUE"/> -->
<define name="I2C2_CLOCK_SPEED" value="100000"/>
<!-- Strain gauges connected to hx711 load cell -->
<module name="hx711">
<define name="HX711_DEVICES_NB" value="4"/>
<define name="HX711_DEVICES" value="{PWM_SERVO_2_GPIO,PWM_SERVO_2_PIN,0},{PWM_SERVO_3_GPIO,PWM_SERVO_3_PIN,0},{PWM_SERVO_4_GPIO,PWM_SERVO_4_PIN,0},{PWM_SERVO_5_GPIO,PWM_SERVO_5_PIN,0}"/>
<define name="HX711_DEBUG" value="true"/>
</module>
<define name="GROUND_DETECT_USE_FORCE_SENSOR" value="TRUE"/>
<define name="GROUND_DETECT_FORCE_SENSOR_THRESHOLD" value="100000"/>
<define name="GROUND_DETECT_FORCE_SENSOR_MEDIAN_FILT_SIZE" value="3"/>
</target>
<target name="nps" board="pc">
+1 -1
View File
@@ -218,7 +218,7 @@
<exception cond="GetPosHeight() @LT flare_height" deroute="flare"/>
<stay climb="-1.0" vmode="climb" wp="TD"/>
</block>
<block name="flare">
<block name="flare" on_enter="ground_detect_set_offset_sensors(true)">
<call_once fun="rotwing_state_set(ROTWING_STATE_FORCE_HOVER)"/>
<stay climb="-0.5" vmode="climb" wp="TD"/>
<!--<exception cond="!(GetPosHeight() @LT 2.0)" deroute="flare_low"/>-->
+20 -4
View File
@@ -1,15 +1,31 @@
<!DOCTYPE module SYSTEM "module.dtd">
<module name="ground_detect" dir="nav">
<doc>
<description>Ground detection module to detect ground</description>
<define name="USE_GROUND_DETECT_INDI_THRUST" value="1" description="Use INDI thrust less than 50 percent as detection"/>
<description>
Ground detection module to detect ground.
</description>
<define name="GROUND_DETECT_USE_INDI_THRUST" value="1" description="Use INDI thrust less than 50 percent as detection"/>
<define name="GROUND_DETECT_SPECIFIC_THRUST_THRESHOLD" value="-5.0" description="[m/s2] positive down"/>
<define name="USE_GROUND_DETECT_AGL_DIST" value="0" description="Use agl_dist_filtered"/>
<define name="GROUND_DETECT_USE_AGL_DIST" value="0" description="Use agl_dist_filtered"/>
<define name="GROUND_DETECT_AGL_MIN_VALUE" value="0.1" description="AGL value low enough to be used as ground detection [m]"/>
<define name="GROUND_DETECT_USE_FORCE_SENSOR" value="0" description="Use FORCE_SENSOR ABI for ground detection"/>
<define name="GROUND_DETECT_FORCE_SENSOR_THRESHOLD" value="100000" description="Threshold on filtered force sensor value to detect ground"/>
<define name="GROUND_DETECT_FORCE_SENSOR_MEDIAN_FILT_SIZE" value="3" description="Median filter size for force sensor processing"/>
<define name="GROUND_DETECT_COUNTER_TRIGGER" value="10" description="Number of times a trigger must be valid to accept ground detection."/>
</doc>
<settings>
<dl_settings>
<dl_settings name="Ground Detect">
<dl_setting var="reverse_th_level" min="-9600" max="0" step="1" shortname="Reverse thrust level"/>
<dl_setting var="set_offset_sensors" min="0" max="1" step="1" values="FALSE|TRUE" type="fun" handler="set_offset_sensors" module="modules/nav/ground_detect"/>
<dl_setting var="force_sensor_ground_threshold" min="-1000000" max="1000000" step="1000" shortname="ground_threshold"/>
</dl_settings>
</dl_settings>
</settings>
<header>
<file name="ground_detect.h"/>
</header>
@@ -20,7 +36,7 @@
<file name="ground_detect.c"/>
<test firmware="rotorcraft">
<define name="PERIODIC_FREQUENCY" value="512"/>
<define name="USE_GROUND_DETECT_INDI_THRUST" value="1"/>
<define name="GROUND_DETECT_USE_INDI_THRUST" value="1"/>
<define name="DOWNLINK_TRANSPORT" value="pprz_tp"/>
<define name="DOWNLINK_DEVICE" value="uart0"/>
<define name="USE_UART0"/>
+32
View File
@@ -0,0 +1,32 @@
<!DOCTYPE module SYSTEM "module.dtd">
<module name="hx711" dir="sensors">
<doc>
<description>
Reading of multiple HX711 strain gauge sensors.
</description>
<define name="HX711_GAIN" value="1" description="The gain of the sensor in pulses (128: 1)"/>
<define name="HX711_PWM_FREQUENCY" value="6000000" description="Frequency of the generated PWM signal for clock precision of 0.5MHz"/>
<configure name="HX711_CLOCK" default="PWM_SERVO_1" description="PWM to use for the clock"/>
<define name="HX711_DEVICES_NB" value="4" description="The amount of sensors to read"/>
<define name="HX711_DEVICES" value="{{port,pin,0},{port,pin,0},{port,pin,0},{port,pin,0}}" description="The list of sensor port/pin pairs to read"/>
</doc>
<header>
<file name="hx711.h"/>
</header>
<init fun="hx711_init()"/>
<event fun="hx711_event()"/>
<makefile target="ap">
<configure name="HX711_CLOCK" default="PWM_SERVO_1"/>
<define name="HX711_PWM_PORT" value="$(HX711_CLOCK)_GPIO"/>
<define name="HX711_PWM_PIN" value="$(HX711_CLOCK)_PIN"/>
<define name="HX711_PWM_AF" value="$(HX711_CLOCK)_AF"/>
<define name="HX711_PWM_CHANNEL" value="$(HX711_CLOCK)_CHANNEL"/>
<define name="HX711_PWM_DRIVER" value="$(HX711_CLOCK)_DRIVER"/>
<file_arch name="hx711.c"/>
</makefile>
</module>
+2
View File
@@ -66,6 +66,7 @@
<message name="FUELCELL" period="1.5"/>
<message name="POWER_DEVICE" period="0.12"/>
<message name="IMU_HEATER" period="15.1"/>
<message name="GROUND_DETECT" period="0.13"/>
</mode>
<mode name="sim" key_press="d">
@@ -387,6 +388,7 @@
<message name="POWER_DEVICE" period="0.02"/>
<message name="FUELCELL" period="1.5"/>
<message name="AIRSPEED_RAW" period="0.02"/>
<message name="GROUND_DETECT" period="0.023"/>
</mode>
</process>
@@ -0,0 +1,242 @@
/*
* Copyright (C) Freek van Tienen <freek.v.tienen@gmail.com>
*
* This file is part of paparazzi.
*
* paparazzi 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 2, or (at your option)
* any later version.
*
* paparazzi 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 paparazzi; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* @file arch/chibios/modules/sensors/hx711.c
* Interface for the HX711 sensor
*
*/
#include "hx711.h"
#include "mcu_periph/gpio.h"
#include "mcu_periph/sys_time.h"
#include "modules/core/abi.h"
#include "modules/datalink/downlink.h"
#include "modules/datalink/telemetry.h"
#include BOARD_CONFIG
#ifndef HX711_DEVICES_NB
#define HX711_DEVICES_NB 4
#endif
#ifndef HX711_GAIN
#define HX711_GAIN 1
#endif
#ifndef HX711_PWM_FREQUENCY
#define HX711_PWM_FREQUENCY 6000000
#endif
#ifndef HX711_DEVICES
#define HX711_DEVICES {}
#endif
#ifndef HX711_FORCE_SENSOR_ID
#define HX711_FORCE_SENSOR_ID ABI_BROADCAST
#endif
#ifndef HX711_DEBUG
#define HX711_DEBUG false
#endif
#if HX711_DEBUG
#include <stdio.h>
#endif
// Running at 50kHz for a full clock pulse (10us high, 10us low)
#define HX711_PERIOD (HX711_PWM_FREQUENCY / 50000)
float hx711_meas_time = 0;
struct hx711_dev_t {
ioportid_t data_port;
uint16_t data_pin;
int32_t measurement;
};
struct hx711_t {
volatile bool busy;
volatile bool measurement_ready;
struct hx711_dev_t devices[HX711_DEVICES_NB];
volatile uint8_t read_bit_idx;
};
static void pwmpcb(PWMDriver *pwmp __attribute__((unused)));
static volatile struct hx711_t hx711 = {
.busy = false,
.measurement_ready = false,
.devices = {
HX711_DEVICES
},
.read_bit_idx = 0
};
static PWMConfig pwmcfg = {
.frequency = HX711_PWM_FREQUENCY,
.period = HX711_PERIOD,
.callback = NULL,
.channels = {
{ PWM_OUTPUT_DISABLED, NULL },
{ PWM_OUTPUT_DISABLED, NULL },
{ PWM_OUTPUT_DISABLED, NULL },
{ PWM_OUTPUT_DISABLED, NULL },
},
.cr2 = 0,
.bdtr = 0,
.dier = 0
};
void hx711_init(void)
{
/*----------------
* Configure GPIO
*----------------*/
gpio_setup_pin_af(HX711_PWM_PORT, HX711_PWM_PIN, HX711_PWM_AF, true);
pwmcfg.channels[HX711_PWM_CHANNEL].mode = PWM_OUTPUT_ACTIVE_HIGH;
pwmcfg.channels[HX711_PWM_CHANNEL].callback = pwmpcb;
for(uint8_t i = 0; i < HX711_DEVICES_NB; i++) {
gpio_setup_input(hx711.devices[i].data_port, hx711.devices[i].data_pin);
}
/*---------------
* Configure PWM
*---------------*/
pwmStart(&HX711_PWM_DRIVER, &pwmcfg);
hx711.busy = false;
hx711.measurement_ready = false;
hx711.read_bit_idx = 0;
}
/**
* Start a measurement and publish the results when possible
*/
void hx711_event(void)
{
// If busy reading data, return
if(hx711.busy)
return;
// Check if we have a measurement to read
if(hx711.measurement_ready) {
int32_t values[HX711_DEVICES_NB];
for(uint8_t i = 0; i < HX711_DEVICES_NB; i++) {
values[i] = hx711.devices[i].measurement;
}
AbiSendMsgFORCE_SENSOR(HX711_FORCE_SENSOR_ID, get_sys_time_usec(), HX711_DEVICES_NB, values);
// Send down for debug
float raw_val[HX711_DEVICES_NB];
for(uint8_t i = 0; i < HX711_DEVICES_NB; i++) {
raw_val[i] = values[i];
}
#if HX711_DEBUG
float current_time = get_sys_time_float();
float freq = 1 / (current_time - hx711_meas_time);
hx711_meas_time = current_time;
char hx711_str[10];
char hx711_freq_str[15];
int hx711_len = snprintf(hx711_str, sizeof(hx711_str), "HX711");
int hx711_freq_len = snprintf(hx711_freq_str, sizeof(hx711_freq_str), "HX711 freq");
RunOnceEvery(10, {
DOWNLINK_SEND_DEBUG_VECT(DefaultChannel, DefaultDevice, hx711_len, hx711_str, HX711_DEVICES_NB, raw_val);
});
RunOnceEvery(15, {
DOWNLINK_SEND_DEBUG_VECT(DefaultChannel, DefaultDevice, hx711_freq_len, hx711_freq_str, 1, &freq);
});
pprz_msg_send_DEBUG_VECT(&pprzlog_tp.trans_tx, &flightrecorder_sdlog.device, AC_ID, hx711_len, hx711_str, HX711_DEVICES_NB, raw_val);
pprz_msg_send_DEBUG_VECT(&pprzlog_tp.trans_tx, &flightrecorder_sdlog.device, AC_ID, hx711_freq_len, hx711_freq_str, 1, &freq);
#endif
hx711.measurement_ready = false;
}
// Check if all data pins are low, ready to read data
for(uint8_t i = 0; i < HX711_DEVICES_NB; i++) {
if(gpio_get(hx711.devices[i].data_port, hx711.devices[i].data_pin) != 0)
return;
}
// Start reading data
chSysLock();
hx711.busy = true;
hx711.measurement_ready = false;
hx711.read_bit_idx = 0;
pwmEnableChannel(&HX711_PWM_DRIVER, HX711_PWM_CHANNEL, HX711_PERIOD/2); // 50% duty cycle
pwmEnableChannelNotification(&HX711_PWM_DRIVER, HX711_PWM_CHANNEL);
chSysUnlock();
}
/**
* Callback on the falling edge of the clock signal
*/
static void pwmpcb(PWMDriver *pwmp __attribute__((unused))) {
chSysLockFromISR();
/* In case we where not reading just stop */
if(!hx711.busy) {
pwmDisableChannelNotificationI(&HX711_PWM_DRIVER, HX711_PWM_CHANNEL);
pwmDisableChannelI(&HX711_PWM_DRIVER, HX711_PWM_CHANNEL);
chSysUnlockFromISR();
return;
}
/* Parse the measurements for all devices */
if(hx711.read_bit_idx < 24) {
for(uint8_t i = 0; i < HX711_DEVICES_NB; i++) {
volatile struct hx711_dev_t *dev = &hx711.devices[i];
// Reset or bitshift
if(hx711.read_bit_idx == 0)
dev->measurement = 0;
else
dev->measurement <<= 1;
// Read the bit
if(gpio_get(dev->data_port, dev->data_pin) != 0)
dev->measurement |= 1;
// Fix 2s complement for 32 bit signed integer from 24 bit
if(dev->measurement & (1 << 23)) {
dev->measurement |= 0xFF000000;
}
}
}
hx711.read_bit_idx++;
/* Send gain and finish reading data */
if(hx711.read_bit_idx >= (24 + HX711_GAIN)) {
pwmDisableChannelNotificationI(&HX711_PWM_DRIVER, HX711_PWM_CHANNEL);
pwmDisableChannelI(&HX711_PWM_DRIVER, HX711_PWM_CHANNEL);
hx711.busy = false;
hx711.measurement_ready = true;
chSysUnlockFromISR();
return;
}
chSysUnlockFromISR();
}
@@ -0,0 +1,36 @@
/*
* Copyright (C) Freek van Tienen <freek.v.tienen@gmail.com>
*
* This file is part of paparazzi.
*
* paparazzi 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 2, or (at your option)
* any later version.
*
* paparazzi 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 paparazzi; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* @file arch/chibios/modules/sensors/hx711.h
* Interface for the HX711 sensor
*
*/
#include "std.h"
#ifndef HX711_H
#define HX711_H
void hx711_init(void);
void hx711_event(void);
#endif /* HX711_H */
+227 -40
View File
@@ -24,48 +24,207 @@
*/
#include "ground_detect.h"
#include "filters/low_pass_filter.h"
#include "filters/median_filter.h"
#include "firmwares/rotorcraft/autopilot_firmware.h"
#include "modules/core/abi.h"
#include "state.h"
#if USE_GROUND_DETECT_INDI_THRUST
#include "firmwares/rotorcraft/stabilization/stabilization_indi.h"
#include "pprzlink/messages.h"
#include "modules/datalink/downlink.h"
/* Number of triggers which need to be active to assume ground has been detected */
#ifndef GROUND_DETECT_NUM_TRIGGERS
#define GROUND_DETECT_NUM_TRIGGERS 3
#endif
#if USE_GROUND_DETECT_AGL_DIST
#if GROUND_DETECT_USE_INDI_THRUST
#include "firmwares/rotorcraft/stabilization/stabilization_indi.h"
#endif
PRINT_CONFIG_VAR(GROUND_DETECT_USE_INDI_THRUST)
#if GROUND_DETECT_USE_AGL_DIST
#include "modules/sonar/agl_dist.h"
#ifndef GROUND_DETECT_AGL_MIN_VALUE
#define GROUND_DETECT_AGL_MIN_VALUE 0.1
#endif
#endif
PRINT_CONFIG_VAR(GROUND_DETECT_USE_AGL_DIST)
#ifndef GROUND_DETECT_USE_FORCE_SENSOR
#define GROUND_DETECT_USE_FORCE_SENSOR 0
#endif
#ifndef GROUND_DETECT_FORCE_SENSOR_THRESHOLD
#define GROUND_DETECT_FORCE_SENSOR_THRESHOLD 100000
#endif
#ifndef GROUND_DETECT_FORCE_SENSOR_MEDIAN_FILT_SIZE
#define GROUND_DETECT_FORCE_SENSOR_MEDIAN_FILT_SIZE 3
#endif
#ifndef FORCE_SENSOR_MAX_NB
#define FORCE_SENSOR_MAX_NB 16
#endif
#if GROUND_DETECT_USE_FORCE_SENSOR
#ifndef GROUND_DETECT_FORCE_SENSOR_ID
#define GROUND_DETECT_FORCE_SENSOR_ID ABI_BROADCAST
#endif
#endif
PRINT_CONFIG_VAR(GROUND_DETECT_USE_FORCE_SENSOR)
#ifndef GROUND_DETECT_REVERSE_THRUST_ON_GROUND_DETECTED
#define GROUND_DETECT_REVERSE_THRUST_ON_GROUND_DETECTED false
#endif
PRINT_CONFIG_VAR(GROUND_DETECT_REVERSE_THRUST_ON_GROUND_DETECTED)
#if GROUND_DETECT_REVERSE_THRUST_ON_GROUND_DETECTED
#ifndef GROUND_DETECT_REVERSE_THRUST_LEVEL
#error "GROUND_DETECT_REVERSE_THRUST_LEVEL needs to be defined if GROUND_DETECT_REVERSE_THRUST_ON_GROUND_DETECTED is true"
#endif
uint16_t reverse_th_level = GROUND_DETECT_REVERSE_THRUST_LEVEL;
#else
uint16_t reverse_th_level = 0;
#endif
#ifndef GROUND_DETECT_SPECIFIC_THRUST_THRESHOLD
#define GROUND_DETECT_SPECIFIC_THRUST_THRESHOLD -5.0
#endif
PRINT_CONFIG_VAR(GROUND_DETECT_SPECIFIC_THRUST_THRESHOLD)
#include "pprzlink/messages.h"
#include "modules/datalink/downlink.h"
#ifndef GROUND_DETECT_VERTICAL_SPEED_THRESHOLD
#define GROUND_DETECT_VERTICAL_SPEED_THRESHOLD 0.1
#endif
PRINT_CONFIG_VAR(GROUND_DETECT_VERTICAL_SPEED_THRESHOLD)
#ifndef GROUND_DETECT_VERTICAL_ACCEL_THRESHOLD
#define GROUND_DETECT_VERTICAL_ACCEL_THRESHOLD -3.0
#endif
PRINT_CONFIG_VAR(GROUND_DETECT_VERTICAL_ACCEL_THRESHOLD)
#ifndef GROUND_DETECT_COUNTER_TRIGGER
#define GROUND_DETECT_COUNTER_TRIGGER 5
#endif
// Cutoff frequency of accelerometer filter in Hz
#ifndef GROUND_DETECT_FILT_FREQ
#define GROUND_DETECT_FILT_FREQ 5.0
Butterworth2LowPass accel_filter;
#endif
bool disarm_on_not_in_flight = false;
int32_t counter = 0;
bool ground_detected = false;
#define DEBUG_GROUND_DETECT TRUE
bool reverse_thrust = false;
union ground_detect_bitmask_t ground_detect_status;
struct ground_detect_values_t ground_detect_values;
float force_sensor_ground_threshold = GROUND_DETECT_FORCE_SENSOR_THRESHOLD;
struct force_sensor_data_t {
uint8_t count;
int32_t offsets[FORCE_SENSOR_MAX_NB];
int32_t values_filt[FORCE_SENSOR_MAX_NB];
};
static struct force_sensor_data_t force_sensor = {0};
#if GROUND_DETECT_USE_FORCE_SENSOR
static struct MedianFilterInt force_sensor_filt[FORCE_SENSOR_MAX_NB];
static bool force_sensor_valid = false;
static abi_event force_sensor_ev;
static void force_sensor_autoset_offset(void)
{
if (!force_sensor_valid) {
return;
}
for (uint8_t i = 0; i < force_sensor.count; i++) {
force_sensor.offsets[i] += force_sensor.values_filt[i];
}
}
static void force_sensor_cb(uint8_t sender_id UNUSED, uint32_t stamp UNUSED, uint8_t count, int32_t *values)
{
uint8_t n = count;
if (n > FORCE_SENSOR_MAX_NB) {
n = FORCE_SENSOR_MAX_NB;
}
force_sensor.count = n;
force_sensor_valid = true;
for (uint8_t i = 0; i < n; i++) {
force_sensor.values_filt[i] = update_median_filter_i(&force_sensor_filt[i], values[i] - force_sensor.offsets[i]);
}
}
static bool force_sensor_detect_ground(void)
{
if (!force_sensor_valid) {
return false;
}
for (uint8_t i = 0; i < force_sensor.count; i++) {
if (fabsf((float)force_sensor.values_filt[i]) > force_sensor_ground_threshold) {
return true;
}
}
return false;
}
#endif
#if PERIODIC_TELEMETRY
#include "modules/datalink/telemetry.h"
static void send_ground_detect(struct transport_tx *trans, struct link_device *dev)
{
uint8_t _ground_detected = ground_detected;
pprz_msg_send_GROUND_DETECT(trans, dev, AC_ID,
&_ground_detected,
&ground_detect_values.speed_down,
&ground_detect_values.spec_thrust_down,
&ground_detect_values.accel_down_filt.o[0],
&ground_detect_values.agl_dist_value_filtered,
force_sensor.count, force_sensor.values_filt,
&ground_detect_status.value
);
}
#endif
void ground_detect_init()
{
// Initialize the ground detection status
ground_detect_status.value = 0;
ground_detect_values.speed_down = 0.0;
ground_detect_values.spec_thrust_down = 0.0;
ground_detect_values.agl_dist_value_filtered = 0.0;
force_sensor.count = 1; // Needed for force sensor field in ground detect message
for (uint8_t i = 0; i < FORCE_SENSOR_MAX_NB; i++) {
force_sensor.offsets[i] = 0;
force_sensor.values_filt[i] = 0;
}
float tau = 1.0 / (2.0 * M_PI * GROUND_DETECT_FILT_FREQ);
float sample_time = 1.0 / PERIODIC_FREQUENCY;
init_butterworth_2_low_pass(&accel_filter, tau, sample_time, 0.0);
init_butterworth_2_low_pass(&ground_detect_values.accel_down_filt, tau, sample_time, 0.0);
#if GROUND_DETECT_USE_FORCE_SENSOR
for (uint8_t i = 0; i < FORCE_SENSOR_MAX_NB; i++) {
init_median_filter_i(&force_sensor_filt[i], GROUND_DETECT_FORCE_SENSOR_MEDIAN_FILT_SIZE);
}
force_sensor.count = 0;
force_sensor_valid = false;
AbiBindMsgFORCE_SENSOR(GROUND_DETECT_FORCE_SENSOR_ID, &force_sensor_ev, force_sensor_cb);
#endif
#if PERIODIC_TELEMETRY
register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_GROUND_DETECT, send_ground_detect);
#endif
}
bool ground_detect(void)
@@ -86,7 +245,7 @@ void ground_detect_periodic()
// Use the control effectiveness in thrust in order to estimate the thrust delivered (only works for multicopters)
float specific_thrust = 0.0; // m/s2
#if USE_GROUND_DETECT_INDI_THRUST
#if GROUND_DETECT_USE_INDI_THRUST
uint8_t i;
for (i = 0; i < INDI_NUM_ACT; i++) {
specific_thrust += actuator_state_filt_vect[i] * g1g2[3][i] * -((int32_t) act_is_servo[i] - 1);
@@ -94,26 +253,44 @@ void ground_detect_periodic()
#endif
// vertical component
float spec_thrust_down;
struct FloatRMat *ned_to_body_rmat = stateGetNedToBodyRMat_f();
spec_thrust_down = ned_to_body_rmat->m[8] * specific_thrust;
ground_detect_values.spec_thrust_down = ned_to_body_rmat->m[8] * specific_thrust;
// Evaluate vertical speed (close to zero, not at terminal velocity)
float vspeed_ned = stateGetSpeedNed_f()->z;
ground_detect_values.speed_down = stateGetSpeedNed_f()->z;
// Detect free fall (to be done, rearm?)
// Detect noise level (to be done)
// Detect ground based on AND of all triggers
if ((fabsf(vspeed_ned) < 5.0)
&& (spec_thrust_down > GROUND_DETECT_SPECIFIC_THRUST_THRESHOLD)
&& (fabsf(accel_filter.o[0]) < 2.0)
#if USE_GROUND_DETECT_AGL_DIST
&& (agl_dist_valid && (agl_dist_value_filtered < GROUND_DETECT_AGL_MIN_VALUE))
// Detect ground based on AND of some triggers
#if GROUND_DETECT_USE_FORCE_SENSOR
ground_detect_status.force_sensor_trigger = force_sensor_detect_ground();
#else
ground_detect_status.force_sensor_trigger = false;
#endif
) {
ground_detect_status.vspeed_trigger = (fabsf(ground_detect_values.speed_down) < GROUND_DETECT_VERTICAL_SPEED_THRESHOLD)? 1:0;
#if GROUND_DETECT_USE_INDI_THRUST
ground_detect_status.spec_thrust_trigger = (ground_detect_values.spec_thrust_down > GROUND_DETECT_SPECIFIC_THRUST_THRESHOLD)? 1:0;
#else
ground_detect_status.spec_thrust_trigger = false;
#endif
ground_detect_status.accel_filt_trigger = (fabsf(ground_detect_values.accel_down_filt.o[0]) < GROUND_DETECT_VERTICAL_ACCEL_THRESHOLD)? 1:0;
#if GROUND_DETECT_USE_AGL_DIST
ground_detect_status.agl_trigger = (agl_dist_valid && (agl_dist_value_filtered < GROUND_DETECT_AGL_MIN_VALUE))? 1:0;
ground_detect_values.agl_dist_value_filtered = agl_dist_value_filtered;
#else
ground_detect_status.agl_trigger = false;
#endif
int trigger_sum = 0;
for(uint8_t i = 0; i < 16; i++) trigger_sum += (ground_detect_status.value >> i) & 0x1;
if (trigger_sum >= GROUND_DETECT_NUM_TRIGGERS) {
counter += 1;
if (counter > GROUND_DETECT_COUNTER_TRIGGER) {
ground_detected = true;
@@ -127,24 +304,6 @@ void ground_detect_periodic()
ground_detected = false;
counter = 0;
}
#ifdef DEBUG_GROUND_DETECT
float payload[7];
payload[0] = vspeed_ned;
payload[1] = spec_thrust_down;
payload[2] = accel_filter.o[0];
payload[3] = stateGetAccelNed_f()->z;
#if USE_GROUND_DETECT_AGL_DIST
payload[4] = agl_dist_valid;
payload[5] = agl_dist_value_filtered;
#else
payload[4] = 0;
payload[5] = 0;
#endif
payload[6] = 1.f * ground_detected;
RunOnceEvery(10, {DOWNLINK_SEND_PAYLOAD_FLOAT(DefaultChannel, DefaultDevice, 7, payload);});
#endif
}
/**
@@ -154,5 +313,33 @@ void ground_detect_periodic()
void ground_detect_filter_accel(void)
{
struct NedCoor_f *accel = stateGetAccelNed_f();
update_butterworth_2_low_pass(&accel_filter, accel->z);
update_butterworth_2_low_pass(&ground_detect_values.accel_down_filt, accel->z);
}
bool ground_detect_reverse_thrust(void)
{
// Reverse thrust needs to be enabled with GROUND_DETECT_REVERSE_THRUST_ON_GROUND_DETECTED, and started through for example a flight plan block
if (GROUND_DETECT_REVERSE_THRUST_ON_GROUND_DETECTED && reverse_thrust) {
return true;
} else {
return false;
}
}
void ground_detect_stop_reverse_thrust(void)
{
reverse_thrust = false;
}
void ground_detect_start_reverse_thrust(void)
{
reverse_thrust = true;
}
void ground_detect_set_offset_sensors(bool set_offset)
{
(void) set_offset;
#if GROUND_DETECT_USE_FORCE_SENSOR
force_sensor_autoset_offset();
#endif
}
+27
View File
@@ -27,6 +27,25 @@
#define GROUND_DETECT_H
#include "std.h"
#include "filters/low_pass_filter.h"
union ground_detect_bitmask_t {
uint16_t value;
struct {
bool vspeed_trigger : 1;
bool spec_thrust_trigger : 1;
bool accel_filt_trigger: 1;
bool agl_trigger : 1;
bool force_sensor_trigger : 1;
};
};
struct ground_detect_values_t {
float speed_down;
float spec_thrust_down;
Butterworth2LowPass accel_down_filt;
float agl_dist_value_filtered;
};
extern void ground_detect_init(void);
extern void ground_detect_periodic(void);
@@ -36,5 +55,13 @@ extern bool ground_detect(void);
extern void ground_detect_filter_accel(void);
extern bool disarm_on_not_in_flight;
extern bool ground_detect_reverse_thrust(void);
extern void ground_detect_stop_reverse_thrust(void);
extern void ground_detect_start_reverse_thrust(void);
extern void ground_detect_set_offset_sensors(bool set_offset);
extern float force_sensor_ground_threshold;
extern uint16_t reverse_th_level; // Reverse thrust level in pprz units
#endif // GROUND_DETECT_H