Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Predição de chuva DIY usando Arduino, Python e Keras

Componentes e suprimentos

Pequena caixa de plástico com tampas destacáveis ​​(a minha tem parafusos)
× 1
porta-pilhas AAA para três
× 1
três pilhas recarregáveis ​​AAA
× 1
Painel solar pequeno de 6 V
× 1
SparkFun Arduino Pro Mini 328 - 5 V / 16 MHz
× 1
1N4004 diodo
× 1
Transistor de uso geral NPN
× 1
um sensor de chuva
× 1
Módulo de comunicação serial HC-12
× 1
Módulo serial USB HC-12
× 1
SparkFun Atmospheric Sensor Breakout - BME280
× 1
BH1750
× 1
PCB, fios, solda, plugue KF301-2P no conector de parafuso , conectores pcb macho e fêmea, cola
× 1
Regulador de 3,3 V
× 1

Ferramentas e máquinas necessárias

USB para adaptador FTDI serial FT232RL
Drill
Ferro de soldar (genérico)
Chaves de fenda

Sobre este projeto


Primeiramente algumas palavras sobre este projeto, a motivação, as tecnologias envolvidas e o produto final que iremos construir.

Portanto, o grande objetivo aqui é obviamente prever a chuva no futuro (tentaremos 6 horas). A previsão será sim ou não (booleano em termos de programação). Pesquisei em tutoriais sobre o assunto e não encontrei nenhum que seja completo em todos os sentidos. Então, o meu levará isso para uma abordagem totalmente nova e entrará em cada aspecto dela. Para isso, vamos:
  • construir a estação meteorológica. A estação deve estar completamente desligada da rede com um painel solar e um modo de energia extremamente baixa (algumas dezenas de microamperes por hora)
  • programa a estação para que reúna dados e os transmita a cada dez minutos para uma estação base
  • coletar os dados na estação base e os armazena (em um banco de dados)
  • usando redes neurais (Biblioteca Keras) e outras bibliotecas Python, como pandas, filtram, limpam e pré-processam os dados e, em seguida, os alimentam com uma rede neural para treinar um "modelo" para prever se choverá ou não.
  • finalmente prever se vai chover ou não nas próximas 6 horas e notificar os usuários por e-mail

Eu pessoalmente usei esta estação meteorológica para coletar dados (você pode baixar os dados nas próximas etapas, se desejar). Com apenas cerca de 600 dias de dados meteorológicos, o sistema pode fazer uma previsão se choverá ou não nas próximas 6 horas com uma precisão de cerca de 80% dependendo dos parâmetros que não são tão ruins.

Este tutorial irá guiá-lo por todas as etapas necessárias para prever a precipitação desde o início. Faremos um produto final que faz um trabalho prático, sem usar clima externo ou API-s de aprendizado de máquina. Aprenderemos ao longo do caminho como construir uma estação meteorológica prática (com baixo consumo de energia e fora da rede) que realmente coleta dados por longos períodos de tempo sem manutenção. Depois disso, você aprenderá como programá-lo usando o IDE do Arduino. Como coletar dados em um banco de dados em uma estação base (servidor). E como processar os dados (Pandas) e aplicar redes neurais (Keras) e então prever a precipitação.





Etapa 1:peças e ferramentas para construir a estação


Partes:

1. Pequena caixa de plástico com tampas destacáveis ​​(a minha tem parafusos). O tamanho da caixa deve ser grande o suficiente para acomodar os componentes pequenos e as baterias. Minha caixa tem 11 x 7 x 5 cm

2. três porta-pilhas AAA

3. três baterias recarregáveis ​​AAA

4. Painel solar pequeno de 6 V

5. Arduino Pro Mini 328p

6. um diodo, 1N4004 (para evitar corrente reversa das baterias para o painel)

7. um pequeno transistor NPN e resistor de 1k (para ligar e desligar os componentes)

8. um sensor de chuva

9. Módulo de comunicação serial HC-12

10. Módulo serial USB HC-12 (para a estação base)

11. Módulo sensor bosch BME280 (para umidade, temperatura, pressão)

12. Módulo sensor de luz BH1750

13. PCB, fios, solda, plugue KF301-2P no conector de parafuso, conectores pcb macho e fêmea, cola

14. Regulador de 3,3 V

15. uma estação base:um PC ou uma placa de desenvolvimento funcionando o tempo todo. Sua função é coletar dados, treinar o modelo de previsão de chuva e fazer previsões

Ferramentas:

1. USB para adaptador FTDI serial FT232RL para programar o Arduino Pro Mini

2. Arduino IDE

3. Exercício

4. Serra de lâmina fina

5. Chaves de fenda

6. Ferro de soldar

7. Cortador de fio

Habilidades:

1. Solda, verifique este tutorial

2. Programação básica do Arduino

3. Configuração de serviço Linux, instalação de pacote

4. Algumas habilidades de programação





Etapa 2:construção da estação meteorológica


A estação meteorológica é composta pelos seguintes conjuntos de componentes:

1. a caixa com o painel solar colado a ela

2 . o PCB com os eletrônicos dentro

3. o suporte da bateria também dentro

4. o BME280 e sensores de luz e chuva do lado de fora

1. A caixa precisa de 4 furos, um para os fios do painel solar, outros três para os sensores que serão colocados na parte externa. Primeiro faça os furos, eles devem ser grandes o suficiente para que os fios macho-fêmea fiquem para fora e vão para os sensores. Depois que os furos forem perfurados, cole o painel em um lado da caixa e passe os fios por um orifício dentro

