quarta-feira, janeiro 22, 2020

Arduino Nano 33 BLE - Sensor Inercial de 9 Eixos

Um ponto que eu considero curioso no Arduino Nano 33 BLE é a inclusão na placa de um sensor. Por um lado este sensor será inútil para várias aplicações, por outro lado possibilita fazer algo mais que simplesmente piscar um LED sem maiores complicações.

O sensor incluído é um "sensor inercial de 9 Eixos", vejamos o que é isso e como usá-lo.



O sensor utilizado é o LSM9D1 da ST (datasheet), que contém acelerômetro, giroscópio e bússola (magnetômetro), cada um deles capaz de medir nas três dimensões.


O acelerômetro fornece a força que está sendo aplicada no sensor (gravidade incluída). O giroscópio informa a velocidade com que o sensor está sendo rotacionado. O magnetômetro mede a intensidade do campo magnético.

O Getting Started do arduino.cc sugere usar a biblioteca oficial para acessar o LSM9D1, o gerenciador de bibliotecas nos oferece 5 opções.


Vejamos rapidamente o que estas opções fornecem:
  • Arduino: é a oficial mas é extremamente simples, retornando os valores dos sensores e não permite mudar as sensibilidades.
  • Adafruit: apesar da documentar dizer que foi projetada para o módulo LSM9D1 da Adafruit, deve funcionar com o Nano 33 BLE. É ligeiramente mais completa que a biblioteca oficial.
  • Melopero: além da leitura dos valores dos sensores, tem suporte a interrupção. Infelizmente no Nano 33 BLE os pinos de interrupção do LSM9D1 estão desconectados.
  • SmartEverything: mais ou menos equivalente à oficial.
  • Sparkfun: é a mais completa, com métodos para mudar a sensibilidade e métodos para medir e compensar o offset dos sensores.
Todas estas bibliotecas retornam somente os valores do sensores, ficando para a aplicação decidir o que significam. Em particular, retornam as intensidades de campo magnético e não o ângulo em direção ao norte.

Vamos fazer um teste rápido, enviando pela serial os valores X e Y dos sensores lidos pela biblioteca oficial:
/*
 * Teste simples do sensor inercial do Nano 33 BLE
 * Envia leitura dos sensores pela serial
 * 
 * Daniel Quadros
 */

#include <Arduino_LSM9DS1.h>

// Iniciação
void setup() {
  Serial.begin(9600);

  if (!IMU.begin()) {
    Serial.println("Falha ao iniciar a IMU!");
    while (1);
  }
}

// Laço principal
void loop() {
  float xAcc = 0.0, yAcc = 0.0, zAcc = 0.0;
  float xMag = 0.0, yMag = 0.0, zMag = 0.0;
  float xGir = 0.0, yGir = 0.0, zGir = 0.0;

  if (IMU.accelerationAvailable()) {
    IMU.readAcceleration(xAcc, yAcc, zAcc);
  }

  if (IMU.magneticFieldAvailable()) {
    IMU.readMagneticField(xMag, yMag, zMag);
  }
    
  if (IMU.gyroscopeAvailable()) {
    IMU.readGyroscope(xGir, yGir, zGir);
  }
  
  Serial.print (xMag);
  Serial.print (";");
  Serial.print (yMag);
  Serial.print (";");
  Serial.print (xAcc);
  Serial.print (";");
  Serial.print (yAcc);
  Serial.print (";");
  Serial.print (xGir);
  Serial.print (";");
  Serial.print (yGir);
  Serial.println ();
  delay(200);
}
O programa python abaixo, para ser executado em um micro, lê os valores enviados pelo Nano 33 BLE e os apresenta de forma gráfica. O programa foi testado com Python 3.6 (era o que tinha instalado no micro) e requer a instalação do pySerial. Uma dificuldade foi descobrir a escala a usar para os sensores. Os valores máximos retornados são +/-2000 graus/segundo para o giroscópio, +/-4G para a aceleração e +/-400 microTesla para o magnetômetro. O programa usa valores máximos menores para ficar mais visível os valores típicos.
import tkinter
import serial
import threading

from tkinter import Canvas
from tkinter import Label

top = tkinter.Tk()

canvBussola = Canvas(top, bg = "white", height = 300, width = 300)
canvAccel = Canvas(top, bg = "white", height = 300, width = 300)
canvGiro = Canvas(top, bg = "white", height = 300, width = 300)

lblBussola = Label(top, text="Bussola")
lblAccel = Label(top, text="Acelerometro")
lblGiro = Label(top, text="Giroscopio")

canvBussola.grid(row=0, column=0)
lblBussola.grid(row=1, column=0)
canvAccel.grid(row=0, column=1)
lblAccel.grid(row=1, column=1)
canvGiro.grid(row=0, column=2)
lblGiro.grid(row=1, column=2)

canvBussola.create_line(0, 150, 300, 150, fill="blue")
canvBussola.create_line(150, 0, 150, 300, fill="blue")
canvAccel.create_line(0, 150, 300, 150, fill="blue")
canvAccel.create_line(150, 0, 150, 300, fill="blue")
canvGiro.create_line(0, 150, 300, 150, fill="blue")
canvGiro.create_line(150, 0, 150, 300, fill="blue")

def read_from_port(ser):
    global running
    ptMag = None
    ptAcc = None
    ptGir = None
    while running:
        linha = ser.readline().decode()
        partes = linha.split(";")
        if len(partes) == 6:
           xMag = (float(partes[0])+60.0)/120.0*300.0
           yMag = 300.0 - (float(partes[1])+60.0)/120.0*300.0
           xAcc = (float(partes[2])+4.0)/8.0*300.0
           yAcc = 300.0 - (float(partes[3])+4.0)/8.0*300.0
           xGir = (float(partes[4])+1000.0)/2000.0*300.0
           yGir = 300.0 - (float(partes[5])+1000.0)/2000.0*300.0
           try:
               if not (ptMag is None):
                   canvBussola.delete(ptMag)
               ptMag = canvBussola.create_oval(xMag+2, yMag+2, xMag-2, yMag-2, fill="red")
               if not (ptAcc is None):
                   canvAccel.delete(ptAcc)
               ptAcc = canvAccel.create_oval(xAcc+2, yAcc+2, xAcc-2, yAcc-2, fill="red")
               if not (ptGir is None):
                   canvGiro.delete(ptGir)
               ptGir = canvGiro.create_oval(xGir+2, yGir+2, xGir-2, yGir-2, fill="red")
           except:
               print ("Fim")

ser = serial.Serial('COM7', 9600)
thread = threading.Thread(target=read_from_port, args=(ser,))
running = True
thread.start()
top.mainloop()
running = False

Nos testes deu para observar um offset bem grande nos valores retornados pelo magnetômetro.

Nenhum comentário: