ROS STUDY #8: ROSSERIAL
0. 임베디드 시스템
1. rosserial 개념
마이크로컨트롤러와 컴퓨터간의 메시지 통신을 위해 변환 작업을 수행하는 패키지이다.
* 제어기 > 시리얼(rosserial 프로토콜) > PC(ROS 메시지로 재전송)
* 제어기 < 시리얼(rosserial 프로토콜) < PC(ROS 메시지를 시리얼로 변경)
일반적으로 마이크로컨트롤러는 ROS에서 기본 통신으로 사용하는 TCP/IP보다 시리얼 통신을 많이 사용해 rosserial과 같은 중계자 역할이 필요하다.
2. rosserial package 구성
2.1 rosserial server
ROS가 구동되는 PC에서 rosserial protocol을 통해 중계자 역할을 하는 노드로 구현된 프로그래밍 언어에 따라 3가지 노드가 있다.
- rosserial_python: python 언어 기반의 rosserial server로 일반적으로 많이 사용한다.
- rosserial_server: C++ 언어 기반의 rosserial server로 동작 성능이 상대적으로 상향되었으나 rosserial_python에 비해 일부 기능 제약이 존재한다.
- rosserial_java: Java 언어 기반의 rosserial server로 Java 언어 기반의 모듈이 필요할 때, 혹은 안드로이드 SDK와 함께 사용될 때 이용한다.
2.2 rosserial client
rosserial의 client 역할을 하는 라이브러리로 rosserial_client 라이브러리가 마이크로컨트롤러에서 사용되는 플랫폼에 포팅되었다.
- rosserial_arduino: 아두이노 UNO와 Leonardo 보드를 지원하지만 소스 수정을 통해 다른 보드에서도 사용이 가능하다. 터틀봇3에 사용된 OpenCR 보드에서도 rosserial_arduino의 일부를 수정해서 사용하고 있다.
- rosserial_embeddedlinux: 임베디드용 리눅스에서 사용 가능한 라이브러리다.
- rosserial_window: 윈도우 운영체제를 지원하고 윈도우의 응용프로그램과 통신을 지원한다.
- rosserial_mbed: 임베디드 개발환경인 mbed 플랫폼을 지원해 mbed 보드들을 사용할 수 있다.
- rosserial_tivac: TI사에서 제작한 Launchpad 보드에서 사용하기 위한 라이브러리다.
3. rosserial protocol
rosserial server와 client는 시리얼 통신 기반의 패킷 형태로 데이터를 송/수신한다. rosserial protocol은 바이트 단위로 정의되어 있고 패킷의 동기화 및 데이터 검증을 위한 정보들이 포함되어 있다.
rosserial 패킷 구성
1st Byte | 2nd Byte | 3rd Byte | 4th Byte | 5th Byte | 6th Byte | 7th Byte | N Bytes | Byte N+8 |
---|---|---|---|---|---|---|---|---|
Sync Flag | Sync Flag/ Protocol version | Message Length(N) | Message Length(N) | Checksum over message length | Topic ID | Topic ID | Serialized Message Data | Checksum over Topic ID and Message Data |
각각의 Byte가 나타내는 바는 다음과 같다.
- Sync Flag: 패킷의 시작 위치를 알기 위한 헤더로 항상 0xff의 값을 가진다.
- Sync Flag / Protocol version: rosserial 프로토콜 버전으로 ROS Groovy가 0xff, 이후 Hydro, Indigo, Jade, Kinetic는 0xfe 값을 가진다.
- Message Length (N): 메시지의 데이터 길이 헤더이며 2 Byte로 구성된다. Low 바이트가 먼저 전송되고 이어서 High 바이트가 전송된다. High(4th byte), Low(3rd byte) 순으로 이어 붙인 값이 메시지의 길이를 나타낸다.
- Checksum over message length: 메시지 길이 헤더의 유효성 검증을 위한 체크섬이다.
Checksum = 255 - ( (Message Length Low Byte + Message Length High Byte) %256)
- Topic ID: 메시지의 형태를 구분하기 위한 ID이며 2 Byte로 구성된다. 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)
4. rosserial 제약사항
4.1. 메모리 제약
임베디드에 사용되는 마이크로컨트롤러는 사용할 수 있는 메모리가 제한되어 있고 일반 PC에 비해서는 상당히 작은 용량을 가지고 있다. 송신, 수신 버퍼의 크기를 초과하는 메시지의 데이터는 송신, 수신할 수 없으므로 주의가 필요하다.
*MCU: 마이크로컨트롤러 유닛, 마이크로프로세서와 입출력 모듈을 하나의 칩으로 만들어 정해진 기능을 수행하는 컴퓨터를 말한다. 개인용 컴퓨터가 다양한 요구에 따라 동작하는 일반적인 일에 사용된다면, MCU는 기능을 설정하고 정해진 일을 수행하도록 프로그래밍되어 장치 등에 장착되어 동작한다.
4.2. Float 64
rosserial client로 rosserial_auduino를 사용할 경우에 아두이노 보드에 사용된 MCU는 64비트 실수연산을 지원하지 않아 라이브러리를 생성할 때 자동으로 32비트형으로 변경한다. 만약 64비트 실수연산을 지원한다면 make_libraries.py에서 데이터 타입 변환 부분을 수정하면 된다.
4.3. Strings
마이크로컨트롤러의 메모리 제한으로 문자열 데이터를 String 메시지 안에 저장하지 않고 외부에서 정의한 문자열 데이터의 포인터값만 메시지에 저장한다. String 메시지를 사용하기 위해서는 다음과 같은 절차가 필요하다.
std_msgs::String str_msg; unsigned char hello[13] = "hello world!"; str_msg.data = hello;
4.4. Arrays
string과 마찬가지로 메모리 제약사항으로 배열 데이터에 대한 포인터를 사용해서 배열의 끝을 알 수가 없다. 따라서 배열의 크기에 대한 정보를 추가하고 메시지를 송신, 수신할 때 사용한다.
4.5. 통신 속도
UART 같은 경우 115200bps와 같은 속도로는 메시지의 개수가 많아지면 응답 및 처리속도가 느려질 수 있다. 하지만 OpenCR에서는 USB를 이용한 가상의 시리얼 통신을 적용하여 고속통신이 가능하다.
5. rosserial 설치
rosserial을 사용하기 위해서는 ROS의 필요한 패키지들을 설치하고 사용하고자 하는 디바이스의 플랫폼 client 라이브러리를 사용하도록 한다.
5.1. 패키지 설치
다음 명령어로 rosserial과 아두이노 계열 지원 패키지를 설치한다.
sudo apt-get install ros-melodic-rosserial ros-melodic-rosserial-server ros-melodic-rosserial-arduino
5.2. 라이브러리 생성
아두이노에서 사용하기 위해서는 아두이노용 rosserial 라이브러리를 생성해야 한다. 다음과 같이 아두이노 IDE의 개인 폴더의 라이브러리 폴더로 이동한 다음에 기존에 생성한 라이브러리가 있다면 삭제한다. rosserial_arduino 패키지의 make_libraries.py 파일을 실행하여 ros_lib를 생성한다.
cd ~/Arduino/libraries/ rm -rf ros_lib rosrun rosserial_arduino make_libraries.py .
5.3. 통신 포트 변경
아두이노 ROS 라이브러리를 생성한 경우에 기본으로 설정된 통신 포트를 사용하도록 되어 있다. 일반적인 아두이노 보드인 경우 HardwareSerial 클래스의 Serial 객체를 사용하도록 되어 있기 때문에 통신 포트를 변경하기 위해서는 생성된 라이브러리 소스를 수정해야 한다. 라이브러리 폴더의 ros.h 파일 내용을 보면 ArduinoHardware 클래스를 사용하여 NodeHandle을 정의 한다. 따라서 사용할 포트의 하드웨어 기능을 ArduinoHardware 클래스에서 변경하면 된다.
#include "ros/node_handle.h" #include "ArduinoHardware.h" namespace ros { /* Publishers, Subscribers, Buffer Sizes */ typedef Nodehandle_<ArduinoHardware, 25, 25, 1024, 1024> NodeHandle; }
6. Rosserial 예제-LED
6.1. 회로도
6.2.1. 코드(Arduino IDE)
#include <ros.h> #include <std_msgs/Empty.h> ros::NodeHandle nh; void messageCb( const std_msgs::Empty& toggle_msg){ digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) delay(1000); // wait for a second digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW delay(1000); // wait for a second } ros::Subscriber<std_msgs::Empty> sub("toggle_led", &messageCb ); void setup() { pinMode(LED_BUILTIN, OUTPUT); nh.initNode(); nh.subscribe(sub); } void loop() { nh.spinOnce(); delay(1); }
6.2.2. 코드(Ros)
roscore
rosrun rosserial_python serial_node.py __name:=arduino _port:=/dev/ttyACM0 _baud:=57600
rostopic pub /toggle_led std_msgs/Empty