2. O PCB conterá o arduino, HC-12, regulador de 3,3 V, diodo, transistor, resistor e dois KF301-2P
  • solde primeiro os dois conectores PCB fêmeas no PCB para o arduino, solde os conectores pcb machos no arduino e coloque o arduino no PCB.
  • o led do arduino deve ser removido ou pelo menos um de seus pinos. isso é muito importante porque o led vai consumir uma grande quantidade de energia. Tenha cuidado para não danificar outros componentes
  • solde o transistor, o resistor e o regulador de 3,3 V
  • solde os dois KF301-2P. Um será para o painel solar, o outro para o suporte da bateria
  • soldar três conectores PCB fêmea:para o sensor de luz, BME280 e sensor de chuva
  • soldar pequenos fios para conectar todos os componentes do PCB (verifique as fotos e o schamatic fritzing)

3. coloque 3 baterias AAA NiMH carregadas dentro do suporte e coloque-as dentro da caixa, conectando os fios ao conector KF301-2P

4. conecte o BME280 e os sensores de luz de fora da caixa aos seus conectores machos correspondentes

Para o sensor de chuva, solde três fios (Gnd, Vcc, sinal) nele, e do outro lado solde os pinos machos que irão dentro da caixa para seus conectores machos correspondentes

A última coisa seria colocar a estação em sua posição final. Escolhi uma posição protegida da chuva e da neve. Escolhi fios mais longos para o sensor de chuva e coloquei-os separadamente na chuva em um suporte estável. Para a caixa principal, escolhi um tipo especial de fita adesiva (veja as fotos), mas qualquer coisa que segure a caixa serve.
sketch.fzz





Etapa 3:Código Arduino


Nesta etapa, você aprenderá quais bibliotecas externas são necessárias, temos uma visão geral do código e como ele funciona e, claro, você poderá fazer o download ou copiar e colar no IDE do Arduino e carregá-lo na estação meteorológica.

O papel da estação meteorológica é transmitir a uma estação base a cada 10 minutos dados sobre seus sensores.





Primeiro, vamos descrever o que o programa da estação meteorológica faz:


1 ler os dados dos sensores (umidade, temperatura, pressão, chuva, luz, tensão)

2. transmite os dados codificados por meio de uma segunda linha serial de software.

Os dados codificados têm a seguinte aparência:
  H1:78 | T1:12 | PS1:1022 | L1:500 | R1:0 | V1:4010 |  

A declaração acima significará que:umidade da estação "1" é 78 por cento, temperatura da estação 1 é 12 graus, pressão é 1.022 bar, nível de luz 500 lux, chuva 0 e tensão 4010 milivolts

3. desligue os componentes auxiliares:sensores e dispositivo de comunicação

4. coloca o arduino no modo de espera por 10 minutos (isso fará com que ele consuma menos 50 microampères)

5. ligue os componentes e repita as etapas 1 - 4

Um pequeno ajuste extra aqui, se o nível de tensão estiver acima de 4,2 V, o arduino usará a função normal de hibernação "atraso (milissegundos)". Isso aumentará muito o consumo de energia e diminuirá a tensão rapidamente. Isso evita efetivamente que o painel solar sobrecarregue as baterias.

Você pode obter o código do meu repositório Github aqui:https://github.com/danionescu0/home-automation/tre ...

Ou copie e cole abaixo, de qualquer forma apenas remova a linha com "transmitSenzorData (" V ", sensores.voltage);"
  #include "LowPower.h" 
#include "SoftwareSerial.h" #include "Wire.h" #include "Adafruit_Sensor.h" #include "Adafruit_BME280.h" #include "BH1750.h "SoftwareSerial serialComm (4, 5); // RX, TXAdafruit_BME280 bme; BH1750 lightMeter; const byte rainPin =A0; byte sensorCode =1; / ** * nível de tensão que colocará o microcontrolador em sono profundo em vez de sono regular * / int voltageDeepSleepThreshold =4200; const byte periferialsPowerPin =6; char buffer [] ={'', '', '', '', '', '', ''}; struct sensorData {byte umidade; temperatura interna; byte rain; pressão interna; tensão longa; luz interna; }; sensores sensorData; configuração vazia () {Serial.begin (9600); serialComm.begin (9600); pinMode (periferialsPowerPin, OUTPUT); digitalWrite (periféricosPowerPin, HIGH); atraso (500); if (! bme.begin ()) {Serial.println ("Não foi possível encontrar um sensor BME280 válido, verifique a fiação!"); while (1) {customSleep (100); }} Serial.println ("Inicialização concluída com sucesso"); atraso (50); digitalWrite (periferialsPowerPin, HIGH);} void loop () {updateSenzors (); transmitData (); customSleep (75); } void updateSenzors () {bme.begin (); lightMeter.begin (); atraso (300); sensores.temperatura =bme.readTemperature (); sensores.pressure =bme.readPressure () / 100.0F; sensores.umidade =bme.readHumidez (); sensores.light =lightMeter.readLightLevel (); sensores.voltagem =readVcc (); sensores.rain =readRain ();} void transmitData () {emptyIncommingSerialBuffer (); Serial.print ("Temp:"); Serial.println (sensores.temperatura); Serial.print ("Úmido:"); Serial.println (sensores.umidade); Serial.print ("Pressão:"); Serial.println (sensores.pressure); Serial.print ("Luz:"); Serial.println (sensores.luz); Serial.print ("Voltagem:"); Serial.println (sensores.voltagem); Serial.print ("Rain:"); Serial.println (sensores.rain); transmitSenzorData ("T", sensores.temperatura); transmitSenzorData ("H", sensores.umidade); transmitSenzorData ("PS", sensores.pressure); transmitSenzorData ("L", sensores.light); transmitSenzorData ("V", sensores.voltagem); transmitSenzorData ("R", sensores.rain);} void emptyIncommingSerialBuffer () {while (serialComm.available ()> 0) {serialComm.read (); atraso (5); }} void transmitSenzorData (tipo String, valor int) {serialComm.print (tipo); serialComm.print (sensoresCode); serialComm.print (":"); serialComm.print (valor); serialComm.print ("|"); atraso (50);} void customSleep (long eightSecondCycles) {if (sensores.voltage> voltageDeepSleepThreshold) {delay (eightSecondCycles * 8000); Retorna; } digitalWrite (periféricosPowerPin, LOW); para (int i =0; i

