개발자/Arduino

Arduino 와 Processing 나침반 만들기

지구빵집 2021. 11. 11. 14:37
반응형

 

 

이 Arduino 프로젝트에서는 Arduino, MEMS 자력계 및 Processing IDE를 사용하여 멋진 모양의 나침반을 만드는 방법을 알아봅니다. 다음은 나침반 데모 비디오입니다. 이전 포스팅과 마찬가지로 우리는 Nano 33 BLE Sense 보드를 사용해서 프로그래밍을 합니다. 따라서 작성한 코드를 표시하고 이 코드를 Nano 33 BLE Sense 보드의 자기장 센서 Magnetomer에 맞도록 수정합니다. 마지막으로 Processing에서 자기장 데이터를 받아 데모를 진행합니다. 

 

이 프로젝트에 필요한 것은 지구 자기장 측정을위한 MEMS 자력계, Arduino 보드 및 일부 점퍼 와이어입니다. 예를 들어 GY–80 브레이크 아웃 보드에 통합된 3 축 자력계 인 HMC5883L을 사용합니다.

 

HMC5883L

 

Arduino 부품 먼저 I2C 프로토콜을 통해 Arduino 보드를 사용하여 센서에서 데이터를 가져와야합니다 . 그런 다음 센서의 X – 축 및 Y – 축 값을 사용하여 Heading을 계산하고 직렬 포트를 통해 Processing IDE에 값을 보냅니다. 다음 코드가 해당 작업을 수행합니다. 

 

/*   Arduino Compass 
 *      
 *  by Dejan Nedelkovski, 
 *  www.HowToMechatronics.com
 *  
 */
#include <Wire.h> //I2C Arduino Library
#define Magnetometer_mX0 0x03  
#define Magnetometer_mX1 0x04  
#define Magnetometer_mZ0 0x05  
#define Magnetometer_mZ1 0x06  
#define Magnetometer_mY0 0x07  
#define Magnetometer_mY1 0x08  
int mX0, mX1, mX_out;
int mY0, mY1, mY_out;
int mZ0, mZ1, mZ_out;
float heading, headingDegrees, headingFiltered, declination;
float Xm,Ym,Zm;
#define Magnetometer 0x1E //I2C 7bit address of HMC5883
void setup(){
  //Initialize Serial and I2C communications
  Serial.begin(115200);
  Wire.begin();
  delay(100);
  
  Wire.beginTransmission(Magnetometer); 
  Wire.write(0x02); // Select mode register
  Wire.write(0x00); // Continuous measurement mode
  Wire.endTransmission();
}
void loop(){
 
  //---- X-Axis
  Wire.beginTransmission(Magnetometer); // transmit to device
  Wire.write(Magnetometer_mX1);
  Wire.endTransmission();
  Wire.requestFrom(Magnetometer,1); 
  if(Wire.available()<=1)   
  {
    mX0 = Wire.read();
  }
  Wire.beginTransmission(Magnetometer); // transmit to device
  Wire.write(Magnetometer_mX0);
  Wire.endTransmission();
  Wire.requestFrom(Magnetometer,1); 
  if(Wire.available()<=1)   
  {
    mX1 = Wire.read();
  }
  //---- Y-Axis
  Wire.beginTransmission(Magnetometer); // transmit to device
  Wire.write(Magnetometer_mY1);
  Wire.endTransmission();
  Wire.requestFrom(Magnetometer,1); 
  if(Wire.available()<=1)   
  {
    mY0 = Wire.read();
  }
  Wire.beginTransmission(Magnetometer); // transmit to device
  Wire.write(Magnetometer_mY0);
  Wire.endTransmission();
  Wire.requestFrom(Magnetometer,1); 
  if(Wire.available()<=1)   
  {
    mY1 = Wire.read();
  }
  
  //---- Z-Axis
  Wire.beginTransmission(Magnetometer); // transmit to device
  Wire.write(Magnetometer_mZ1);
  Wire.endTransmission();
  Wire.requestFrom(Magnetometer,1); 
  if(Wire.available()<=1)   
  {
    mZ0 = Wire.read();
  }
  Wire.beginTransmission(Magnetometer); // transmit to device
  Wire.write(Magnetometer_mZ0);
  Wire.endTransmission();
  Wire.requestFrom(Magnetometer,1); 
  if(Wire.available()<=1)   
  {
    mZ1 = Wire.read();
  }
  
  //---- X-Axis
  mX1=mX1<<8;
  mX_out =mX0+mX1; // Raw data
  // From the datasheet: 0.92 mG/digit
  Xm = mX_out*0.00092; // Gauss unit
  //* Earth magnetic field ranges from 0.25 to 0.65 Gauss, so these are the values that we need to get approximately.
  //---- Y-Axis
  mY1=mY1<<8;
  mY_out =mY0+mY1;
  Ym = mY_out*0.00092;
  //---- Z-Axis
  mZ1=mZ1<<8;
  mZ_out =mZ0+mZ1;
  Zm = mZ_out*0.00092;
  // ==============================
  //Calculating Heading
  heading = atan2(Ym, Xm);
 
  // Correcting the heading with the declination angle depending on your location
  // You can find your declination angle at: https://www.ngdc.noaa.gov/geomag-web/
  // At my location it's 4.2 degrees => 0.073 rad
  declination = 0.073; 
  heading += declination;
  
  // Correcting when signs are reveresed
  if(heading <0) heading += 2*PI;
  // Correcting due to the addition of the declination angle
  if(heading > 2*PI)heading -= 2*PI;
  headingDegrees = heading * 180/PI; // The heading in Degrees unit
  // Smoothing the output angle / Low pass filter 
  headingFiltered = headingFiltered*0.85 + headingDegrees*0.15;
  //Sending the heading value through the Serial Port to Processing IDE
  Serial.println(headingFiltered);
  
  delay(50);
}

 

