Introduction: Sistema Autônomo Localizador De Vazamentos

Este projeto consiste em um robô, que através da leitura realizada por um dispositivo, equipado com um sensor piezoelétrico, captura os espectros das vibrações no solo, pode identificar e localizar, com o processamento dos dados por uma rede neural, possíveis vazamentos de água em uma tubulação.

O processamento destes dados é realizado por algoritmos instalados na DRAGONBOARD 410c. Os dados também são enviados para um serviço na nuvem, responsável por auxiliar no processo de Integência Artificial do projeto.

Este projeto foi desenvolvido no Hackaton Qualcomm, durante a TecnoFACENS da Faculdade de Engenharia de Sorocaba (FACENS), participaram do projeto os graduandos de engenharia mecatrônica Eneo Juliano Bovino, Felipe Xavier, Lucas de Sousa Rodrigues, Rogério Ap. Gomes Polo e Ronaldo P. Gomes Polo. Também participou do projeto o aluno Daniel de Castro Pacheco graduando de engenharia mecânica na Universidade Newton Paiva de Belo Horizonte. O projeto contou com o apoio dos graduandos de engenharia mecatrônica da FACENS, Lucas Nunes Monteiro e Felipe Crispim da Silva Salvagnini.

Step 1: Lista De Materiais

Para realização deste projeto, os seguintes materiais foram utilizados:

1 Arduino Due

1 Dragonboard 410c

2 Drivers para motor de corrente continua contendo cada um:

4 Transistores BC548

4 Diodos 1n4007

4 Resistores 4k7Ω ¼ W

1 Driver para servo motor contendo:

1 Transistores BC548

1 Diodos 1N4007

1 Resistores 4k7Ω ¼ W

1 Mouse USB

1 Teclado USB

1 Monitor

1 Cabo HDMI

1 Robô De Esteiras - Plataforma Zumo

1 Mini dispositivo de cremalheira e engrenagem

1 Servo motor 9g

Step 2: Adaptação Mecânica

Para a aquisição dos dados pelo sensor piezoelétrico, faz se necessário, o desenvolvimento de um dispositivo com pinhão e cremalheira, conforme desenhos anexados, neste caso as peças foram fabricadas por uma impressora 3D, devido ao fato de se tratar de um protótipo e ao curto tempo de execução, fixou-se o dispositivo na plataforma zumo, utilizando fita dupla face, conforme vídeo.

Step 3: Acionamento Dos Motores

Para executar a movimentação dos motores do robô ZUMO e do dispositivo de captura, fez-se necessária a montagem de dois drivers para os motores de corrente continua e um driver para o servo motor, conforme as figuras acima, sendo a primeira figura o driver para um motor de corrente continua e a segunda o driver para um servo motor.

Step 4: Obtenção Do Áudio

Para obtenção do espectro de áudio das vibrações do solo, foi utilizado um dispositivo desenvolvido como parte do TCC de Engenharia Mecatrônica dos graduandos Lucas Nunes Monteiro e Felipe C. da Silva Salvagnini, maiores detalhes sobre o TCC e sobre o dispositivo podem ser solicitados através do email lucasnunes_tec@hotmail.com.

Este dispositivo utiliza-se de um sensor piezoelétrico e uma placa de circuito que realiza a filtragem e amplificação do sinal.

As frequências de interesse para o projeto estão entre 100Hz e 800Hz. Para isso o dispositivo de sensoriamento foi configurado com uma frequência de amostragem de 3 kHz para que sejam respeitada as condições do teorema de amostragem de Nyquist, onde a frequência de aquisição deve estar pelo menos duas vezes acima das frequências estudadas.

A aquisição é habilitada e desabilitada através da interrupção do Arduino DUE.

Step 5: Configuração Do Arduino DUE (linguagem C)

Devido a grande quantidade de dados, cerca de 3000 pontos por segundo, do tipo inteiro de 32 bits, gerados pelo dispositivo de sensoriamento e necessários para processamento dos algoritmos na DRAGONBOARD 410c, foi utilizado o Arduino DUE para fazer uso de uma entrada analógica com maior poder de processamento, isso foi necessário por que o Shield de interfaceamento Grove Seeed Sensor Mezzanine instalado na DRAGONBOARD 410c, que possui um microcontrolador ATmega 328, não possui poder de processamento para essa operação.