Antes de enviar o código, baixe e instale as seguintes bibliotecas do Arduino:

* Biblioteca BH1750:https://github.com/claws/BH1750 * Biblioteca LowPower:https://github.com/rocketscream/Low-Power

* Biblioteca Adafruit Sensor:https://github.com/adafruit/Adafruit_Sensor

* Biblioteca Adafruit BME280:https://github.com/adafruit/Adafruit_Sensor

Se você não sabe fazer isso, dê uma olhada neste tutorial.





Etapa 4:Preparando a estação base


A estação base consistirá em um computador Linux (desktop, laptop ou placa de desenvolvimento) com o HC-12 USB módulo anexado. O computador deve permanecer sempre ligado para coletar dados a cada 10 minutos da estação.

Usei meu laptop com o Ubuntu 18.





As etapas de instalação:


1. Instale o anaconda. Anaconda é um gerenciador de pacotes Python e tornará mais fácil para nós trabalharmos com as mesmas dependências. Seremos capazes de controlar a versão do Python e cada versão do pacote

Se você não sabe como instalá-lo, verifique este:https://www.digitalocean.com/community/tutorials/h ... tutorial e siga as etapas 1 - 8

2. Instale o mongoDb. O MongoDb será nosso principal banco de dados para este projeto. Ele armazenará dados sobre todas as séries temporais dos sensores. Não tem esquema e, para nosso propósito, é fácil de usar.

Para as etapas de instalação, verifique a página:https://docs.mongodb.com/v3.4/tutorial/install-mon ...

Eu usei uma versão mais antiga do mongoDb 3.4.6, se você seguir o tutorial acima, você obterá exatamente isso. Em princípio, ele deve funcionar com a versão mais recente.

[Opcionalmente] adicione um índice no campo de data:
  mongouse weather db.weather_station.createIndex ({"date":1})  

3. Baixe o projeto aqui:https://github.com/danionescu0/home-automation. Estaremos usando a pasta de previsão do tempo
  sudo apt-get install clone gitgit https://github.com/danionescu0/home-automation.gi ...  

4. Crie e configure o ambiente anaconda:
  cd weather-predict # cria um ambiente anaconda chamado "weather" com python 3.6.2conda create --name weather python =3.6.2 # activate environmentconda activate weather # instala todos os pacotes pip install -r requirements.txt  

Isso criará um novo ambiente anaconda e instalará os pacotes necessários. Alguns dos pacotes são:

Keras (camada de rede neural de alto nível, com esta biblioteca faremos todas as nossas previsões de rede neural)

pandas (ferramenta útil que manipula dados, vamos usá-la pesadamente)

pymongo (driver python mongoDb)

sklearn (mineração de dados e ferramentas de análise de dados)





Configure o projeto


O arquivo de configuração está situado na pasta previsão do tempo e é denominado config.py

1. se você instalar o MongoDb remotamente ou em uma porta diferente, altere o "host" ou "porta" no
  mongodb ={'host':'localhost', 'port':27017} ...  

2. Agora precisamos conectar o adaptador serial USB HC-12. Antes de correr:
  ls -l / dev / tty *  

e você deve obter uma lista de dispositivos montados.

Agora insira o HC-12 em uma porta USB e execute o mesmo comando novamente. Deve ser uma nova entrada na lista, nosso adaptador serial. Agora mude a porta do adaptador na configuração se necessário
  serial ={'port':'/ dev / ttyUSB0', 'baud_rate':9600}  

As outras entradas de configuração são alguns caminhos padrão de arquivo, não há necessidade de uma alteração lá.





Etapa 5:use a estação meteorológica na prática


Aqui vamos discutir coisas básicas sobre como importar meus dados de teste, executar alguns testes neles, configurar seus próprios dados, exibir alguns gráficos e configurar um e-mail com previsão para as próximas horas.

Se quiser saber mais sobre como funciona, dê uma olhada na próxima etapa "Como funciona"

Importando meus dados já coletados

O MongoDb vem com um comando cli para importar dados do json:
  mongoimport -d clima -c weather_station --file sample_data / weather_station.json  

Isso importará o arquivo dos dados de amostra para o banco de dados "meteorológico" e a coleção de "pontos de dados"

Um aviso aqui, se você usar meus dados coletados e combiná-los com seus dados locais recentes, a precisão pode cair devido às pequenas diferenças no hardware (sensores) e nos padrões climáticos locais.

Coletando novos dados

