mavlink 사용방법


BLDC 드라이버에 사용할 통신 프로토콜, MAVLINK 사용법을 알아 보도록 하겠습니다.

MAVLINK는 작은 비행체와 통신 하기 위해서 만든 오픈소스 프로토콜 입니다.
 만든 사람 등 자세한 내용은 다음 링크를 참조 하세요.
http://qgroundcontrol.org/mavlink/start

MAVLINK를 선택한 이유는
ROS에서 MAVLINK로 바로 통신 할 수 있다는 것과 통신에 필요한 대부분의 코드를 자동으로 생성해 준다는 것 입니다.

송신을 위한 전송 프레임 생성, 수신을 위한 프레임 파싱 상태머신, 프레인 디코딩 등의 코드를 자동으로 생성 해 줍니다.
특히나 C언어용 생성된 코드는 모두 헤더 파일로 제공 됩니다.
이를 이용하기 위해서는 단순 헤더 파일 하나만 인크루드 하면 됩니다

그럼 새로운 메세지를 만들고 이를 사용하는 것을 단계별로 보도록 하겠습니다.

메세지 정의

     예제로 명령 과  인자 2개를 전달하는 내용 입니다.

     메세지는 XML 형식으로 정의 합니다.
     메세지는 C언어로 생각하면 구조체와 비슷하다고 생각 하면 됩니다.
     메세지 형식은
         명령은 부호업는 8bit 형식의 명령 (uint8_t)
         1번째 인자는 부호없는 8bit 형식의 인자1 (uint8_t)
         2번째 인자는 부호없는 16bit 형식의 인자2 (uint16_t)


   정의할 내용은 메세지ID, 메세지 이름, 필드 형식 필드 이름 입니다.

    여러 메세지를 구분하기 위해 메제지 ID를 반드시 다른 값으로 설정 해야 합니다.
    새로 할당 가능한 ID는 180~229 입니다.
    (문서 에서는 150 부터 라고 돼 있는데 소스코드 일부에서는 180 이라는 주석도 있네요)

    예제는 아래와 같습니다.

<?xml version='1.0'?>
    <mavlink>
        <include>common.xml</include>
        <version>3</version>
        <enums>
        </enums>
        <messages>
            <!-- Messages for OROCA BLDC  -->
            <message id="220" name="TEST_CMD">
                <description>Message For Test 1</description>
                <field type="uint8_t" name="cmd_1">test cmd 1</field>
                <field type="uint8_t" name="targ1">argument 1</field>
                <field type="uint16_t" name="arg2">argument 2</field>
             </message>
        </messages>
    </mavlink>
 
XML 요소중에 message 항목에  ID 는 220 , 이름은 TEST_CMD 입니다.
필드는 각각 cmd_1, arg1,arg2 입니다.
파일 이름은 oroca_bldc.xml 로 저장 하도록 하겠습니다.

이번에는 정의한 메세지 내용을 처리하는 소스코드 생성에 대해서 알아 보겠습니다.

소스코드를 다운로드 합니다.  https://github.com/mavlink/mavlink/

    머세지 정의한 oroca_bldc.xml  파일을 message_definitions/v1.0 디렉 토리에 복사 합니다.

    python mavgenerate.py를 실행 합니다.
    ( 참고로 저는 Windows10 에서 파이썬 3.5.1을 사용해서 실험했습니다.)
    다음과 같은 화면이 나옵니다.


    XML 항목은 oroca_bldc.xml을 선택 합니다.
    Out 은 소스코드 생성될 디렉토리를 선택 합니다.
    생성 언어는 C언어를 생성하고
    프로토콜 버전은 1.0을 선택 합니다.

   그리고 생성 버튼을 누릅니다.

mavgenerate.png


   정상 이라면 Out 항목에서 선택한 디렉토리에 소스가 생성 됩니다.
   디렉토리 구조는  다음과 같습니다.
생성디렉토리.png

이제 생성된 코드를 사용하는 내용을 살펴 보도록 하겠습니다.


생성된 디렉토리를 보면 아래처럼 2개가 있습니다.
common ( MAVLINK기 제공하는 기본 기능들 구현) 과
oroca_bldc( 새로 정의한 메세지 구현 부분)
생성디렉토리.png

oroca_bldc 디렉토리 내용을 보면 다음과 같습니다.

생성디렉토리2.png

디렉토리 파일중에 인크루드 해서 사용할 파일은 mavlink.h 입니다.

