쿼드콥터 제작기 08 - PID 튜닝, 시소테스트

Posted by dw0rdptr
2015. 10. 13. 01:59 IoT/QuadCopter

기체마다 추력, 크기나 무게 등 여러 변수가 많기 때문에 기체에 맞게 PID제어를 각각 어느정도 해줘야 하는지에 대한 상수값을 정해줘야 하는데 이를 PID 튜닝이라고 한다
PID튜닝에는 스텝 응답법과 한계 감도법등 방법이 여러가지가 있는데, 그냥 노가다하는것도 나쁘지않다 ㅎ

상수를 결정하고 D와 I를 차례로 조정한 뒤 전체적으로 조정해나가는 방식으로 튜닝을 하고 있다.

(1) 목표값을 변화한 후, 응답이 너무 느리다.

→ P-게인(Kp)을 올린다. 응답은 빠르나 불안정하다.

→ P-게인(Kp)을 내린다.


(2) 목표값과 피드백값이 같아지지 않는다.

→ 적분 시간 Ti를 감소시킨다. 불안정하게 진동하며 일치한다.

→ 적분 시간 Ti를 증가시킨다.


(3) Kp를 올린 후, 응답이 여전히 느리다.

→ D-게인(Kd)을 올린다. 여전히 불안정하다.

→ D-게인(Kd)을 내린다.

*출처 : http://www.hardcopyworld.com/ngine/aduino/index.php/archives/650


시소테스트 영상

계속 모터출력이 튀는데 아마

모터진동 -> 센서값진동-> D제어에 영향

이 원인이 되는 것 같다




쿼드콥터 제작기 07 - PID제어

Posted by dw0rdptr
2015. 9. 9. 01:30 IoT/QuadCopter

이제 PID로 자세를 제어하는일만 남았다..

PID제어란 비례-미분-적분제어의 약자로

P - 비례(Proportional)
I  - 적분(Integral)
D - 미분(Derivative)


쿼드콥터에서의 PID는 센서에서 나오는 각도값을 받아서 원하는 목표각도를 유지하기 위해 쓰는 알고리즘이다.
예를 들어 현재 기체가 30도로 기울어져있으면 PID 제어기를 거쳐 기울어진 쪽의 모터출력을 높여주면 수평을 되찾을 수 있다.

처음엔 P 비례제어만써서 기울어진만큼만 모터출력을 높이면 되겠지 라고 생각했는데 그렇게 안하는데엔 다 이유가 있더라ㅠ


P 비례제어

P-비례제어는 말그대로 기울어진 각도에 비례해 출력을 높이는 제어다. 비례제어는 PID제어중 가장 기본이지만 문제점이 있다. 바로 목표값에 정확히 일치하지 않고 어느정도 차이가 생긴다는것..

목표값에 근접함에 따라 조작량이 점점 줄어들고 결국엔 목표값에 정확히 일치하지 못하고 조작량이 0이되면 편차가 생기는데 이를 정상편차라고 한다. 이를 해결하기 위해 아래 I-적분제어를 쓴다


PI 비례-적분제어

I-적분제어는 단독으로 쓰이지 않고 P제어를 보정하기위해 쓰인다. P제어에서 약 5도정도 정상편차가 생겼다고 가정해보자. 그럼 기울어진쪽의 적분값이 계속 쌓이면서 조작량으로 발생하고 발생한 5도의 편차를 없앨 수 있다.


PI로만 해도 제어할수 있을 것 같지만, 목표값에 도달하는 속도가 너무 느려 쿼드콥터는 어느새 저멀리.. 날아간다 그래서 쓰는게 D미분제어이다.


PID 비례-적분-미분제어

D제어 역시 단독으로 쓰지 않고 P비례제어를 보조해주는 역할을 한다. I-적분제어가 정상편차를 없애주는거라면 D-비례제어는 PI제어의 단점인 느린 목표값 도달을 빠르게 도달할 수 있게 해준다.

미분은 순간변화량이다. 그래서 D-미분제어는 기체의 순간기울기변화가 클수록 크게작용해 신속하게 목표값에 도달할 수 있게 해주는 것이다.


PID제어는 게인값을 구하는게 중요한데, 각각의 게인값은 구한 P,I,D 제어값을 어느정도로 적용할지를 뜻한다. 기체마다 환경이 다르니 직접 게인을 맞춰봐야 구할수 있는데 이 게인값을 구하는과정을 PID 튜닝이라고 한다.


조작량 = p게인*error + i게인*error적분값 + d게인*전회 error와의 차이

조작량이 바로 모터의 출력이 된다.









사진 출처

http://bearrhee.tistory.com/entry/90008274486

http://www.aistudy.co.kr/control/process_kim.htm





쿼드콥터 제작기 06 - mpu9250(6050) 가속도와 자이로센서

Posted by dw0rdptr
2015. 8. 25. 22:02 IoT/QuadCopter

쿼드콥터는 모터만 돌린다고 알아서 날지 못한다. 이유는 여러가지가 있는데 바람이 변수가 될 수도 있고 캘리브레이션을 해도 모터각각의 출력이 조금씩 차이가 날수도 있다. 또 쿼드콥터의 무게중심이 정확하게 맞는 것도 아니기 때문에 기체가 한쪽으로 기울어진 정도에 따라 기울어진쪽 모터를 더 돌려야 기체가 안정되게 뜰 수 있고 이러한 신호를 주는 주기는 앞글에서 말했듯이 2~3ms 내외여야 빠르게 외란에 대응해 쿼드콥터가 자세를 잡을 수 있게 된다.