Uma das funções da estação base é armazenar os dados de entrada da estação meteorológica no banco de dados para processamento posterior. Para iniciar o processo que escuta a porta serial e armazena no banco de dados, basta executar:
  conda ativar weatherpython serial_listener.py # a cada 10 minutos você deve ver os dados da estação meteorológica chegando:[Sensor:tipo (temperatura), valor (14.3)] [Sensor:tipo (pressão), valor ( 1056.0)] ...  

Gerando o modelo de previsão

Presumo que você importou meus dados ou "executou o script por alguns anos" para reunir seus dados personalizados, portanto, nesta etapa, processaremos os dados para criar um modelo usado para prever chuvas futuras.
  conda activate weatherpython train.py --days_behind 600 --test-file-percent 10 --datapoints-behind 8 --hour-granularity 6  

* O primeiro parâmetro --days_behind significa quantos dados no passado o script deve processar. É medido em dias

* --test-file-percent significa quanto dos dados deve ser considerado para fins de teste, esta é uma etapa regular em um algoritmo de aprendizado de máquina

* --hour-granularity significa basicamente quantas horas no futuro queremos a previsão

* --datapoints-por trás deste parâmetro será discutido mais adiante na próxima seção

Visualize alguns gráficos de dados com todos os sensores de estação meteorológica

Digamos que nos últimos 10 dias:
  conda ativar gráficos de clima python - dias atrás 10  

Preveja se vai chover no próximo período

Vamos prever se vai chover e enviaremos um e-mail de notificação
  conda ativar weather python predict.py --datapoints-behind 8 --hour-granularity 6 --from-addr a_gmail_address --from-password gmail_password --to-addr a_email_destination  

Execute uma previsão em lote nos dados de teste:
  python predict_batch.py ​​-f sample_data / test_data.csv  

É importante usar os mesmos parâmetros do script de treinamento acima.

Para que a notificação por e-mail funcione, faça login em sua conta do gmail e ative Permitir aplicativos menos seguros. Esteja ciente de que isso torna mais fácil para outras pessoas obterem acesso à sua conta.

Você precisará de dois endereços de e-mail, um endereço do Gmail com a opção acima ativada e um outro endereço no qual receberá sua notificação.

Se você gosta de receber notificações a cada hora, coloque o script no crontab

Para ver como tudo isso é possível, verifique a próxima etapa





Etapa 6:como funciona





Nesta última etapa, discutiremos vários aspectos da arquitetura deste projeto:


1. Visão geral do projeto, discutiremos a arquitetura geral e as tecnologias envolvidas

2. Conceitos básicos de aprendizado de máquina

3. Como os dados são preparados (a etapa mais importante)

4. Como funciona a API real do wrapper da rede neural (Keras)

5. Melhorias futuras

Vou tentar dar alguns exemplos de código aqui, mas tenha em mente que não é 100% o código do projeto. No projeto, o código é um pouco mais complicado com classes e uma estrutura





1. Visão geral do projeto, discutiremos a arquitetura geral e as tecnologias envolvidas


Como conversamos anteriormente, o projeto tem duas partes distintas. A estação meteorológica é autônoma, cuja única função é coletar e transmitir dados. E a estação base onde todo o treinamento e previsão de coleta acontecerão.

Vantagens da separação da estação meteorológica e da estação base:
  • requisitos de energia, se a estação meteorológica também pudesse processar os dados, ela precisaria de energia substancial, talvez grandes painéis solares ou uma fonte de energia permanente
  • portabilidade, devido ao seu tamanho pequeno, a estação meteorológica pode coletar dados a algumas centenas de metros de distância e você pode facilmente mudar de lugar se necessário
  • escalabilidade, você pode aumentar a precisão da previsão construindo mais de uma estação meteorológica e espalhando-as por algumas centenas de metros
  • baixo custo, porque é um dispositivo barato, você pode facilmente construir outro no caso de um ser perdido ou roubado

A escolha do banco de dados . Escolhi o mongoDb porque seus recursos são interessantes:API sem esquema, gratuita e fácil de usar

Cada vez que os dados do sensor são recebidos, os dados são salvos no banco de dados, e a entrada de dados se parece com isto:
  {"_id":"04_27_2017_06_17", "umidade":65, "data":ISODate ("2017-04-27T06:17:18Z"), "pressão":1007, "temperatura":9, "chuva":0, "luz":15}  

O banco de dados armazena dados no formato BSON (semelhante ao JSON) para que seja fácil de ler e trabalhar. Agreguei os dados em um identificador que contém a data formatada como string para os minutos, então o menor agrupamento aqui é um minuto.

A estação meteorológica (quando funcionando corretamente) irá transmitir um ponto de dados a cada 10 minutos. Um ponto de dados é uma coleção de valores de "data", "umidade", "pressão", "temperatura", "chuva" e "luz".

O processamento de dados e a rede neural escolha de tecnologia

Eu escolhi Python para o back-end porque muitas das principais inovações em redes neurais são encontradas em Python. Uma comunidade crescente com muitos repositórios Github, blogs de tutoriais e livros estão aqui para ajudar.

