diff --git a/conf/abi.xml b/conf/abi.xml
index 57522280bf..b32fb57601 100644
--- a/conf/abi.xml
+++ b/conf/abi.xml
@@ -275,6 +275,12 @@
Pointer pointing at the actuators_t4_extra_data_in array
+
+ Timestamp in micro-seconds
+ Number of force sensor values in the array
+ Pointer to force sensor values (driver raw units)
+
+
diff --git a/conf/airframes/AG/rot_wing_v3c_oneloop_optitrack_ext_pose_AG.xml b/conf/airframes/AG/rot_wing_v3c_oneloop_optitrack_ext_pose_AG.xml
index 86f3f1699a..63c5735863 100644
--- a/conf/airframes/AG/rot_wing_v3c_oneloop_optitrack_ext_pose_AG.xml
+++ b/conf/airframes/AG/rot_wing_v3c_oneloop_optitrack_ext_pose_AG.xml
@@ -263,8 +263,8 @@
-
-
+
+
diff --git a/conf/airframes/tudelft/rotwing_25kg_common.xml b/conf/airframes/tudelft/rotwing_25kg_common.xml
index 056f1d43b5..99b58faf36 100644
--- a/conf/airframes/tudelft/rotwing_25kg_common.xml
+++ b/conf/airframes/tudelft/rotwing_25kg_common.xml
@@ -51,7 +51,7 @@
-
+
diff --git a/conf/airframes/tudelft/rotwing_7kg_common.xml b/conf/airframes/tudelft/rotwing_7kg_common.xml
index 712661f4a9..067a308ec0 100644
--- a/conf/airframes/tudelft/rotwing_7kg_common.xml
+++ b/conf/airframes/tudelft/rotwing_7kg_common.xml
@@ -53,10 +53,11 @@
-
-
-
-
+
+
+
+
+
diff --git a/conf/airframes/tudelft/rotwing_v3c_oneloop.xml b/conf/airframes/tudelft/rotwing_v3c_oneloop.xml
index 5004266791..1ca5ce8e48 100644
--- a/conf/airframes/tudelft/rotwing_v3c_oneloop.xml
+++ b/conf/airframes/tudelft/rotwing_v3c_oneloop.xml
@@ -265,8 +265,8 @@
-
-
+
+
diff --git a/conf/airframes/tudelft/rotwing_v3c_oneloop_optitrack.xml b/conf/airframes/tudelft/rotwing_v3c_oneloop_optitrack.xml
index 4a77196bb0..bd90b27177 100644
--- a/conf/airframes/tudelft/rotwing_v3c_oneloop_optitrack.xml
+++ b/conf/airframes/tudelft/rotwing_v3c_oneloop_optitrack.xml
@@ -267,8 +267,8 @@
-
-
+
+
diff --git a/conf/airframes/tudelft/rotwing_v3c_oneloop_optitrack_ext_pose.xml b/conf/airframes/tudelft/rotwing_v3c_oneloop_optitrack_ext_pose.xml
index 47d8628160..2b4f050f0f 100644
--- a/conf/airframes/tudelft/rotwing_v3c_oneloop_optitrack_ext_pose.xml
+++ b/conf/airframes/tudelft/rotwing_v3c_oneloop_optitrack_ext_pose.xml
@@ -256,8 +256,8 @@
-
-
+
+
diff --git a/conf/airframes/tudelft/rotwing_v3c_oneloop_simulation.xml b/conf/airframes/tudelft/rotwing_v3c_oneloop_simulation.xml
index 70bbd141c3..ec799add69 100644
--- a/conf/airframes/tudelft/rotwing_v3c_oneloop_simulation.xml
+++ b/conf/airframes/tudelft/rotwing_v3c_oneloop_simulation.xml
@@ -224,8 +224,8 @@
-
-
+
+
diff --git a/conf/airframes/tudelft/rotwing_v3g_oneloop.xml b/conf/airframes/tudelft/rotwing_v3g_oneloop.xml
index 1ba5906812..1aee84be62 100644
--- a/conf/airframes/tudelft/rotwing_v3g_oneloop.xml
+++ b/conf/airframes/tudelft/rotwing_v3g_oneloop.xml
@@ -261,8 +261,8 @@
-
-
+
+
diff --git a/conf/airframes/tudelft/rotwing_v3g_oneloop_optitrack.xml b/conf/airframes/tudelft/rotwing_v3g_oneloop_optitrack.xml
index 4cc3f48ccd..178c36b216 100644
--- a/conf/airframes/tudelft/rotwing_v3g_oneloop_optitrack.xml
+++ b/conf/airframes/tudelft/rotwing_v3g_oneloop_optitrack.xml
@@ -261,8 +261,8 @@
-
-
+
+
diff --git a/conf/airframes/tudelft/rotwing_v3g_oneloop_optitrack_ext_pose.xml b/conf/airframes/tudelft/rotwing_v3g_oneloop_optitrack_ext_pose.xml
index e75070c353..ea1ba07867 100644
--- a/conf/airframes/tudelft/rotwing_v3g_oneloop_optitrack_ext_pose.xml
+++ b/conf/airframes/tudelft/rotwing_v3g_oneloop_optitrack_ext_pose.xml
@@ -255,8 +255,8 @@
-
-
+
+
diff --git a/conf/airframes/tudelft/rotwing_v3h.xml b/conf/airframes/tudelft/rotwing_v3h.xml
index 9dd7d26bd5..7bff0605fd 100644
--- a/conf/airframes/tudelft/rotwing_v3h.xml
+++ b/conf/airframes/tudelft/rotwing_v3h.xml
@@ -42,6 +42,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/flight_plans/tudelft/rotwing_EHVB.xml b/conf/flight_plans/tudelft/rotwing_EHVB.xml
index d075f6d24b..e1a18bcb25 100644
--- a/conf/flight_plans/tudelft/rotwing_EHVB.xml
+++ b/conf/flight_plans/tudelft/rotwing_EHVB.xml
@@ -218,7 +218,7 @@
-
+
diff --git a/conf/modules/ground_detect.xml b/conf/modules/ground_detect.xml
index 57d63793d4..510efe9bbc 100644
--- a/conf/modules/ground_detect.xml
+++ b/conf/modules/ground_detect.xml
@@ -1,15 +1,31 @@
- Ground detection module to detect ground
-
+
+ Ground detection module to detect ground.
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -20,7 +36,7 @@
-
+
diff --git a/conf/modules/hx711.xml b/conf/modules/hx711.xml
new file mode 100644
index 0000000000..8174a9f6c3
--- /dev/null
+++ b/conf/modules/hx711.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+ Reading of multiple HX711 strain gauge sensors.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/conf/telemetry/highspeed_rotorcraft.xml b/conf/telemetry/highspeed_rotorcraft.xml
index f7cf0d21f8..3f32332fec 100644
--- a/conf/telemetry/highspeed_rotorcraft.xml
+++ b/conf/telemetry/highspeed_rotorcraft.xml
@@ -66,6 +66,7 @@
+
@@ -387,6 +388,7 @@
+
diff --git a/sw/airborne/arch/chibios/modules/sensors/hx711.c b/sw/airborne/arch/chibios/modules/sensors/hx711.c
new file mode 100644
index 0000000000..399ca35a69
--- /dev/null
+++ b/sw/airborne/arch/chibios/modules/sensors/hx711.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) Freek van Tienen
+ *
+ * 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
+#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();
+}
diff --git a/sw/airborne/arch/chibios/modules/sensors/hx711.h b/sw/airborne/arch/chibios/modules/sensors/hx711.h
new file mode 100644
index 0000000000..9898d60654
--- /dev/null
+++ b/sw/airborne/arch/chibios/modules/sensors/hx711.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) Freek van Tienen
+ *
+ * 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 */
diff --git a/sw/airborne/modules/nav/ground_detect.c b/sw/airborne/modules/nav/ground_detect.c
index a3d4ad7431..97980a98d8 100644
--- a/sw/airborne/modules/nav/ground_detect.c
+++ b/sw/airborne/modules/nav/ground_detect.c
@@ -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
}
diff --git a/sw/airborne/modules/nav/ground_detect.h b/sw/airborne/modules/nav/ground_detect.h
index 9dcea4f985..f58608b07c 100644
--- a/sw/airborne/modules/nav/ground_detect.h
+++ b/sw/airborne/modules/nav/ground_detect.h
@@ -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
diff --git a/sw/ext/pprzlink b/sw/ext/pprzlink
index a6225a2c99..820a010f14 160000
--- a/sw/ext/pprzlink
+++ b/sw/ext/pprzlink
@@ -1 +1 @@
-Subproject commit a6225a2c999fda27c2e6f5f71563abdaa36aa740
+Subproject commit 820a010f14c073c7e6d189181fde5dfb777bd630