quinta-feira, 4 de setembro de 2025

Coleta e processamento de dados IMU

Untitled
Os dois códigos a seguire serão importante quando na leitura dos dados brutos (raw data) advindos diretamente do meu sistema IMU quando em calibração. A coleta de dados para calibração ocorre de maneira remota (bluetooth) empregando um aplicativo desenvolvido por mim utilizando o App Inventor, tarefa facilitada devido a facilitação em contruir aplicativos por meio de blocos (linguagem de alto nível). A figura abaixo mostra a primeira e única página do app. No caso, faz-se necessário conectar com o telefone, via appp, ao dispositivo ESP32. Tem que se ativar o bluetooth Em caso de sucesso a aba de conexão indica o Status na cor verde. A partir desse ponto, é só selecionar a coleta de dados que se deseja fazer e voilà.
O código embarcado no ESP32 é um pouco mais complicado, roda em tempo real por meio do freeRTOS. Ele utiliza algumas classes desenvolvidas: a do sensor inercial e a do cartão SD. Existem algumas tasks que realizam a coleta e gravação dos dados. Tudo, conforme dito anteriormente, é organizado por meio do controle por bluetooth (recepção de dados, processamento e execução de atividades desejadas). Após a realização da coleta em cada uma das posições próprias para a calibração, um arquivo do tipo '*.txt' é gerado. Esse arquivo, do jeito que foi construída a sua arquitetura, encontra-se numa situação que não passível de leitura rápido e fácil. Todos os dados estão organizados em formato uint8_t. De modo a poder facilitar a gravação dos dados no cartão SD durante as posições de calibração,
// (*) Parâmetros dos sensores inerciais
typedef struct imu_base{
float ax;
float ay;
float az;
float gx;
float gy;
float gz;
float temperature;
} IMU_base_t;
 
 
// (*) Para armazenamento de dados advindos do MPU6050
// (*) Essa estrutura possui o tamanho de 32 bytes,
// desse modo, consegue ser armazenada no buffer
// de 512 bytes 16 vezes em sequência.
typedef struct imu_to_sd{
float time;
IMU_base_t sensor;
} IMU_t;
Cada rodada de leitura de dados capta 7 tipos de dados: 3 dos acelerômetros, 3 do giros e 1 de temperatura. Foi inserido o parâmetro de tempo para que outras características futuras sejam exploradas (coisa lá para frente e que será abordado oportunamente). A coleta de dados ocorre a cada 10ms. Cada dado coletado tem a dimensão de 4 bytes (composição de float). Como são 8 tipos de dados por rodada, serão 32 bytes por leitura. De modo a facilitar a gravação em blocos no cartão SD, existe um buffer embarcado de dimensão 512 bytes. A cada coleta, faz-se o preenchimento desse buffer com 32 bytes. Quando completado o preenchimento desse buffer, envia esses dados para uma queue no freeRTOS, que faz o gerenciamento para a gravação no cartão SD. Ao término da coleta de dados, gera-se um arquivo '*.txt'.
O que se faz, então, é utilizar o código a seguir para ler os arquivos para cada situação da calibração (six-positions) e transformar os dados inintelegíveis em informações úteis (unidades de engenharia). Ao término do programa, um arquivo em formato '*.mat' é gerado. Será, então, a partir da manipulação desse arquivo novo '.mat' gerado que serão realizados os demais trabalhos de engenharia. Maiores detalhes ficam para um outro post.
clear all; close all; clc;
 
% ===================================
% Seletor de arquivo
% ===================================
[fileName, pathLocation] = uigetfile('*.txt');
if (isempty(fileName))
print("Arquivo com problema");
else
ID = fopen(fileName, 'rb');
data = fread(ID);
fclose(ID);
end
 
% ===================================
% Quantidade de blocos de 512 bytes
% ===================================
nBlocks = floor(length(data)/512);
 
% ===================================
% Alocando memória
% ===================================
time = zeros(nBlocks * 16,1);
ax = zeros(nBlocks * 16,1);
ay = zeros(nBlocks * 16,1);
az = zeros(nBlocks * 16,1);
gx = zeros(nBlocks * 16,1);
gy = zeros(nBlocks * 16,1);
gz = zeros(nBlocks * 16,1);
temp = zeros(nBlocks * 16,1);
 
% ===================================
% Ler cada bloco e processa
% ===================================
cont = 0;
for i = 1:nBlocks
for j = 1:16
% Índice inicial da mensagem
K = (i-1)*512 + (j-1)*32;
 
% Extrair os 4 bytes de cada variável
time_bytes = data(K + 1 : K + 4);
ax_bytes = data(K + 5 : K + 8);
ay_bytes = data(K + 9 : K + 12);
az_bytes = data(K + 13: K + 16);
gx_bytes = data(K + 17: K + 20);
gy_bytes = data(K + 21: K + 24);
gz_bytes = data(K + 25: K + 28);
temp_bytes = data(K + 29: K + 32);
 
% Atualiza o contador
cont = cont + 1;
 
% Converte os bytes para float (single)
time(cont) = typecast(uint8(time_bytes), 'single');
ax(cont) = typecast(uint8(ax_bytes), 'single');
ay(cont) = typecast(uint8(ay_bytes), 'single');
az(cont) = typecast(uint8(az_bytes), 'single');
gx(cont) = typecast(uint8(gx_bytes), 'single');
gy(cont) = typecast(uint8(gy_bytes), 'single');
gz(cont) = typecast(uint8(gz_bytes), 'single');
temp(cont) = typecast(uint8(temp_bytes), 'single');
end
end
 
 
% ===================================
% Salva em dados de engenharia
% ===================================
saveName = split(fileName, ".");
save([saveName{1},'.mat'], 'time', 'ax', 'ay', 'az', 'gx', 'gy', 'gz');
disp('Leitura concluída!');
clear all;