HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Averaging a signal to remove noise with Python

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
signalwithaveragingremovenoisepython

Problem

I am working on a small project in the lab with an Arduino Mega 2560 board. I want to average the signal (voltage) of the positive-slope portion (rise) of a triangle wave to try to remove as much noise as possible. My frequency is 20Hz and I am working with a data rate of 115200 bits/second (fastest recommended by Arduino for data transfer to a computer).

The raw signal looks like this:

My data is stored in a text file, with each line corresponding to a data point. Since I do have thousands of data points, I expect that some averaging would smooth the way my signal looks and make a close-to-perfect straight line in this case. However, other experimental conditions might lead to a signal where I could have features along the positive-slope portion of the triangle wave, such as a negative peak, and I absolutely do need to be able to see this feature on my averaged signal.

I am a Python beginner so I might not have the ideal approach to do so and my code might look bad for most of you. I would still like to get your hints / ideas on how I can improve my signal processing code to achieve a better noise removal by averaging the signal.

```
#!/usr/bin/python
import matplotlib.pyplot as plt
import math

# OPEN AND PLOT THE RAW DATA
data_filename = "My_File_Name"
filepath = "My_File_Path" + data_filename + ".txt"

# Open the Raw Data
with open(filepath, "r") as f:
rawdata = f.readlines()

# Remove the \n
rawdata = map(lambda s: s.strip(), rawdata)

# Plot the Raw Data
plt.plot(rawdata, 'r-')
plt.ylabel('Lightpower (V)')
plt.show()

# *** FIND THE LOCAL MAXIMUM AND MINIMUM
# Number of data points for each range
datarange = 15 # This number can be changed for better processing
max_i_range = int(math.floor(len(rawdata)/datarange))-3

#Declare an empty lists for the max and min
min_list = []
max_list = []

min_list_index = []
max_list_index = []

i=0

for i in range(0, max_i_range):
delimiter0 = i * datarange
delimiter1 = (i+1) * datarange
del

Solution

As David Morris indicates, it might be simpler to use a filtering/smoothing function, such as a moving window average. This is pretty simple to implement using the rolling function from pandas.Series. (Only 501 points are shown.) Tweak the numerical argument (window size) to get different amounts of smoothing.

import pandas as pd
import matplotlib.pyplot as plt

# Plot the Raw Data
ts = rawdata[0:500]
plt.plot(ts, 'r-')
plt.ylabel('Lightpower (V)')

# original version
# smooth_data = pd.rolling_mean(ts,5).plot(style='k')
# pandas has changed; so, here's the new version:
smooth_data = pd.Series(ts).rolling(window=7).mean().plot(style='k')
plt.show()


(Moving average)

A moving average is, basically, a low-pass filter. So, we could also implement a low-pass filter with functions from SciPy as follows:

import scipy.signal as signal

# First, design the Butterworth filter
N  = 3    # Filter order
Wn = 0.1 # Cutoff frequency
B, A = signal.butter(N, Wn, output='ba')
smooth_data = signal.filtfilt(B,A, rawdata[0:500])
plt.plot(rawdata[0:500],'r-')
plt.plot(smooth_data[0:500],'b-')
plt.show()


(Low-Pass Filter)

This filter method is from OceanPython.org BTW.

Code Snippets

import pandas as pd
import matplotlib.pyplot as plt

# Plot the Raw Data
ts = rawdata[0:500]
plt.plot(ts, 'r-')
plt.ylabel('Lightpower (V)')

# original version
# smooth_data = pd.rolling_mean(ts,5).plot(style='k')
# pandas has changed; so, here's the new version:
smooth_data = pd.Series(ts).rolling(window=7).mean().plot(style='k')
plt.show()
import scipy.signal as signal

# First, design the Butterworth filter
N  = 3    # Filter order
Wn = 0.1 # Cutoff frequency
B, A = signal.butter(N, Wn, output='ba')
smooth_data = signal.filtfilt(B,A, rawdata[0:500])
plt.plot(rawdata[0:500],'r-')
plt.plot(smooth_data[0:500],'b-')
plt.show()

Context

StackExchange Code Review Q#120918, answer score: 7

Revisions (0)

No revisions yet.