ROS 스터디 #7: 임베디드 시스템

2021년 01월 20일 18:00
ZOOM 화상회의

임베디드 시스템(embedded system, 내장형 시스템)

기계나 기타 제어가 필요한 시스템에 대해 제어를 위한 특저 기능을 수행하는 컴퓨터 시스템으로 장치 내에 존재하는 전자 시스템이다. 즉 임베디드 시스템은 전체 장치의 일부분으로 구성되며 제어가 필요한 시스템을 위한 두뇌 역할을 하는 특정 목적의 컴퓨터 시스템이라고 정의할 수 있다.

임베디드 보드 종류

8/16-bit MCU(small) 32-bit MCU(big) 32-bit MCUARM A-classx86
보드 예Atem AVRARM Cortex-M0ARM Cortex-M7Samsung ExynosIntel Core i5
시스템 예Arduino, LeonardoArduino M0 proSAM V71ODROIDIntel NUC
MIPS1)10's100's1000's10000's
RAM1~32KB32KB384KBa few GB(off-chip)2-16 GB (SODIMM)
최대 출력10'of mW100'of mW100'of mW1000'of mW10000'of mW
주변 기기UART, USB FS 등USB FSEthernet,USB HSGigabit EthernetUSB SS, PCle

리눅스 같은 운영체제는 실시간성을 보장하지 못하기 때문에 엑추에이터나 센서등을 제어하기 위해서는 실시간 제어에 적합한 마이크로컨트롤러를 사용한다. 이런 임베디드 시스템에는 ROS를 설치할 수 없기 때문에 시스템과 (ROS가 설치된) PC 사이의 통신이 필요하다.2)

opencr.jpg

ROS를 지원하는 임베디드 보드이며 터틀봇3에서 메인 제어기로 사용된다. 회로, BOM, 거버 데이터 등의 H/W 정보 및 OpenCR의 모든 S/W가 오픈소스로 공개되어 있으며 성능이 좋고3) 인터페이스가 다양하다. 또한 IMU 센서를 사용하며 전원 출력이 다양4)하다.

PC와 제어기 간의 메시지5) 통신을 위해 중계자 역할을 수행하는 ROS 패키지

  • 예) 제어기 > 시리얼(rosserial 프로토콜) > PC(ROS 메시지로 재전송)
  • 예) 제어기 < 시리얼(rosserial 프로토콜) < PC(ROS 메시지를 시리얼로 변경)

일반적으로 마이크로컨트롤러는 ROS에서 기본 통신으로 사용하는 TCP/IP 통신보다 시리얼 통신을 많이 사용하므로 rosserial과 같은 중재자 역할이 필요하다.

rosserial은 rosserial server와 rosserial client로 구성되어있다. rosserial server는 구현된 프로그래밍 언어에 따라 3가지 노드가 있다.

  • rosserial_python: Python 언어 기반의 rosserial server로 일반적으로 많이 사용된다.
  • rosserial_server: C++ 언어 기반의 rosserial server로 동작 성능이 상대적으로 향상되었으나 rosserial_python에 비해 일부 기능의 제약이 있다.
  • rosserial_java: Java 언어 기반의 rosserial server로 안드로이드 SDK를 사용할 때 함께 이용한다.

rosserial client는 client 역할을 하는 라이브러리에 마이크로컨트롤러에서 사용되는 플랫폼에 포팅되었다.

  • rosserial_arduino: Arduino와 Leonardo 보드를 지원하지만 소스 수정을 통해 다른 보드에서도 사용 가능하다. 터틀봇3의 OpenCR 보드에서도 수정하여 사용하고 있다.
  • rosserial_embeddedlinux: 임베디디용 리눅스에서 사용 가능한 라이브러리다.
  • rosserial_windows: 윈도우 운영체제를 지원하고 윈도우의 응용프로그램과 통신을 지원한다.
  • rosserial_mbed: ARM사의 mbed를 지원한다.
  • rosserial_tivac: TI사의 Launchpad를 지원한다.

rosserial server와 client는 시리얼 통신 시반의 패킷 형태로 데이터를 송/수신한다. rosserial protocol은 바이트 단위로 정의되어있고 패킷의 동기화 및 데이터 검증을 위한 정보들이 포함되어있다.

rosserial 패킷 구성

1st Byte2nd Byte3rd Byte4th Byte5th Byte6th Byte7th ByteN BytesByte N+8
Sync FlagSync Flag/ Protocol versionMessage Length(N)Message Length(N)Checksum over message lengthTopic IDTopic IDSerialized Message DataChecksum over Topic ID and Message Data
  • Sync Flag: 패킷의 시작 위치를 알기 위한 헤더로 항상 0xFF 이다.
  • Sync Flag / Protocol version: 프로토콜 버전으로 ROS Groovy는 0xFF이고, ROS Hydro, Indigo, Jade 그리고 Kinetic은 0xFE 이다.
  • Message Length(N): 메시지의 데이터 길이 헤더이며 2 Byte로 구성된다. Low 바이트가 먼저 전송되고 이어서 High 바이트가 전송된다.
  • Checksum over message length: 메시지 길이 헤더의 유효성 검증을 위한 체크섬으로 계산 방법은 아래와 같다.

