Introdução
Neste projeto, vamos prever a possibilidade de chuva com base em dados climáticos de temperatura, umidade e pressão atmosférica. Utilizaremos uma API para coletar dados históricos do clima, que servirão como base para treinar um modelo de classificação. Depois, esse modelo será implementado na Franzininho WiFi, permitindo a previsão local a partir de novas leituras.
Visual Crossing Weather
Para coletar dados históricos utilizaremos a API da plataforma visual crossing weather. No plano gratuito, é possível realizar até 1000 requisições de dados por dia, o que será mais do que suficiente para as necessidades deste projeto. Pretendemos fazer requisições para obter dados de um período de 365 dias para treino e de 73 dias (20% de 365) para teste.
Essa api nos fornece diversas informações climáticas ao realizar a requisição.
Para mais informações acesse a documentação: documentação
Código Python: Requisição à API Visual Crossing Weather e Preparação dos Dados
- Criar Conta na Visual Crossing Weather: Acesse o site da Visual Crossing Weather e crie uma conta. Em seguida, vá até a página de dados históricos, filtre pela data e cidade que deseja obter as informações e, na aba “API”, selecione o formato de saída como JSON. Copie a URL gerada, pois ela será usada no código Python para realizar as requisições.
- Abra o Google Colab: Para executar o código Python sem a necessidade de configurar o ambiente em sua máquina pessoal, você pode utilizar o Google Colab, uma plataforma online gratuita que já vem com todas as bibliotecas necessárias. Caso prefira, também pode rodar o código localmente em seu próprio ambiente.
- Execute o código abaixo para dados de 365 dias:
import requests
import csv
from io import StringIO
from datetime import datetime
condicao_map = {
"Clear": 0,
"Partially cloudy": 1,
"Cloudy": 2,
"Rain": 3,
"Overcast": 4,
"Rain, Partially cloudy": 5,
"Rain, Overcast": 6,
"Thunderstorm": 7,
"Fog": 8,
"Snow": 9,
"Sleet": 10,
"Hail": 11,
"Windy": 12,
"Clear, Windy": 13,
"Cloudy, Windy": 14,
"Partially cloudy, Windy": 15,
# Adicione outras condições conforme necessário
}
# URL da API para dados históricos dos últimos 365 dias
#url = f"sua-url-aqui"
# Fazendo a requisição para a API
response = requests.get(url)
# Verificando se a requisição foi bem-sucedida
if response.status_code == 200:
# Parseando o conteúdo da resposta JSON
data = response.json()
# Nome do arquivo CSV de saída
csv_filename = 'dados_clima_historico.csv'
# Campos que queremos salvar no CSV
fields = ["timestamp", "temperatura", "sensacao", "umidade", "pressao", "condicao"]
# Abre (ou cria) o arquivo CSV e escreve os dados filtrados
with open(csv_filename, mode='w', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
writer.writerow(fields) # Escreve o cabeçalho no CSV
# Itera sobre os itens do JSON e escreve os campos filtrados no CSV
for day in data['days']:
data_dia = day['datetime']
# Converte a data para timestamp
timestamp = int(datetime.strptime(data_dia, '%Y-%m-%d').timestamp())
temperatura = day.get('temp', 'N/A')
sensacao = day.get('feelslike', 'N/A')
umidade = day.get('humidity', 'N/A')
pressao = day.get('pressure', 'N/A')
condicao_climatica = day.get('conditions', 'N/A')
# Mapear condicao_climatica para um valor numérico
condicao_num = condicao_map.get(condicao_climatica, 0) # Se não for encontrada, usa 0
# Escreve uma linha de dados filtrados no CSV
writer.writerow([timestamp, temperatura, sensacao, umidade, pressao, condicao_num])
print(f"Dados filtrados salvos com sucesso no arquivo {csv_filename}")
else:
print(f"Falha ao obter dados: {response.status_code}")
Este código começa importando algumas bibliotecas, sendo a requests para fazer a requisição HTTP, csv para manipular arquivos CSV, StringIO para lidar com strings como fluxos de dados, e datetime para manipular datas. Em seguida, define um dicionário (condicao_map) que mapeia condições climáticas como “Clear” ou “Rain” para valores numéricos, que serão usados posteriormente. Depois, o código realiza a requisição à API usando uma URL predefinida. Se a requisição for bem-sucedida (código de status 200), os dados retornados no formato JSON são processados e extraídos as informações de data, temperatura, sensação térmica, umidade, pressão e condição climática para cada dia, lembrando que a data é convertida no em timestamp pois a plataforma edge impulse aceita apenas nesse formato. Por fim, as condições climáticas são convertidas em valores numéricos usando o dicionário condicao_map e então escritos em um arquivo CSV, com as colunas correspondentes aos campos extraídos. Caso a requisição falhe, o código imprime o código de erro retornado pela API.
- Modifique o código anterior para pegar dados históricos do clima de 73 dias e modifique o nome do csv que será salvo, esses dados serão usados para teste na criação do nosso modelo:
import requests
import csv
from io import StringIO
from datetime import datetime
condicao_map = {
"Clear": 0,
"Partially cloudy": 1,
"Cloudy": 2,
"Rain": 3,
"Overcast": 4,
"Rain, Partially cloudy": 5,
"Rain, Overcast": 6,
"Thunderstorm": 7,
"Fog": 8,
"Snow": 9,
"Sleet": 10,
"Hail": 11,
"Windy": 12,
"Clear, Windy": 13,
"Cloudy, Windy": 14,
"Partially cloudy, Windy": 15,
# Adicione outras condições conforme necessário
}
# URL da API para dados históricos dos últimos 365 dias
#url = f"nova-url-73-dias"
# Fazendo a requisição para a API
response = requests.get(url)
# Verificando se a requisição foi bem-sucedida
if response.status_code == 200:
# Parseando o conteúdo da resposta JSON
data = response.json()
# Nome do arquivo CSV de saída
csv_filename = 'dados_clima_historico_teste.csv'
# Campos que queremos salvar no CSV
fields = ["timestamp", "temperatura", "sensacao", "umidade", "pressao", "condicao"]
# Abre (ou cria) o arquivo CSV e escreve os dados filtrados
with open(csv_filename, mode='w', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
writer.writerow(fields) # Escreve o cabeçalho no CSV
# Itera sobre os itens do JSON e escreve os campos filtrados no CSV
for day in data['days']:
data_dia = day['datetime']
# Converte a data para timestamp
timestamp = int(datetime.strptime(data_dia, '%Y-%m-%d').timestamp())
temperatura = day.get('temp', 'N/A')
sensacao = day.get('feelslike', 'N/A')
umidade = day.get('humidity', 'N/A')
pressao = day.get('pressure', 'N/A')
condicao_climatica = day.get('conditions', 'N/A')
# Mapear condicao_climatica para um valor numérico
condicao_num = condicao_map.get(condicao_climatica, 0) # Se não for encontrada, usa 0
# Escreve uma linha de dados filtrados no CSV
writer.writerow([timestamp, temperatura, sensacao, umidade, pressao, condicao_num])
print(f"Dados filtrados salvos com sucesso no arquivo {csv_filename}")
else:
print(f"Falha ao obter dados: {response.status_code}")
Se tudo correr conforme o esperado, você terá dois arquivos: dados_clima_historico.csv e dados_clima_historico_teste.csv com as colunas timestamp, temperatura, sensacao, umidade, pressao, condicao.
- Com base nos dados obtidos, vamos dividir o arquivo CSV em duas categorias: dados sem chuva (condições com os números 0, 1, 2 e 4) e dados com chuva (condições 3, 5 e 6). Essa divisão será aplicada tanto aos conjuntos de treino quanto aos de teste. Se estivéssemos lidando com dados climáticos de uma cidade onde neva, por exemplo, poderíamos criar outras categorias, como neve, sol e chuva, adaptando o modelo conforme as condições específicas do local. Execute o código python abaixo:
# Nome do arquivo CSV de entrada
csv_filename = 'dados_clima_historico.csv'
# Lê o CSV de entrada
with open(csv_filename, mode='r', newline='', encoding='utf-8') as file:
reader = csv.reader(file)
header = next(reader) # Lê o cabeçalho
# Remove a coluna "condição" do cabeçalho
header_sem_condicao = header[:-1]
# Dicionário para armazenar as linhas separadas por condição
condicoes = {}
# Itera sobre as linhas do arquivo CSV
for row in reader:
condicao = row[-1] # A última coluna é a condição numérica
# Agrupando as condições 0, 1, 4, 2 e 3, 5, 6 em arquivos separados
if condicao in ["0", "1", "2", "4"]: # Clear, Partially cloudy e Overcast
key = "No_Rain"
elif condicao in ["3", "5", "6"]: # Rain
key = "Rain"
else: # Outras condições
key = "Others"
# Se a condição ainda não estiver no dicionário, cria uma lista vazia
if key not in condicoes:
condicoes[key] = []
# Remove a última coluna (condição) e adiciona a linha à lista da condição correspondente
condicoes[key].append(row[:-1])
# Para cada condição no dicionário, cria um arquivo CSV separado
for condicao, linhas in condicoes.items():
# Nome do arquivo CSV de saída para essa condição
output_filename = f'{condicao.replace(" ", "_")}.csv'
# Escreve os dados no arquivo CSV correspondente
with open(output_filename, mode='w', newline='', encoding='utf-8') as output_file:
writer = csv.writer(output_file)
writer.writerow(header_sem_condicao) # Escreve o cabeçalho sem a coluna "condição"
writer.writerows(linhas) # Escreve as linhas sem a coluna "condição"
print(f"Arquivo {output_filename} criado com sucesso!")
Para os dados teste mude o nome do csv_filename para ‘dados_clima_historico_teste.csv’ e a key para No_Rain_teste e Rain_teste. Se tudo correr conforme o esperado, você terá os seguintes arquivos.
- Faça o download desses 4 arquivos: No_Rain, No_Rain_teste, Rain e Rain_teste.
Criação do modelo na Edge Impulse
- Crie um novo projeto e depois clique em “add existing data”. Você será encaminhado para a página de upload.
- Faça o upload dos arquivos “Rain.csv” e “No_Rain.csv” para o conjunto de treino, e os arquivos “Rain_teste.csv” e “No_Rain_teste.csv” para o conjunto de teste.
- Agora, ao acessar a seção de Data Acquisition, você verá os arquivos que foram enviados. Nos arquivos de teste, renomeie a label para que corresponda exatamente à mesma usada nos arquivos de treino.
- Em Impulse Design, clique em Create Impulse. Vamos testar inicialmente as seguintes configurações:
- No Input Block, selecione Time Series Data
- No Processing Block, escolha Raw Data
- No Learning Block, selecione Classification
- Salve o Impulso
- Clique em “Raw data” e gere as features.
- Clique em Classifier para iniciar o treinamento. Notaremos que o modelo resultante não apresenta uma acurácia muito alta. Por isso, vamos testar uma nova configuração de impulso para tentar melhorar os resultados.
- Volte para o Create Impulse. Exclua o Processing e Learning block. Tentaremos utilizar o seguinte:
- No Processing Block, escolha Flatten
- No Learning Block, selecione Classification
- Clique em “Flatten” e gere as features.
Nesse tipo de processamento, diferentemente do Raw Data, os dados de entrada são transformados em um único vetor. E, para cada dado, são geradas várias medidas estatísticas, como máximo, mínimo, média, curtose e assimetria. No contexto de dados climáticos, o Flatten pode ser útil por agregar informações ao calcular essas estatísticas que auxiliam ao destacar padrões relevantes, como a variação ou tendência ao longo do tempo, que podem ser indicativos de eventos como chuva.
- Clique em Classifier e treine o modelo novamente. Observamos que a acurácia do modelo aumentou em quase 50% em comparação à versão anterior, o que é um ótimo resultado. O modelo está acertando 100% quando não há chuva, mas comete alguns erros em situações onde há previsão de chuva.
- Clique em “Retrain model” para realizar o treinamento novamente.
- Agora vamos testar o modelo com os arquivos de teste. Clique em “Model testing” e rode o teste. Os resultados obtidos foram ótimos, chegando a uma accuracy de 97% para os dados de teste.
- Por fim, vamos salvar o modelo para utilizá-lo posteriormente na Franzininho WiFi. Clique em Deployment, selecione Arduino Library e execute o deploy, que gerará um arquivo zip contendo o modelo.
Conclusão
Nesta primeira parte do projeto, abordamos a coleta e o pré-processamento de dados climáticos utilizando a API da Visual Crossing Weather. A partir de dados históricos de temperatura, umidade e pressão atmosférica, treinamos um modelo de classificação que permitirá prever a possibilidade de chuva em um determinado dia. Na próxima parte, aplicaremos esse modelo na Franzininho WiFi.





