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

    구상 및 계획

    RC카 하드웨어를 만들었으니 이제 제어 프로그램을 준비할겁니다.

    프로그램을 처음 만들어보는 사람은 아마 가장 처음에 무엇을 어떻게 시작해야 할지 헷갈릴수도 있습니다.

     

    솔직히 케바케라고 생각하는데 저는신호(signal)를 가장 먼저 생각 해봤습니다.

    먼저 저의 목표는"내가 조종값을 넣으면 그 값대로 RC카가 움직였으면 좋겠다."정도 일겁니다

    .

    이를 좀더 자세하게 풀어보자면 아래와 같겠네요.

    1. 제가 조종한 RC 송신기는 바인딩 되어있는 RC 수신기에게 명령값을 전달합니다.
    2. RC 수신기는 받은 명령 신호를 내보내는 출력핀이 있고, 그 핀을 통해 아두이노로 신호를 읽어 들일겁니다.
    3. 아두이노로 들어온 신호(PWM)를 적절하게 가공합니다.
      1. 입력(input) 신호는 2가지 입니다: Steering과 Throttle.
      2. 위의 두 신호를 적절히 조합하여 가공할 'MIXER'를 제작합니다.
      3. MIXER는 두 개의 입력 신호를 받아 좌/우측 모터를 각각 제어할 로직을 포함하고 있어야 합니다.
      4. 한 쪽당 3개의 신호가 있어 총 6개의 출력 신호를 생성해야 합니다. 그 신호는 아래와 같습니다.
        1. 좌측
          1. 방향 A
          2. 방향 B
          3. 속도 PWM
        2. 우측
          1. 방향 A
          2. 방향 B
          3. 속도 PWM
    4. MIXER가 만든 출력 신호는 모터 드라이브로 전달됩니다.
    5. 구동을 확인합니다.

    이렇게 보면 죄다 신호를 받고, 받은 신호를 가공하고, 다시 다른 곳으로 신호를 던져줍니다.

    따라서 신호가 가장 먼저 발생되는 부분을 시작점으로 잡겠습니다.


    신호 받아보기

    가장 처음 신호가 발생되는 부분은RC 송신기입니다.

    RC 송신기는 위와 같이 대부분 스위치(switch)와 가변 저항(potentiometer)들로 구성되어 있습니다.

    비싼 제품일수록 이 부품들의 해상도가 높거나 출력 신호의 세기를 포함한 신호의 퀄리티가 높을 것입니다.

    (물론 출력 가능한 채널 수, LCD가 있냐 없냐, 흑백이냐 컬러냐 이런 부분이 가장 크게 좌우하긴 합니다.)

    이 부품들의 조작 위치에 따라 신호가 발생되는 것입니다.

     

    스위치는 당연히 2-position이면 ON/OFF, 3-position이면 LOW/MID/HIGH값을 나타냅니다.

    가변 저항은 저항값을 읽는 ADC converter의 해상도에 따라 에 따라 다르겠죠.

    제 경험에 의하면 대부분의 신호값의 범위는 980가 최소, 1900가 최대입니다.

    즉 스위치는 LOW가 980, MID가 1450, HIGH가 1900을 나타내고

    가변 저항은 위치에 따라 이 최대~최소 값에 맞는 값을 나타냅니다.

    우선은 이 정도만 진도를 잡고 시작했습니다.

    그렇다면 RC 수신기의 신호를 아두이노로 어떻게 받아들이냐가 문제겠네요.

    당연히 저도 구글링으로 먼저 조사를 했습니다.

    송신기와 수신기 바인딩을 아직 안했다면 구글링을 통해 바인딩을 해주셔야 합니다.

    제가 사용할 RC 수신기는 위와 같이 조종기의 3개 채널 값을 출력합니다.

    PWM 신호가 들어오며 아두이노에서는 pulseIn()함수를 통해 특정 디지털 핀의 PWM값을 읽을 수 있습니다.

    시간이 남으면 나중에 아두이노 핀맵도 그려보겠습니다.

    (지금은 귀찮아서 코드만..)

    #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;
    
    void setup()
    {
      Serial.begin(9600);
      pinMode(recvCH1, INPUT);
      pinMode(recvCH2, INPUT);
      pinMode(recvCH3, INPUT);
    }
    
    void loop()
    {
      // receive PWM signals from receiver channel 1 to 3.
      valueCH1 = pulseIn(recvCH1, HIGH);
      valueCH2 = pulseIn(recvCH2, HIGH);
      valueCH3 = pulseIn(recvCH3, HIGH);
    
      // print the values with serial monitor
      Serial.print("CH1: "); Serial.print(valueCH1); Serial.print("\t");
      Serial.print("CH2: "); Serial.print(valueCH2); Serial.print("\t");
      Serial.print("CH3: "); Serial.println(valueCH3);
    }

    위의 코드는 정말 단순하게 RC 수신기의 신호를 읽어들여 시리얼 모니터로 출력하는 프로그램 입니다.

    아직은 그냥 핀만 지정해주고 단순하게 loop()문에서 읽어들이기만 한 상태입니다.

     

    아래 링크에 들어가보시면 pulseIn()함수에 대한 설명이 나와있습니다.

    www.arduino.cc/reference/en/language/functions/advanced-io/pulsein/

     

    pulseIn() - Arduino Reference

    Description Reads a pulse (either HIGH or LOW) on a pin. For example, if value is HIGH, pulseIn() waits for the pin to go from LOW to HIGH, starts timing, then waits for the pin to go LOW and stops timing. Returns the length of the pulse in microseconds or

    www.arduino.cc

    읽어보기 귀찮겠지만, PWM 신호가 무엇인지 알고 계신다면 금방 이해할 것입니다.

    PWM 신호를 처음 접하거나 아직 잘 모르겠다 하는 경우, 아두이노를 계속 사용할 예정이라면 무조건 알아두는 것이 좋습니다.

     

    위의 코드에서는 pulseIn()의 parameter인 timeout을 사용하지 않았는데, default 값은 1초로 설정되어 있습니다.

    설정된 시간동안 설정된 HIGH나 LOW를 읽어들이지 못했다면 0값을 반환하게 됩니다.

    timeout값은 다음의 평균 함수를 만드는 포스팅에서 유용하게 사용합니다.


    특이 사항

    실행 결과를 모니터링 하다 보니 이상한 점을 발견했습니다!

    중간에 RC 송신기가 OFF되었다고 가정하고 중간에 일부러 전원을 OFF해봤는데 아래와 같이 나타났습니다.

    timeout을 별도로 설정하지 않았기 때문에 기본값은 1초로 설정되어 있을겁니다.

    수신기로부터 HIGH 펄스를 읽지 못했다면 pulseIn()을 호출했을 때 0을 반환 받아야 합니다.

    CH1(steering)과 CH3(toggle button)은 0값을 잘 반환했지만,CH2(throttle)은 계속 중간값을 반환합니다.

     

    아마 이는 해당 RC 송/수신기를 사용했기 때문에 CH2에 설정된 기본 설정 같습니다.

    CH2는 로봇의 전/후진을 담당하는 throttle 조작 신호이고,송신기가 OFF되었다고 0값을 주면 로봇은 아마 전속력으로 후진할 것입니다.

    이를 방지하고자, 신호를 받지 못할 때 CH2만 중간값을 던져주는 것이 아닌가 싶습니다.

     

    "CH2는 timeout이 없구나!"라고 생각했습니다만,,

    확실히 하고자 테스트할 수 있는 경우의 수를 최대한 테스트 해보았더니 아예 timeout이 없는 것은 아니었습니다.

     

    1. 아두이노에 전원이 공급되는 순간, 송신기가 켜있는 경우 : 정상 동작
    2. 아두이노에 전원이 공급되는 순간, 송신기가 꺼져있는 경우 : CH2는 0값을 반환함. (timeout 발생 O)
    3. 정상 동작 중, 송신기 OFF, 수신기는 계속해서 전원이 공급되고 있는 경우 : CH2는 중간값을 반환함. (timeout 발생 X)
    4. 정상 동작 중, 송신기 OFF, 수신기도 전원이 끊긴 경우 : CH2는 0값을 반환함. (timeout 발생 O)
    5. 정상 동작 중, 송신기 및 수신기 모두 OFF 이후 수신기만 전원 재공급 : CH2는 0값을 반환함. (timeout 발생 O)
    6. 5번 상황에서 송신기를 켜는 경우, 1번 상황으로 복귀.

    이로써 조금 확실해진 것은 변수를 최대한 줄이기 위해 아두이노 전원과 수신기 전원을 분리하여 각각 독립적으로 넣어줘야 한다는 것입니다.

    최소한 수신기의 전원만이라도 확보해 준다면 경우의 수가 줄어듭니다.

    (누가 전원선을 고의로 뽑지만 않는다면..)

     

    송신기가 OFF된 경우 모든 채널이 동일한 현상을 보여주는 것이 아니기 때문에, 송신기 OFF 상태인 RC_LOSS라는 예외를 정의하는 것은 조금 더 생각해 봐야 할 것 같습니다.

    다음 포스팅에서는 수신받은 신호의 잡음을 줄여줄 평균 함수를 만들어 보겠습니다.

    댓글

    Designed by JB FACTORY