11use std:: time:: { Duration , Instant } ;
22
33use super :: SensorConfig ;
4- use crate :: { motion:: walk:: SwingFootSwitchedEvent , prelude:: * } ;
4+ use crate :: prelude:: * ;
5+ use crate :: sensor:: low_pass_filter:: LowPassFilter ;
56use bevy:: prelude:: * ;
7+ use nalgebra:: SVector ;
8+
9+ use crate :: motion:: walk:: SwingFootSwitchedEvent ;
610use nidhogg:: {
711 types:: { FillExt , Fsr , FsrFoot } ,
812 NaoState ,
913} ;
1014use serde:: { Deserialize , Serialize } ;
1115use serde_with:: { serde_as, DurationMilliSeconds } ;
1216
17+ // Omega for the low-pass filter.
18+ const OMEGA : f32 = 0.7 ;
19+
1320/// Plugin offering the Force Sensitive Resistor (FSR) sensor data of the Nao,
1421/// derived from the raw [`NaoState`].
1522pub struct FSRSensorPlugin ;
@@ -43,13 +50,21 @@ impl Plugin for FSRSensorPlugin {
4350pub struct FsrConfig {
4451 /// Threshold for ground contact detection using average FSR sensor values from both feet.
4552 pub ground_contact_threshold : f32 ,
53+
54+ /// Timeout for change of value of the ground contact state in milliseconds.
55+ #[ serde_as( as = "DurationMilliSeconds" ) ]
56+ pub ground_contact_timeout : Duration ,
57+
4658 /// Maximum amount of pressure measured by a single sensor.
4759 pub max_pressure : f32 ,
60+
4861 /// Initial value for minimum pressure measured by a single sensor.
4962 pub min_pressure : f32 ,
63+
5064 /// The time to sample pressure values before updating the maximum value for each sensor, in milliseconds.
5165 #[ serde_as( as = "DurationMilliSeconds" ) ]
5266 pub highest_pressure_update_rate : Duration ,
67+
5368 /// The number of foot switches required before updating the minimum value for each sensor.
5469 pub num_foot_switches : u32 ,
5570}
@@ -67,30 +82,67 @@ impl FsrConfig {
6782}
6883
6984/// Struct containing the various contact points of the Nao.
70- #[ derive( Resource , Default ) ]
85+ #[ derive( Resource ) ]
7186pub struct Contacts {
7287 /// Whether the robot has ground contact.
7388 pub ground : bool ,
89+
7490 /// Whether the robot's left foot has ground contact.
7591 pub left_foot : bool ,
92+
7693 /// Whether the robot's right foot has ground contact.
7794 pub right_foot : bool ,
95+
96+ /// Timestamp of detected change in pressure state.
97+ pub last_switched : Instant ,
98+
99+ /// The first-order Butterworth low-pass filter to apply to the FSR sensor data.
100+ pub lpf : LowPassFilter < 1 > ,
78101}
79102
80- pub fn force_sensitive_resistor_sensor (
81- nao_state : Res < NaoState > ,
82- mut force_sensitive_resistors : ResMut < Fsr > ,
83- ) {
84- force_sensitive_resistors. left_foot = nao_state. fsr . left_foot . clone ( ) ;
85- force_sensitive_resistors. right_foot = nao_state. fsr . right_foot . clone ( ) ;
103+ impl Default for Contacts {
104+ fn default ( ) -> Self {
105+ Contacts {
106+ ground : true ,
107+ left_foot : false ,
108+ right_foot : false ,
109+ last_switched : Instant :: now ( ) ,
110+ lpf : LowPassFilter :: new ( OMEGA ) ,
111+ }
112+ }
113+ }
114+
115+ pub fn force_sensitive_resistor_sensor ( nao_state : Res < NaoState > , mut fsr : ResMut < Fsr > ) {
116+ fsr. left_foot = nao_state. fsr . left_foot . clone ( ) ;
117+ fsr. right_foot = nao_state. fsr . right_foot . clone ( ) ;
86118}
87119
88- fn update_contacts ( config : Res < SensorConfig > , fsr : Res < Fsr > , mut contacts : ResMut < Contacts > ) {
120+ fn update_contacts (
121+ config : Res < SensorConfig > ,
122+ fsr : Res < Fsr > ,
123+ mut contacts : ResMut < Contacts > ,
124+ mut last_pressure : Local < bool > ,
125+ ) {
89126 let config = & config. fsr ;
90127
91- contacts. ground = fsr. avg ( ) >= config. ground_contact_threshold ;
92128 contacts. left_foot = fsr. left_foot . sum ( ) >= config. min_pressure ;
93129 contacts. right_foot = fsr. right_foot . sum ( ) >= config. min_pressure ;
130+
131+ // Retrieve FSR values and apply low-pass filter.
132+ let fsr_vector = SVector :: < f32 , 1 > :: from ( [ fsr. avg ( ) ] ) ;
133+ let filtered_fsr = contacts. lpf . update ( fsr_vector) ;
134+ let current_pressure = filtered_fsr. x > config. ground_contact_threshold ;
135+
136+ if current_pressure != * last_pressure {
137+ contacts. last_switched = Instant :: now ( ) ;
138+ }
139+
140+ // Only update the ground state if timeout duration has elapsed.
141+ if contacts. last_switched . elapsed ( ) >= config. ground_contact_timeout {
142+ contacts. ground = current_pressure;
143+ }
144+
145+ * last_pressure = current_pressure;
94146}
95147
96148#[ derive( Debug , Clone , Default ) ]
0 commit comments