728x90

 오늘 3D 프린팅을 할수 없게되서 대신 코드 정리부터 해보려고한다. 어떻게 정리해나갈지 생각하고자 우선 로터리 엔코더, PID, 모터 제어 코드들을 가져 온 후 어떻게 연동시킬지 생각해본다.

 

 

1. 로터리 엔코더

https://howtomechatronics.com/tutorials/arduino/rotary-encoder-works-use-arduino/

 #define outputA 6
 #define outputB 7
 int counter = 0; 
 int aState;
 int aLastState;  
 
 void setup() { 
   pinMode (outputA,INPUT);
   pinMode (outputB,INPUT);
   
   Serial.begin (9600);
   // Reads the initial state of the outputA
   aLastState = digitalRead(outputA);   
 } 
 void loop() { 
   aState = digitalRead(outputA); // Reads the "current" state of the outputA
   // If the previous and the current state of the outputA are different, that means a Pulse has occured
   if (aState != aLastState){     
     // If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
     if (digitalRead(outputB) != aState) { 
       counter ++;
     } else {
       counter --;
     }
     Serial.print("Position: ");
     Serial.println(counter);
   } 
   aLastState = aState; // Updates the previous state of the outputA with the current state

 

2. PID 예제

https://www.teachmemicro.com/arduino-pid-control-tutorial

//PID constants
double kp = 2
double ki = 5
double kd = 1
 
unsigned long currentTime, previousTime;
double elapsedTime;
double error;
double lastError;
double input, output, setPoint;
double cumError, rateError;
 
void setup(){
        setPoint = 0;                          //set point at zero degrees
}    
 
void loop(){
        input = analogRead(A0);                //read from rotary encoder connected to A0
        output = computePID(input);
        delay(100);
        analogWrite(3, output);                //control the motor based on PID value
 
}
 
double computePID(double inp){     
        currentTime = millis();                //get current time
        elapsedTime = (double)(currentTime - previousTime);        //compute time elapsed from previous computation
        
        error = Setpoint - inp;                                // determine error
        cumError += error * elapsedTime;                // compute integral
        rateError = (error - lastError)/elapsedTime;   // compute derivative
 
        double out = kp*error + ki*cumError + kd*rateError;                //PID output               
 
        lastError = error;                                //remember current error
        previousTime = currentTime;                        //remember current time
 
        return out;                                        //have function return the PID output
}

 

3. DC 모터 제어예제

https://deneb21.tistory.com/281

void setup() {
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
}
 
void loop() 
{
        //최대속도의 50%로 정회전
        digitalWrite(7, HIGH);
        digitalWrite(8, LOW);
        analogWrite(6, 127);
        delay(3000);
 
        //최대속도의 50% 역회전
        digitalWrite(7, LOW);
        digitalWrite(8, HIGH);
        analogWrite(6, 127);
        delay(3000);
 
        //최대속도로 정회전
        digitalWrite(7, HIGH);
        digitalWrite(8, LOW);
        analogWrite(6, 255);
        delay(3000);
 
        //최대속도로 역회전
        digitalWrite(7, LOW);
        digitalWrite(8, HIGH);
        analogWrite(6, 255);
        delay(3000);
 
        //정지 (7번핀에 HIGH를 주어도 PWM 핀에 값을 0을 주었기 때문에 정지함)
        digitalWrite(7, HIGH);
        digitalWrite(8, LOW);
        analogWrite(6, 0);
        delay(3000);
}

 

 

 

 준비는 거의 다되서 진행하려고 하였으나 하필이면 너무 저가의 로터리 엔코더를 사용하다가 제대로 각변화를 측정할수가 없어 더이상 본 과제는 진행할 수가 없다.

 

 상세한 테스트 내용은 오늘 자 문서에 남기고 할수 없이 스테핑 모터를 이용한. 저거 볼 밸런싱 로봇으로 변견해보아야 되겠다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

300x250
728x90

진자를 제외한 대부분 파츠들 모델링을 마쳤다. 아래의 사진들은 결과물

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

300x250
728x90

외부 CAD 모델을 활용해서

 

역 도립진자 차량 모델링 만들어 가는중

 

 

 

 

 

 

 

 

 

 

 

 

 

300x250
728x90

오늘은 사용할 부품 정리와

 

라이노를 이용해서 간단하게 모델링 했다.

 

내일은 라이노 볼트, 너트 준비 및 코드 정리하면 될거같다.

 

 

 

300x250
728x90

300x250
728x90

3d 프린터 동작 방식

1. FDM 압출형, 고체형

- 고체기반 재료에 열을가해 녹인 후 노즐로 쌓아 올림

- 장점 : 장비, 재료가격이 저렴, 가장 대중화

- 단점 : 정밀도가 낮음, 후가공 힘듬, 느림

- 재료 : PLA(후가공 힘듬, 수축 적음), ABS(후가공 편리, 수축 심함)

 

2. SLA /DLP(꽝조형, 액체형)

- SLA : 광경화성 액상수지를 레이저로 응고

- DLP : 광경화성 액상수지를 빔 프로젝터로 응고

(기념품가게 크리스탈안에 물건넣은거)

- 장점: 세밀하고 정교하게 구현

- 단점 : 색상, 원료 제하, 장비 재료가 고가

-재료 : 광경화성 액상수지

 

 

 

 

모델링 데이터 변환과정

1. 모델링 파일 STL

2. 슬라이싱(G Code)

3. 결과물

 

 

 

슬라이싱 프로그램 -> 큐라

- 무료

- 직관적

- 다양한 설정

* 여기는 ultimakor 2+

 

 

 

 

 

 

 

 

 

 

 

 

 

300x250
728x90

일단 내가 원래 생각하고 있던건 밸런싱 로봇인데

 

제어 공학적 관점에서 어떻게 접근해야 될지 몰라

 

손댈수가 없더라. 일단 이거 한 코드는 있어보이긴한데

 

 

 

대충 그린 밸런싱 로봇 그림 ㅋㅋㅋ..

 

 

 

 

그래서 제어 공학 공부에 많이 사용하고, 심플한

 

역 도립진자를 우선 해보고자 한다.

 

 

 

 

 

 

 

대강 이런식으로

 

하지만 제대로 구현하려면 각 부품들 크기를 제대로 조사해야되겟다.

 

1. 아두이노 본체

2. 아두이노 전원 배터리

3. 모터 드라이버

4. 모터 전원부

5. 로터리 인코더

6. 기어 모터

 

300x250
728x90

역 도립진자에 로터리 엔코더 모터가 필요한건 알겠다.

 

하지만 이걸 뭘 골라야될까 고민하다가 몇가지 자료를 찾았다.

 

 

 

1. 로터리 엔코더 작동 원리

- 자세히 읽지는 (못)않았지만 사람들의 평이 되게 좋길래 남긴다.

 

https://elecs.tistory.com/181?category=605348

 

 

 

2. 회전 역 도립진자

- 아두이노 메가를 가지고 역 도립진자 만든 자료

https://www.seas.upenn.edu/~jiyuehe/rotary-inverted-pendulum/Encoder.html

 

- 아깝게도 동영상이 나오는게 하나도 없다..

 

- 하지만 기본 하드웨어 설정이랑 소스코드 관련 내용을 정리해서 올려주셨더라

 

 

 

 

- 이 자료를 보니까 팬듈럼 엔코더에다가 짝대기를 달고, 짝대기가 하늘을 가리킬때 입력을

 -> 기준입력 r(t) = 0

 -> 엔코더 측정 값이 y(t)

 -> 오차 e(t) = r(t) - y(t)

 => PID 제어기로 오차를 줄여주도록 u(t) 생성

 

 

 

- 오차가 있으면 어떻게 차가 움직여야 할까..

 ->  e(t) < 0라면 전진. e(t) > 0는 후진 대강 이런식으로 해야하지 않을까..

 -> 오차와 모든 게인들을 정리한 값을 모터 입력으로 사용해주면 될듯하다.

https://throwexception.tistory.com/851

 

 

 

하지만 나는 로터리 엔코더가 없으니 하나 사야될것같다..

 

 

대충 준비물 정리

1. 로터리 엔코더 -> x

2. 작대기 -> x

3. 아두이노 우노보드 본체 -> 있음

4. 모터 2개와 모터드라이버, 차량(base) 몸체 -> 있음?

 

 

 

일단 준비물은 정리했으니

 

 

로터리 엔코더에서 어떻게 값이 들어오는지 보고

이에 맞게 DC모터 제어하는 방법을 고안해보자 ...

 

 

 

 

 

 

 

 

300x250
728x90

아두이노 부품에 대해 잘몰랐던것같다

 

매번 LCD, DC 모터, 스탭모터, 초음파 센서 같은게 있구나

 

정도만 알았지

 

정확히는 잘몰랐다.

 

 

 

 

그런데 역도립진자를 만들려면 회전각 측정이 가능한 모터를 찾아야 된다고 하더라

 

그런 모터를 로터리 엔코더가 장착된 모터라고 하더라

 

 

 

 

그러다보니 로터리 엔코더를 잠시 확인해보았다.

 

그런데 로터리 엔코더들 설명들이 하나같이 너무다 어렵더라

 

 

 

 

그러다가 로터리 엔코더 사용법에 대한 영상을 보는데

 

GA, GB 핀이 있고

 

GB 핀 신호가 라이징 에지일때 0이면 왼쪽

 

1이면 오른쪽으로 돌아가 있다고 한다.

 

 

 

실제로는 더 복잡하겠지만

 

라이징 에지시점의 A값을 확인해서 회전각을 안다니

 

그동안 본것보다는 더 이해가 잘된다.

 

 

 

 

 

 

 

 

하지만 문제는

 

여기서 설명해주는건 로터리 엔코더 하나만 보여주다보니

 

나는 모터 각을측정해야되는데!!

 

로터리 모터를 찾아봐야할거같다.

 

 

참 좋은 자료는 많은데

 

뭐가 좋은 자료인지 나한태 맞는지 참 판단하기 힘들다...

 

 

 

 

중간에 잠깐 본 오실로스코프

 

오슬로 스코프는 전기 흐름을 보르는 전기의 현미경

 

- 노이즈가 어디서 발생하는가? 전기의 튀김

- 시리얼 모니터로 보기도 하지만 이걸로는 판단하기 힘듬

 => delay를 주기도하지만 오실로스코프로 보면 센서가 어떻게 인식되는지 알수있음

 

 

 

 

아래 재품 성능

- 채널 1 -> 신호를 1개만 받을수 있다 (대부분 2채널)

 

 

 

 

 

 

 

 

 

 

 

 

300x250
728x90

https://www.teachmemicro.com/arduino-pid-control-tutorial

https://ko.m.wikipedia.org/wiki/PID_%EC%A0%9C%EC%96%B4%EA%B8%B0

 

 

 제어 시스템에서 제어기는 시스템의 출력이 목표 입력값이 되도록 고쳐나가야 합니다. 가장 유명한 제어기중 하나는 PID 제어기로 비례, 적분, 미분의 약어라고 할수 있습니다. 이번에는 아두이노 PID 튜토리얼에 대해서 살펴보면서 이 프로젝트에 제어기가 어떻게 사용되는지 알아봅시다.

 

 

앞으로 볼 내용은 아래와 같겠습니다.

 

1. PID가 뭘까?

 1.1 비례 제어

 1.2 적분 제어

 1.3 미분 제어

2. PID 제어 구현

3. 아두이노 PID 라이브러리

4. 마치면서

 

 

 

1. PID란 무엇일까?

 PID는 비례, 적분, 미분의 줄임말이라고 할수 있습니다. 또한 시스템에서 제어기가 요란을 대처하는 방법이라고도 할수 있는데요. 여기서 말하는 제어기는 피드백 시스템에 존재하는 제어기를 말합니다. 그래서 특정 수치값을 읽고 내가 할수있는 일을 하는거라고 할수 있겠습니다.

 

 피드벡 시스템은 출력 결과를 입력으로 되돌려주는 시스템이라고 할수 있는데요. 난로의 불을 제어하는 예제로 한번 배워보겠습니다. 간단한 그림은 아래와 같아요.

 

 여러분들이 일정 온도를 유지하고 싶으면, 센서는 매 시간마다 온도를 측정해주고, 이 측정한 온도가 희망 기준 온도에서 증가하거나 감소했을탠대 이 차이를 오차로 피드백하게 해줍니다.

 

 

 

 

 

 

1.1 비례 제어 Proportional Control

 비례 제어는 에러를 얼마나 많이 비례해서 제어할수 있는지를 말하고 있습니다. 예를들자면 위 난로에서 연료를 조절하는 전기 밸브가 있다고 합시다. 오차가 작다면, 설정 값과 측정 값이 일치하도록 밸브는 작은 양의 연료를 줄것이고, 오차가 크다면 벨브로 더 많은 연료를 줄겁니다.

=> 현재 상태에서 오차 값 크기에 비례하여 제어(원하는 값에 빨리 도달)

 

-  좌측은 제어이전, 우측은 P 제어 이후

=> 제어 이전에는 출력이 목표 값(1)에 도달하지 못함

=> P 제어 이후 오차 값(목표 값 - 현재 값)만큼 비례해서 신호를 증폭한다.

 

 

 

 

 

 

 1.2 적분 제어 Integral Control

 비례제어가 요란을 제거해 주었다면, 적분 제어는 이 오프셋 오차를 제거해서 에러를 0으로 만들어줍니다. 제어기는 시간에 대해 오차가 누적되므로 적분 제어기 없이는 오차에 제대로 대응할수 없습니다.

 

 이전의 예시 계속 보면 연료 출력이 증가하거나 감소한 이후에도 초기 위치가 아닌 지점에서 가만히 있게 됩니다. 적분 제어는 이를 감지해서 연료 밸브를 기존의 자리로 돌려주는 역활을 하겠습니다.

=> 정상상태 오차를 제거(누적된 오차로 인해 원하는 값에 수렴하지 못하는 부분을 제거)

 

 

- 좌측은 비례제어만 한 결과, 우측은 PI 제어 결과

=> 비례제어만 한 경우 존재하는 정상상태 오차를 적분 제어를 통해 제거함

=> 하지만 오버슛이 심각해짐

 

 

 

 

 

 

1.3 미분 제어 Derivative Control

 마지막으로 미분 졔어는 오차의 변화율을 조절시킵니다. 만약 적분 제어에 누적 오차가 보이는 경우에 미분 제어가 이 오차를 예측하여 얼마나 오차가 빠르게 변하는지를 보고 얼마나 고쳐야 할지 찾아냅다. 비례 제어와 적분 제어가 대처하지 못하는 동역학적 에러에 대해서 최적의 동작을 한다고 할수 있겠습니다.

 

 위 난로 예제에서 120도를 설정했지만 130도에서 140도로 올라가는 상황이라고 해봅시다. 비례적분 제어기는 오차의 크기에 대응해서 동작하고는 있지만, 오차가 빠르게 발생하는 경우에서는 대처하기가 어려우나 미분 제어기는 오치의 변화율을 보고 이에 맞게 조절할수가 있습니다.

 

 

- 좌측은 PI 제어기, 우측은 PID 제어기

=> 기존의 출력갑이 크게 변하는데 미분 제어를 통해 오버슛을 줄이고 안정하게 만든다

 

 

 

- PID는 위와 같은 형태로 사용하기도 되지만 P 제어기, PI 제어기, PD 제어기만 사용하는 경우도 있음

- PID 제어시 발생가능한 문제

 1) 계산된 제어값이 구동기가 동작할수 있는 한계 보다 커 구동기 포화(sturation)이 발생하는 경우

 2) 오차 적분값이 크게 누적되어 출력이 설정에 가까워지면 제어가 작아져야하나 큰 값을 출력하여

  설정값에 도달하는데 많은 시간이 걸리는 경우

 -> 위 문제를 적분기의 와이드업이라 함 => 안티 와이드업 기법으로 PID 제어기를 보완해주어야함

