Header

  1. View current page

    안산동산고 로봇연구반 상상: 지식 노트

Profile_img_60x60_06
30 7

Loop Wait vs nMotorEncoderTarget

 

RobotC에서 특정 엔코더 각도만큼 모터를 회전시키는 데에는 다양한 방법이 존재한다. 그 중 하나로 nMotorEncoderTarget이 있다.

 

nMotorEncoderTarget은 NXT 펌웨어 수준에서 엔코더를 감시하면서 특정 각도에 도달하도록 하는 역할을 하는데, 이것의 동작 방식이 고전적인 '루프타고 엔코더 기다리기'(이하 Loop Wait)와 미묘한 차이를 보인다. 이 문서에서는 그 차이점에 대해 다루려고 한다.

 

 Loop Wait vs nMotorEncoderTarget#

먼저, Loop Wait 방식(루프타고 수동으로 엔코더 기다리기)이란 이런 것을 뜻한다.

// Loop Wait 방식 - 수동 엔코더 검사

nMotorEncoder[motorA] = 0;
motor[motorA] = 100;
while(nMotorEncoder[motorA] < 360);
motor[motorA] = 0;

 while루프를 통해 nMotorEncoder를 감시하면서 특정 각도에 도달하면 모터를 정지시키는 방식이다. 딱히 특별할 게 없는 방식이다.

 

그리고 아래는 nMotorEncoderTarget을 이용하는 방식이다.

// nMotorEncoderTarget 사용

nMotorEncoderTarget[motorA] = 360;
motor[motorA] = 100;
while(motor[motorA] != 0);

 nMotorEncoderTarget에 목표 각도를 저장하고 모터에 원하는 파워를 넣어주면, 펌웨어 수준에서 감시를 하여 엔코더가 목표 각도에 도달하면 정지시킨다. 회전을 충분히 할 때까지 대기하도록 while(motor[motorA] != 0); 구문을 붙여주었다.

 

부호 주의하기#

위 두 예제의 차이점을 살펴보자. 일단 Loop Wait는 모터 파워에 따라 while문 내의 부등호와 부호를 주의해야 한다. 음수 파워를 부여하면 엔코더는 감산되기 때문에, 부호의 음양을 제대로 따지지 않으면 오작동하는 프로그램을 작성할 가능성이 높다.

// 잘못된 Loop Wait 방식 - 파워와 목표 엔코더의 부호(& 부등호)가 불일치

nMotorEncoder[motorA] = 0;
motor[motorA] = -100;
while(nMotorEncoder[motorA] < 360);
motor[motorA] = 0;

 

// 올바른 Loop Wait 방식 - 파워와 목표 엔코더의 부호(& 부등호)가 일치

nMotorEncoder[motorA] = 0;
motor[motorA] = -100;
while(nMotorEncoder[motorA] > -360);
motor[motorA] = 0;

nMotorEncoderTarget은 파워값만 뒤집으면 반대 방향으로도 적용이 가능하므로, 위와 같은 어처구니없는 실수의 여지가 없어지게 된다.

 

엔코더 값 보존#

그리고, nMotorEncoderTarget는 엔코더 값을 보존할 수 있다. 즉 누적이 가능하다는 뜻이다. 반면에 Loop Wait 방식은 최초에 nMotorEncoder[motorA] = 0; 으로 엔코더를 초기화시켜 버린다(누적 불가능).

 

물론 이것은 변수 하나를 추가시키면 금방 해결되는 문제다.

// Loop Wait 방식 변형 - 엔코더를 누적 가능하게

long targetEnc = nMotorEncoder[motorA] + 360;
motor[motorA] = 100;
while(nMotorEncoder[motorA] < targetEnc);
motor[motorA] = 0;

targetEnc라는 변수를 도입하여 nMotorEncoder[motorA]의 값을 건들지 않도록 하였다. 물론 부호 문제는 역시나 존재한다. 음수 방향으로 동작하려면 targetEnc에서 +360 을 -360 으로 고치고, while문의 부등호 또한 뒤집어야 한다. 참고로 nMotorEncoder 변수에서 취급하는 값들은 모두 long형이다. int형을 써서 오버플로우의 여지를 남기는 실수를 하지 않도록 하자.

 

목표 엔코더 각도에 도달하는 오차#

이 외에도 Loop Wait 방식과 nMotorEncoderTarget 방식간의 또 다른 중요한 동작상의 차이가 있다. 일단 다음 그래프를 보자.

 encoder-graph.png

이 그래프는 다음의 실험을 데이터로깅한 결과물이다. 실험용 프로그램의 소스코드가 궁금하다면 target_test.c을 다운받도록 하자.

  • manual w/o reg: nMotorPIDSpeedCtrl = mtrNoReg; 인 상태에서, 파워 100으로, Loop Wait 방식으로 360도 회전.
  • manual with reg: nMotorPIDSpeedCtrl = mtrSpeedReg; 인 상태에서, 파워 100으로, Loop Wait 방식으로 360도 회전.
  • target w/o reg: nMotorPIDSpeedCtrl = mtrNoReg; 인 상태에서, 파워 100으로, nMotorEncoderTarget 이용으로 360도 회전.
  • target with reg: nMotorPIDSpeedCtrl = mtrSpeedReg; 인 상태에서, 파워 100으로, nMotorEncoderTarget 이용으로 360도 회전.