* Para a parte de processamento de dados, usei Pandas ( https://pandas.pydata.org/ ) . O Pandas facilita o trabalho com dados. Você pode carregar tabelas de estruturas de dados CSV, Excel, Python e reordená-las, descartar colunas, adicionar colunas, indexar por coluna e muitas outras transformações.

* Para trabalhar com redes neurais, escolhi Keras (https://keras.io/). Keras é um wrapper de rede neural de alto nível sobre APIs de nível mais baixo, como Tensorflow, e pode-se construir uma rede neural de várias camadas com uma dúzia de linhas de código ou mais. Esta é uma grande vantagem porque podemos construir algo útil sobre o excelente trabalho de outras pessoas. Bem, este é o material básico da programação, construir sobre outros blocos de construção menores.





2. Conceitos básicos de aprendizado de máquina


O escopo deste tutorial não é ensinar aprendizado de máquina, mas apenas descrever um de seus possíveis casos de uso e como podemos aplicá-lo de forma prática a esse caso de uso.

Redes neurais são estruturas de dados que se assemelham a células cerebrais chamadas neurônios. A ciência descobriu que um cérebro possui células especiais chamadas neurônios que se comunicam com outros neurônios por impulsos elétricos através de "linhas" chamadas axônios. Se estimulados o suficiente (por muitos outros neurônios), os neurônios irão desencadear um impulso elétrico mais distante nesta "rede" estimulando outros neurônios. É claro que isso é uma simplificação exagerada do processo, mas basicamente os algoritmos de computador tentam replicar esse processo biológico.

Em redes neurais de computador, cada neurônio tem um "ponto de gatilho" onde, se estimulado sobre esse ponto, propagará a estimulação para a frente, caso contrário, não o fará. Para isso, cada neurônio simulado terá um viés, e cada axônio, um peso. Após uma inicialização aleatória desses valores, um processo chamado "aprendizado" é iniciado, o que significa que em um loop um algoritmo executará estas etapas:
  • estimular os neurônios de entrada
  • propagar os sinais através das camadas da rede até os neurônios de saída
  • leia os neurônios de saída e compare os resultados com os resultados desejados
  • ajuste os pesos dos axônios para um melhor resultado da próxima vez
  • comece novamente até que o número de loops seja alcançado

Se você quiser saber mais detalhes sobre este processo, você pode conferir este artigo:https://mattmazur.com/2015/03/17/a-step-by-step-ba .... Existem também vários livros e tutoriais por aí.

Mais uma coisa, aqui estaremos usando um método de aprendizado supervisionado. Isso significa que ensinaremos ao algoritmo as entradas e as saídas também, de modo que, dado um novo conjunto de entradas, ele possa prever a saída.





3. Como os dados são preparados (a etapa mais importante)


Em muitos problemas de aprendizado de máquina e rede neural, a preparação de dados é uma parte muito importante e cobrirá:
  • obtenha os dados brutos
  • limpeza de dados:isso significará remover valores órfãos, aberrações ou outras anomalias
  • agrupamento de dados:pegando muitos pontos de dados e transformando em um ponto de dados agregado
  • aprimoramento de dados:adicionar outros aspectos dos dados derivados de seus próprios dados ou de fontes externas
  • divisão dos dados em treinamento e dados de teste
  • divida cada um dos dados de trem e teste em entradas e saídas. Normalmente, um problema terá muitas entradas e poucas saídas
  • rescale the data so it's between 0 and 1 (this will help the network removing high/low value biases)

Getting the raw data

In our case getting data for MongoDb in python is really easy. Given our datapoints collection just this lines of code will do
client =MongoClient(host, port).weather.datapoints cursor =client.find( {'$and' :[ {'date' :{'$gte' :start_date}}, {'date' :{'$lte' :end_date}} ]} )data =list(cursor).. 

Data cleanup

The empty values in the dataframe are dropped
dataframe =dataframe.dropna() 

Data grouping &data enhancing

This is a very important step, the many small datapoins will be grouped into intervals of 6 hours. For each group several metrics will be calculated on each of the sensors (humidity, rain, temperature, light, pressure)
  • min value
  • max value
  • mean
  • 70, 90, 30, 10 percentiles
  • nr of times there has been a rise in a sensor
  • nr of times there has been a fall in a sensor
  • nr of times there has been steady values in a sensor

All of these things will give the network information for a datapoint, so for each of the 6 hours intervals these things will be known.

From a dataframe that looks like this:
_id date humidity light pressure rain temperature 04_27_2017_03_08 2017-04-27 03:08:36 67.0 0.0 1007.0 0.0 11.004_27_2017_03_19 2017-04-27 03:19:05 66.0 0.0 1007.0 0.0 11.004_27_2017_03_29 2017-04-27 03:29:34 66.0 0.0 1007.0 0.0 11.0  

And the transformation will be:"
_id date humidity_10percentile humidity_30percentile humidity_70percentile humidity_90percentile humidity_avg ... temperature_avg temperature_fall temperature_max temperature_min temperature_rise temperature_steady ... 04_27_2017_0 2017-04-27 03:08:36 59.6 60.8 63.2 66.0 62.294118 ... 10.058824 2 11.0 9.0 1 1404_27_2017_1 2017-04-27 06:06:50 40.3 42.0 60.0 62.0 50.735294 ... 14.647059 3 26.0 9.0 11 2004_27_2017_2 2017-04-27 12:00:59 36.0 37.0 39.8 42.0 38.314286 ... 22.114286 1 24.0 20.0 5 29  

After this a new column named "has_rain" will be added. This will be the output (our predicted variable). Has rain will be 0 or 1 depending if the rain average is above a threshold (0.1). With pandas it's as simple as:
dataframe.insert(loc=1, column='has_rain', value=numpy.where(dataframe['rain_avg']> 0.1, 1, 0)) 

Data cleanup (again)

- we'll drop the date column because it's no use to us, and also remove datapoints where the minimum temperature is below 0 because our weather station it doesn't have a snow sensor, so we won't be able to measure if it snowed
dataframe =dataframe.drop(['date'], axis=1)dataframe =dataframe[dataframe['temperature_min']>=0] 

Data enhancing

Because data in the past might influence our prediction of the rain, we need for each of the dataframe rows to add columns reference to the past rows. This is because each of the row will serve as a training point, and if we want the prediction of the rain to take into account previous datapoints that's exactly what we should do:add more columns for datapoints in the past ex:
_id has_rain humidity_10percentile humidity_30percentile humidity_70percentile humidity_90percentile ... temperature_steady_4 temperature_steady_5 temperature_steady_6 temperature_steady_7 temperature_steady_8 ... 04_27_2017_3 0 36.0 44.8 61.0 63.0 ... NaN NaN NaN NaN NaN04_28_2017_0 0 68.0 70.0 74.0 75.0 ... 14.0 NaN NaN NaN NaN04_28_2017_1 0 40.0 45.0 63.2 69.0 ... 20.0 14.0 NaN NaN NaN04_28_2017_2 0 34.0 35.9 40.0 41.0 ... 29.0 20.0 14.0 NaN NaN04_28_2017_3 0 36.1 40.6 52.0 54.0 ... 19.0 29.0 20.0 14.0 NaN04_29_2017_0 0 52.0 54.0 56.0 58.0 ... 26.0 19.0 29.0 20.0 14.004_29_2017_1 0 39.4 43.2 54.6 57.0 ... 18.0 26.0 19.0 29.0 20.004_29_2017_2 1 41.0 42.0 44.2 47.0 ... 28.0 18.0 26.0 19.0 29.0  

So you see that for every sensor let's say temperature the following rows will be added:"temperature_1", "temperature_2".. meaning temperature on the previous datapoint, temperature on the previous two datapoints etc. I've experimented with this and I found that a optimum number for our 6 hour groupings in 8. That means 8 datapoints in the past (48 hours). So our network learned the best from datapoins spanning 48 hours in the past.

Data cleanup (again)

As you see, the first few columns has "NaN" values because there is nothing in front of them so they should be removed because they are incomplete.

Also data about current datapoint should be dropped, the only exception is "has_rain". the idea is that the system should be able to predict "has_rain" without knowing anything but previous data.

Splitting the data in train and test data

This is very easy due to Sklearn package:
from sklearn.model_selection import train_test_split ...main_data, test_data =train_test_split(dataframe, test_size=percent_test_data) ... 

This will split the data randomly into two different sets

Split each of the train and test data into inputs and outputs

Presuming that our "has_rain" interest column is located first
X =main_data.iloc[:, 1:].valuesy =main_data.iloc[:, 0].values  

Rescale the data so it's between 0 and 1

Again fairly easy because of sklearn
from sklearn.preprocessing import StandardScalerfrom sklearn.externals import joblib..scaler =StandardScaler()X =scaler.fit_transform(X) ...# of course we should be careful to save the scaled model for later reusejoblib.dump(scaler, 'model_file_name.save')  





4. How the actual neural network wrapper API works (Keras)


Building a multi layer neural network with Keras is very easy:
from keras.models import Sequentialfrom keras.layers import Densefrom keras.layers import Dropout ...input_dimensions =X.shape[1] optimizer ='rmsprop'dropout =0.05model =Sequential()inner_nodes =int(input_dimensions / 2)model.add(Dense(inner_nodes, kernel_initializer='uniform', activation='relu', input_dim=input_dimensions))model.add(Dropout(rate=dropout))model.add(Dense(inner_nodes, kernel_initializer='uniform', activation='relu'))model.add(Dropout(rate=dropout))model.add(Dense(1, kernel_initializer='uniform', activation='sigmoid'))model.compile(optimizer=optimizer, loss='mean_absolute_error', metrics=['accuracy']) model.fit(X, y, batch_size=1, epochs=50)...# save the model for later useclassifier.save('file_model_name') 

So what does this code mean? Here we're building a sequential model, that means sequentially all the layers will be evaluated.

a) we declare the input layer (Dense), here all the inputs from our dataset will be initializedm so the "input_dim" parameter must be equal to the row length

b) a Dropout layer is added. To understand the Dropout first we must understand what "overfitting" means:it's a state in which the network has learned too much particularities for a specific dataset and will perform badly when confronted to a new dataset. The dropout layer will disconnect randomly neurons at each iteration so the network won't overfit.