O Arduino DUE foi configurado para receber os comandos enviados da plataforma QUALCOMM DRAGONBOARD 410c via comunicação serial.

As ações configuradas no Arduino foram:

Realizar a aquisição dos dados;

Transmitir os dados obtidos para a DRAGONBOARD 410c;

Segue a programação:

#include<math.h>
#define Numb_Sample 3000 #define DAC_Input A0

#define SERVO 7 #define PosServoMin 4 #define PosServoMax 6 #define Period 60 unsigned int Scont=0,SNow=PosServoMin; unsigned long int DAC[Numb_Sample],ind=Numb_Sample; void TC3_Handler() { TC_GetStatus(TC1, 0); if (ind < Numb_Sample) DAC[ind++] = analogRead(DAC_Input); if(Scont<Period) { if(Scont==SNow) digitalWrite(SERVO,LOW); Scont++; } else { digitalWrite(SERVO,HIGH); Scont=0; } } void SendNumber(unsigned long int numb) { for(int i=0; i<sizeof(unsigned long int);i++) { char c = (numb&(255<<sizeof(unsigned long int)-i-1)); Serial.print(c); } } // função de iniciação de interrupção void TimerStart(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t freq) { // desabilita write protect pmc_set_writeprotect(false); // habilita periférico do clock TC1 Channel 0 pmc_enable_periph_clk(irq); TC_Configure(/*clock*/ tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1); uint32_t rc = VARIANT_MCK / 2 / freq; // clock master / 2 / frequencia requisitada TC_SetRA(tc, channel, rc >> 1); // 50% duty cycle TC_SetRC(tc, channel, rc); TC_Start(tc, channel); tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPCS | TC_IER_CPAS; // habilita os registradores tc->TC_CHANNEL[channel].TC_IDR = ~(TC_IER_CPCS | TC_IER_CPAS); // desabilita os registradores NVIC_EnableIRQ(irq); // habilita interrupção }