빨간색 막대는 모터 가동(START)과 정지(END)시점을 뜻한다. 모터의 회전은 허공에서 이루어졌다(즉, 부하가 가해지지 않았다).

 

가장 눈에 띄는 차이는 목표 각도인 360도를 지키고 있느냐이다. Loop Wait 방식은 360도를 훌쩍 뛰어넘어 420도 부근에서 정지하는 모습을 보인다. 그에 반해 nMotorEncoderTarget을 이용하면 360도 도달 전에 감속을 시작하여 360를 조금 넘기고 정지한다. 이를 통해서 목표 각도에 좀 더 정확하게 도달할 수 있다. 이것으로 보아 nMotorEncoderTarget은 펌웨어 수준에서 정교한 제어를 제공하는 것으로 보인다.

 

왜 이렇게 차이가 날까? 그 이유는 Loop Wait 방식은 360도를 넘은 후에 브레이크를 걸기 때문이다. 이미 360도를 넘은 시점에서 브레이크를 걸면, 완전히 정지하는 데 시간이 걸리기 때문에, 그만큼 더 많은 각도를 회전하는 것이다. nMotorEncoderTarget은 목표 각도에 도달하기 전에 미리 감속한다. 그렇기 때문에 목표각도 360도에서 보다 적은 차이를 보이면서 정지할 수 있었던 것이다. 이 차이를 줄이려면, 파워를 낮추는 것이 도움이 될 것이다.

 

그렇지만, 두 방식 모두 360를 초과하는 것은 매한가지이다.

 

그 외에 덤으로 알아보려 한 mtrSpeedReg와 mtrNoReg의 차이는, Loop Wait 방식에서 360도 도달 후 말미에 약간의 차이를 보인 것 외에는, 특별한 것이 없었다. 대신 모터에 부하를 주고 실험한다면 차이를 확인할 수 있을지도 모른다. 여기서의 Regulator레귤레이터는 원래 부하 상황에서 정확도를 보장하기 위한 것이니까 말이다.

 

그래서 무엇이 우월합니까?#

 위 3가지를 보자면 nMotorEncoderTarget이

  • 실수를 덜 하며
  • 엔코더 값을 보존할 수 있고
  • 더 높은 정확도를 보장

한다고 말할 수 있다. 이것만 본다면 nMotorEncoderTarget이 우월한 것처럼 보인다. 그러나 nMotorEncoderTarget에는 치명적 단점이 존재한다. 그것은 바로...

목표 각도에 근접했을 때 큰 부하가 있으면 회전을 마무리짓지 못한다!

는 것이다.

 

무슨 말이냐면 이렇다. 위 실험에서는 360도를 회전하도록 했지만, 이번에는 5도 정도의 적은 각도를 돌거나, 또는 360도를 거의 다 돌고 몇 도 정도만 남았다고 하자. 그리고 바로 그 때 갑자기 큰 부하가 걸린다고 가정해보자. nMotorEncoderTarget의 특성상 정확도를 높이기 위해 목표 각도에 근접하면 파워를 감소시킨다. 그런데 큰 부하가 걸렸으므로 파워를 올리지 않고는 모터가 회전을 하지 못한다. 결국 영영 목표 각도에 도달하지 못하는 꼴이 되고, 프로그램은 다음 코드를 실행하지 않고 계속 루프를 돌면서 대기하는 교착상태에 빠지게 된다.

 

물론 Loop Wait 방식에서는 이런 일이 일어나지 않는다. 단순무식하게 목표 각도에 도달할 때까지 일정한 파워를 유지하니까 말이다. (물론 그 주어진 파워가 부하를 이기기에 역부족이라면, 두 방식 모두 똑같은 결과가 일어날 것이다. 그러나 그것은 상황적으로 견딜 수 없는 부하가 걸렸기 때문이지, 소프트웨어적 처리 방식의 문제가 아니므로 논외로 하겠다.)

 

nMotorEncoderTarget이 부여된 모터의 파워 수치를 올림으로써 이런 문제를 최소화 할 수 있을 것이라고 추측한다. 그렇지만 단지 '최소화'일 뿐 근본적인 대책은 되지 못한다. 배터리 전압이 정상 작동할 때보다 조금 낮게 되거나 등등의 상황에서 문제가 다시 발생할 수 있다.

 

결론은 둘 다 적당히 가려서 써야 한다는 것이다. 위의 내용을 바탕으로 언제 어느 때 써야할 지 대강 감을 잡을 수 있을 것이다. 그러니까...

  • 적은 부하 상황 그리고 높은 정확도를 요구한다면 nMotorEncoderTarget을 사용!
  • 큰 부하(무겁거나 큰 바퀴의 차체의 주행 등)상황 또는 몇 도 단위의 높은 정확도가 필요하지 않다면 Loop Wait방식 사용!

으로 정리할 수 있겠다.

 

 

Tags

History

Last edited on 07/31/2011 20:01 by Flyer

Comments (0)

You must log in to leave a comment. Please sign in.