'Spikes in ADXL335 voltage read from Arduino Uno

Background : I've connected an ADXL335 accelerometer to pins A0,A1,A2 on the Arduino Uno. During the measurement, the Arduino is connected to the laptop via usb, and accelerometer is powered directly by the 3.3V pin on the Arduino. The intention is to read voltage from the accel and plot the corresponding time and frequency spectrum using Python. The overall process is quite straightforward and I'm actually able to achieve this. A snapshot of a signal I recorded is shown below. Arduino code to read/write from the accelerometer and Python code to read from Arduino and construct the plots are provided at the bottom for reference.

Measured signal with a harmonic motion input

The issue I'm facing is with the actual values read by the Arduino (I think).

Problem: There seems to be some noise picked up by the Arduino during the read/write process. To demonstrate this, I ran two separate data acquisitions with the sensor at rest. Snapshots are provided below. I had used a similar system in 7yrs ago in grad school, using National Instruments DAC and LabView. I did experience some noise back then but that was akin to electrical noise ~60 Hz. This one is quite peculiar and I don't know how to deal with it.

Measurement-1 with accelerometer unperturbed Measurement-2 with accelerometer unperturbed

Closing: Could someone please help me understand what noise it is, that I'm seeing? Perhaps knowing what this is called will help me research about it in detail and try to tackle it. Any pointers/references would help as well.

Future Scope: I believe that a stable at-rest signal as the first step will greatly help mitigate the offset in Voltage (DC-component in freq-spectrum) even with a simple average. Also, I need to learn how to increase the sampling rate from the uno.

Programs used:

Arduino code to read/write from the accelerometer

#define xPin A0
#define yPin A1
#define zPin A2

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(115200);
}

void loop()
{
  // put your main code here, to run repeatedly:
  float xVal = analogRead(xPin);
  float yVal = analogRead(yPin);
  float zVal = analogRead(zPin);
  long time = micros(); //current time
  delay(1);

  //Serial.print("x=");
  Serial.print(time);
  Serial.print("\t");
  Serial.print(xVal);
  Serial.print("\t");
  Serial.print(yVal);
  Serial.print("\t");
  Serial.println(zVal);
  
  
  delay(1);
}

Python code to read from Arduino and construct the plots

#Thanks for the tutorial, Curio Res
#https://www.youtube.com/watch?v=fIlklRIuXoY

from serial.tools import list_ports
import serial
import time
import csv

#find the port corresponding to the Arduino
ports = list_ports.comports()
for port in ports: print(port)

#setup the file for saving voltage data from the accel/Arduino
f=open("data.csv","w",newline='')
f.truncate()

serialCom = serial.Serial('COM9',115200) #ENSURE the correct COM#

#Reset the arduino
serialCom.setDTR(False)
time.sleep(1)
serialCom.flushInput()
serialCom.setDTR(True)

#read from the Arduino
kmax=2000 #number of data points to record
#kmax* sampling rate in millisec delay = total time of data recorded
for k in range(kmax):
    try:
        #read a line of data
        s_bytes = serialCom.readline()
        #Decode binary
        decoded_bytes = s_bytes.decode("utf-8").strip('\r\n')
        #print(decoded_bytes)
        
        #parse lines
        if k==0:
            values = [x for x in decoded_bytes.split(",")]
        else:
            values = [float(x) for x in decoded_bytes.split()]
        print(values)
        
        writer = csv.writer(f,delimiter=",")
        writer.writerow(values)
        
        
    except:
        print("ERROR. Line was not recorded.")

f.close() #close csv file



import numpy as np
import pandas as pd
import matplotlib.pyplot as plt


#import data using padas
data = pd.read_csv("data.csv",skiprows=1)
data.columns = ["time", "accel-x", "accel-y", "accel-z"]    #using three channels
# data.columns = ["time", "accel-x"]                        #using one channel
# print(data)

#retrieve data columns
D=data.to_numpy();
time=(D[:,0])/1000000       #Arduino prints time in micro-second
x_accel=D[:,1]
y_accel=D[:,2]
z_accel=D[:,3]


signal=(x_accel**2)**0.5    #sum y and z for rms in the future

# first method of removing dc component.
i=1
dc_component=[]
for i in range(1,10):
    dc_component.append(signal[i])

signal=x_accel-np.average(dc_component)

#setup doesn't provide data with uniform time-interval
#create an array of all successive time steps to see the spread later
i=1
delta_t=[]
#find the average time step in the array 'time'
for i in range(1,len(time)):
    delta_t.append(time[i]-time[i-1])

dt=np.average(delta_t)  #average time step
fs=1/dt                 #sampling frequency
N=len(time)             #number of samples
f_step=fs/N             #req interval

freq=np.linspace(0,(N-1)*f_step,N) #construct the frequency array


# time=np.linspace(0,(N-1)*dt,N)    #Test time array
# y=1*np.sin(2*np.pi*1*time)        #Test sine signal
x=np.fft.fft(signal)                #perform a fast fourier transform
x_mag = np.abs(x)/N                 #obtain the magnitude of the transform

freq_plot=freq[0:int(N/2+1)]
x_mag_plot=2*x_mag[0:int(N/2+1)] #enter, Nyquist
x_mag_plot[0]=x_mag_plot[0]/2    #DC component does not need to be multiplied by 2

# plot
fig,[ax1,ax2] = plt.subplots(figsize=(7.5,5),nrows=2,ncols=1,constrained_layout=True)
ax1.plot(time,signal)
ax1.set_title('Time History')
ax1.set_xlabel('time(s)')
ax1.set_ylabel('Voltage(V)')
# plt.xlim([min(time),max(time)])
ax2.plot(freq_plot,x_mag_plot)
ax2.set_title('Frequency Spectrum')
ax2.set_xlabel('Frequency(Hz)')
ax2.set_ylabel('Amplitude')
plt.show()


Solution 1:[1]

This is spike noise. You should use a median filter to clean your signal. It is a digital non linear filter which you can implement in your software.

Just a note: I don't know which sample frequency are you using but the higher is the sample frequency, the more is the collected noise. So, limit the sample frequency to 2 x signal bandwidth (Shannon theorem)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1