MEMS 자력계의 작동 방식과 데이터를 얻는 방법에 대한 자세한 내용이 필요하면 MEMS 센서 자습서를 확인하십시오 . 

 

위 코드를 Nano 33 BLE Sense 보드에서 동일한 출력을 내도록 코드를 작성하는 일이 우리가 원하는 일이었죠? 일단 예제 샘플을 불러오겠습니다. 파일 - 예제 - Arduino_LSM9DS1 - SimpleMagnetometer 파일을 엽니다.

 

/*
  Arduino LSM9DS1 - Simple Magnetometer
  The circuit:
  - Arduino Nano 33 BLE Sense
*/

#include <Arduino_LSM9DS1.h>

void setup() {
  Serial.begin(9600);
  while (!Serial);
  Serial.println("Started");

  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1);
  }
  Serial.print("Magnetic field sample rate = ");
  Serial.print(IMU.magneticFieldSampleRate());
  Serial.println(" uT");
  Serial.println();
  Serial.println("Magnetic Field in uT");
  Serial.println("X\tY\tZ");
}

void loop() {
  float x, y, z;

  if (IMU.magneticFieldAvailable()) {
    IMU.readMagneticField(x, y, z);

    Serial.print(x);
    Serial.print('\t');
    Serial.print(y);
    Serial.print('\t');
    Serial.println(z);
  }
}

 

코드에서 Heading 계산하는 부분을 추가해야 합니다. microTesla 판독 값을 0-360도 나침반 방향으로 변환하려면 atan2 () 함수를 사용하여 Y 및 X 축 판독 값으로 정의 된 벡터 각도를 계산할 수 있습니다. 결과는 라디안으로 표시되므로 180도를 곱하고 Pi로 나누어 각도로 변환합니다.(참고)

 

float heading = (atan2 (event.magnetic.y, event.magnetic.x) * 180) / Pi;

 

일단 읽는 값은 0~ 360도가 나오는데 동작은 정확하지 않기도 하고, 테스트를 잘 못하는지 도통 모드겠습니다. 일단 코드를 옮깁니다.

 

/*
  Arduino LSM9DS1 - Simple Magnetometer

  This example reads the magnetic field values from the LSM9DS1
  sensor and continuously prints them to the Serial Monitor
  or Serial Plotter.

  The circuit:
  - Arduino Nano 33 BLE Sense

  created 10 Jul 2019
  by Riccardo Rizzo

  This example code is in the public domain.
*/