void setup () { Serial.begin(115200); pinMode(DAC_Input, INPUT); TimerStart(TC1, 0, TC3_IRQn, 1500);//Init the Timer //TC_Stop(TC1, 0); pinMode(SERVO,OUTPUT); }

void loop() { /*//while(!Serial.available()); char rc;//=Serial.read(); int indice = 0; if(rc==0) { while(!Serial.available()); rc=Serial.read(); switch(rc) { case 1: indice=0; while(!Serial.available()); while((rc=Serial.read())!=0xFF) { indice <<= 8; indice += rc; while(!Serial.available()); } Serial.print(0); Serial.print(2); SendNumber(DAC[indice]); Serial.print(0xFF); break; case 3: while(!Serial.available()); if((Serial.read())==0xFF) { SNow=PosServoMax; delay(500); ind=0; //TC_Start(TC1, 0); while(ind<Numb_Sample); //TC_Stop(TC1, 0); SNow=PosServoMin; delay(500); Serial.print(0); Serial.print(4); Serial.print(0xFF); } break; } } else if(rc=='2') { Serial.print("Test Servo Motor\n"); while(!Serial.available()); rc=Serial.read(); if(rc=='1') { Serial.print("Mode 1\n"); SNow=PosServoMax; } if(rc=='2') { Serial.print("Mode 2\n"); SNow=PosServoMin; } } */ SNow=PosServoMax; delay(100); SNow=PosServoMin; delay(100); }

Step 6: Interfaceamento Das Tecnologias

Para a comunicação dos dados entre o Arduíno DUE e a DRAGONBOARD 410c, inicialmente utilizou-se a interface da figura acima, o que não foi possível executar, então optou-se pelo uso de uma interface USB CDC entre o Arduino DUE e a DRAGONBOARD 410c, que necessitaria da recompilação do KERNEL da DRAGONBOARD 410c, que não se fez por causa do curto tempo disponível.

Step 7: Configuração Da DRAGONBOARD 410c (Python)

Foi configurada para enviar ao Arduino DUE os comandos para realizar a aquisição de dados e transmitir os dados obtidos. Segue código abaixo.

Observação: A abordagem utilizada no código, não funcionou devido aos níveis de tensão utilizados pelo Arduíno DUE e o Mezzanine serem incompatíveis. Por isso optou-se pelo interfaceamento USB, que necessitaria da recompilação do KERNEL na DRAGONBOARD 410c para que a porta fosse criada corretamente para a comunicação.

import time
import serial import pandas as pd import numpy as np

# Configuração da conexão serial ser = serial.Serial( port='/dev/ttyAMC0', #tty96B0', baudrate=250000, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS )

ser.isOpen()

print('Enter your commands below.\r\nInsert "exit" to leave the application.')

input=1 while 1 : input = input(">> ") if input == 'exit': ser.close() exit() elif input == 'read': ser.write(0) # Envia o comando para o Arduino DUE realizar a coleta dos dados ser.write(1) # Envia o comando para o Arduino DUE transmitir os dados coletados

lista = []

for i in range(3000): ser.write(i/256) ser.write((i<<8)/256) while ser.inWaiting() > 0: out = ser.read(2) if(out == 0x0002): atual = 0 c = ser.read(1) while(c != 0xFF): atual << 8 atual += c c = ser.read(1) lista.append(atual)

Step 8: INTERPRETAÇÃO DOS ARQUIVOS DE AUDIO

Para poder realizar uma primeira análise dos dados obtidos através do sensor, se fez necessária a conversão dos arquivos no formato WAV, fornecidos pelos alunos autores do TCC e colaboradores do projeto, para valores numéricos, que são utilizados nos algoritmos de análise embarcados na DRAGONBOARD 410c. Para realizar esta conversão foi escrito um algoritmo em PYTHON 3 que lê o arquivo WAV e salva os dados do espectro em um arquivo CSV. O algoritmo utilizado segue abaixo e em anexo para download.

Esse algoritmo não se faz necessário para o funcionamento do sistema, já que o Arduino DUE já enviará esses dados em um array de valores numéricos.

# coding: utf-8

# Leitura e conversão dos audios para csv

# MÓDULOS UTILIZADOS import wave import numpy as np import pandas as pd import matplotlib.pyplot as plt

# FUNÇÃO PARA CONVERTER WAV EM DADOS DO ESPECTRO E SALVAR CSV def audio_to_csv(file_name): wave_file = wave.open(file_name+'.wav', 'rb') data_size = wave_file.getnframes() sample_rate = wave_file.getframerate() time_step = 1/sample_rate waveData = wave_file.readframes(data_size-1) signal = np.fromstring(waveData, dtype='int32') Time=np.linspace(start=0,stop=data_size/sample_rate,num=data_size,endpoint=True) df = pd.concat([pd.DataFrame(signal), pd.DataFrame(Time)], axis=1) df.to_csv(file_name + '.csv', index=False) return df

# CARREGANDO DATA FRAME COM OS DADOS DO AUDIO file_name = 'Solo_com_Vazamento' df_vazamento = audio_to_csv(file_name) df_vazamento.columns = ['amp', 'time'] file_name = 'Solo_sem_Vazamento' df_sem_vazamento = audio_to_csv(file_name) df_sem_vazamento.columns = ['amp', 'time']

# GRÁFICO DO ESPECTRO DE AUDIO figure, (ax1, ax2) = plt.subplots(nrows=2, ncols=1, figsize=(20,10)) ax1.plot(df_vazamento['time'], df_vazamento['amp']) ax1.set_title('Solo com Vazamento', fontdict={'fontsize': 20, 'fontweight': 'bold'}) ax1.set_xlabel('Tempo [s]', fontdict={'fontsize': 16}) ax1.set_ylim([-4e8,4e8]) ax2.plot(df_sem_vazamento['time'], df_sem_vazamento['amp']) ax2.set_title('Solo sem Vazamento', fontdict={'fontsize': 20, 'fontweight': 'bold'}) ax2.set_xlabel('Tempo [s]', fontdict={'fontsize': 16}) ax2.set_ylim([-4e8,4e8]) figure.tight_layout(h_pad=5) plt.show()

Step 9: Análise Visual Do Sinal

Com o PYTHON 3 é realizada a transformada de Fourier, este artificio matemático realiza a transformação do sinal do domínio do tempo para o domínio da frequência, onde se torna possível analisar as varias frequências, e suas amplitudes, que compõem aquele sinal. Pela análise visual do gráfico da transformada de Fourier um profissional com conhecimentos específicos poderá identificar a existência de algum vazamento na tubulação. Estes gráficos servirão para validação das análises realizadas pelo algoritmo de detecção automática.

Limitando o eixo das frequências entre 100Hz e 800Hz, fica claro a existência de vazamentos quando se observam distúrbios nesse range de frequências.

# coding: utf-8<br><br># Módulos utilizados para processamento da transformada de Fourier
import pandas as pd
import numpy as np
import wave
from matplotlib import pyplot as plt<br><br># Função que realiza a transformada de Fourier e plota os gráficos para análise
def Fourier(df_list):
    Fs = 44100;  # Taxa de amostragem em Hz
    Ts = 1.0/Fs; # Intervalo de amostragem
    y = pd.concat(df_list)
    t = y['time'] # Vetor de tempos    
    y = y['amp'] # Vetor de amplitudes<br>    n = len(y) # Comprimento do sinal
    k = np.arange(n)
    T = n/Fs
    frq = k/T
    frq = frq[range(n//2)]<br>    Y = np.fft.fft(y)/n
    Y = Y[range(n//2)]
    tmp = pd.DataFrame()
    tmp['amp'] = abs(Y)
    tmp['freq'] = frq
    max_lim = max(tmp['amp'][(tmp['freq']>=100) & (tmp['freq']<=800)])    
    fig, ax = plt.subplots(2, 1, figsize=(20,10))
    ax[0].plot(t,y)
    ax[0].set_xlabel('Time')
    ax[0].set_ylabel('Amplitude')
    ax[1].plot(frq,abs(Y),'r')
    ax[1].set_xlim([100,800])
    ax[1].set_ylim([0,max_lim])
    ax[1].set_xlabel('Freq (Hz)')
    ax[1].set_ylabel('|Y(freq)|')
    plt.show()    
    return frq, abs(Y)<br># Função que realiza a carga dos dados do CSV e chama a função de Fourier
def read_csv(file_name, init, final):
    df = pd.read_csv(file_name + '.csv')
    df.columns = ['amp','time']
    delta = final-init
    if init*44100 > len(df) or final*44100 > len(df):
        init = (len(df)/44100)-delta
        if init < 0:
            init = 0
        final = len(df)/44100
    freq, amp = Fourier([df[int(init*44100):int(final*44100)]])
    df = pd.DataFrame()
    df['freq'] = freq
    df['amp'] = amp
    df = df[(df['freq']>=100) & (df['freq']<=800)]
    mx = sorted(df['amp'])
    print("Média das amplitudes:", np.round(np.mean(mx)))
    print("Percentuais em relação a média das amplitudes.")
    print("100 maiores amplitudes ", np.mean(mx[-100:])//df['amp'].mean()*100, "%", sep="")
    print("50 maiores amplitudes:", np.mean(mx[-50:])//df['amp'].mean()*100, "%", sep="")
    print("10 maiores amplitudes:", np.mean(mx[-10:])//df['amp'].mean()*100, "%", sep="")
    print("Maior amplitude:", np.mean(mx[-1:])//df['amp'].mean()*100, "%", sep="")<br>read_csv('Solo_com_Vazamento', 2, 10) # Exemplo de gráficos para vazamento<br>read_csv('Solo_sem_Vazamento', 2, 10) # Exemplo de gráficos para sem vazamento

Step 10: Algoritmo Em R Para Extração Das Features Dos Dados

Utilizou-se um algoritmo em R para realizar o processamento e extração das features (características) dos dados obtidos.

Este primeiro algoritmo realiza uma extração identificada, onde é necessário saber se o arquivo de áudio trata-se de uma amostra vazamento detectado ou não, isso por que os dados resultantes desse processo servirão para o treinamento da rede neural utilizada.

Para quando o sistema estiver em modo de operação um algoritmo um pouco diferente será executado, onde não este fará a extração não identificada, gerando somente as características sem uma identificação.

Estas features ou caraterísticas são propriedades acústicas compostas por varias informações referentes ao espectro de áudio capturado, abaixo seguirá uma descrição (em inglês) destas características.

Este algoritmo faz parte de um projeto disponível no GitHub e pode ser acessado através deste link, o mesmo foi modificado para atender as especificações do projeto.

O software usado para rodar o algoritmo é gratuito, download do interpretador R e do R Studio.

Características extraídas:

  • meanfreq: mean frequency (in kHz)
  • sd: standard deviation of frequency
  • median: median frequency (in kHz)
  • Q25: first quantile (in kHz)
  • Q75: third quantile (in kHz)
  • IQR: interquantile range (in kHz)
  • skew: skewness (see note in specprop description)
  • kurt: kurtosis (see note in specprop description)
  • sp.ent: spectral entropy
  • sfm: spectral flatness
  • mode: mode frequency
  • centroid: frequency centroid (see specprop)
  • peakf: peak frequency (frequency with highest energy)
  • meanfun: average of fundamental frequency measured across acoustic signal
  • minfun: minimum fundamental frequency measured across acoustic signal
  • maxfun: maximum fundamental frequency measured across acoustic signal
  • meandom: average of dominant frequency measured across acoustic signal
  • mindom: minimum of dominant frequency measured across acoustic signal
  • maxdom: maximum of dominant frequency measured across acoustic signal
  • dfrange: range of dominant frequency measured across acoustic signal
  • modindx: modulation index. Calculated as the accumulated absolute difference between adjacent measurements of fundamental frequencies divided by the frequency range
  • label: leakage or without_leakage

Algoritmo usado:


packages <- c('tuneR', 'seewave', 'fftw', 'caTools', 'randomForest', 'warbleR', 'mice', 'e1071', 'rpart', 'xgboost', 'e1071')
if (length(setdiff(packages, rownames(installed.packages()))) > 0) { install.packages(setdiff(packages, rownames(installed.packages()))) }

library(tuneR) library(seewave) library(caTools) library(rpart) library(rpart.plot) library(randomForest) library(warbleR) library(mice) library(xgboost) library(e1071)

specan3 <- function(X, bp = c(0,22), wl = 2048, threshold = 5, parallel = 1){ # To use parallel processing: library(devtools), install_github('nathanvan/parallelsugar') if(class(X) == "data.frame") {if(all(c("sound.files", "selec", "start", "end") %in% colnames(X))) { start <- as.numeric(unlist(X$start)) end <- as.numeric(unlist(X$end)) sound.files <- as.character(unlist(X$sound.files)) selec <- as.character(unlist(X$selec)) } else stop(paste(paste(c("sound.files", "selec", "start", "end")[!(c("sound.files", "selec", "start", "end") %in% colnames(X))], collapse=", "), "column(s) not found in data frame")) } else stop("X is not a data frame") #if there are NAs in start or end stop if(any(is.na(c(end, start)))) stop("NAs found in start and/or end") #if end or start are not numeric stop if(all(class(end) != "numeric" & class(start) != "numeric")) stop("'end' and 'selec' must be numeric") #if any start higher than end stop if(any(end - start<0)) stop(paste("The start is higher than the end in", length(which(end - start<0)), "case(s)")) #if any selections longer than 20 secs stop if(any(end - start>20)) stop(paste(length(which(end - start>20)), "selection(s) longer than 20 sec")) options( show.error.messages = TRUE) #if bp is not vector or length!=2 stop if(!is.vector(bp)) stop("'bp' must be a numeric vector of length 2") else{ if(!length(bp) == 2) stop("'bp' must be a numeric vector of length 2")} #return warning if not all sound files were found fs <- list.files(path = getwd(), pattern = ".wav$", ignore.case = TRUE) if(length(unique(sound.files[(sound.files %in% fs)])) != length(unique(sound.files))) cat(paste(length(unique(sound.files))-length(unique(sound.files[(sound.files %in% fs)])), ".wav file(s) not found")) #count number of sound files in working directory and if 0 stop d <- which(sound.files %in% fs) if(length(d) == 0){ stop("The .wav files are not in the working directory") } else { start <- start[d] end <- end[d] selec <- selec[d] sound.files <- sound.files[d] } # If parallel is not numeric if(!is.numeric(parallel)) stop("'parallel' must be a numeric vector of length 1") if(any(!(parallel %% 1 == 0),parallel < 1)) stop("'parallel' should be a positive integer") # If parallel was called if(parallel > 1) { options(warn = -1) if(all(Sys.info()[1] == "Windows",requireNamespace("parallelsugar", quietly = TRUE) == TRUE)) lapp <- function(X, FUN) parallelsugar::mclapply(X, FUN, mc.cores = parallel) else if(Sys.info()[1] == "Windows"){ cat("Windows users need to install the 'parallelsugar' package for parallel computing (you are not doing it now!)") lapp <- pbapply::pblapply} else lapp <- function(X, FUN) parallel::mclapply(X, FUN, mc.cores = parallel)} else lapp <- pbapply::pblapply options(warn = 0) if(parallel == 1) cat("Measuring acoustic parameters:") x <- as.data.frame(lapp(1:length(start), function(i) { r <- tuneR::readWave(file.path(getwd(), sound.files[i]), from = start[i], to = end[i], units = "seconds") b<- bp #in case bp its higher than can be due to sampling rate if(b[2] > ceiling(r@samp.rate/2000) - 1) b[2] <- ceiling(r@samp.rate/2000) - 1 #frequency spectrum analysis songspec <- seewave::spec(r, f = r@samp.rate, plot = FALSE) analysis <- seewave::specprop(songspec, f = r@samp.rate, flim = c(0, 280/1000), plot = FALSE) #save parameters meanfreq <- analysis$mean/1000 sd <- analysis$sd/1000 median <- analysis$median/1000 Q25 <- analysis$Q25/1000 Q75 <- analysis$Q75/1000 IQR <- analysis$IQR/1000 skew <- analysis$skewness kurt <- analysis$kurtosis sp.ent <- analysis$sh sfm <- analysis$sfm mode <- analysis$mode/1000 centroid <- analysis$cent/1000 #Frequency with amplitude peaks peakf <- 0#seewave::fpeaks(songspec, f = r@samp.rate, wl = wl, nmax = 3, plot = FALSE)[1, 1] #Fundamental frequency parameters ff <- seewave::fund(r, f = r@samp.rate, ovlp = 50, threshold = threshold, fmax = 280, ylim=c(0, 280/1000), plot = FALSE, wl = wl)[, 2] meanfun<-mean(ff, na.rm = T) minfun<-min(ff, na.rm = T) maxfun<-max(ff, na.rm = T) #Dominant frecuency parameters y <- seewave::dfreq(r, f = r@samp.rate, wl = wl, ylim=c(0, 280/1000), ovlp = 0, plot = F, threshold = threshold, bandpass = b * 1000, fftw = TRUE)[, 2] meandom <- mean(y, na.rm = TRUE) mindom <- min(y, na.rm = TRUE) maxdom <- max(y, na.rm = TRUE) dfrange <- (maxdom - mindom) duration <- (end[i] - start[i]) #modulation index calculation changes <- vector() for(j in which(!is.na(y))){ change <- abs(y[j] - y[j + 1]) changes <- append(changes, change) } if(mindom==maxdom) modindx<-0 else modindx <- mean(changes, na.rm = T)/dfrange #save results return(c(duration, meanfreq, sd, median, Q25, Q75, IQR, skew, kurt, sp.ent, sfm, mode, centroid, peakf, meanfun, minfun, maxfun, meandom, mindom, maxdom, dfrange, modindx)) })) #change result names rownames(x) <- c("duration", "meanfreq", "sd", "median", "Q25", "Q75", "IQR", "skew", "kurt", "sp.ent", "sfm","mode", "centroid", "peakf", "meanfun", "minfun", "maxfun", "meandom", "mindom", "maxdom", "dfrange", "modindx") x <- data.frame(sound.files, selec, as.data.frame(t(x))) colnames(x)[1:2] <- c("sound.files", "selec") rownames(x) <- c(1:nrow(x)) return(x) }

processFolder <- function(folderName) { # Start with empty data.frame. data <- data.frame() # Get list of files in the folder. list <- list.files(folderName, '\\.wav') # Add file list to data.frame for processing. for (fileName in list) { row <- data.frame(fileName, 0, 0, 20) data <- rbind(data, row) } # Set column names. names(data) <- c('sound.files', 'selec', 'start', 'end') # Move into folder for processing. setwd(folderName) # Process files. acoustics <- specan3(data, parallel=1) # Move back into parent folder. setwd('..') acoustics }

gender <- function(filePath) { if (!exists('genderBoosted')) { load('model.bin') } # Setup paths. currentPath <- getwd() fileName <- basename(filePath) path <- dirname(filePath) # Set directory to read file. setwd(path) # Start with empty data.frame. data <- data.frame(fileName, 0, 0, 20) # Set column names. names(data) <- c('sound.files', 'selec', 'start', 'end') # Process files. acoustics <- specan3(data, parallel=1) # Restore path. setwd(currentPath) predict(genderCombo, newdata=acoustics) }

# Load data leakage <- processFolder('caminho para o pasta com samples de áudio com vazamento') without_leakage <- processFolder('caminho para o pasta com samples de áudio sem vazamento')

# Set labels. leakage$label <- 1 without_leakage$label <- 2 data <- rbind(leakage, without_leakage) data$label <- factor(data$label, labels=c('leakage', 'without_leakage'))

# Remove unused columns. data$duration <- NULL data$sound.files <- NULL data$selec <- NULL data$peakf <- NULL

# Remove rows containing NA's. data <- data[complete.cases(data),]

# Write out csv dataset. write.csv(data, file='features.csv', sep=',', row.names=F)

Step 11: Rede Neural

A ideia do uso de uma rede neural, é a de realizar um reconhecimento automatizado através dos dados coletados pelo dispositivo de sensoriamento.

A rede neural utilizada é do tipo MLP (Multilayer Perceptron), este modelo é treinado com dados previamente identificados e após esse treinamento o modelo implantado no sistema conseguirá realizar a identificação automática do sinal recebido, informando se naquele ponto existe um vazamento ou não.

Foi necessário realizar uma filtragem dos dados de entrada, pois algumas características estavam diminuindo a taxa de acerto da rede ao invés de melhora-la. Não foi realizado nenhuma abordagem estatística muito aprofundada, mas mesmo com um trabalho mais superficial pode-se chegar a algumas variáveis com bons desempenhos.

Para os testes realizados o modelo obteve um desempenho muito bom, alcançando na maioria dos testes uma taxa de acerto de 100%, como pode ser observado na imagem anexa.

Este algoritmo é utilizado para treinar o modelo da rede e retornar a taxa de acerto do mesmo. No sistema de detecção um algoritmo um pouco diferente seria usado, pois ele realizaria o treino ou receberia um modelo já treinado da nuvem ou de alguma outra fonte e com esse modelo realizaria as predições para cada leitura realizada.

# coding: utf-8

import pandas as pd import numpy as np from sklearn.model_selection import train_test_split as tts from sklearn.neural_network import MLPClassifier as MLP from sklearn.metrics import classification_report as cr from sklearn.metrics import confusion_matrix as cm

# Leitura dos dados do CSV df = pd.read_csv('features.csv') # Separação das entradas df_X = df[df.columns[:len(df.columns)-1]] # Filtrando as entradas df_X = df_X[['IQR','meanfun','sd','Q25','sfm','centroid','meandom','mindom']] # Separação das saidas df_Y = df[df.columns[-1]] # Discretização das saidas df_Y = pd.get_dummies(df_Y) df_Y = df_Y['leakage']

# Separando dados para treino e teste X_train, X_test, Y_train, Y_test = tts(df_X, df_Y, test_size=0.1)

# Criando modelo de rede neural modelo = MLP(alpha=0.0001, learning_rate_init=0.0001, hidden_layer_sizes=(50,50,50,50), max_iter=10000, activation='tanh', solver='lbfgs')

# Treinando modelo modelo.fit(X_train, Y_train) result = modelo.predict(X_test)

# Imprimindo resultados report = cr(Y_test, result) mat = cm(y_pred=result, y_true=Y_test) print("Matriz de confusão") print(mat, end="\n\n") print("Relatório de Classificação") print(report)