c) another layer of Dense is added

d) another Dropout

e) the last layer is added with one output dimension (it will predict only yes/no)

f) the model is "fitted" that means the learning process will begin, and the model will learn

Other parameters here:
  • activation functions (sigmoid, relu). This are functions that dictate when the neuron will transmit it's impulse further in the network. There are many, but sigmoid and relu are the most common. Check out this link for more details:https://towardsdatascience.com/activation-function...
  • kernel_initializer function (uniform). This means that all the weights are initialized with random uniform values
  • loss function (mean_absolute_error). This function measures the error comparing the network predicted result versus the ground truth. There are many alternatives:https://keras.io/losses/
  • metrics function (accuracy). It measures the performance of the model
  • optimiser functions (rmsprop). It optimizes how the model learn through backpropagation.
  • batch_size. Number of datapoints to take once by Keras before applying optimizer function
  • epochs:how many times the process it's started from 0 (to learn better)

There is no best configuration for any network or dataset, all these parameters can an should be tuned for optimal performance and will make a big difference in prediction success.





5. Future improvements


Let's start from the weather station , I can see a lot of improving work to be done here:
  • add a wind speed / direction sensor. This could be a very important sensor that i'm missing in my model
  • experiment with UV rays, gas and particle sensors
  • add at least two stations in the zone for better data (make some better averages)
  • collect a few more years of data, i've experimented with just a year and a half

