본문 바로가기

개발자/Arduino

Arduino 와 Processing 나침반 만들기

반응형

 

 

이 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. 번역하기 이전의 문서

 

 

 

 

 

 

 

반응형

캐어랩 고객 지원

취업, 창업의 막막함, 외주 관리, 제품 부재!

당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약, 아이디어는 있지만 구현할 기술이 없는 막막함.

우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.

이제 고민을 멈추고, 캐어랩을 만나세요!

코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.

제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!

귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.

지난 30년 여정, 캐어랩이 얻은 모든 것을 함께 나누고 싶습니다.

카카오 채널 추가하기

카톡 채팅방에서 무엇이든 물어보세요

당신의 성공을 위해 캐어랩과 함께 하세요.

캐어랩 온라인 채널 바로가기

캐어랩