PX4FLOW 센서를 사용하기위한 사전 준비 - whdlgp/look_into_pixhawk_with_apm GitHub Wiki

Optical Flow 센서

솔직히 나도 잘 알지는 못한다. 일단 내가 이미지처리에 관해서는 문외한이고(공부할 예정, 아마도) Optical Flow(특정 픽셀이 움직이는거라는데,,,)가 구체적으로 어떻게 계산되는지도 솔직히 잘 모르겠다.

현제로서는 해당 행렬관련 연산을 대신 해주는 모듈(센서)를 이용해야 하는데, 수령받은 센서는 PX4FLOW 센서다. 실제로 찾아보니 사용법이 그렇게 어렵지는 않다. (오히려 간단한다)

센서 사용법을 이해하기 위한 기존 라이브러리 확인

원래라면 직접 데이터시트를 찾고, 데이터를 주고 받기위한 타이밍도를 확인하고, 직접 하나하나 보내보고 받아보면서 확인하는게 정석이겠지만, 요즘에는 시대가 참 좋아졌다.

요즘나오는 모듈, 특히 다양한 인터페이스(UART, SPI, I2C 등등)를 지원하는 센서의 경우 "아두이노"용으로 이미 드라이버(라이브러리, 또는 API라 할 수도 있겠다.)가 만들어지는 경우가 많다. 전통적으로(?) MCU의 경우 각각의 구조가 제각각(매모리맵 구조는 같아도 페리퍼럴 위치가 제각각)인 경우가 많고, 레지스터에 메모리 접근 명령어를 사용해서 페리퍼럴을 이용해야 하기 때문에 각각의 MCU에 대해 일일이 공부하고 어떤식으로 페리퍼럴이 맵핑 되있는지 확인하는 등의 작업을 해야 했었다. 쉽게말해 표준이 없으니까 다 봐야 됬었다.
근대 아두이노 라는 녀석이 나오면서 상황이 많이 달라졌는데, 사실상 표준이랄만한 MCU(또는 MPU)개발용 플랫폼이 생겼다. 물론 모든 MCU를 아두이노 환경 위에서 사용할 수 없으며 사용 가능한 환경이 제한적이지만, 원하는 성능의 하드웨어를 선정할만한 환경은 갖춰졌다.

위와같은 변화때문에, 본인의 경우 하드웨어 관련 정보를 찾기 이전에, 먼저 아두이노용 라이브러리를 찾아보는 편이다. 왜냐하면 이제 센서 개발을 하는 쪽에서도 제품 출시를 하면서 아두이노용 라이브러리를 같이 제공하는 경우가 많기 때문이다(거의 100이면 100 다 있다. 진짜 마이너한 경우 빼고,,,). PX4FLOW 또한 예외는 아니다. "PX4FLOW arduino" 정도만 검색해도 관련 레포지터리를 쉽게 찾을 수 있었다.

https://github.com/eschnou/arduino-px4flow-i2c

아두이노의 또다른 좋은 점은 센서용 라이브러리 제작 방법이 사실상 거의 정형화 되어있다는 점이다. 물론 개발자의 특색이 나타나는것은 어쩔 수 없지만, 구조는 거의 비슷하다.

  • 센서의 전반적인 인터페이스(Class 원형)
  • 내용물(주로 read, write, begin 등이 있다.)
  • 라이브러리의 사용 예(example)

본 센서는 사용법이 간단한 경우이기 때문에 소스를 보고 사용법을 이해하는데도 큰 무리가 없었다.

센서 사용법

본 센서의 경우 I2C 통신을 통해 값을 주고 받으며, 특정 바이트 하나를 보내면 그에 해당하는 데이터를 몇바이트씩 통쩨로 보내는 구조다.

  • 0x0을 보낼 경우
    일반적인 데이터 22바이트를 보낸다.
  • 0x16을 보낼 경우
    integral(일반 데이터를 적분한 값들인가 보다.)데이터 25바이트를 보낸다.

소스를 보면 쉽게 이해할 수 있다.

0x0 데이터를 보낼 경우

  Wire.beginTransmission(PX4FLOW_ADDRESS);
  Wire.write(0x0);  
  Wire.endTransmission();  
  
  // request 22 bytes from the module
  Wire.requestFrom(PX4FLOW_ADDRESS, 22);    

  // wait for all data to be available
  if (!wait(22)) {
      return false;
  }

  // read the data
  frame.frame_count       = read16();
  frame.pixel_flow_x_sum  = read16();
  frame.pixel_flow_y_sum  = read16();
  frame.flow_comp_m_x     = read16();
  frame.flow_comp_m_y     = read16();
  frame.qual              = read16();
  frame.gyro_x_rate       = read16();
  frame.gyro_y_rate       = read16();
  frame.gyro_z_rate       = read16();
  frame.gyro_range        = read8();
  frame.sonar_timestamp   = read8();
  frame.ground_distance   = read16();

0x16 데이터를 보낼 경우

  //send 0x16 to PX4FLOW module and receive back 25 Bytes data 
  Wire.beginTransmission(PX4FLOW_ADDRESS);
  Wire.write(0x16);  
  Wire.endTransmission();  
  
  // request 25 bytes from the module
  Wire.requestFrom(PX4FLOW_ADDRESS, 26);    

  // wait for all data to be available
  // TODO we could manage a timeout in order not to block
  // the loop when no component is connected
  if (!wait(26)) {
      return false;
  }
  
  // read the data
  iframe.frame_count_since_last_readout = read16();
  iframe.pixel_flow_x_integral  = read16();
  iframe.pixel_flow_y_integral  = read16();
  iframe.gyro_x_rate_integral   = read16();
  iframe.gyro_y_rate_integral   = read16();
  iframe.gyro_z_rate_integral   = read16();
  iframe.integration_timespan   = read32();
  iframe.sonar_timestamp        = read32();
  iframe.ground_distance        = read16();
  iframe.gyro_temperature       = read16();
  iframe.quality                = read8();

  // This is due to the lack of structure packing
  // in the PX4Flow code.
  read8();

마지막에 한 바이트를 그냥 버리는건 보내는 쪽에서 뭔가 더미값 하나를 더 보내는 듯 하다.

반드시 두 종류의 응답을 같이 사용해야 하는것은 아니다. 어느 한쪽만 선택해서 사용하는것이 일반적인 듯 하다.

APM에서의 PX4FLOW

위와 같은 예제소스와 잘 갖춰진 환경 덕분에 쉽게 장치 테스트를 해볼 수 있었다. 테스트 중 문제가 발생했는데, Pixhawk flight stack 쪽에서 사용하는 PX4FLOW 모듈의 펌웨어와 APM flight Stack쪽에서 사용하는 PX4FLOW 모듈의 펌웨어가 서로 다르다는 것이다.

실제로 보내주는 값이 완전 다른 값을 보내주는 것인지 까지는 확인해보지 못했는데, 같은 1바이트 데이터(0x0)를 보냈을 때 다른 값을 보내는 것은 확인했으며, 확인해봐야 할 사항이다.

APM쪽의 펌웨어는 현제 업데이트를 멈춘 상태인듯 하다(2014년도에 멈춘듯,,,).