- 제어 파라미터 K_p, K_i, K_d를 제어 이득, 게인이라 부름. 적절한 이득값을 얻는 과정을 튜닝(tunning)이라고 함.

 

 

 

https://ko.m.wikipedia.org/wiki/PID_%EC%A0%9C%EC%96%B4%EA%B8%B0

 

 

 

 

 

 

 

 다시 돌아와서

 

 

 

 설정 입력값 r(t)가 있고, 출력으로 y(t), 제어 입력 u(t). 오차 e(t)가 있겠습니다. 위 난로 예제에서 보면 r(t)는 희망 온도, y(t)는 실제 온도라 할수 있습니다. e(t)는 실제 온도와 희망 온도의 차이라할수있고, u(t)는 PID 제어기로 교정한 신호의 총합이라 할수 있겠습니다.

 

 원하는  성능을 얻을수 있또록 PID 제어기의 값들을 고치는 작업을 튜닝이라 부르며, 위 그림에서 K 상수들을 잘 조절해주어야 합니다.

 

 

 

 

 

 

 

 

 

코드로 PID 제어하기

 아두이노 스캐치에서 PID 제어기를 구현하려면 5개의 파라미터가 필요합니다

-> 비례, 적분, 미분 상수와 입력값과 기준 설정 값

 

 PID 계산은 반드시 내부 루프 함수에 있어야하며, 함수 시작시에는 수행 시간 (elapsed time)을 설정해주어야 합니다. 현재 시간은 millis()로 얻겠습니다.

currentTime = millis();
elapsedTime = currentTime - previousTime;

 

 

오차는 설정 지점과 입력의 차이로 얻겠습니다.

error = setPoint - input;

 

 

적분 오차는 시간에 대한 누적 오차라 하였으니 아두이노로 구현하려면 다음과 같이 경과시간 * 오차를 해주면 되겠습니다.

cumError += error * elapsedTime;

 

 미분 오차는 에러의 변화율이므로 다음과 같이 하겠습니다.

rateError = (error - lastError)/elapsedTime;

 

이제 제어 입력 u(t)를 정리하면 아래와 같겠습니다. 여기서 Kp, Ki, Kd는 이전에 설정한 상수가 됩니다.

output = Kp * error + Ki * cumError + Kd * rateError;

 

마지막으로 다음 반복회차를 위해서 변수들을 아래와 같이 지정해줍시다.

lastError = error;
previousTime = currentTime;

 

 

 

 

 

이제 조금 더 제대로 해보면, 다음과같은 휠이 달린 모터가 있다고 해봅시다. 우리는 휠이 저 위치에 있기를 바라고 있습니다. 바퀴에 로터리 엔코더가 재 바퀴 각도에 대한 정보를 알려주므로 우리가 바라는 지점에서의 각도를 0이라 합시다.

 

 

 

 휠이 위치를 벗어날떄마다 조정을 해주어야 하는데, 보통 모터는 PWM 펄스 폭 변조를 통해 제어됩니다. 펄스가 넓을수록 모터 회전이 더 빨라집니다.

 

 이제 아두이노에서 다음과 같이 간단한 제어 예시를 구현해봅시다.

 

//PID constants
double kp = 2
double ki = 5
double kd = 1
 
unsigned long currentTime, previousTime;
double elapsedTime;
double error;
double lastError;
double input, output, setPoint;
double cumError, rateError;
 
void setup(){
        setPoint = 0;                          //set point at zero degrees
}    
 
void loop(){
        input = analogRead(A0);                //read from rotary encoder connected to A0
        output = computePID(input);
        delay(100);
        analogWrite(3, output);                //control the motor based on PID value
 
}
 
double computePID(double inp){     
        currentTime = millis();                //get current time
        elapsedTime = (double)(currentTime - previousTime);        //compute time elapsed from previous computation
        
        error = Setpoint - inp;                                // determine error
        cumError += error * elapsedTime;                // compute integral
        rateError = (error - lastError)/elapsedTime;   // compute derivative
 
        double out = kp*error + ki*cumError + kd*rateError;                //PID output               
 
        lastError = error;                                //remember current error
        previousTime = currentTime;                        //remember current time
 
        return out;                                        //have function return the PID output
}

 위 루프 함수에서는 로터리 엔코더가 휠의 현재 위치를 주고, 그 값이 commputePID()함수의 파라미터로 사용됩니다. 이 함수는 PWM으로 모터 제어에 사용할 값을 반환해 줍니다.

 

 

 

300x250

+ Recent posts