자이로 센서 L3G4200D는 드론에서 채택되는 많이 보급된 저렴한 센서 중의 하나이다. 자이로 센서는 드론이 기울어지거나 회전할 경우에 각속도 즉 rad/sec 또는 deg/sec를 측정하는 센서로서 드론 비행제어에 있어서 대단히 중요한 센서이다.
이 센서를 시험하기 위한 많은 샘플 프로그램이 있으나 여기서는 YMFC-3D 아두이노 비행제어 보드 기반 드론 프로그램에서 사용했던 자이로 프로그램을 제시하기로 한다.
(참고 http://www.brokking.net/ymfc-3d_main.html)
실험 준비물로서는 아두이노 보드 1종과 L#G4200D 자이로센서 1점 및 사진에서 보는 것처럼 베어링 상면에 우드락 보드를 설치하고 자이로 센서를 거꾸로 눌러 테이핑하여 고정 후 높이 축 Z축을 중심으로 시계 및 반시계 방향으로 회전 시켜 보는 것이다.
L3G4200D 자이로 센서와 아두이노는 I2C인터페이스를 사용한다. 그러므로 SCL과 SDA 핀을 사용하는 것이 원칙이나 한편으로는 아날로그 데이터 핀인 A5와 A4 사용으로 대체한다. SCL과 SDA을 사용해도 문제 될 것은 없다. 하지만 다른 센서가 이 I2C인터페이스를 사용할 수도 있으므로 반드시 확인해야 할 필요가 있다. 이론적으로 1개 이상의 센서가 I2C인터페이스를 사용할 수도 있을 것이며 각각의 센서가 서로 다른 어드레스를 가진다면 문제될 것이 없겠으나 I2C인터페이스가 그렇게 신속하진 못하므로 연산 속도 면에서 다소 손해를 볼 수도 있을 것이다.
아두이노 업로딩 후 시리얼 모니터를 켜면 2000회 샘플링 및 캘리브레이션 하는 시간이 필요하며 그 이 후에 피치 롤 및 요 데이터가 모두 0으로 출력됨을 알 수 있다.
그 다음에 사진의 베어링 장치를 이용해서 바닥을 손으로 잡아 고정한 후 상면의 센서를 시계 방향과 반시계 방향으로 돌려주면 Z축 각속도가 출력됨을 확인할 수 가 있다. 단 피치와 롤은 그 값이 작아 거의 0임을 알 수 있다.
피피와 롤 방향도 베어링 실험 장치를 느리게 들었다 놓았다 하면 값이 출력 됨을 확인할 수 있다.
이프로그램은 YMFC-3D 드론 비행 제어 프로그램이므로 아두이노 우노에 적절한 출력 명령과 함께 비행제어 프로그램을 업로딩한 상태에서 드론을 움직여 봐도 확인이 가능할 것이다.
한편 전원 전압 조건이 5V 뿐만 아니라 3.3V 조건하에서도 자이로 센서가 잘 작동함을 알수 있다.
#include <Wire.h> //Include the Wire.h library
//Declaring variables
int cal_int;
unsigned long UL_timer;
double gyro_pitch, gyro_roll, gyro_yaw;
double gyro_roll_cal, gyro_pitch_cal, gyro_yaw_cal;
byte highByte, lowByte;
//Setup routine
void setup(){
Wire.begin(); //Start the I2C as master
// Serial.begin(9600); //Start the serial connetion @ 9600bps
Serial.begin(115200);// 9600에서 바꾸었음
//The gyro is disabled by default and needs to be started
Wire.beginTransmission(105)//Start communication with the gyro (adress 1101001)
Wire.write(0x20); //We want to write to register 20
Wire.write(0x0F); /*Set the register bits as 00001111 (Turn on the gyro and enable all axis)*/
Wire.endTransmission(); //End the transmission with the gyro
Wire.beginTransmission(105); /*Start communication with the gyro (adress 1101001) */
Wire.write(0x23); //We want to write to register 23
Wire.write(0x80); /*Set the register bits as 10000000 (Block Data Update active) */
Wire.endTransmission(); //End the transmission with the gyro
delay(250); //Give the gyro time to start
//Let's take multiple samples so we can determine the average gyro offset
Serial.print("Starting calibration..."); //Print message
for (cal_int = 0; cal_int < 2000 ; cal_int ++){ //Take 2000 readings for calibration
gyro_signalen(); //Read the gyro output
gyro_roll_cal += gyro_roll; //Ad roll value to gyro_roll_cal
gyro_pitch_cal += gyro_pitch; //Ad pitch value to gyro_pitch_cal
gyro_yaw_cal += gyro_yaw; //Ad yaw value to gyro_yaw_cal
if(cal_int%100 == 0)Serial.print("."); //Print a dot every 100 readings
delay(4); //Wait 4 milliseconds before the next loop
}
/*Now that we have 2000 measures, we need to devide by 2000 to get the average gyro offset*/
Serial.println(" done!"); //2000 measures are done!
gyro_roll_cal /= 2000; //Divide the roll total by 2000
gyro_pitch_cal /= 2000; //Divide the pitch total by 2000
gyro_yaw_cal /= 2000; //Divide the yaw total by 2000
}
//Main program
void loop(){
delay(250); //Wait 250 microseconds for every loop
gyro_signalen(); //Read the gyro signals
print_output(); //Print the output
}
void gyro_signalen(){
Wire.beginTransmission(105); /*Start communication with the gyro (adress 1101001) */
Wire.write(168); /*Start reading @ register 28h and auto increment with every read */
Wire.endTransmission(); //End the transmission
Wire.requestFrom(105, 6); //Request 6 bytes from the gyro
while(Wire.available() < 6); //Wait until the 6 bytes are received
lowByte = Wire.read(); //First received byte is the low part of the angular data
highByte = Wire.read(); /*Second received byte is the high part of the angular data */
gyro_roll = ((highByte<<8)|lowByte); //Multiply highByte by 256 and ad lowByte
if(cal_int == 2000)gyro_roll -= gyro_roll_cal; /* only compensate after the calibration */
lowByte = Wire.read(); //First received byte is the low part of the angular data
highByte = Wire.read(); /*Second received byte is the high part of the angular data */
gyro_pitch = ((highByte<<8)|lowByte); //Multiply highByte by 256 and ad lowByte
gyro_pitch *= -1; //Invert axis
if(cal_int == 2000)gyro_pitch -= gyro_pitch_cal; /* only compensate after the calibration */
lowByte = Wire.read(); //First received byte is the low part of the angular data
highByte = Wire.read(); /* Second received byte is the high part of the angular data */
gyro_yaw = ((highByte<<8)|lowByte); //Multiply highByte by 256 and ad lowByte
gyro_yaw *= -1; //Invert axis
if(cal_int == 2000)gyro_yaw -= gyro_yaw_cal; /*Only compensate after the calibration */
}
void print_output(){
Serial.print("Pitch:");
if(gyro_pitch >= 0)Serial.print("+");
Serial.print(gyro_pitch/57.14286,0); //Convert to degree per second
if(gyro_pitch/57.14286 - 2 > 0)Serial.print(" NoU");
else if(gyro_pitch/57.14286 + 2 < 0)Serial.print(" NoD");
else Serial.print(" ---");
Serial.print(" Roll:");
if(gyro_roll >= 0)Serial.print("+");
Serial.print(gyro_roll/57.14286,0); //Convert to degree per second
if(gyro_roll/57.14286 - 2 > 0)Serial.print(" RwD");
else if(gyro_roll/57.14286 + 2 < 0)Serial.print(" RwU");
else Serial.print(" ---");
Serial.print(" Yaw:");
if(gyro_yaw >= 0)Serial.print("+");
Serial.print(gyro_yaw/57.14286,0); //Convert to degree per second
if(gyro_yaw/57.14286 - 2 > 0)Serial.println(" NoR");
else if(gyro_yaw/57.14286 + 2 < 0)Serial.println(" NoL");
else Serial.println(" ---");
}