이번 글이 좀 늦었네요.
원래 테스트는 이미 작성했는데, 헤더파일끼리 충돌이 나서 원인을 찾느라 늦었습니다. ㅎㅎ
이번에는 드론 본체를 테스트 할수있는 소스를 작성해 보겠습니다.
<Servo.h>랑 <SoftwareSerial.h> 이 두개의 해더파일이 같은 타이머1을 사용해서
충돌이 발생합니다.
그래서 ESC모터 제어는 따로 작성했습니다.
Main Test Code
#include <Wire.h>
#include <SPI.h>
#include <SoftwareSerial.h>
#include "RF24.h"
#include "I2Cdev.h"
#include "TinyGPS.h"
#include "MPU6050.h"
#include "MS5611.h"
#include "Kalman.h"
#define Button 2
#define Motor_1 4
#define Motor_2 5
#define Motor_3 6
#define Motor_4 7
#define Servo_1 8
#define Servo_2 9
#define Servo_3 10
#define Servo_4 11
#define Tx 12
#define Rx 13
#define CE 22
#define CSN 23
#define LED_1 24
#define LED_2 25
#define LED_3 26
#define LED_4 27
#define LED_5 28
#define LED_6 29
#define LED_7 30
#define LED_8 31
#define LED_9 32
#define LED_10 33
#define LED_11 34
#define LED_12 35
#define LED_13 36
#define LED_14 37
#define LED_15 38
#define Buzzer 39
#define Cell_1 A1
#define Cell_2 A2
#define Cell_3 A3
#define Cell_4 A4
#define HMC5883L_DEFAULT_ADDRESS 0x1E
#define HMC5883L_RA_DATAX_H 0x03
#define HMC5883L_RA_DATAZ_H 0x05
#define HMC5883L_RA_DATAY_H 0x07
#define MIN_MOTOR_SPEED 1000
#define MAX_MOTOR_SPEED 1700
#define SEA_PRESSURE 102040
static byte address[2][6] = { "00000", "11111" };
char send_data[32];
char receive_data[32];
float Battery[4];
int16_t accX, accY, accZ;
int16_t gyroX, gyroY, gyroZ;
int16_t magX, magY, magZ;
double accXangle, accYangle;
double gyroXangle, gyroYangle;
double gyroXrate, gyroYrate;
double compAngleX, compAngleY;
double kalAngleX, kalAngleY;
double dt;
double alpha;
uint32_t timer;
// http://www.kma.go.kr/weather/observation/aws_table_popup.jsp
// 위치 검색후 sea_press에 입력
const float sea_press = 1015.4;
double altitude, pressure;
long temperature;
// 사용할 클래스 선언
RF24 nRF24(CE, CSN);
SoftwareSerial gps_Serial(Tx, Rx);
TinyGPS gps;
MPU6050 mpu6050 = MPU6050();
MS5611 ms5611 = MS5611();
Kalman kalmanX;
Kalman kalmanY;
void setup() {
Init();
}
void loop() {
// 하나씩 주석을 해제하면서 사용하세요.
//Buzzer_test();
//Button_test();
//LED_test();
//GPS_test();
//Check_test();
//nRF24_test();
//GY_test();
}
/****************************************************************************/
void Init() {
Serial.begin(9600);
// 부저
pinMode(Buzzer, OUTPUT);
// 버튼
pinMode(Button, INPUT_PULLUP);
// LED
pinMode(LED_1, OUTPUT);
pinMode(LED_2, OUTPUT);
pinMode(LED_3, OUTPUT);
pinMode(LED_4, OUTPUT);
pinMode(LED_5, OUTPUT);
pinMode(LED_6, OUTPUT);
pinMode(LED_7, OUTPUT);
pinMode(LED_8, OUTPUT);
pinMode(LED_9, OUTPUT);
pinMode(LED_10, OUTPUT);
pinMode(LED_11, OUTPUT);
pinMode(LED_12, OUTPUT);
pinMode(LED_13, OUTPUT);
pinMode(LED_14, OUTPUT);
pinMode(LED_15, OUTPUT);
// GPS
gps_Serial.begin(9600);
// 전압 측정
pinMode(Cell_1, INPUT);
pinMode(Cell_2, INPUT);
pinMode(Cell_3, INPUT);
pinMode(Cell_4, INPUT);
// nRF24L01
// RF24_PA_MIN < RF24_PA_LOW < RF24_PA_HIGH < RF24_PA_MAX
// RF24_PA_MAX로 해주려면 10uF를 연결해주는 것이 좋다.
nRF24.begin();
nRF24.setPALevel(RF24_PA_MIN);
nRF24.openReadingPipe(1, address[1]);
nRF24.openWritingPipe(address[0]);
nRF24.startListening();
// GY-86
mpu6050.initialize();
ms5611.reset();
if(!mpu6050.testConnection()) {
Serial.println("MPU6050 Error!");
return 0;
}
if(!ms5611.begin(MS5611_ULTRA_HIGH_RES)) {
Serial.println("MS5611 Error!");
return 0;
}
mpu6050.setI2CMasterModeEnabled(false);
mpu6050.setI2CBypassEnabled(true);
Wire.beginTransmission(HMC5883L_DEFAULT_ADDRESS);
Wire.write(0x02);
Wire.write(0x00); // 연속 읽기 모드로 설정
Wire.endTransmission();
delay(5);
Wire.beginTransmission(HMC5883L_DEFAULT_ADDRESS);
Wire.write(0x00);
Wire.write(B00011000); // 75Hz
Wire.endTransmission();
delay(5);
mpu6050.setI2CBypassEnabled(false);
mpu6050.setSlaveAddress(0, HMC5883L_DEFAULT_ADDRESS | 0x80);
mpu6050.setSlaveRegister(0, HMC5883L_RA_DATAX_H);
mpu6050.setSlaveEnabled(0, true);
mpu6050.setSlaveWordByteSwap(0, false);
mpu6050.setSlaveWriteMode(0, false);
mpu6050.setSlaveWordGroupOffset(0, false);
mpu6050.setSlaveDataLength(0, 2);
mpu6050.setSlaveAddress(1, HMC5883L_DEFAULT_ADDRESS | 0x80);
mpu6050.setSlaveRegister(1, HMC5883L_RA_DATAY_H);
mpu6050.setSlaveEnabled(1, true);
mpu6050.setSlaveWordByteSwap(1, false);
mpu6050.setSlaveWriteMode(1, false);
mpu6050.setSlaveWordGroupOffset(1, false);
mpu6050.setSlaveDataLength(1, 2);
mpu6050.setSlaveAddress(2, HMC5883L_DEFAULT_ADDRESS | 0x80);
mpu6050.setSlaveRegister(2, HMC5883L_RA_DATAZ_H);
mpu6050.setSlaveEnabled(2, true);
mpu6050.setSlaveWordByteSwap(2, false);
mpu6050.setSlaveWriteMode(2, false);
mpu6050.setSlaveWordGroupOffset(2, false);
mpu6050.setSlaveDataLength(2, 2);
mpu6050.setI2CMasterModeEnabled(true);
delay(50);
}
/****************************************************************************/
void Button_test() {
Serial.print(digitalRead(Button));
delay(200);
}
/****************************************************************************/
void Buzzer_test() {
tone(Buzzer, 2000, 200);
delay(1000);
}
/****************************************************************************/
void LED_test() {
digitalWrite(LED_1, HIGH);
digitalWrite(LED_2, HIGH);
digitalWrite(LED_3, HIGH);
digitalWrite(LED_4, HIGH);
digitalWrite(LED_5, HIGH);
digitalWrite(LED_6, HIGH);
digitalWrite(LED_7, HIGH);
digitalWrite(LED_8, HIGH);
digitalWrite(LED_9, HIGH);
digitalWrite(LED_10, HIGH);
digitalWrite(LED_11, HIGH);
digitalWrite(LED_12, HIGH);
digitalWrite(LED_13, HIGH);
digitalWrite(LED_14, HIGH);
digitalWrite(LED_15, HIGH);
delay(500);
digitalWrite(LED_1, LOW);
digitalWrite(LED_2, LOW);
digitalWrite(LED_3, LOW);
digitalWrite(LED_4, LOW);
digitalWrite(LED_5, LOW);
digitalWrite(LED_6, LOW);
digitalWrite(LED_7, LOW);
digitalWrite(LED_8, LOW);
digitalWrite(LED_9, LOW);
digitalWrite(LED_10, LOW);
digitalWrite(LED_11, LOW);
digitalWrite(LED_12, LOW);
digitalWrite(LED_13, LOW);
digitalWrite(LED_14, LOW);
digitalWrite(LED_15, LOW);
delay(500);
}
/****************************************************************************/
void GPS_test() {
while(gps_Serial.available()) {
Serial.write(gps_Serial.read());
}
}
/****************************************************************************/
void Check_test() {
Battery[0] = (analogRead(Cell_1) * 25.0) / 1024.0;
Battery[1] = (analogRead(Cell_2) * 25.0) / 1024.0 - Battery[0];
Battery[2] = (analogRead(Cell_3) * 25.0) / 1024.0 - Battery[0] - Battery[1];
Battery[3] = (analogRead(Cell_4) * 25.0) / 1024.0 - Battery[0] - Battery[1] - Battery[2];
Serial.print(Battery[0]);
Serial.print(" V\t");
Serial.print(Battery[1]);
Serial.print(" V\t");
Serial.print(Battery[2]);
Serial.print(" V\t");
Serial.print(Battery[3]);
Serial.println(" V");
delay(200);
}
/****************************************************************************/
void nRF24_test() {
if(nRF24.available()) {// 송수신기가 정상적이면
// 데이터 받기
nRF24.read(&receive_data, sizeof(receive_data));
if(strlen(receive_data) > 0) { //문자를 받으면
Serial.print("받음 : ");
Serial.println(receive_data);
}
}
if(!digitalRead(Button)) { // 버튼1을 누르면
nRF24.stopListening(); // 수신모드를 정지
strcpy(send_data, "Main");
if(nRF24.write(send_data, sizeof(send_data))) { // 데이터 보내기
Serial.print("보냄 : ");
Serial.println(send_data);
}
else Serial.println("fail");
nRF24.startListening(); // 다시 수신모드
}
delay(50);
}
/****************************************************************************/
void GY_test() {
mpu6050.getMotion6(&accX, &accY, &accZ, &gyroX, &gyroY, &gyroZ);
magX = mpu6050.getExternalSensorWord(0);
magY = mpu6050.getExternalSensorWord(2);
magZ = mpu6050.getExternalSensorWord(4);
temperature = ms5611.readTemperature(true);
pressure = ms5611.readPressure(true);
altitude = ms5611.getAltitude(pressure, SEA_PRESSURE);
dt = (double)(micros() - timer) / 1000000;
timer = micros();
accXangle = (atan2(accY, accZ)) * RAD_TO_DEG;
accYangle = (atan2(accX, accZ)) * RAD_TO_DEG;
gyroXrate = (double)gyroX / 131.0;
gyroYrate = -((double)gyroY / 131.0);
gyroXangle += gyroXrate * dt;
gyroYangle += gyroYrate * dt;
alpha = 1.0 / (1.0 + dt);
//compAngleX = (0.93 * (compAngleX + (gyroXrate * dt))) + (0.07 * accXangle);
//compAngleY = (0.93 * (compAngleY + (gyroYrate * dt))) + (0.07 * accYangle);
compAngleX = (alpha * (compAngleX + (gyroXrate * dt))) + ((1.0 - alpha) * accXangle);
compAngleY = (alpha * (compAngleY + (gyroYrate * dt))) + ((1.0 - alpha) * accYangle);
kalAngleX = kalmanX.getAngle(accXangle, gyroXrate, dt);
kalAngleY = kalmanY.getAngle(accYangle, gyroYrate, dt);
// 가공하지 않은 데이터
/*
Serial.print("Ax : ");Serial.print(ax);
Serial.print("\tAy : ");Serial.print(ay);
Serial.print("\tAz : ");Serial.println(az);
Serial.print("\t|\tGx :\t");Serial.print(gx);
Serial.print("\tGy :\t");Serial.print(gy);
Serial.print("\tGz :\t");Serial.print(gz);
Serial.print("\t|\tMx :\t");Serial.print(mx);
Serial.print("\tMy :\t");Serial.print(my);
Serial.print("\tMz :\t");Serial.print(mz);
Serial.print("\t|\t온도 :\t");Serial.print(temperature);
Serial.print("\t기압 :\t");Serial.print(pressure);
Serial.print("\t고도 :\t");Serial.println(altitude);
*/
// 가공된 데이터
Serial.print("accX : ");
Serial.print(kalAngleX);
Serial.print("\taccY : ");
Serial.println(kalAngleY);
//Serial.println(alpha);
delay(100);
}
/****************************************************************************/
1. GPS_test()
GPS테스트를 실행시키면
$GPRMC,--------------
$GPVTG,-------------
$GPGGA,-------------
$GPGSA,-------------
$GPGSV,-------------
$GPGSV,-------------
$GPGSV,-------------
$GPGLL,-------------
이러한 형식으로 나오는것을 볼수 있습니다.
요기서 3번째 줄인 "$GPGGA---"를 아래에 사이트에 입력해주면 현재 위치를 알수 있습니다.
http://www.gonmad.co.uk/nmea.php
NMEA GPS data to GoogleMaps display
GPS NMEA data to Google Map converter (v4.0) This page allows you to paste captured raw NMEA data from your GPS device, and generates a Google Map with track markers from that data. The conversion is all performed by JavaScript and is therefore all client
www.gonmad.co.uk
"NMEA Data Capture" 칸에 "$GPGGA---"를 넣어주고 "Filter again"을 눌러줍니다.
그리고 나서 "Mapping"을 눌러주면 아래에 위도와 경도가 나오는데 이것을 "위도,경도" 형식으로 구글지도에 검색을하면 정확한 위치가 나오게 됩니다.
2. GY_test()
GY_test()를 실행시키면
센서로부터 받아온 값을 가공하고 이를 다시 칼만 필터에 넣어주었습니다.
칼만 필터란?
https://ko.wikipedia.org/wiki/%EC%B9%BC%EB%A7%8C_%ED%95%84%ED%84%B0
칼만 필터 - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. 칼만 필터(Kalman filter)는 잡음이 포함되어 있는 측정치를 바탕으로 선형 역학계의 상태를 추정하는 재귀 필터로, 루돌프 칼만이 개발하였다. 칼만 필터는 컴퓨터 비전, 로봇 공학, 레이다 등의 여러 분야에 사용된다. 칼만 필터는 과거에 수행한 측정값을 바탕으로 현재의 상태 변수의 결합분포를 추정한다. 알고리즘은 예측과 업데이트의 두 단계로 이루어진다. 예측 단계에서는 현재 상태 변수의 값과 정확도를 예측한다. 현
ko.wikipedia.org
3. nRF24_test()
송수신 테스트는 컨트롤러와 본체 둘다 있어야 가능한 테스트입니다.
본체와 컨트롤러에 전원을 넣어주고
본체의 버튼, 컨트롤러의 1번 버튼을 눌러주면 시리얼 모니터를 통해 서로 전송이 되는것을 볼수 있습니다.
혹시 본 소스에대해 이해가 되지 않는 분들은 댓글 달아주세요.
'프로젝트 > DIY 드론 만들기' 카테고리의 다른 글
DIY드론 프로젝트 - BLDC모터 제어 (2) | 2020.04.25 |
---|---|
DIY드론 프로젝트[5] - Controller Test Code (0) | 2020.04.13 |
DIY드론 프로젝트[4] - PCB도착 (0) | 2020.04.05 |
DIY드론 프로젝트[3] - PCB제작(2) (0) | 2020.03.24 |
DIY드론 프로젝트[3] - PCB제작(1) (0) | 2020.03.24 |