Some processing improvements:
  • try to incorporate data from other sources into the model. You can start to import wind speed data and combine with the local station data for a better model. This website offers historical data:https://www.wunderground.com/history/
  • optimize the Keras model better by adjusting:layers, nr of neurons in layers, dropout percents, metrics functions, optimiser functions, loss functions, batch size, learning epochs
  • try other model architectures, for example i've experimented with LSTM (long short term memory) but it gived slightly poorer results)

To try different parameters on the learning model you can use
python train.py --days_behind 600 --test-file-percent 10 --datapoints-behind 6 --hour-granularity 6 --grid-search 

This will search through different "batch_size", "epoch", "optimizer" and "dropout" values, evaluate all and print out the best combination for your data.

If you have some feedback on my work please share it, thanks for staying till the end of the tutorial!





Step 7:Bonus:Using an Official Weather Dataset


I was wondering if I can get better results with a more reliable weather station, so i've searched a bit, and i've came across "Darksky AP I" (https://darksky.net/dev), this is a great tool that provides current and historical weather data with many more sensor data:
  • temperature
  • humidity
  • pressure
  • wind speed
  • wind gust
  • ub index
  • visibilitySo this beeing data from an official weather station, and having more parameters I thought it should perform better so i've gave it a try. To replicate my findings:

1.Download the data from darsky or import my MongoDb collection:

a) Download
  • to download your self, first create an account in darsky and get the API key
  • replace the API key in download_import/config.py
  • also in the config replace the geographic coordonates for the location you want to predict the rain
  • in a console activate "weather" anaconda environment and run:
python download_import/darksky.py -d 1000 

- the free version of the API is limited to 1000 requests per day so if you want more data you need to wait for a longer time

b) Import my downloaded data for Bucharest city

- in a console run
mongoimport -d weather -c darksky --file sample_data/darksky.json  

2. When you train the model specify that it should run on "darksy" dataset
python train.py -d 2000 -p 20 -dp 4 -hg 6 --data-source darksky 

3. To see the results run predict batch script as before
python predict_batch.py -f sample_data/test_data.csv 

You'll see that the overall prediction percent has gone from about 80% to 90%. Also the prediction accuracy when accounting only rainy days has gone up.

So yes, the dataset really matters.

Código

  • Code snippet #2
  • Code snippet #5
  • Code snippet #6
  • Code snippet #10
  • Code snippet #15
  • Code snippet #16
  • Code snippet #18
  • Code snippet #22
  • Code snippet #23
  • Code snippet #25
  • Code snippet #26
