Search code examples
pythonscipydata-analysislowpass-filter

How to filter data with angle wraps?


I have noisy heading data for a vehicle that I would like to filter. However, the vehicle heading must be an angle between -180 and +180 degrees, so there are several 360 degree "jumps" in the data (that I need to keep) where the vehicle's heading crosses this threshold (see figure). Using a basic lowpass filter on the data works to filter out the noise, but does not give the desired result where these angle wraps occur (see figure). Does anyone have any ideas on a good solution for this problem? enter image description here (blue is raw, orange is filtered)


Solution

  • I ended up unwrapping the data, applying the filter, and re-wrapping the data as Warren suggested. Attached is the function that I created to accomplish this. I apologize for any poor python practices in this code. I am new to python.

    from scipy.signal import filtfilt
    
    def wrap_filter(filt_num, filt_denom, data, wrap_threshold=3):
        """
        Parameters
        ----------
        filt_num : Filter numerator (designed from scipy.signal.butter)
        filt_denom : Filter denominator (designed from scipy.signal.butter)
        data : data series to be filtered
        wrap_threshold: delta in consecutive datapoints that will be considered a wrap
    
        Returns
        -------
        Filtered data accounting for angle wraps.
        """
        #initialize
        key = 0
        data_dict = {}
        data_list = []
        filt_data_list = []
        prev_data=data[0]
        filter_pad_length = 500
        
        #break data into dictionary at wrap points
        for data_point in data:
            if abs(prev_data - data_point) > wrap_threshold:
                data_dict[key] = data_list
                data_list = [data_point]
                key += 1
            else:
                data_list.append(data_point)
            prev_data = data_point
        
        #add last section of data
        data_dict[key] = data_list
        
        #filter each section of data and append to final data list
        for data_section in data_dict:
            if len(data_dict[data_section]) > filter_pad_length: #filter padlen
                filt_data_section = list(filtfilt(filt_num, filt_denom, data_dict[data_section], padlen=filter_pad_length))
                #filt_data_list.append(filt_data_section)
                filt_data_list += filt_data_section
            else:
                filt_data_list += data_dict[data_section]
            
        return filt_data_list