======ROS 스터디 #7: 임베디드 시스템====== 2021년 01월 20일 18:00\\ ZOOM 화상회의 {{youtube>xNYE07gjrds?medium}} =====1. 개념===== **임베디드 시스템(embedded system, 내장형 시스템)** 기계나 기타 제어가 필요한 시스템에 대해 제어를 위한 특저 기능을 수행하는 컴퓨터 시스템으로 장치 내에 존재하는 전자 시스템이다. 즉 임베디드 시스템은 전체 장치의 일부분으로 구성되며 제어가 필요한 시스템을 위한 두뇌 역할을 하는 특정 목적의 컴퓨터 시스템이라고 정의할 수 있다. || **임베디드 보드 종류** ^ ^8/16-bit MCU^(small) 32-bit MCU^(big) 32-bit MCU^ARM A-class^x86^ |보드 예|Atem AVR|ARM Cortex-M0|ARM Cortex-M7|Samsung Exynos|Intel Core i5| |시스템 예|Arduino, Leonardo|Arduino M0 pro|SAM V71|ODROID|Intel NUC| |MIPS((컴퓨터의 연산 속도의 단위. 1MIPS는 1초에 1백만 회의 명령을 실행하한다는 의미이다))|10's|100's|1000's|10000's| |RAM|1~32KB|32KB|384KB|a few GB(off-chip)|2-16 GB (SODIMM)| |최대 출력|10'of mW|100'of mW|100'of mW|1000'of mW|10000'of mW| |주변 기기|UART, USB FS 등|USB FS|Ethernet,USB HS|Gigabit Ethernet|USB SS, PCle| || 리눅스 같은 운영체제는 실시간성을 보장하지 못하기 때문에 엑추에이터나 센서등을 제어하기 위해서는 실시간 제어에 적합한 마이크로컨트롤러를 사용한다. 이런 임베디드 시스템에는 ROS를 설치할 수 없기 때문에 시스템과 (ROS가 설치된) PC 사이의 통신이 필요하다.((ROS에서는 이를 위해 rosserial이라는 패키지를 제공한다)) =====2. Open CR===== ====2.1 개념==== {{https://img.kasimov.synology.me/2020/ros/9/OpenCR.JPG?400}} ROS를 지원하는 임베디드 보드이며 터틀봇3에서 메인 제어기로 사용된다. 회로, BOM, 거버 데이터 등의 H/W 정보 및 OpenCR의 모든 S/W가 오픈소스로 공개되어 있으며 성능이 좋고((마이크로컨트롤러에서는 최상위인 Cortex-M7 코어를 사용해 최대 216Mhz로 구동됨)) 인터페이스가 다양하다. 또한 IMU 센서를 사용하며 전원 출력이 다양((입력 전원이 7~24V일 때 12, 5V, 3.3V의 출력을 지원))하다. || ====2.2 개발환경 구축==== [[https://emanual.robotis.com/docs/en/platform/turtlebot3/appendix_opencr1_0/|Turtlebot3 e-Manual]] 참조 =====3. rosserial===== ====3.1. 개념==== PC와 제어기 간의 메시지((메시지, 토픽, 서비스 모두 가능)) 통신을 위해 중계자 역할을 수행하는 ROS 패키지 * 예) 제어기 > 시리얼(rosserial 프로토콜) > PC(ROS 메시지로 재전송) * 예) 제어기 < 시리얼(rosserial 프로토콜) < PC(ROS 메시지를 시리얼로 변경) 일반적으로 마이크로컨트롤러는 ROS에서 기본 통신으로 사용하는 TCP/IP 통신보다 시리얼 통신을 많이 사용하므로 rosserial과 같은 중재자 역할이 필요하다. || ====3.2. package 구성==== 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를 지원한다. || ====3.3. 프로토콜==== 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| * 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) || ====3.4. 제약 상황==== * **메모리**: 마이크로컨트롤러는 사용할 수 있는 메모리의 용량이 작고 제한되어있기 때문에 퍼블리셔, 서브스크라이버 개수 및 송신, 수신 버퍼의 크기를 미리 정의해야 한다. * **Float64**: 마이크로컨트롤러는 64비트 실수연산을 지원하지 않아 32비트형으로 변경된다. * **Strings**: 문자열 데이터를 String 메시지 안에 저장하지 않고 외부에서 정의한 문자열 데이터의 포인터 값만 메시지에 저장한다. 따라서 스트링 메시지를 사용하려면 아래와 같은 절차가 필요하다. std_msg::String str_msg; unsigned char hello[13] = "hello world"; str_msg.data = hello; * **Arrays**: 메모리 제약사항으로 배열 데이터에 대한 포인터를 사용해서 배열의 끝을 알 수 없다. 따라서 배열의 크기에 대한 정보를 추가해야 한다. * **통신 속도**: UART 같은 경우((OpenCR에서는 USB를 통한 가상의 시리얼 통신을 적용하여 고속통신이 가능하다.)) 115200bps와 같은 속도로는 메시지의 개수가 많아지면 응답 및 처리속도가 느려 질 수 있다. || ====3.5. 예제==== 예제를 진행하기 전에 roscore를 먼저 실행한 후 진행해야 한다. 다음은 OpenCR에서 제공하는 기본 예제이다. #include #include #include 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< 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