Sensorizando maquina de impactos con Arduino
Algunas de las propiedades de las maquinas de impactos deben verificarse para ver que siguen en sus rangos antes de usarla en algún ensayo
Muchas maquinas deben ser testeadas de vez en cuando para confirmar que los valores de fabrica permanecen entre los limites establecidos. Esta semana pasada me encargaron confirmar estos valores de una maquina de impactos con los valores establecidos en el Anexo A de la norma ISO 140-7:1998, una norma para la mediciones acústicas en edificaciones.
Una maquina de impactos tiene es una cosa como lo de la imagen de arriba pero del que yo he tomado medidas es esta de abajo.
En la norma se especifica que:
- El tiempo medio entre impactos debe ser de 100 ms con un margen de mas menos 5 ms.
- El tiempo entre el impacto de un martillo y su elevación debe ser menor a 80 ms.
- El tiempo entre el impacto de dos martillos consecutivos debe ser de 100 ms mas menos 20 ms.
¿Como lo he hecho?
Usando un microcontrolador de Arduino con un código hecho para la ocasión que recoge la infromación de los sensores y la vuelca a un archivo de texto. También hay que afinar la precisión de los sensores para que respondan a lo que se quiere que respondan, especialmente el sensor de infrarrojos del cual hay que asegurarse que reciba la luz rebotada en una superficie blanca pero no en una superficie negra.
El tiempo medio entre impactos lo he medido indirectamente midiendo la velocidad de giro del cigueñal, sabiendo que por cada vuelta de cigueñal hay 10 golpes de martillo es sencillo saber el tiempo medio entre impactos.
El código de Arduino para este experimento es el siguiente, al contrario de mis otros códigos este está escrito en C y no en Python.
unsigned long myTime;
int wave;
int sensor = 3;
void setup() {
Serial.begin(9600);
pinMode(sensor, INPUT);
}
void loop() {
wave = digitalRead(sensor);
myTime = millis();
Serial.print(myTime);
Serial.print(" ");
Serial.println(wave);
delay(1);
}
Ejecutando este código tendría impresor por el monitor serial de la pantalla el tiempo en milisegundos y la señal binaria del receptor de infrarrojos, una vez tenía este archivo listo haré los calculos en un Notebook de Jupyter con Python.
El codigo de abajo es el que usado para medir los requisitos segundo y tercero, especificamente para el tercer los experimentos de los fotos.
int trigPin4 = 8;
int echoPin4 = 9;
int trigPin5 = 10;
int echoPin5 = 11;
unsigned long myTime;
void setup()
{
Serial.begin(9600);
pinMode(trigPin4, OUTPUT);
pinMode(echoPin4, INPUT);
pinMode(trigPin5, OUTPUT);
pinMode(echoPin5, INPUT);
}
void loop()
{
myTime = millis();
long duration4, distance4;
digitalWrite(trigPin4, LOW);
delayMicroseconds(2);
digitalWrite(trigPin4, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin4, LOW);
duration4 = pulseIn(echoPin4, HIGH);
distance4 = (duration4 / 2) / 2.91;
long duration5, distance5;
digitalWrite(trigPin5, LOW);
delayMicroseconds(2);
digitalWrite(trigPin5, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin5, LOW);
duration5 = pulseIn(echoPin5, HIGH);
distance5 = (duration5 / 2) / 2.91;
Serial.print(myTime);
Serial.print(" ");
Serial.print(distance4);
Serial.print(" ");
Serial.println(distance5);
}
Los es quemas de ambos circuitos son como los que se enseñan abajo, el de lz izquierda mide la distancia y habría que añadirle otro snesor y el de la derecha mide si una superficie refleja o no una luz infrarroja.
Tratamiento de la señal
Aquí abajo se puede ver el código que he usado y los resultados finales en gráficas con una breves reseñas en las anotaciones del cuaderno de Jupyter
Resultados
Una revolución tarda de media 99.63 ms, dado que idealmente debería ser de 100 ms con un margen de mas menos 5 ms, en este aspecto la maquina cumple perfectamente. El tiempo mediano entre los impactos de los martillos 3 y 4 es de 108 ms e idealmente debería ser 100 ms con un margen de mas menos 20 ms, y el tiempo que transcurre entre el impacto del martillo y sus elevación es de 58 ms, en este apartado el unico requisito es que esté por debajo de 80 ms con lo cual cumple perfectamente también.
Jupyter Notebook
Tiempo medio entre impactos¶
Importación de librerias
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import savgol_filter
from scipy import stats
Importación de archivos y conversion en un DataFrame
filepath = r"C:\Users\Usuario\Desktop\tacometro\CoolTerm Capture 2021-05-29 11-14-37.txt"
df = pd.read_csv(filepath, sep = " ", names = ["Time","step"] )
Conversión del DataFrame a listas de Python
time = df["Time"].tolist()
step = df["step"].tolist()
Incialización de variables
time_2 = []
time_3 = []
time_rev = []
step_a = []
step_b = []
step_c = []
Tratamiento de señal
for i in range(len(step)):
if i == 0:
a = step[i]
b = 0
else:
a = step[i]
b = step[i-1]
step_a.append(a)
step_b.append(b)
for i in range(len(step_a)):
step_c.append(step_a[i] - step_b[i])
for i in range(len(step_c)):
if (step_c[i] != 0):
time_2.append(time[i])
for i in range(1, len(time_2)):
a = time_2[i]
b = time_2[i-1]
time_3.append(a-b)
for i in range(1, len(time_3)):
a = time_3[i]
b = time_3[i-1]
time_rev.append(a+b)
Filtro de señal
yhat = savgol_filter(time_rev, 105, 1)
rev_number = np.arange(len(time_rev))
Estadísticos de la señal tratada
df_2 = pd.DataFrame({"Time/rev (millis)": time_rev,
"Time/rev (smooth) (millis)": yhat,
"Rev#": rev_number})
df_2.describe()
Time/rev (millis) | Time/rev (smooth) (millis) | Rev# | |
---|---|---|---|
count | 147.000000 | 147.000000 | 147.000000 |
mean | 1171.176871 | 1217.333625 | 73.000000 |
std | 2614.912863 | 335.487334 | 42.579338 |
min | 18.000000 | 939.657143 | 0.000000 |
25% | 992.000000 | 997.242857 | 36.500000 |
50% | 993.000000 | 997.951800 | 73.000000 |
75% | 1001.500000 | 1469.120751 | 109.500000 |
max | 32577.000000 | 2007.564241 | 146.000000 |
Gráficas de la señal
plt.figure(figsize=(15,5))
plt.scatter(rev_number, time_rev, s=20, edgecolor="black", c="darkorange", label="Señal")
#plt.plot(rev_number, yhat, color="darkgreen", label="Señal (Filtro de Savitzky-Golay) ", linewidth=2)
plt.xlabel("Numero de revolución")
plt.ylabel("Duración de revolución (milisegundos)")
plt.title("Duración de revolución")
plt.ylim([800, 1200])
plt.legend()
plt.show()
Tiempo entre impacto y elevación de martillo¶
Importación de archivos y creación de un DataFrame de pandas
filepath = r"C:\Users\Usuario\Desktop\tacometro\CoolTerm Capture 2021-05-30 19-29-20.txt"
df_3 = pd.read_csv(filepath, sep = " ", names = ["Time","Distance"] )
time = df_3["Time"].tolist()
distance = df_3["Distance"].tolist()
Tratamiento de la señal
Gráficas de la señal tratada y sin tratar
plt.figure(figsize=(15,5))
plt.scatter(time, distance, s=20, edgecolor="black", c="darkorange", label="Señal")
#plt.plot(time, yhat_2, color="darkgreen", label="Señal (Filtro de Savitzky-Golay) ", linewidth=2)
plt.xlabel("Tiempo [ms]")
plt.ylabel("Elevación martillo [mm]")
plt.title("Altura martillo vs. Tiempo")
plt.ylim([25, 80])
plt.xlim([9600, 11000])
plt.legend()
plt.show()
Verificación parametros de máquina de impactos¶
Importación de librerias
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import savgol_filter
from scipy import stats
from scipy.stats import zscore
from statistics import mean
from sklearn.linear_model import LinearRegression
Importación de archivos y conversion en un DataFrame
filepath = r"C:\Users\Usuario\Desktop\tacometro\CoolTerm Capture 2021-05-29 11-14-37.txt"
df = pd.read_csv(filepath, sep = " ", names = ["Time","step"] )
Conversión del DataFrame a listas de Python
time = df["Time"].tolist()
step = df["step"].tolist()
Incialización de variables
time_2 = []
time_3 = []
time_rev = []
step_a = []
step_b = []
step_c = []
Tratamiento de señal
for i in range(len(step)):
if i == 0:
a = step[i]
b = 0
else:
a = step[i]
b = step[i-1]
step_a.append(a)
step_b.append(b)
for i in range(len(step_a)):
step_c.append(step_a[i] - step_b[i])
for i in range(len(step_c)):
if (step_c[i] != 0):
time_2.append(time[i])
for i in range(1, len(time_2)):
a = time_2[i]
b = time_2[i-1]
time_3.append(a-b)
for i in range(1, len(time_3)):
a = time_3[i]
b = time_3[i-1]
time_rev.append(a+b)
Dataframe de la señal en bruto
rev_number = np.arange(len(time_rev))
df_2 = pd.DataFrame({"Time": time_rev,
"Rev#": rev_number})
Tratamiento de la señal para eliminar espurios con el estadístico Z-scores
df_2["Time"].median()
993.0
z_scores = stats.zscore(df_2)
abs_z_scores = np.abs(z_scores)
filtered_entries = (abs_z_scores < 3).all(axis=1)
new_df = df_2[filtered_entries]
new_df.loc[new_df.Time < 950, "Time"] = new_df["Time"].median()
C:\Users\Usuario\Anaconda\lib\site-packages\pandas\core\indexing.py:1765: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy isetter(loc, value)
new_df.head()
Time | Rev# | |
---|---|---|
1 | 993 | 1 |
2 | 993 | 2 |
3 | 993 | 3 |
4 | 993 | 4 |
5 | 992 | 5 |
Tratamiento de la señal para filtrar el ruido con el filtro de Savitzky-Golay
yhat = savgol_filter(new_df["Time"], 105, 1)
rev_number = np.arange(len(new_df["Time"]))
Dataframe de la señal en bruto y la señal filtrada por ruido
Gráficas de la señal
plt.figure(figsize=(15,5))
plt.scatter(rev_number, new_df["Time"], s=20, edgecolor="black", c="darkorange", label="Señal")
plt.plot(rev_number, yhat, color="darkgreen", label="Señal (Filtro de Savitzky-Golay) ", linewidth=2)
plt.xlabel("Numero de revolución")
plt.ylabel("Duración de revolución (milisegundos)")
plt.title("Duración de revolución")
plt.ylim([900, 1100])
plt.legend()
plt.show()
yhat.mean()/10
99.6384703196347
El tiempo medio entre impactos es de 99.63 ms.
Tiempo impactos de dos martillos consecutivos¶
Importación de archivos y creación de un DataFrame de pandas
filepath = r"C:\Users\Usuario\Desktop\tacometro\CoolTerm Capture 2021-06-01 21-40-59.txt"
df_3 = pd.read_csv(filepath, sep = " ", names = ["Time","Distance_1","Distance_2"] )
time_2 = df_3["Time"].tolist()
time_3 = []
distance_1 = df_3["Distance_1"].tolist()
distance_2 = df_3["Distance_2"].tolist()
for i in time_2:
time_3.append(i - time_2[0])
Filtrado de la señal
yhat_dis_1 = savgol_filter(distance_1, 11, 2)
yhat_dis_2 = savgol_filter(distance_2, 11, 2)
Gráficas de las señales de dos martillos consecutivos
plt.figure(figsize=(15,5))
plt.scatter(time_3, df_3["Distance_1"], s=30, edgecolor="black", c="firebrick", label="Martillo 1")
plt.scatter(time_3, df_3["Distance_2"], s=30, edgecolor="black", c="palegreen", label="Martillo 2")
plt.plot(time_3, yhat_dis_1, color="red", label="Martillo 1 (Filtro de Savitzky-Golay) ", linewidth=2)
plt.plot(time_3, yhat_dis_2, color="darkgreen", label="Martillo 2 (Filtro de Savitzky-Golay) ", linewidth=2)
plt.xlabel("Tiempo [ms]")
plt.ylabel("Elevación martillo [mm]")
plt.title("Altura martillo vs. Tiempo")
plt.ylim([0,90])
plt.xlim([500,3500])
plt.legend()
plt.show()
Obtención de diferencia de tiempos entre los golpeos de dos martillos consecutivos
step = 35
time_min_d1 = []
time_min_d2 = []
for i in range(21,len(time_3),step):
a = time_3[list(yhat_dis_1).index(min(yhat_dis_1[i:(i+step)]),i)]
time_min_d1.append(a)
for i in range(14,len(time_3),step):
b = time_3[list(yhat_dis_2).index(min(yhat_dis_2[i:(i+step)]),i)]
time_min_d2.append(b)
time_diff = []
for i in range(len(time_min_d1)):
time_diff.append(time_min_d1[i] - time_min_d2[i])
Valor mediano de la diferencia entre los golpeos de dos martillos consecutivos tomados en una muestra de 40 impactos
np.median(np.array(time_diff))
108.0
El valor mediano de la diferencia del tiempo entre el impacto de dos martillos consecutivos es de 108 ms.
Tiempo entre impacto y elevación de un martillo¶
plt.figure(figsize=(15,5))
plt.scatter(time_3, df_3["Distance_1"], s=30, edgecolor="black", c="firebrick", label="Martillo 1")
plt.xlabel("Tiempo [ms]")
plt.ylabel("Elevación martillo [mm]")
plt.title("Altura martillo vs. Tiempo")
plt.ylim([0,90])
plt.xlim([3000,5000])
plt.legend()
plt.show()
time_low = []
for i,j in enumerate(distance_1):
if (j < 35):
time_low.append(time_3[i])
time_low_2 = []
for i in range(len(time_low)-1):
time_low_2.append(time_low[i+1] - time_low[i])
time_low_3 = []
a = 0
for i in range(len(time_low_2)):
if(time_low_2[i] < 100):
a = a + time_low_2[i]
elif (time_low_2[i] > 100):
time_low_3.append(a)
a = 0
np.median(np.array(time_low_3))
58.0
El tiempo mediano que transcurre entre desde el impacto del martillo hasta el comienzo de su ascensión es de 58 ms.
Gracias por la información
ReplyDelete