기울어진정도에 따라 모터의 출력을 제어하는 알고리즘을 PID 자세제어라고 한다. PID에 대한 자세한 설명은 다음글에서 하고, 먼저 기체의 기울기를 알려면 가속도와 자이로센서가 필요하다. 가속도+자이로 6축센서인 MPU6050이 저렴하면서도 다른센서에 비해 안정적이라 쿼드콥터에서 많이 쓰이고 있다. 난 지자계센서가 추가된 mpu9250을 샀지만 지자계는 나중에 쓰니까 지자계를 제외하면 mpu6050과 똑같다.


센서를 이용해 각도를 구하는 방법은 처음에 생각했던 것보다 조금(아니 많이...) 복잡했다.. 각도를 구하려면 가속도와 자이로센서를 써야한다. 둘은 서로 장단점이 있는데 가속도센서는 값은 오차없이 잘나온다. 하지만 진동에 취약해 값이 요동치게 나온다.

반면에 자이로센서는 요동치는일 없이 깔끔하게 잘나오지만 자이로센서의 출력값에는 바이어스(bais=치우침)값이 포함되어있고 자이로센서 값을 구할때 적분을 하면서 이값이 계속 누적되면 점점 오차가 커지게 된다.


손으로 들고 x축으로 왔다갔다 한 결과다. 빨간색 그래프가 자이로센서값이고 파란색 그래프가 가속도센서값이다. 자이로는 깔끔하고 정확하게 출력되지만 점점 오차가 쌓이는 것을 볼 수 있는데 반면에 가속도센서 그래프는 손노이즈에 값이 심하게 요동쳐서 부정확하지만 시간이 지나도 오차값이 누적되지 않는다.

이러한 두 센서의 장점만을 골라 우리가 원하는 각도를 구할 수 있다. 이 때 사용되는 필터가 칼만필터와 상보(또는 보상)필터가 있는데, 칼만필터는 아두이노에서 처리하기엔 무겁다는 말이 많아 상보필터를 쓸것이다.


참고로 필터를 써도 시간이 지남에 따라 자이로센서에서 생기는 오차값은 생기기 마련이다.  루프를 돌기 전 여러번의 센서값을 구해 생기는 오차의 평균값을 주기마다 자이로값에 빼주면 오차가 줄어드는 것을 볼 수 있지만 없어지지는 않는다. 이를 보완해주는게 HDR(Heuristic Drift Reduction) 알고리즘이다


아두이노 소스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "Wire.h"
#include "MPU6050.h"
#include "I2Cdev.h"
 
#define pi 3.141592
#define RADIANS_TO_DEGREES 180/3.14159
#define fs 131.0;
MPU6050 mpu;
 
int16_t ax,ay,az;
int16_t gx,gy,gz;
int16_t mx,my,mz;
 
//자이로(각속도)
float gyro_x, gyro_y;
 
//최종 가속도,자이로 각도
float accel_x, accel_y;
float gyro_angle_x=0, gyro_angle_y=0;
 
//상보필터 거친 각도
float angle_x=0,angle_y=0,angle_z=0;
 
//자이로센서 바이어스값
float base_gx=0, base_gy=0, base_gz=0;
 
unsigned long pre_msec=0;
 
void calibrate(){  
   
  int loop =10;
  for (int i=0;i<loop;i++)
  {
    mpu.getMotion9(&ax,&ay,&az,&gx,&gy,&gz,&mx,&my,&mz);
    base_gx += gx;
    base_gy += gy;
    base_gz += gz;
    delay(80);
  }
  
  base_gx /=loop;
  base_gy /=loop;
  base_gz /=loop;
 
}
 
 
void setup() {
  Wire.begin();
 
  Serial.begin(19200);
  mpu.initialize();
  calibrate(); 
}
 
void loop() {
  //단위시간 변화량
  float dt = (millis()-pre_msec)/1000.0;
  pre_msec = millis();
  
  mpu.getMotion9(&ax,&ay,&az,&gx,&gy,&gz,&mx,&my,&mz);
 
  //가속도값 아크탄젠트->각도변환
  accel_x = atan(ay/sqrt(pow(ax,2+ pow(az,2)))*RADIANS_TO_DEGREES;
  accel_y = atan(-1*ax/sqrt(pow(ay,2+ pow(az,2)))*RADIANS_TO_DEGREES;
  
 
  
  //자이로 -32766~+32766을 실제 250degree/s로 변환
  //앞에서 계산한 오차의 평균값을 빼줌 
  gyro_x = (gx-base_gx)/fs;  
  gyro_y = (gy-base_gy)/fs;
 
  //변화량을 적분 
  gyro_angle_x = angle_x + dt*gyro_x;
  gyro_angle_y = angle_y + dt*gyro_y;
 
  //상보필터
  angle_x = 0.95*gyro_angle_x + 0.05*accel_x;
  angle_y = 0.95*gyro_angle_y + 0.05*accel_y;
 
  Serial.print(gyro_angle_x);
  Serial.print("   ");
  Serial.print(accel_x);
  Serial.print("   ");
  Serial.println(angle_x);
  
}
cs

이소스는 HDR없이 상보필터만 적용했는데 오차범위가 40분이 넘어가도록 0.1을 넘지않는다.. 게인값을 잘준건가 아니면 구한 바이어스값이 딱맞아떨어지는건가 ㅋㅋㅋㅋㅋ



--------------2015.10.31수정---------------

I2Cdev와 MPU6050 헤더 물어보시는 분들이 많아서 추가합니다

https://github.com/jrowberg/i2cdevlib



-참고

http://mechasolutionwiki.com/index.php?title=%EC%83%81%EB%B3%B4%ED%95%84%ED%84%B0 -상보필터

http://hs36.tistory.com/32   - HDR 알고리즘

http://hs36.tistory.com/18   - 자이로, 가속도센서 설명