[38호]기울기 방식의 조종기를 적용한 쿼드콥터
2016 ICT 융합 프로젝트 공모전 참가상
기울기 방식의 조종기를 적용한 쿼드콥터
글 | 한국교통대학교 임성묵, 전동흡, 박재호, 오성석, 신민철
심사평
뉴티씨 재미있는 아이템인 쿼드콥터에서 센서 등을 이용해 상보필터로 이를 보상한 결과를 적용, 쿼드콥터 조정 시에 생길 수 있는 균형잡는 부분의 문제를 해결하려고 한 점 등, 매우 고생하며 만든 노력의 흔적이 보이지만 구현이 완료되지 않아 아쉬움이 남는다. 다음에 좀 더 보완하여 다시 도전하여 좋은 작품으로 만나고 싶다.
칩센 학생들이 같이 도전해서 재미있게 즐길만한 주제다. 사실 유사한 형태를 본 적은 있지만 상세한 설명으로 따라하기 쉬울 것 같다.
위드로봇 구현한 시스템에 대한 결과 분석이 부족한 부분이 아쉽다. 또한 기울기를 검출하는데 있어 상보 필터를 적용한 내용이 잘 동작하고 있는지에 대한 면밀한 분석이 있으면 더 좋겠다. 하드웨어 제작에 많은 고생을 했을 것으로 보이며, 제어기 부분을 좀 더 학습하면 많은 도움이 될 것이다.
작품 개요
작품 소개
기울기 방식의 조종기(조종기의 기울어짐에 따라 제어하는 조종기)를 적용한 Quad Copter로, 기존 시중에 나와 있는 RC 조종기와 수신기를 사용하지 않고 직접 제작하였습니다. 기존의 Scrolling Stick조종방식을 이용한 Quad Copter와 다르게 조종기의 움직임과 Quad Copter의 움직임이 같기 때문에 직관적인 조종으로 보다 빠르게 대처할 수 있으며 초심자의 경우 조종법을 쉽게 익힐 수 있습니다.
작품의 개발 배경
드론(Quad Copter)이 큰 인기를 얻은 이유는 상업용, 군용으로서의 가치 때문만이 아닙니다. 이전부터 군용드론에 대한 관심은 뜨거웠으며, 상업분야에서도 마찬가지였습니다. 하지만 드론이 지금처럼 많은 인기를 누리게 된 이유는, 누구나 가지고 조종할 수 있는 상용드론의 출현 때문이었습니다.
중국의 DJI는 바로 이 점을 정확히 집어내었습니다. [reference1] http://www.techm.kr/bbs/board.php?bo_table=article&wr_id=724
상용드론이 일반인들에게 다가갈 수 있었던 이유는 스마트폰 기술과 배터리 성능의 발전 그리고 쉬운 조종기 때문이었는데, 스마트폰 기술이 발전 하면서 드론에 들어가는 부품(센서)등의 크기가 많이 줄었으며 최소 3개 이상의 모터를 동시에 고속으로 돌려야 하는 가혹한 조건에서의 배터리 사용은 이전에는 큰 문제였으나 최근 이러한 문제가 해결됨으로써 저가의 Quad Copter가 탄생할 수 있었습니다. 하지만, 가장 큰 요인은 상대적으로 쉬운 조종법 때문이라 생각했습니다. 다만 Scrolling Stick 조종방식을 이용한 조종법 또한 익숙해지는데 어려워 안전사고나 추락으로 인한 쿼드의 파손 등이 있었습니다. 그래서 우리는 좀 더 직관적이고 쉬운 조종기를 만들어 보려했습니다.
최근 Quad Copter의 모듈화된 보드와 오픈소스가 많이있지만, 이와 관계없이 직접 필요한 회로와 부품을 선정해 납땜 수작업으로 만들었습니다.
Own Frame(완전수제작) Quad Copter를 만들기 위해 자료 조사와 몸체제작을 시도했습니다. 그러나 몸체를 직접 만들면 기구로 인해 실패할 확률이 높다 판단하여 ARF(반완제품)키트를 구매하여 제작했습니다.
작품 설명
주요동작 및 특징
기존의 조종법보다 직관적이고 쉬운 조종기를 만들려고 했습니다. 저희 Quad Copter는 조종기의 기울임에 따라 쿼드를 움직일 수 있게 설계되었습니다.
조종기와 Quad Copter를 평지에 내려놓고(이때 조종기의 stroll을 최저로 두어야합니다.) Quad Copter의 전원을 켜고 조종기를 켜면 처음에 쿼드와 조종기를 페어링을 하게 됩니다. 이 과정은 1~2초 정도 소요됩니다.
그 다음 ESC(Electronic Speed Control) Setting을 위해 먼저 조종을 위한 PWM의 최대치를 송신 합니다.
이후 설정음이 들리고 최저치를 송신하면 ESC의 setting이 완료됩니다.
이후 자이로 센서를 calibration합니다. 이 과정에서 센서가 처음에 켜지면서 들어온 쓰레기 값을 삭제하고 쿼드와 조종기 사이의 센서 값을 일치시키는 프로세스가 진행됩니다.
이전의 모든 과정이 끝나면 조종기는 Quad Copter로 stroll, 자이로 값(Roll, Pitch, Yaw)값을 송신하며, 쿼드는 이 값을 수신 받아 비행하게 됩니다.
시스템 구성도
작품 개발 환경
단계별 제작과정
하드웨어 구성
Controller
조종기는 CLCD와 1개의 가변저항 그리고 3개의 switch로 조종 및 현재 상태를 볼 수 있습니다. 가변저항은 모터의 base speed를 조절하며 이 값으로 Quad Copter의 상승과 하강을, 자이로 값을 가감하며 전진, 후진 및 좌우 이동을 합니다. 배터리는 안정감을 위해 조종기 하부에 부착하였으며, 자이로 센서는 중앙에 위치시켜 기울기에 오차없이 반응하도록 하였습니다.
Quad Copter
Quad Copter 또한 기체의 중심부에 자이로센서를 장착하였고, 무게중심을 맞추기 위해 최대한 좌우대칭이 되도록 기판을 설계하였습니다. 배터리 역시 안정적인 구조를 실험하다 프레임 중앙 하부에 끼웠습니다.
Motor & Propeller
부품을 선정하고 Motor와 Propeller가 Quad Copter의 하중을 부담할 수 있는지 계산해보아야 합니다.
그림 1과 그림 2는 Quad Copter의 무게와 호버링시 4개의 각 모터가 부담해야할 중량을 계산한 것이며, 그림 3은 Excel식의 이론값과 모터의 추력을 실험적으로 구한 값과 비교하여 rpm과 추력에 대한 실험식을 구한 그래프입니다.(계열1=이론값, 계열2=실험값) 고속회전에서 실험값이 이론값 보다 작게 측정되는 이유는 고속회전으로 인해 프로펠러의 효율이 떨어지기 때문으로 생각됩니다.
기체 test용 기구
Roll, Pitch, Yaw축 제어 test를 위한 기구를 철물점과 재료실의 부품을 이용하여 직접 제작하였습니다.
소프트웨어(주요 알고리즘 및 적용 기술)
상보필터
가속도센서
중력 가속도를 기준으로 물체의 기울어진 각도를 중력의 세기로 x, y, z축으로 백터값 계산해내는 센서 (범위 -32768~32767)
단점: 외력에 민감하게 반응 (고주파에 취약)
장점: 가속도 값이 시간이 지나도 일정. (드리프트가적다)
노이즈가 심하고 값이 왜곡될 수 있지만, 오차의 누적이 적습니다.
가속도 값을 오일러 각으로 변환
자이로 센서
각 축을 기준으로 축의 각속도를 측정하여 크기로 표현하는 센서 (범위 -32768~32767)
단점: 외력에 의한 오차가 시간에 따라 축적되어 발산.(드리프트)
장점: 자세 변화를 잘 감지.
실제 물체의 자세변화를 잘 나타내지만 누적 오차가 발생합니다.
상보필터
이러한 가속도 센서와 자이로 센서의 장단점을 융합한 필터가 상보필터입니다.
//PI제어_X축
cf_err_x = cf_result_x-accel_angle_x; // 필터값.accel 오일러각, P제어에 사용 오차=목표치·현재값
cf_err_sum_x += cf_erR_x * dt; // 에러를 적분해서 저장, I제어에 사용
cf_kp_val_x = cf_err_x*cf_kp; // P value
cf_ki_val_x = cf_err_sum_x*CF_KI; // I value
cf_pid_out_x = (p*qsinx*tany*rcosx*tany) – (cf_kp_val_x + cf_ki_val_x);
// 자이로 각도값을 PID제어를 통해 얼마나 빠르게 가속도 각도값에 맞출 것인지 결정
cf_result_x += (cf_pid_out_x*dt);
쿼드 코드에서 구현된 상보필터 (X축)
2중 PID제어
일반 pid 제어기는 p제어 i제어 d제어기를 합친 것입니다.
각각 비례 적분 미분을 통하여 목표 값이 도달하는 특성을 원하는 모델에 맞게 gain 값을 튜닝하여 사용합니다. Quad Copter에는 조종기에서 보낸 각도를 목표치로 Quad Copter 몸체의 각도를 현재 값으로 합니다. P, I, D gain 값에 대한 특성은 아래와 같습니다.
PID gain 값과 제어기의 특성 | |
P제어 | 목표값 도달시간을 줄인다. |
I제어 | 정상 상태오차를 줄인다. |
D제어 | 오버슈트를 억제한다. |
각각의 gain 값은 서로 종속적이기 때문에 하나의 gain 값이 변화하면 다른 두 개의 값에 영향을 줍니다.
2중 pid 제어기는 자이로센서 값으로 1차 pid를 한 뒤 가속도센서 값으로 2차 pid 제어를 한 것입니다.
void ROLL PID(void) {
//Roll_Cmd = C_Roll_Cmd*0.01;
Roll_Ang_Err = Roll_Cmd – cf_cf_result_x;
Roll_Out_Err = Roll_Ang_Err * Roll_Out_Pgain – gyro_x_t;
Roll_In_P = Roll_Out_Err * Roll_In_Pgain;
Roll_In_I = Roll_In_I + (Roll_Out_Err * Roll_In_Igain)*dt_pid;
// Limit_var(&Roll_In_I,-300,300);
Roll_In_D = (Roll_Out_Err – Pre_Roll_Out_Err)/dt_pid * Roll_In_Dgain;
Pre_Roll = (Roll_In_P + Roll_In_I + Roll_In_D);
//// 이중 PID ROLL
Quad Copter 코드에서의 2중 PID함수
Check sum
check sum은 중복검사의 한 가지로, 전송된 자료의 값을 통해 계산된 암호를 같이 송신하여 수신부에서 수신 받은 값으로 송신부와 같은 방법으로 check sum암호를 계산하고 이것을 송신 받은 암호와 비교하여 송신된 자료의 무결점을 보장해주는 단순한 방법입니다.
처음 조종기와 쿼드의 데이터 통신은 문제가 되지 않는 것 같았지만, 이후 진행된 비행테스트에서 모터의 RPM이 튀는 현상이 발견되었고 이를 고치기 위해 많은 자료조사와 필터를 설계해보려 했지만, 이 방법이 가장 간단한 코드로 가장 효과가 좋았습니다.
case 34: GPIOC -> BSRR=0xe7ff; flag)cycle = 1;
while(flag_cycle){
while(rx_flag == 0); rx_flag=0;
parsing();
count = atoi(da[0];
C_Roll_Cmd = atoi(da[1]);
C_Pitch_Cmd = atoi(da[2]);
C_Throttle = atoi(da[3]);
C_Check_sum = atoi(da[4]);
if((C_Roll_Cmd+C_Pitch_Cmd+C_throttle)==C_CHECK_SUM){
gy_ac();
Result_PID();
//Limit_var(&PID_Roll, -50,50);
//Limit_var(&Pid_Pitch,-50,50);
X_Motor();
}
Key_backselect(34);
} ? end while flag_cycle? break;
기타
소스코드
#include “stm32f10x.h”
#include “MPU6050.h”
#include “MPU6050_Lib.h”
#include “HAL_MPU6050.h”
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#define on 0
#define off 1
#define TRUE 1
#define FALSE 0
#define PWM_ocr1 (TIM2->CCR1)
#define PWM_ocr2 (TIM2->CCR2)
#define PWM_ocr3 (TIM4->CCR3)
#define PWM_ocr4 (TIM4->CCR4)
volatile int send=0,mflag=0;
unsigned int Key_count=0;
//실시간 자이로 가속도 값
int16_t accelgyro[6];
volatile int RxCounter=0,tokencount=0;
uint8_t NbrOfDataToTransfer=10;
char TxBuffer[50], TxCounter, RxBuffer[52]={0,},G_DATA[52]={0,};
char *da[5]={0,};
char *last = NULL;
char *token;
char ch;
char* context = NULL;
float pitch_cont=0,roll_cont=0,yaw_cont=0;
int calibrate_flag = 1;
float calibrate_cf_x = 0;
float calibrate_cf_y = 0;
unsigned int calibrate_flag_if = 100;
int C_check_sum=0;
int throttle=0;
//float test1=0,test2=0;
int test_t=800;
//I2C TEST 용
struct {
u16 tmr;
u16 connect;
}test;
// KEY TEST
struct {
u16 bf;
} key;
u16 key_flag;
u16 key_flag1=0;
int K_count = 0;
volatile int flag_num = 1;
int flag_count = 0;
int flag_cycle = 1;
int rx_flag = 0;
int count=0;
#define SW_up (GPIOD->IDR&GPIO_Pin_0)
#define SW_sel (GPIOD->IDR&GPIO_Pin_1)
#define SW_down (GPIOD->IDR&GPIO_Pin_2)
//—————————————————
void RCC_Configuration(void);
void NVIC_Configuration(void);
void GPIO_Configuration(void);
void Uart1_Initialize(void);
int fputc(int a, FILE*f);
void Sensor_Test();
void I2C_ini(void);
void TIM2_Configuration(void);
void TIM2_IRQHandler(void);
void SerialPutChar(uint8_t c);
void Serial_PutString(uint8_t *s);
void Delay_us(vu32 nCount);
void Delay_ms(vu32 nCount);
void Usart1_init(void);
void SendSerial(uint8_t buffer[], uint8_t size);
void USART1_IRQHandler(void);
void parsing(void);
void TIM2_Configuration(void);
void ocr_limit(void);
void GY_AC(void);
void Result_PID();
void X_Motor();
//—————————————————
void Delay_us(vu32 nCount){
nCount *= 8;
for(; nCount!=0; nCount–);
}
void Delay_ms(vu32 nCount){
nCount *= 6000;
for(; nCount!=0; nCount–);
}
void I2C_ini(void){
I2C_InitTypeDef I2C_InitStruct;
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStruct.I2C_OwnAddress1 = MPU6050_DEFAULT_ADDRESS;
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_ClockSpeed = MPU6050_I2C_Speed;
I2C_Init(MPU6050_I2C, &I2C_InitStruct);
I2C_Cmd(MPU6050_I2C, ENABLE);
}
void gy_ac(void){
if(MPU6050_TestConnection()==TRUE){ test.connect++; }
MPU6050_GetRawAccelGyro(accelgyro);
calibrate_sensors(accelgyro);
SendSerialAccelGryro(accelgyro);
}
void RCC_Configuration(void){
ErrorStatus HSEStartUpStatus;
RCC_DeInit(); //RCC system reset(for debug purpose)
RCC_HSEConfig(RCC_HSE_ON); //Enable HSE
HSEStartUpStatus = RCC_WaitForHSEStartUp(); //Wait till HSE is ready
if(HSEStartUpStatus == SUCCESS) {
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
FLASH_SetLatency(FLASH_Latency_2); //Flash 2 wait state
RCC_HCLKConfig(RCC_SYSCLK_Div1); //HCLK = SYSCLK
RCC_PCLK2Config(RCC_HCLK_Div1); //PCLK2 = HCLK
RCC_PCLK1Config(RCC_HCLK_Div4); //PCLK1 = HCLK/4
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK = 8MHz * 9 = 72 MHz
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //Wait till PLL is ready
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //Select PLL as system clock source
while (RCC_GetSYSCLKSource() != 0×08); //Wait till PLL is used as system clock source
}
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE ); //클럭설정
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
//C_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB1PeriphClockCmd(MPU6050_I2C_RCC_Periph|RCC_APB1Periph_TIM3, ENABLE);
}
사용부품
번호 | 부품명 | 수량 | 사용목적 |
1 | 쿼드콥터 프레임세트(7.4~11.1V, 40A) | frame | |
2 | FlyFun-40A | 4 | ESC |
3 | MAI-CLCD-4B420 V2.0 | 1 | LCD |
4 | 11.1V 3200mAh extreme (40C) | 1 | battery |
5 | MPU6050 | 2 | sensor |
6 | [RA5]보급형 원형만능기판(150*200_양면) | 3 | 기판 |
7 | DiffusedRGB(tri-color)10mmLED(10pack) – Common | 1 | LED |
8 | NETmate 케이블 정리용 헤리컬밴드 10M 10mm/화이트 [NMT-SWB10] | 1 | 케이블정리 |
9 | Cortex-M3(144핀) CPU코어모듈 (CORE-STM32-144P) | 1 | MCU |
10 | Cortex-M3(64핀) CPU코어모듈 (CORE-STM32-64P) | 1 | MCU |
11 | RV09H-20SQ 10KΩ | 1 | 가변저항 |
12 | CHINA KN-A04 | 1 | 캡 |
13 | XB24-DMPIT-250 | 2 | 통신 |
참고자료
·[reference1] : www.techm.kr/bbs/board.php?bo_table=article&wr_id=724
· [reference2] : cluster1.cafe.daum.net/_c21_/bbs_search_read?grpid=Q31X&fldid=5p7u&datanum=70&openArticle=true&docid=Q31X5p7u7020041020132919
· 파란만장 개발이야기: http://hs36.tistory.com/
· 이뭐병의 블로그: http://blog.naver.com/ejtkddl
· Git Hub: https://github.com/sincoon/STM32F4xx_MPU6050lib
· 쿼드 로터 무인항공기 제어 및 시뮬레이션 – KITECH 양광웅 작성
· 쿼드 로터 무인항공기 동역학 모델링 – KITECH 양광웅 작성
회로도
[38호]기울기 방식의 조종기를 적용한 쿼드콥터