#include <Arduino_LSM9DS1.h>

void setup() {
  Serial.begin(9600);
  while (!Serial);
  Serial.println("Started");

  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1);
  }
  Serial.print("Magnetic field sample rate = ");
  Serial.print(IMU.magneticFieldSampleRate());
  Serial.println(" uT");
  Serial.println();
  Serial.println("Magnetic Field in uT");
  Serial.println("X\tY\tZ");
}

float Pi = 3.14159;

void loop() {
  float x, y, z;

  if (IMU.magneticFieldAvailable()) {
    IMU.readMagneticField(x, y, z);

    /*Serial.print(x);
    Serial.print('\t');
    Serial.print(y);
    Serial.print('\t');
    Serial.println(z);*/

    float Pi = 3.14159;
    
    // Calculate the angle of the vector y,x
    float heading = (atan2(y, x) * 180) / Pi;

    // Normalize to 0-360
    if (heading < 0) {
      heading = 360 + heading;
    }
    Serial.println(heading);
  }
}

 

Processing 소스코드 부분

 

processing

 

여기서 먼저 직렬 포트에서 오는 Heading 값을 받아야 합니다. 이 작업을 수행하는 방법에 대한 자세한 내용은 Arduino 및 Processing Tutorial을 확인하십시오 . 나침반은 실제로 이미지이거나보다 정확하게는 Processing IDE에로드 된 여러 개의 투명한 이미지로 구성됩니다. 이미지는 스케치의 작업 디렉터리에 있습니다. image () 함수를 사용하여 draw () 섹션에서 이미지 객체를 정의한 후 배경 이미지를 로드합니다 (선택 사항이며 배경에 간단한 색상만 사용할 수 있음). 그런 다음 rotateZ () 함수를 사용하여 제목 값과 함께 회전되는 나침반 이미지가 로드됩니다. 그 위에 나침반 화살표 이미지가 로드됩니다. Processing IDE 코드는 다음과 같습니다.  

 

/*   Arduino Compass 
 *      
 *  by Dejan Nedelkovski, 
 *  www.HowToMechatronics.com
 *  
 */
 
import processing.serial.*;
import java.awt.event.KeyEvent;
import java.io.IOException;
Serial myPort;
PImage imgCompass;
PImage imgCompassArrow;
PImage background;
String data="";
float heading;
void setup() {
  size (1920, 1080, P3D);
  smooth();
  imgCompass = loadImage("Compass.png");
  imgCompassArrow = loadImage("CompassArrow.png");
  background = loadImage("Background.png");
  
  myPort = new Serial(this, "COM4", 115200); // starts the serial communication
  myPort.bufferUntil('\n');
}
void draw() {
  
  image(background,0, 0); // Loads the Background image
    
  pushMatrix();
  translate(width/2, height/2, 0); // Translates the coordinate system into the center of the screen, so that the rotation happen right in the center
  rotateZ(radians(-heading)); // Rotates the Compass around Z - Axis 
  image(imgCompass, -960, -540); // Loads the Compass image and as the coordinate system is relocated we need need to set the image at -960x, -540y (half the screen size)
  popMatrix(); // Brings coordinate system is back to the original position 0,0,0
  
  image(imgCompassArrow,0, 0); // Loads the CompassArrow image which is not affected by the rotateZ() function because of the popMatrix() function
  textSize(30);
  text("Heading: " + heading,40,40); // Prints the value of the heading on the screen
  delay(40);
  
}
// starts reading data from the Serial Port
 void serialEvent (Serial myPort) { 
  
   data = myPort.readStringUntil('\n');// reads the data from the Serial Port and puts it into the String variable "data".
  
  heading = float(data); // Convering the the String value into Float value
}

 

필요한 이미지와 Arduino소스코드 파일을 올려드립니다. 출처는 참고 문서 1번입니다.

 

Arduino_Compass.rar
0.37MB

 

 

참고

1. 번역하기 이전의 문서

 

 

 

 

 

 

 

반응형