Checksum = 255 - ( (Message Length Low Byte + Message Length High Byte) %256) 

  • Topic ID: 메시지의 형태를 구분하기 위한 ID이다. 0~100까지는 시스템 함수로 예약되어있는데 주로 사용되는 ID는 아래와 같다.

ID_PUBLISHER=0, ID_SUBSCRIBER=1, ID_SERVICE_SERVER=2, ID_SERVICE_CLIENT=4,
ID_PARAMETER_REQUEST=6, ID_LOG=7, ID_TIME=10, ID_TX_STOP=11

  • Serialized Message Data: 송/수신 메시지를 시리얼 형태로 전송하기 위한 데이터이다. 예) IMU, TF, GPIO 데이터
  • Checksum over Topic ID and Message Data: Topic ID와 메시지 데이터의 유효성 검증을 위한 체크섬으로 계산 방법은 아래와 같다.

Checksum = 255 - ( (Topic ID Low Byte + Topic ID High Byte + data byte values) % 256) 

  • 메모리: 마이크로컨트롤러는 사용할 수 있는 메모리의 용량이 작고 제한되어있기 때문에 퍼블리셔, 서브스크라이버 개수 및 송신, 수신 버퍼의 크기를 미리 정의해야 한다.
  • Float64: 마이크로컨트롤러는 64비트 실수연산을 지원하지 않아 32비트형으로 변경된다.
  • Strings: 문자열 데이터를 String 메시지 안에 저장하지 않고 외부에서 정의한 문자열 데이터의 포인터 값만 메시지에 저장한다. 따라서 스트링 메시지를 사용하려면 아래와 같은 절차가 필요하다.

std_msg::String str_msg;
unsigned char hello[13] = "hello world";
str_msg.data = hello;

  • Arrays: 메모리 제약사항으로 배열 데이터에 대한 포인터를 사용해서 배열의 끝을 알 수 없다. 따라서 배열의 크기에 대한 정보를 추가해야 한다.
  • 통신 속도: UART 같은 경우6) 115200bps와 같은 속도로는 메시지의 개수가 많아지면 응답 및 처리속도가 느려 질 수 있다.

예제를 진행하기 전에 roscore를 먼저 실행한 후 진행해야 한다. 다음은 OpenCR에서 제공하는 기본 예제이다.

#include <ros.h>
#include <std_msgs/String.h>
#include <std_msgs/Byte.h>

int led_pin_user[4] = { BDPIN_LED_USER_1, BDPIN_LED_USER_2, BDPIN_LED_USER_3, BDPIN_LED_USER_4 };
ros::NodeHandle nh;

void messageCb( const std_msgs::Byte& led_msg) {
 int i;

 for (i=0;i<4;i++){
  if (led_msg.data & (1<<i)){
   digitalWrite(led_pin_user[i], LOW);
   }
  else
  {
   digitalWrite(led_pin_user[i], HIGH);
   }
  }
 }

ros::Subscriber<std_msgs::Byte> sub("led_out", messageCb ); 
 
void setup() {
 pinMode(led_pin_user[0], OUTPUT);
 pinMode(led_pin_user[1], OUTPUT);
 pinMode(led_pin_user[2], OUTPUT);
 pinMode(led_pin_user[3], OUTPUT);

 nh.initNode();
 nh.subscribe(sub);
} 

void loop() {
 nh.spinOnce();
}

rosserial_python을 이용해 rosserial server를 실행한다.

$ rosrun rosserial_python serial_node.py __name:=opencr _port:=/dev/ttyACM0 _baud:=115200
[INFO] [1495609829.326019]: ROS Serial Python Node
[INFO] [1495609829.336151]: Connecting to /dev/ttyACM0 at 115200 baud
[INFO] [1495609831.454144]: Note: subscribe buffer size is 1024 bytes
[INFO] [1495609831.454994]: Setup subscriber on led_out [std_msgs/Byte]

rostopic pub을 이용하여 led_out에 값을 입력하면 해당 값에 따라 LED가 동작한다.

$ rostopic pub -1 led_out std_msgs/Byte 1 // USER1 LED On
$ rostopic pub -1 led_out std_msgs/Byte 2 // USER2 LED On
$ rostopic pub -1 led_out std_msgs/Byte 4 // USER3 LED On
$ rostopic pub -1 led_out std_msgs/Byte 8 // USER4 LED On
$ rostopic pub -1 led_out std_msgs/Byte 0 // LED Off


1)
컴퓨터의 연산 속도의 단위. 1MIPS는 1초에 1백만 회의 명령을 실행하한다는 의미이다
2)
ROS에서는 이를 위해 rosserial이라는 패키지를 제공한다
3)
마이크로컨트롤러에서는 최상위인 Cortex-M7 코어를 사용해 최대 216Mhz로 구동됨
4)
입력 전원이 7~24V일 때 12, 5V, 3.3V의 출력을 지원
5)
메시지, 토픽, 서비스 모두 가능
6)
OpenCR에서는 USB를 통한 가상의 시리얼 통신을 적용하여 고속통신이 가능하다.
  • activity/public/2020/ros/210120.txt
  • 마지막으로 수정됨: 4년 전
  • 저자 david