[아두이노] Differential Wheeled RC카 만들기 #1-2 "신호 평균내기"

    평균 (average)

    이전의 포스팅에서 RC 송신기에서 신호를 만들어내는 방식에 대해 잠깐 언급했었습니다.

    2021/02/11 - [로봇] - [아두이노] Differential Wheeled RC카 만들기 #1-1 "RC 수신기 신호 읽어들이기"

     

    [아두이노] Differential Wheeled RC카 만들기 #1-1 "RC 수신기 신호 읽어들이기"

    구상 및 계획 RC카 하드웨어를 만들었으니 이제 제어 프로그램을 준비할겁니다. 프로그램을 처음 만들어보는 사람은 아마 가장 처음에 무엇을 어떻게 시작해야 할지 헷갈릴수도 있습니다. 솔직

    conceptbug.tistory.com

    가변 저항과 스위치의 위치를 읽어들여 신호를 발생시킵니다.

    RC 수신기도 송신기로부터 들어오는 신호를 수신측 센서에서 감지합니다.

    신호를 만들어내는 녀석도 센서, 만들어낸 신호를 받아들이는 녀석도 센서이기 때문에 값에 노이즈(잡음)

    가 생길 수 밖에 없습니다.

    현재 받고있는 신호는 맨 밑의 신호와 노이즈가 혼합된 신호입니다.

    센서는 자연의 아날로그(analog) 신호를 디지털(digital)신호로 바꿔주는 부품이기 때문에 해상도와 그로 인한 오차

    또한 존재합니다.

    이렇게 노이즈 때문에 흔들리고 오차가 있는 신허값은 신뢰성이 떨어지는데, 이 신호의 신뢰성을 확보하기 위해 신호의 평균을 계산하는 함수를 제작할 것입니다.

     

    평균을 내는데에는 횟수 평균, 시간 평균, 이동 평균등이 있습니다.

    이들 중 가장 쉽고 편하게 만들수 있는 횟수 평균을 이용한 함수를 만들어 보겠습니다.

    다만, 본인이 아두이노 내부 인터럽트와 타이머를 사용할 수 있다면 개인적으로 횟수 평균보다는 시간 평균이 더 낫다고 봅니다.

    (루프문과 시간 딜레이를 사용해 '타이머 비슷한 것'을 만들어볼 수는 있지만, 엄밀히 말하면 정확한 타이머는 아닙니다)

     

    개념은 간단합니다.

    이전 포스팅에서 간단하게 만들어 보았던 코드는 신호값을 읽어들이는 즉시 시리얼 모니터에 디스플레이 해줍니다.

    loop()문에 delay()함수도 넣어놓지 않았기 때문에 매우 빠른 속도로 읽어들일 겁니다.

    하지만 횟수 평균은 노이즈가 있는 신호를 바로 출력하는 것이 아니라 "먼저 n개 만큼의 값을 받아 갖고 있다가, 이들의 평균을 출력한다."는 개념입니다.

    눈치 빠르신분은 바로 "아, 그러면 for()문이나 while()문을 이용하면 되겠다" 라고 캐치하셨을 겁니다.

     

    메인 loop()문 안에서 채널 하나당 각각 for()문을 돌려도 되지만, 이 신호를 받아들여 평균을 내는 프로세스는 프로그램이 실행되는 한 계속 진행되는 일종의 루틴이기 때문에 따로 함수를 만들어 호출하도록 하겠습니다.

    #define recvCH1 9   // PWM signal from receiver CH1: Throttle
    #define recvCH2 10  // PWM signal from receiver CH2: Rudder
    #define recvCH3 11  // PWM signal from receiver CH3: Toggle Button
    
    #define motorLeft_EN 3    // Left Motor: ENA pin
    #define motorLeft_OUT1 2  // Left Motor: IN1 pin
    #define motorLeft_OUT2 1  // Left Motor: IN2 pin
    #define motorRight_EN 5   // Right Motor: ENB pin
    #define motorRight_OUT1 7 // Right Motor: IN3 pin
    #define motorRight_OUT2 6 // Right Motor: IN4 pin
    
    #define ACT 4   // Linear Actuator enable signal output pin
    
    unsigned long valueCH1;
    unsigned long valueCH2;
    unsigned long valueCH3;
    
    int getAvg(int channel, int count) {
      int sum = 0;
      int cnt = 0;
      int avg;
    
      while(cnt < count) {
        sum = sum + pulseIn(channel, HIGH, 50000);
        cnt++;
      }
    
      avg = sum / count;
    
      Serial.print("Ch: "); Serial.print(channel);
      Serial.print("\tsum: "); Serial.print(sum);
      Serial.print("\tAvg: "); Serial.println(avg);
    
      return avg;
    }
    
    void setup()
    {
      Serial.begin(115200);
      pinMode(recvCH1, INPUT);
      pinMode(recvCH2, INPUT);
      pinMode(recvCH3, INPUT);
    }
    
    void loop()
    {
      // receive PWM signals from receiver channel 1 to 3.
      valueCH1 = getAvg(recvCH1, 10);
      valueCH2 = getAvg(recvCH2, 10);
      valueCH3 = getAvg(recvCH3, 10);
    }

    channel 핀의 펄스 신호를 count번 받아 평균을 내는 함수 getAvg()를 간단하게 만들어 보았습니다.

    (pulseIn()의 기본 반환값은 unsigned long 형인데, 해당 반환값을 저장할 변수를 그냥 int로 선언해도 무방한지 아직은 잘 모르겠습니다)

     

    int getAvg(int channel, int count) {
      int sum = 0;
      int cnt = 0;
      int avg;
    
      while(cnt < count) {
        sum = sum + pulseIn(channel, HIGH, 50000); // <-timeout specified
        cnt++;
      }
    
      avg = sum / count;
    
      Serial.print("Ch: "); Serial.print(channel);
      Serial.print("\tsum: "); Serial.print(sum);
      Serial.print("\tAvg: "); Serial.println(avg);
    
      return avg;
    }

    함수만 따로 보면 이번에는 pulseIn()을 호출할 때 timeout 파라미터를 사용한 것을 볼 수 있습니다.

    timeout 값의 단위는 'micro second(us)' 입니다.

    시간 값을 정할 때 천 분의 1초인 'milli second(ms)'가 아니라 100만 분의 1초라는 것을 헷갈리면 안됩니다.

    저는 50,000이라는 값을 사용했고 이는 50ms가 될것입니다.

    이 값은 제가 RC 신호가 유실되었을 때 최소 0.05초만에 이를 알아챘으면 좋겠다는 생각에 계산한 값입니다.

    Ch: 9를 보면 값이 1488~1489 사이의 안정된 값이 나오는 것을 볼 수 있습니다.


    신호 유실에 관해

    앞서 RC_LOSS 라는 상태를정의하려고 곰곰히 생각해 보았는데, RC_LOSS 을 판단하는 로직은 메인 loop()문 제일 처음 부분에 위치시켜야 할 것 같습니다.

    RC_LOSS 상태에서 로봇은 어떠한 동작도 하면 안되기 때문입니다.

    신호가 들어오지 않는다면 무조건 정지 후 신호가 들어올때 까지 기다리면 됩니다.

    또한 소프트웨어적으로 처리하는 것 보다 릴레이를 사용해 모터로 들어가는 명령선을 ON/OFF하여 확실하게 처리하는 것이 더 안전할 것 같습니다.

    댓글

    Designed by JB FACTORY