Code snippet #2Plain text
#include "LowPower.h"
#include "SoftwareSerial.h"#include "Wire.h"#include "Adafruit_Sensor.h"#include "Adafruit_BME280.h"#include "BH1750.h"SoftwareSerial serialComm(4, 5); // RX, TXAdafruit_BME280 bme; BH1750 lightMeter;const byte rainPin =A0;byte sensorsCode =1;/** * voltage level that will pun the microcontroller in deep sleep instead of regular sleep */int voltageDeepSleepThreshold =4200; const byte peripherialsPowerPin =6;char buffer[] ={' ',' ',' ',' ',' ',' ',' '};struct sensorData { byte humidity; int temperature; byte rain; int pressure; long voltage; int light; };sensorData sensors;void setup() { Serial.begin(9600); serialComm.begin(9600); pinMode(peripherialsPowerPin, OUTPUT); digitalWrite(peripherialsPowerPin, HIGH); atraso (500); if (!bme.begin()) { Serial.println("Could not find a valid BME280 sensor, check wiring!"); while (1) { customSleep(100); } } Serial.println("Initialization finished succesfully"); delay(50); digitalWrite(peripherialsPowerPin, HIGH);}void loop() { updateSenzors(); transmitData(); customSleep(75); }void updateSenzors() { bme.begin(); lightMeter.begin(); delay(300); sensors.temperature =bme.readTemperature(); sensors.pressure =bme.readPressure() / 100.0F; sensors.humidity =bme.readHumidity(); sensors.light =lightMeter.readLightLevel(); sensors.voltage =readVcc(); sensors.rain =readRain();}void transmitData(){ emptyIncommingSerialBuffer(); Serial.print("Temp:");Serial.println(sensors.temperature); Serial.print("Humid:");Serial.println(sensors.humidity); Serial.print("Pressure:");Serial.println(sensors.pressure); Serial.print("Light:");Serial.println(sensors.light); Serial.print("Voltage:");Serial.println(sensors.voltage); Serial.print("Rain:");Serial.println(sensors.rain); transmitSenzorData("T", sensors.temperature); transmitSenzorData("H", sensors.humidity); transmitSenzorData("PS", sensors.pressure); transmitSenzorData("L", sensors.light); transmitSenzorData("V", sensors.voltage); transmitSenzorData("R", sensors.rain);}void emptyIncommingSerialBuffer(){ while (serialComm.available()> 0) { serialComm.read(); delay(5); }}void transmitSenzorData(String type, int value){ serialComm.print(type); serialComm.print(sensorsCode); serialComm.print(":"); serialComm.print(value); serialComm.print("|"); delay(50);}void customSleep(long eightSecondCycles){ if (sensors.voltage> voltageDeepSleepThreshold) { delay(eightSecondCycles * 8000); Retorna; } digitalWrite(peripherialsPowerPin, LOW); for (int i =0; i
Code snippet #5Plain text
cd weather-predict # create anaconda environment named "weather" with python 3.6.2conda create --name weather python=3.6.2 # activate environmentconda activate weather# install all packages pip install -r requirements.txt 
Code snippet #6Plain text
mongodb ={ 'host':'localhost', 'port':27017}...
Code snippet #10Plain text
conda activate weatherpython serial_listener.py# every 10 minutes you should see data from the weather station coming in :[Sensor:type(temperature), value(14.3)][Sensor:type(pressure), value(1056.0)]...
Code snippet #15Plain text
{ "_id" :"04_27_2017_06_17", "humidity" :65, "date" :ISODate("2017-04-27T06:17:18Z"), "pressure" :1007, "temperature" :9, "rain" :0, "light" :15}
Code snippet #16Plain text
client =MongoClient(host, port).weather.datapoints cursor =client.find( {'$and' :[ {'date' :{'$gte' :start_date}}, {'date' :{'$lte' :end_date}} ]} )data =list(cursor)..
Code snippet #18Plain text
_id date humidity light pressure rain temperature 04_27_2017_03_08 2017-04-27 03:08:36 67.0 0.0 1007.0 0.0 11.004_27_2017_03_19 2017-04-27 03:19:05 66.0 0.0 1007.0 0.0 11.004_27_2017_03_29 2017-04-27 03:29:34 66.0 0.0 1007.0 0.0 11.0 
Code snippet #22Plain text
_id has_rain humidity_10percentile humidity_30percentile humidity_70percentile humidity_90percentile ... temperature_steady_4 temperature_steady_5 temperature_steady_6 temperature_steady_7 temperature_steady_8 ... 04_27_2017_3 0 36.0 44.8 61.0 63.0 ... NaN NaN NaN NaN NaN04_28_2017_0 0 68.0 70.0 74.0 75.0 ... 14.0 NaN NaN NaN NaN04_28_2017_1 0 40.0 45.0 63.2 69.0 ... 20.0 14.0 NaN NaN NaN04_28_2017_2 0 34.0 35.9 40.0 41.0 ... 29.0 20.0 14.0 NaN NaN04_28_2017_3 0 36.1 40.6 52.0 54.0 ... 19.0 29.0 20.0 14.0 NaN04_29_2017_0 0 52.0 54.0 56.0 58.0 ... 26.0 19.0 29.0 20.0 14.004_29_2017_1 0 39.4 43.2 54.6 57.0 ... 18.0 26.0 19.0 29.0 20.004_29_2017_2 1 41.0 42.0 44.2 47.0 ... 28.0 18.0 26.0 19.0 29.0 
Code snippet #23Plain text
from sklearn.model_selection import train_test_split ...main_data, test_data =train_test_split(dataframe, test_size=percent_test_data) ...
Code snippet #25Plain text
from sklearn.preprocessing import StandardScalerfrom sklearn.externals import joblib..scaler =StandardScaler()X =scaler.fit_transform(X) ...# of course we should be careful to save the scaled model for later reusejoblib.dump(scaler, 'model_file_name.save') 
Code snippet #26Plain text
from keras.models import Sequentialfrom keras.layers import Densefrom keras.layers import Dropout ...input_dimensions =X.shape[1] optimizer ='rmsprop'dropout =0.05model =Sequential()inner_nodes =int(input_dimensions / 2)model.add(Dense(inner_nodes, kernel_initializer='uniform', activation='relu', input_dim=input_dimensions))model.add(Dropout(rate=dropout))model.add(Dense(inner_nodes, kernel_initializer='uniform', activation='relu'))model.add(Dropout(rate=dropout))model.add(Dense(1, kernel_initializer='uniform', activation='sigmoid'))model.compile(optimizer=optimizer, loss='mean_absolute_error', metrics=['accuracy']) model.fit(X, y, batch_size=1, epochs=50)...# save the model for later useclassifier.save('file_model_name')
Github
https://github.com/claws/BH1750https://github.com/claws/BH1750
Github
https://github.com/rocketscream/Low-Powerhttps://github.com/rocketscream/Low-Power
Github
https://github.com/adafruit/Adafruit_Sensorhttps://github.com/adafruit/Adafruit_Sensor
Github
https://github.com/adafruit/Adafruit_BME280_Libraryhttps://github.com/adafruit/Adafruit_BME280_Library
Github
https://github.com/danionescu0/home-automationhttps://github.com/danionescu0/home-automation

Esquemas

sketch_KAtDa2VReF.fzz
Weather station arduino sketch
https://github.com/danionescu0/home-automation/tree/master/arduino-sketches/weatherStation

Processo de manufatura

  1. Sensor de temperatura Python e Raspberry Pi
  2. Console de edição DIY Photoshop usando Arduino Nano RP 2040
  3. Python Renomear Arquivo e Diretório usando os.rename()
  4. Sistema de atendimento usando Arduino e RFID com Python
  5. Controle remoto universal usando Arduino, 1Sheeld e Android
  6. Faça você mesmo voltímetro usando Arduino e Smartphone
  7. Sensor de batimento cardíaco infravermelho DIY usando Arduino
  8. Medição de frequência e ciclo de trabalho usando Arduino
  9. Voltímetro DIY com Arduino e um visor Nokia 5110
  10. Sonar usando arduino e exibição no IDE de processamento