그리고 세부 구현은 mavlink_msg_test_cmd.h 파일에 있습니다.

그럼 전송에 필요한 소스 코드를 살표 보도록 하겠습니다.

먼저 생성된 디렉토리의 헤더 파일을 다음 처럼 인크루드 합니다.

#include <mavlink/oroca_bldc/mavlink.h>

int send( )
{
    mavlink_message_t msg; // Mavlink 메세지 구조체
      uint8_t buf[1024];           // 메세지의 엔코딩된 전송 프레임

    // TEST_CMD 메세지에 대한 자동으로 생성된 함수,
    // 인자 1은 System ID  ==> ex)같은 메세지도 여러 보드로 구분할때 사용.
    // 인자 2는 컴포넌트 ID  ==> ex) 한 보드내에 여러 구룹이 있을때 구분용으로 사용 .
    // 인자 4 부터는 메세지 정의시 필드 항목 - cmd_1, arg1,arg2
    mavlink_msg_test_cmd_pack( 9, 121, &msg, 91,92,93);

    // 다음 함수내부에서 전송할 프레임을 완성함, CRC 생성등
    int len = mavlink_msg_to_send_buffer(buf, &msg);

   // 시리얼 포트로 버퍼 포인터는 buf, 길이는 len 내용을 전달 하면 완성됨
    ...
}

참고로 저는 실험용으로 Qt에서 했습니다.
다음 자료는 ChibiOS ( BLDC 보드에서 사용하는 RTOS)에서 실험한 내용을 올리도록 하겠습니다.


생성된 코드 수신부 이용에 대해서 살펴 보도록 하겠습니다.
UART를 가정해서 설명하도록 하겠습니다.

UART를 통해서 수신된 내용은 메제지 경계를 구분해서 전달 되지 않고 일부분 유실 될수 있기 때문에
프레임의 시작을 판단해야 하고 올바른 메세지가 도착 했는지 검사를 해야 합니다.(CRC 검사등)

이러한 기능을 생성된 코드에서 제공합니다.
한바이트씩 검사 하면서 프레임을 해석 해서 프레임이 정상적으로 수신 완료 됐는지 상태를 리턴 합니다.
mavlink_parse_char() //

정상적인 프레임이 확인되면 수신된 프레임의 내용을 디코딩 합니다.
자동 생성된 다음 함수를 수행 하면 메세지에 해당하는 구조체로 디코딩 해 줍니다.

mavlink_msg_test_cmd_decode() // TEST_CMD 에 해당하는 디코딩 함수.
mavlink_test_cmd_t // 코드생성기에서 만든 메세지에 해당하는 구조체

내부 구현을 보면 다음과 같고, 메모리 최적화를 위해서 변수 순서가 변경된것을 확인 할수 있습니다.

typedef struct __mavlink_test_cmd_t
{
    uint16_t arg2; /*< argument 2*/
    uint8_t cmd_1; /*< test cmd 1*/
    uint8_t arg1; /*< argument 1*/
} mavlink_test_cmd_t;

이제 이용 코드를 보면 다음과 같습니다.
수신된 데이터가 있을때 마다 호출 되는 함수 입니다.

bool recv( const uint8_t *buf, int recv_len)
{
    mavlink_message_t msg; // 로컬변수로 선언해도 잘 수행 되는데 아마도 실제 자료구조는 static 으로 구현 되는거 같아요.
    mavlink_status_t status; // 현재 수신된 데이터 파싱한 상태 리턴값.
    for( int i=0; i< recv_len ; i++)
    {
        if (mavlink_parse_char(MAVLINK_COMM_0, buf[i], &msg, &status) == MAVLINK_FRAMING_OK)
        {
            //qDebug("\nReceived packet: SYS: %d, COMP: %d, LEN: %d, MSG ID: %d\n", msg.sysid, msg.compid, msg.len, msg.msgid);

            if( MAVLINK_MSG_ID_TEST_CMD == msg.msgid ) // 메세지 ID 가  TEST_CMD 라면 해석
             {
                 mavlink_test_cmd_t test_cmd;
                 mavlink_msg_test_cmd_decode( &msg, &test_cmd); // 메세지 디코딩

                 qDebug("seq=%d \n", test_cmd.arg1); // 실험용으로 임자1을 출력해봄.
             }
         }
    }
}

이상 입니다.

위로 스크롤