최근 들어 로보틱스 분야에서 플랫폼 붐이 일고 있다. 플랫폼은 소프트웨어 플랫폼과 하드웨어 플랫폼으로 나뉠 수 있다. 로봇 소프트웨어 플랫폼이란 로봇 응용프로그램을 개발할 때 필요한 하드웨어 추상화, 하위 디바이스 제어, 로보틱스에서 많이 사용되는 센싱, 인식, 자기 위치 추정과 지도 작성(SLAM), 모션 플래닝(Motion Planning) 등의 기능 구현은 물론이고, 패키지 관리, 개발 환경에 필요한 라이브러리와 다양한 개발/디버깅 도구 등을 포함하는 것을 말한다. 로봇 하드웨어 플랫폼의 경우 모바일 로봇, 드론, 휴머노이드 형태의 연구용 하드웨어 플랫폼뿐만 아니라 소프트뱅크의 페퍼, MIT 미디어랩의 지보 등 상업성 제품들도 출시를 코앞에 두고 있다. 

주목할 점은 이 하드웨어들도 앞서 말한 소프트웨어 플랫폼과 연계되어 추상화가 이루어지고 있다는 점이다. 이는 하드웨어에 대한 전문 지식이 없어도 소프트웨어 플랫폼을 이용하여 응용 프로그램을 작성할 수 있다는 것을 의미한다. 이는 최신 스마트폰의 하드웨어 구성이나 세부 내용을 몰라도 애플리케이션(앱)을 작성할 수 있는 것과 마찬가지이다. 또한, 로봇 개발자가 하드웨어 설계부터 소프트웨어 설계까지 도맡아 하던 이전 작업 프로세스와 달리, 더 많은 소프트웨어 인력들이 로봇 응용 프로그램 개발에 참여할 수 있다. 즉, 소프트웨어 플랫폼 덕분에 많은 이들이 로봇 개발에 동참할 수 있게 되었고, 소프트웨어 플랫폼에서 제안하는 인터페이스에 맞춰 로봇 하드웨어가 설계되고 있다.

이러한 소프트웨어 플랫폼에는 대표적으로 로봇 운영체제라 불리는 ROS(Robot Operating System)와 일본의 오픈 로보틱스 미들웨어(OpenRTM), 유럽의 실시간 제어 중심의 OROCOS, 한국의 OPRoS 등이 있다. 각자 이름은 다르지만 로봇 소프트웨어 플랫폼이 만들어진 근본적인 이유는 로봇 소프트웨어가 너무 다양하고 복잡해서 발생하는 여러 문제들을 전 세계의 로봇 연구자가 서로 협업하여 해결하려는 것이다. 

예를 들어, 로봇이 주변 상황을 인식하는 기능을 구현할 때 하드웨어가 다양하고 실생활에 직접 사용된다는 점은 어려움으로 작용한다. 사람의 입장에서는 매우 사소한 일이라고 하더라도 로봇의 입장에서는 센싱, 인식, 지도 작성, 모션 플래닝 등의 기능을 구현해야 하는데 개별 연구실, 회사에서는 이 모든 부분을 처리하기 힘들다. 하지만 전 세계의 관련 종사자가 각자 자신 있는 부분을 공유하여 다른 그룹에서 이를 사용할 수 있게 한다면 이야기는 달라진다. 

소셜펀드 킥스타터와 CES2015에서 주목받은 로봇베이스(Robotbase)라는 로봇 전문기업은 최근에 로봇베이스 퍼스널 로봇(Robotbase Personal Robot)을 개발하여 소셜펀드에서 론칭에 성공하였다. 로봇베이스사의 경우 자신들이 강한 얼굴인식과 물체인식에 집중하고, 모바일 로봇은 유진로봇의 거북이, 액추에이터로는 로보티즈의 다이나믹셀을 이용하였고 장애물 인식, 내비게이션, 모터 드라이브 등은 모두 ROS의 공개 패키지를 사용했다는 점에서 협업 가능성에 대한 실례를 보여주었다.

그럼 “왜 로봇 소프트웨어 플랫폼을 써야 하는가?” 몇가지 예를 들며 알아보도록 하겠다. 

첫째, 프로그램의 재사용성이다. 자신이 개발하고자 하는 부분에 집중하고 나머지 기능에 대해서는 관련 패키지를 다운로드하여 사용할 수 있다. 마찬가지로 자신이 개발한 프로그램은 다른 이들이 사용할 수 있도록 공유할 수도 있다. 예를 들어, 미국 NASA의 경우 국제 우주 정거장에서 사용하는 로보노트2(Robonaut2) 로봇 제어를 위하여 자체 프로그램 이외에도 다양한 드라이버 기능과 멀티 플랫폼에서 사용 가능한 ROS와 실시간 제어, 메시지 통신 복구, 신뢰성을 갖춘 OROCOS를 혼용함으로써 우주에서 임무를 수행할 수 있었다고 한다. 앞서 소개한 로봇베이스사의 경우도 재사용성을 충분히 살린 예이다.

둘째, 통신 기반 프로그램이다. 흔히, 하나의 서비스를 제공하기 위하여 센서나 액추에이터 단의 드라이브부터 센싱, 인식, 동작까지 하나의 프레임에서 프로그램을 작성하는 것이 많은데 로봇 소프트웨어의 재사용을 위해서는 이를 각각 처리 프로세서의 목적에 따라 작게 나누게 된다. 플랫폼마다 이를 컴포넌트화 혹은 노드 패키지화라고 한다. 최소 실행 단위로 나뉜 프로그램은 나누어진 컴포넌트(노드)끼리 데이터를 주고받아야 하는데 플랫폼들은 이 데이터 통신에 대한 전반적인 사항을 모두 갖추고 있다. 각 컴포넌트는 하드웨어 의존성을 떠나 네트워크에서 통신을 제공함으로써 네트워크 프로그래밍이 가능하게 되고 로보틱스에서 흔히 다루는 원격제어에서 매우 유용하다. 또한, 최소 실행 단위로 프로그램을 나누게 되면 작은 단위로 디버깅할 수 있어서 오류를 찾아낼 때도 매우 유용하다.

셋째, 개발도구 지원이다. ROS의 경우 디버깅 관련 툴, 2차원 플롯과 3차원 시각화 툴을 제공함으로써 로봇 개발에 필요한 개발 도구를 사용할 수 있다. 예를 들어, 로봇 개발에 있어서 로봇의 모델을 시각화하는 경우가 많은데 이를 정해진 포맷에 맞추기만 하면 로봇의 모델을 직접 확인할 수 있을 뿐만 아니라 3차원 시뮬레이터도 제공하기 때문에 시뮬레이션으로의 확장도 용이하다. 또한, 요즘 주목받는 마이크로소프트사의 키넥트 등으로 얻은 3차원 거리 정보는 점 군을 나타내는 포인트 클라우드 형태로 쉽게 변환하여 보여준다. 그 이외에도 실험에서 사용된 데이터는 녹화할 수 있으므로 언제든지 필요할 때 재생하여 실험할 때의 상황을 그대로 재현할 수도 있다. 이처럼 로봇 개발에 꼭 필요한 소프트웨어 도구를 제공하여 개발 편의성을 극대화한 점이 중요한 원인 중의 하나이다. 

넷째, 생태계 조성이다. 스마트폰 혁명은 안드로이드와 iOS 등 소프트웨어 플랫폼이 만든 생태계가 있기 때문이라고 한다. 로봇 분야도 마찬가지 흐름으로 이어 가고 있다. 처음에는 각종 하드웨어 기술들이 넘쳐흘렀으나, 이를 통합해 줄 운영체제가 전무했다. 이 상황에서 앞서 설명했던 것과 같이 다양한 소프트웨어 플랫폼이 등장했고, 가장 주목 받은 ROS의 경우 이제 그 생태계의 틀을 갖추기 시작했다. 그리고 로봇과 센서 회사처럼 로봇 관련 하드웨어 분야의 개발자, ROS 개발 운용팀, 응용 소프트웨어 개발자, 사용자 모두가 웃을 수 있는 생태계를 만들어 가고 있다. 아직 그 시작은 미미하지만, 점점 늘고 있는 사용자들과 로봇 관련 회사들 그리고 급격히 늘고 있는 관련 툴 및 라이브러리를 볼 때 머지않아 생태계가 원만하게 돌아갈 것이라고 기대해본다. 

다섯째, 활성화된 커뮤니티이다. 이 부분이 가장 중요하지 않을까 싶다. 지금까지 닫혀 있던 로봇 학계, 로봇 업계는 위에서 언급한 기능들로 인하여 서로 협업을 중시하는 방향으로 나아가고 있고 그 목적이야 서로 다를 수 있겠지만 이러한 소프트웨어 플랫폼을 통하여 협업이 실제로 이루어지고 있다. 이 중심에는 오픈 소스 소프트웨어 플랫폼의 커뮤니티가 있다. 예를 들어 ROS의 경우에는 자발적으로 1,600개 이상의 패키지들이 개발되어 공유되고 있고, 그 사용 방법을 설명한 위키 페이지가 사용자들의 개별적인 참여로 14,600페이지를 넘어서고 있다. 그리고 커뮤니티에서 매우 중요한 질의응답의 경우 18,000건 이상이 오고 가며 상생의 커뮤니티를 만들어 가고 있다. 이는 단순히 사용법에 대한 토론을 넘어서 로보틱스 소프트웨어에서 필요한 구성요소를 찾아내고 규칙을 만들고 있다. 나아가 로보틱스의 발전을 위해 로봇의 소프트웨어가 갖추어야 할 부분에 대해서 고민하고 부족한 부분은 서로 협업을 통하여 다수가 하나의 퍼즐을 맞추는 식으로 발전하고 있다.

로봇공학은 미래 산업, 차세대 성장 동력이라는 이름으로 산업용 로봇을 제외한 어떠한 비즈니스 모델도 없음에도 불구하고 장밋빛 미래를 바라며 기대만을 한껏 받고 있다. 문제는 이러한 이야기는 10년 전이나 지금이나 마찬가지지만 이렇다 할 답을 못 얻고 있다는 것이다. 그 이유는 무엇일까? 다양한 의견들이 있겠지만 로봇공학은 아직 비즈니스 모델로 만들기에는 부족한 부분이 많다는 것이고 아직 풀어야 할 숙제가 많다고 생각한다. 이 해결책으로 국가를 뛰어넘는 초월적인 협업이 이루어져야 한다고 생각하고 이는 소프트웨어 플랫폼과 이를 지탱하는 커뮤니티가 해결할 수 있다고 생각한다. ROS의 경우 학계 연구자, 산업현장의 개발자, 그리고 취미로 활동하는 하비스트(hobbyist)까지 개발에 참여하고 있다. 더욱이 로봇 전공자뿐만 아니라 네트워크 전문가, 컴퓨터 사이언스, 컴퓨터 비전 분야의 사람들도 대거 참여하고 있어서 로봇 분야뿐만 아니라 다양한 배경의 지식이 모여 융합을 이루고 있어서 로봇공학이 지금까지와는 다른 양상으로 발전할 것으로 예상되며, 개방과 협업을 통하여 지금까지 풀지 못한 숙제들을 풀 수 있을 것으로 생각한다. 또한, 이는 로봇 개발이 급속도로 발전할 수 있는 계기가 될 수 있을 것이다. 


▒  표윤석ㆍ일본 큐슈대학 JSPS 연구원

http://www.irobotnews.com/news/articleView.html?idxno=4436

리눅스에서 콘솔 프로그램을 백그라운드로 실행해야 할 경우 screen 명령어가 굉장히 유용하다.

원격 작업하다보면 여러창을 보고 싶을때도 있어 보통 터미널을 여러개 띄워 사용한다. 하지만 왔다갔다하기 귀찮아 많이 이용한다.

간단히 nohup으로 사용할수 있지만 screen명령어가 더 강력하고 유연하므로 많이 사용된다.


우선 사용하기 위해 screen 명령어를 써보니 라즈비안에선 깔려 있지 않다.


라즈비안은 Ubuntu 기반이므로

sudo apt-get install screen 명령어로 손쉽게 깔 수 있다.


CentOS

yum install screen 으로 설치하면 된다.



=========================================================================

screen 명령어의 메뉴얼로는


1. 쉘모드 명령어
  screen
   : screen 을 시작 하는 기본 명령입니다.
   : 기본 세션명으로 시작합니다.
  screen -S 세션명
   : -S 다음에 주는 세션명으로 시작합니다.
  screen -list
   : -list 옵션을 주고 실행하면 이전에 작업했었던 screen 리스트가 있으면 세션명과 함께 리스트를 보여줍니다.
  screen -R 세션명
   : 이전에 세션이 있을 경우 -R 다음에 오는 세션명으로 이전 작업을 불러옵니다.
   : -R 다음에 세션명을 주지 않았을 경우에는 이전 세션이 한개만 있을 경우 그 작업을 불러옵니다.
   : 이전 작업이 여러개 있을 경우에는 이전 작업 리스트를 보여줍니다.
   : 이 경우에는 원하는 세션명을 주고 시작 하면 되겠죠. ^__^
   
2. screen 실행후 명령어
  screen 실행후의 명령어는 Ctrl-a로 시작합니다:

Ctrl-a, c       : (create) 새로운 쉘이 생기면서 그 쉘로 이동
Ctrl-a, a       : 바로 전 창으로 이동
Ctrl-a, n       : (next) 다음 창으로 이동
Ctrl-a, p       : (previous) 이전 창으로 이동
Ctrl-a, 숫자    : 숫자에 해당하는 창으로 이동
Ctrl-a, '       : 창번호 또는 창이름으로 이동 ( ' => 싱글 쿼테이션 )
Ctrl-a, "       : 창번호를 보여준다. ( " => 더블 쿼테이션 )
Ctrl-a, A       : 현재 창의 title을 수정
Ctrl-a, w       : 창 리스트 보여주기
Ctrl-a, esc     : Copy 모드로 전환. Copy 모드에서는 vi의 이동키로 이동을 할 수 있다.
Crtl-a, [         커서 이동을 할 수 있고 특정 블럭을 복사하는 기능으로 사용한다.
                 먼저 시작 위치에서 space 바를 누르고 끝 위치에서 space 바를 누르면 해당 부분이 buffer로 복사된다.
Ctrl-a, ]       : buffer의 내용을 stdin으로 쏟아 넣는다.
                 이 기능은 vi의 입력모드에서 사용하면 유용하다.
Ctrl-a, :(콜론) : 명령행 모드로 전환
Ctrl-a, d       : (detach) 현재 작업을 유지하면서 screen 세션에서 빠져나옴
                 세션이 종료 되지 않습니다.
Ctrl-a, x       : lock screen

아래 부분은 창을 나눠서 사용하는 명령입니다.

Ctrl-a, S       : (split) 창을 나눔 (region)
Ctrl-a, Tab     : 다른 region으로 이동
Ctrl-a, Q       : 현재 region을 제외한 나머지 숨기기


그리고 마지막 명령으로 세션을 완전히 빠져 나오는 명령입니다.

exit : screen 의 쉘상에서 exit 를 치고 엔터를 하면 세션이 완전히 종료 됩니다.


이상 위의 명령어 들만 알고 있으면 screen 사용시 불편하지 않게 screen 을 사용 할 수 있을 겁니다.

참 고
다른 사용자 분들의 의견을 보면 screen 화면을 2, 3 개정도 띄우고 사용하는게 가장 적당하다고 합니다.
4개 이상 띄우고 사용하다보면 불편하다고 하네요.
여러분들은 어느정도가 적당한지 한번 사용해보시고 판단 하시기 바랍니다.


기타 더욱 자세한 내용을 아시고 싶으시면 man 페이지나 /usr/doc/screen 을 참고하시기 바랍니다. 


참고 : KLTP( http://kltp.kldp.org/ )
      리눅스 사랑넷 ( http://linux-sarang.net )

      [펌] http://blog.naver.com/xinfra/80007388674


Arduino 초음파센서(HC-SR04) 사용하기


사용할 센서는 HC-SR04이다.


HC-SR04 센서는 초음파를 이용하여 거리를 측정할 수 있는 센서다초음파는 귀에

들리지 않을 정도의 높은 주파수(약 20MHz이상)의 소리를 말하며초당 340m의 속도를

갖는다이러한 초음파의 속도를 바탕으로 시간관련 함수를 응용하면 초음파를 활용하여

거리나 수위적설 등을 측정할 수 있다.

 

초음파 거리센서의 구조는 생각보다 간단하다총 4개의 핀으로 구성되어있으며 양끝의

VCC와 Gnd를 통해 전원(5V)을 입력받고, Trig핀을 통해 신호를 입력 받으면 초음파를

발신초음파가 다시 수신되면 Echo핀을 통해 신호를 출력한다센서는 5V에서 작동하며

약 15mA의 전류를 사용한다측정 범위는 최소 2cm에서 4m이지만 주변 환경에 의해

오차가 발생할 수 있다.



초음파 거리 측정

 

회로도 연결


Vcc,Pin13,12,GND 차례로 연결해주었다.



실제 연결 모습이다.


초음파 거리센서의 VCCGND는 전원(5V,GND)에 연결을 해주며 Trig는 초음파를 쏘기

위해 디지털 핀 출력 설정을, Echo는 초음파를 받기 위해 디지털 핀 입력을 설정해준다.



Source


int trig = 13; // 변수 trig를 생성하고 13를 대입한다

int echo = 12; // 변수 echo를 생성하고 12을 대입한다

 

void setup() {

pinMode(trig, OUTPUT); // trig(13)핀을 출력모드로 설정한다

pinMode(echo, INPUT); // echo(12)핀을 입력모드로 설정한다

Serial.begin(9600); //보드레이트를 설정합니다.

}

 

void loop() {

digitalWrite(trig, HIGH); // trig(13)핀에 HIGH신호를 보낸다

delayMicroseconds(10); // 10마이크로초(1/100,000) 동안

digitalWrite(trig, LOW); // trig(13)핀에 LOW신호를 보낸다

int distance = pulseIn(echo, HIGH) * 17 / 1000;

// 변수 distance = echo핀이 다시 HIGH신호를 받기까지의 시간 * 17/1000

Serial.print(distance); // 변수 distance를 출력한다

Serial.println("cm"); // 문자열 cm을 출력하고 줄바꿈

delay(200); // 딜레이 0.1

}

 

계산식


거리를 구하기 위한 식은 다음과 같다.

거리 = 시간 x 속도

거리를 구하기 위해서는 시간과 속도를 구해야하며, 시간과 속도는 다음과 같다.

 

속도 : 초음파의 속도는 초당 340m.

 

시간을 구하기 위해 pulseIn()이라는 함수가 등장했다. 위에서 사용한 pulseIn(echo,HIGH)

해석해보자면, echo핀이 HIGH상태에서 LOW신호를 받고, 다시 HIGH를 받기까지의 시간, 즉 다시

HIGH가 되기까지의 시간을 반환한다. , 10 마이크로초동안 쏜 초음파를 받기까지의 시간을

구하게된다.

시간: pulseIn(echo, HIGH); // 초음파가 돌아오는 시간

*17을 한 이유는 pulseln()함수로 구한 시간이 왕복 시간이기

때문입니다. 그렇기에 식에 나누기2를 합니다. /1000을 한 이유는 그냥 보기 좋게 cm로 단위를

맞추기 위함이다.

 

 

 

 

결과



Arduino LED 제어하기





모든 제어동작할 때 항상 기본적으로 시작하는 LED제어를 해보았다.




LED의 회로는 항상 간단하다.


소스

void setup() {

//LED 핀 설정을 해줍니다.

pinMode(11,OUTPUT);

}

 

void loop() {

//1개씩 순차적으로 켭니다.

digitalWrite(11,HIGH);

delay(500);

 

//1개씩 순차적으로 끕니다.

digitalWrite(11,LOW);

delay(500);

}

 

이 함수들을 간단히 이용한다면 사용자가 원하는 다양한 패턴을 사용할 수 있다.



=========================================================================


pinmode() : Arduino 보드의 pin을 입력 핀으로 사용할지 아니면 출력 핀으로 사용할지 설정하기 위하여 사용한다.

 

아두이노 우노에서는 20개의 디지털 입/출력 핀들이 제공되고 있습니다. 디지털 14 (D0 ~ D13), 아나로그 6(A0 ~ A5) 20개를 지원합니다. 즉 디지털 입출력 핀이 모자라면, 아나로그 핀들도 디지털 핀들로 사용 가능하다는 것입니다.

우선 사용할 핀들을 어떻게 사용할 것인지를 결정하는 명령이 바로 pinMode입니다.

문법

void pinMode(pin, mode)

pin은 디지털 입출력으로 사용할 핀 번호이며, modeINPUT, INPUT_PULLUP, OUTPUT과 같이 동작할 모드 값입니다.

설명

pin 값으로 디지털 핀들은 0 ~ 13 값으로 할당되고, 아나로그 A0 ~ A5 핀들은 14 ~ 19로 할당되어 사용됩니다. A0A5는 상수로 정의되어 있어 mode 값으로 같이 사용할 수 있습니다. A0 혹은 14를 사용하여도 같은 결과를 얻을 수 있습니다.

modeINPUT, INPUT_PULLUP, OUTPUT 3가지 모드를 지원하고 있습니다. OUTPUT은 핀을 출력 용으로 사용하고자 할 때 , INPUTINPUT_PULLUP은 둘 모두 입력 용으로 사용하고자 할 때 필요한 것이며, INPUT_PULLUP은 내부적으로 PULLUP 저항을 사용하여 외부에서 신호 값을 넣지 않을 경우에는 PULLUP 저항을 통하여 +5V가 공급되어 "1"로 읽히게 됩니다. 이를 간단한 회로도를 통하여 설명하는 것이 이해가 쉬울 것입니다.



digitalWrite() : digitalWrite(pin, value)는 디지털 출력 핀 pin에 정수 0(LOW) 혹은 정수 1(HIGH) 값을 써 출력 핀의 전압을 0V 혹은 아두이노 보드의 동작 전압에 따라 +3.3V 혹은 +5V로 만드는 함수

 

문법

아두이노에서 사용되는 모든 디지털 핀들의 번호 값을 pin에 넣고, 핀의 출력 전압을 0로 하고자 하면, 0 혹은 LOWvalue에 넣고, 출력 전압을 VCC로 하고자 하면 1 혹은 HIGH를 넣고 호출하면 됩니다. 아날로그 A0 ~A5 핀들도 디지털 핀들로 사용될 수 있으며, pinA0와 같은 값을 넣으면 됩니다.

void digitalWrite(uint8_t pin, uint8_t value)

주의하여야 할 사항으로 먼저 pinMode(...) 함수를 사용하여 핀의 동작 모드를 출력 모드로 설정한 후에 digitalWrite(...) 함수를 사용하여야 원하는 동작 결과를 얻을 수 있습니다. 물론 내부 풀업 저항으로 사용하기 위한 용도일 경우, 필요치 않습니다:

// 사용하는 3, 4, A0, A1 핀들을 출력 핀으로 설정합니다:

pinMode(3, OUTPUT);

pinMode(4, OUTPUT);

pinMode(A0, OUTPUT);

pinMode(A1, OUTPUT);

 

digitalWrite(3, HIGH); // HIGH1과 같습니다

digitalWrite(4, 1); // 출력 핀을 VCC로 만듭니다

digitalWrite(5, HIGH); // 내부 풀업 저항으로 사용

// pinMode(5, INPUT_PULLUP)과 같은 효과

digitalWrite(A0, LOW); // LOW0와 같습니다

digitalWrite(A1, 0); // 출력 핀을 0volt로 만듭니다

또한 analogWrite(pin, value) 함수로 PWM을 신호를 발생하고 있는 출력 핀에 사용하면 당연히 PWM 신호 발생이 중단되게 됩니다.

 


Arduino 온도센서(LM35) 사용


아두이노를 접하면 기본적인 센서 중 하나가 온도센서 LM35이다.

작동전압은 작동 전압은 2.7V ~ 5.5V 이다.

0~100도까지 측정가능하다고 한다.

 

아래 이미지의 순서대로 연결하면 센서가 작동한다.


Source

float temperature;

int reading;

int lm35Pin = A0;

 

void setup()

{

analogReference(DEFAULT); // param

Serial.begin(9600);

}

 

void loop()

{

reading = analogRead(lm35Pin);

//temperature = reading/9.31; //1.1기준전압인 INTERNAL로 할 때 적용 식

temperature = (5.0 * analogRead(lm35Pin) * 100.0) / 1024;

Serial.println(temp);

delay(1000);

}

 

LM35센서에 적용되어 전압을 온도로 변환시키는 공식이 설명되어있다.


http://playground.arduino.cc/Main/LM35HigherResolution


temp = (5.0 * analogRead(tempPin) * 100.0) / 1024;

 

결과

analogReference(INTERNAL)


analogReference(DEFAULT)





====================================================

analogReference() : ADC의 기준전압을 설정하는 함수

param

-DEFAULT: analogReference() 함수를 사용하지 않고 analogRead() 함수를 사용하면 기본으로 동작하는 모드이며, analogReference(DEFAULT) 함수를 사용하면 5V 전압을 사용하는 아두이노 보드들에서는 5 볼트, 3.3V 전압을 사용하는 아두이노 보드들은 3.3 볼트를 아날로그 기준 전압 값으로 사용한다.

 

-INTERNAL: 내장 기준 전압을 사용한다는 의미이며, ATmega168ATmega328 MCU를 사용한 아두이노 보드에서는 1.1 볼트, ATmega8 MCU를 사용한 보드에서는 2.56 볼트가 아날로그 기준 전압이 된다. 이 설정은 아두이노 메가에서는 사용할 수 없다.

 

-INTERNAL1V1 (아두이노 메가 보드 전용): 내장 1.1 볼트를 아날로그 입력 기준 전압으로 사용한다는 의미다. 아두이노 메가 보드에서만 사용 가능하다.

 

-INTERAL2V56 (아두이노 메가 보드 전용): 내장 2.56 볼트를 아날로그 입력 기준 전압으로 사용한다는 의미이며, 아두이노 메가 보드에서만 사용 가능하다.

 

-EXTERNAL: AREF 핀에 외부 전원(0에서 5V 사이의 전압만 사용하여야 함)을 연결하여 기준 전압으로 사용할 수 있다.

경고

===

0V 보다 작거나 5V 보다 높은 외부 전압을 AREF 핀에 연결하지 안됨. AREF 핀에 외부 기준 전압을 연결하여 사용하고자 하면, analogRead() 함수를 사용하기 전에 꼭 EXTERNAL로 기준 전압을 설정해야 한다. 이렇게 하지 않으면, 내부에서 만들어낸 기준 전압과 외부에서 AREF 핀으로 공급되는 전압이 합선되어(short) 아두이노 보드의 마이크로컨트롤러를 손상시킬 수 있다.

 

다른 방법으로서, 외부와 내부 기준 전압을 번갈아 가며 사용할 수 있도록 외부 기준 전압을 5K 저항을 통하여 AREF 핀에 연결할 수 있다. 이렇게 저항을 통하여 외부 기준 전압을 공급하면 AREF 핀에 내부 저항 32K가 연결되어 있어 외부 기준 전압과 다른 전압이 AREF 핀에 공급되게 된다. 두 저항이 전압 분배기와 같이 동작하기 때문이며, 예를 들어 2.5V가 저항을 통하여 AREF 핀에 공급되면, AREF 핀의 전압은 2.5 * 32 / (32 + 5) = ~2.2V가 된다.

 

 



Arduino Tool 설치

 

www.arduino.org 접속한다.


다운로드 탭에서 PC설정에 맞는 설치파일로 받는다. 나는 Window라 Window로 받았다.


설치파일 실행하면 이런 창이 뜬다 "I Agree"


기본설정으로 설치하였다. "Next"


Driver들을 설치해주어야한다. "항상 신뢰 후 설치"


설치 된 항목을 확인할 수 있다.




장치관리자에서 설치가 되었는지 확인할 수 있다.


처음 실행한 Arduino Sketch 모습이다.


사용하고자하는 Board 설정을 해준다.


아까 장치관리자에서 확인한 컴포트 넘버링을 설정해준다.


기본적으로 예제들을 제공하고 있어 손쉽게 다룰 수 있다.


졸업작품을 준비할 때 썼던 assert 함수에 대해 기록하고자한다.


assert 함수는 assert.h 헤더를 추가해 주어야 사용 할 수 있다고 인터넷 설명에 나와있지만


기본적인 사용 (예로 설명하겠다.) 을 하고자 한다면 assert.h없이 그냥 사용하여도 무방하다고 한다.


assert는 조건에 따라 에러메세지를 띄우고 종료되므로 어떠한 가정하에 점검하기 좋은 방법이다. (Debug 모드에서만)

 -> Release 모드에서는 코딩하지 않음.



원리는 간단하다.


assert(parm) 인데 파라미터 값이 1이 된다면 에러없이 코딩 될 것이고

                                          0이 된다면 에러로 코딩이 종료될 것이다.

( 조건문을 떠올리면 이해하기 쉽다. )

--> 그렇다면 어디서 Err난지도모르고 쓸모 없지 않는가 싶다.


어디서 에러난지 구분하기위해 메세지를 넣어야겠다.


a=1;

assert(a!=1 && "a는 1이지");

//assert의 인자값은 거짓(FALSE) 이므로 "a는 1이지"란 메세지를 띄우며 종료 된다.




정리하자면

- assert 매크로는 코드상 과정을 철저하게 점검하기 위해 간단히 사용 할 수 있다.

- 프로그램을 방어적으로 만들어줌.

- Release 모드시 assert 매크로는 컴파일되지 않는다 => 코드완료 후 일일이 안지워도 됨)

여기서 주의할점은

 *** Release 모드일때는 컴파일 되지 않기 때문에 사용되는 데이터는 변경해서는 안된다.



  


메모리 누수에 대해 알아보다가 좋은 글이 있어 담아왔습니다.

(URL) http://powergi.tistory.com/entry/embedded%EC%97%90%EC%84%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC

댓글에도 많은 의견이 있으므로 참고하면 좋겠습니다.

==================================================================

1. 개요

이 문서는 ESP-NS에서 동작하는 응용 프로그램을 
작성할때 메모리 할당과 해제및 메모리 처리에 
대한 주의점을 소개합니다. 

작성자 : 유영창 frog@falinux.com
작성일 : 2004년 9월 17일 
수정일 : 

관련된 ADK( Application Developer Kit ) 디렉토리 

adk/sample/check_index
adk/sample/assert


2. 임베디드에서 메모리 관련 문제 

임베디드 시스템은 사용시간에 따라서 크게 두가지로 
나누어 볼수 있읍니다. 

첫번째는 필요한 경우만 전원을 넣고 동작시켜 사용하는 경우로 
동작 시간이 짧은 경우입니다. (참으로 고마운 시스템입니다.)

두번째는 모니터링 시스템같이 지속적인 제어가 필요하여 
1년 365일 전원이 절대로 나가면 안되는 경우 입니다. 

프로그래머 입장에서 보면 첫번째 방식을 좋아 하게 됩니다. 
이건 무정전 시스템에 사용되는 프로그램을 작성하신분들이라면
온몸으로 느끼는 감정입니다. ( 해본 사람들은 압니다. ㅜㅜ )


시스템이 무정전으로 동작한다는 것은 여러가지를 고려 해야 합니다.
그중 으뜸은 메모리 누수 입니다. 

C 로 작성하는 프로그램은 반드시 메모리에 관련된 문제 때문에 
한번 이상은 반드시 고생하게 됩니다. 

더구나 C 언어에 익숙하지 않으신 분이라면 포인터 참조에 관련된
수 많은 버그로 엄..청..난... 고생을 합니다. 

그래도 

납품하기전에 메모리 관련된 버그 문제점을 알게 되면 그나마 
다행입니다.

그러나 프로그래머 입장에서 두고 두고 속썩이는 것중 하나가 
장기간 동작하다 멈추는 경우입니다.

프로그램을 수정해서 버그를 잡았는지 확인하려고 하면 
몇일씩이나 걸리기 때문에 프로그래머들을 미치기 일보 
직전까지 만듭니다. ( 대부분의 경우 어디에서 발생했는지도 
잘 모르죠.. )

이런 경험을 여러번 하다보면 나름대로의 방법론이 생깁니다.

이런 경험과 관련되어 메모리를 다루는 방법에 대한 몇(?)가지와
메모리 할당과 해제에 관련된 함수를 소개하려고 합니다. 


3. 배열을 사용하라....

PC 프로그램을 작성하시던 분들이 임베디드 시스템에서
프로그램을 작성할때 가장 걱정되는 습관 중 하나가 
메모리 할당과 해제를 아주 좋아 한다는 겁니다. 

PC 시스템에서 작성하는 프로그램은 실장된 메모리가 
많기 때문에 메모리 할당과 해제를 이용하면 
유연한 프로그램이 가능해 집니다. 

그..러..나..

임베디드에는 메모리 할당과 해제를 자주 이용하는 습관은
절대적으로 말리고 싶은 것 습관중 하나입니다. 

보통 제품을 설계하는 분들이 개발자들에게 요구하는 것중 
하나가 만능제품이죠...

그런데 이 만능은 프로그래머가 만능이 되어야 합니다.
이런 경우에도 적용될수 있고 저런 경우에도 적용될수 있고 

마음약한 개발자들은 이런 요구를 수용합니다.

그러다 보니 개발해야 하는 프로그램 구조가 요구 사항에 
가변적인 구조를 가지게 되죠..

결국 메모리 할당 구조와 리스트와 같은 자료 구조를 사용하게
됩니다. 

이때부터 개발자는 머리털 빠지기 시작합니다. 
( 제가 속빈 인간이 된 사연이 여기에 있읍니다. )
리스트구조와 메모리 할당은 시스템의 버그 원인 순위에 
가장 상위 순위를 차지 합니다. 

오랜 연륜을 가지는 개발자들은 일단 이런 영업 요구에 
적절히 대항합니다. 

그리고 어느정도 제품이 필요한 요구 사항을 제한합니다.
그리고 그에 맞게 프로그램을 개발합니다. 

이때 이 분들이 작성한 프로그램을 보면 ( 무정전 제품에 
들어가는 프로그램일수록 ) 전역변수와 배열을 많이 사용하게
됩니다. 

이 전역변수와 배열을 사용하는 것은 
프로그램을 처음 배울때 회피하라고 들었던 것인데 
의외로 고수일수록 많이 사용합니다.
(심지어 goto 문을 남발하시는 고수도 많습니다. )

배열을 사용한다는 것은 일정한 크기를 갖기 때문에 
확장성에 용이하지 않을 것 같은데 
환경 파일로 모든 확장성을 고려하는 것은 임베디드 
제품에 크게 의미가 없읍니다. 

이미 한정된 크기의 메모리를 가지고 있는 시스템에 
확장할수 있는 크기를 가지도록 프로그램을 작성한다는 것은 
의미가 없읍니다. 


배열을 사용하게 되면 다음과 같은 장점이 있읍니다 

1) 메모리 할당과 해제와 관련된 버그가 없다. 
2) 메모리 할당과 해제에 소모되는 시간 소모가 없다. 
3) 인덱스 방식의 참조가 가능하므로 잘못된 포인터 
참조에 의한 버그 가능성이 작다. 
4) 시스템의 메모리를 효율적으로 사용할수 있다. 
5) 참조 속도가 매우 빠르다.
6) 프로그램 소스 코드가 직관적이다. 
( 포인터 참조 연산자가 얼마나 어려운 코드 형식을 
작는지 다들 아시죠? )

등등의 장점이외에 더 있지만 생각이 안 나는 군요...

어쩄든 임베디드 장비에 사용되는 프로그램은 가급적 배열을 
사용하시는 것이 좋습니다. 

3. 가급적 상수를 매크로로 정의해서 사용하라

메모리 이야기에 왠 매크로 상수?

뭐 이렇게 궁금하게 생각하시는 분들이 있을 것 같은데...
이래야 고수 소리를 듣습니다. 소스에 숫자가 적게 보일수록 고수 입니다 


예를 들어 보죠...

어떤 분은 프로그램을 이렇게 작성합니다. 

char check_ids[300];

고수는 이렇게 작성합니다. 

#define MAX_CHECK_IDS 300
char check_ids[MAX_CHECK_IDS];


이것은 나중에 확장성을 가지는 효과가 있고 
접근하는 인덱스의 검사를 할 경우에 유용합니다. 


또한 인데스 검사를 하는 경우에 유용합니다.
보통 프로그램을 작성할때 인덱스 접근에 대하여
다음과 같이 처리하면 좋습니다. 


char get_date( int index )
{
#ifndef NO_CHECK_INDEX_FLAG
if( (index < 0) || (index >= MAX_CHECK_IDS ) )
{
dlp( "index over\n" );
exit(0); 
}
#endif
return check_ids[index];
}

또는 아예 인덱스 검사를 하는 함수를 매크로로 만들어서 사용하는 경우도 있읍니다. 

선언예)

#ifndef NO_CHECK_INDEX_FLAG
#define check_index(idx,max) {\
if( (index < 0) || (index >= MAX_CHECK_IDS ) ) \
{ \
dlp( "index over\n" ); \
exit(0); \
} \

#else 
#define check_index(idx,max) {}
#endif 


사용예)

char get_date( int index )
{
check_index(index,MAX_CHECK_IDS);
return check_ids[index];
}

3. 초기화를 꼭 하라

변수를 사용할때 특히 전역 변수를 사용할때 
초기화를 하는 습관은 버그를 예방하는 효과가 있읍니다. 
특히 포인터 형식의 필드변수를 포함하는 구조체가 있을 경우에는 특히나 그렇습니다.

초기화 값은 0으로 사용하는 것이 좋습니다. 
포인터의 

4. sizeof 함수를 즐겨 사용하라 

메모리 복사나 초기화를 사용할 경우와 같이 배열이나 구조체의 크기를 구할 필요가 
있을때 sizeof 를 자주 사용합니다.

귀찮아서 그냥 숫자를 주는 습관을 가진 분들이 있는데 
이런 분들에게 sizeof 함수의 사용을 강력하게 권장합니다. 

앞의 배열에서 초기화를 처리할때 다음과 같은 형식으로 처리하는 것이 좋죠...

memset( check_ids, 0, sizoef( check_ids ) );

복사할 경우에 역시 이런 식으로 사용하는 것이 좋습니다. 
하지만 복사할 경우에 크기는 어느 것을 사용하는 것이 좋을까요?
권장하는 것은 앞에것을 사용 하는 것입니다 

void copy_item( struct a *bdata )
{
struct a adata;

// 권장하는 예 
memcpy( &adata, bdata, sizeof( adata ) ); 

// 별로 권장하지 않지만 좋은 예 
memcpy( &adata, bdata, sizeof( struct a ) ); 


}


5. 포인터의 간접 인덱스를 사용할 때는 주의하라...

포인터 변수를 사용할때 포인터의 초보자들이 실수하는 큰 것 
중에 하나는 다음입니다. 
특히 하드웨어를 다룰때 많이들 실수 합니다. 

char *app;
int *bpp;

app++;
bpp++:

이것은 1씩 증가 시키는 겁니다. 
그런데 app 나 bpp 에 0x300000 이라는 주소값이 있다면
app는 0x30000 이 되지만 bpp 는 0x300004 가 된다는 것을 
까먹습니다. 

이것이 나중에 속썩일 경우가 많다는 점을 기억하십시오

(app+ 1) 과 (bpp+ 1) 도 마찬가지 입니다. 
이런것은 매크로를 사용해서 선언할 경우 많이들 실수하는 겁니다. 

예를 들어 하드웨어 레지스터를 접근하는 경우에 

#define REG_A(x) (x + 1)
#define REG_B(x) (x + 2)

이런식으로 처리할때 매크로는 단순히 문자열 치환이기 때문에 
위와 같은 문제가 발생할수 있다는 것입니다.


6. 스택변수 즉 로컬 변수를 조심하자 

함수안에 선언하는 로컬 변수는 두가지 장점이 있읍니다. 

선언이 간단하고 할당에 걸리는 시간이 없다는 것입니다. 
그러나 이 로컬 변수는 버그의 온상이 되므로 주의할 필요가 있습니다. 

예를 들어 다음과 같은 경우를 생각해 봅시다. 

int test_func( char *tmpstr )
{
char buff[32];
int p;

p = strlen( tmpstr) ;

sprintf( buff, "ID:%s", tmpstr );
write_func( buff );

return p;
}

이 함수를 소스상에서 본다면 아무런 문제가 발생하지 않는 함수입니다.
그런데 이 함수는 두가지 문제점을 가지고 있읍니다. 

만약 tmpstr 이 NUL 코드를 포함하지 않는다면?
또는 tmpstr 이 28 자 이상이 된다면 ?

아...

프로그램은 어떻게 동작할지 아무도 장담할 수 없읍니다. 

다행히 세그먼트 폴트라도 발생해서 미리 알수있다면 좋지만
스택을 접근 할 경우에는 세그먼트 폴트가 잘 발생하지 않습니다. 

경우에 따라서는 스택이 깨지기 때문에 엄한 곳으로 프로그램이 점프할수도 있고 
다른 변수들이 수정될수도 있읍니다.

더구나 특별한 경우에는 두번 호출되어 도착한 함수가 꺼꾸로 리턴될때 
중간 함수를 거치지 않고 리턴되거나 진행 루틴이 실행되지 않을 경우도 있읍니다. 

또는 뒤에 선언된 변수들 값이 수정될수도 있읍니다. 

이런 경우에는 스택 변수의 크기를 아끼지 않는 것이 가장 최선의 예방책입니다. 
(물론 주의해서 작성하는 것이 더 큰 최선의 에방책이죠.. )

예를 들어 넘어온 크기보다 2 배나 3 배정도의 크기를 잡는 겁니다. 

위의 경우에는 char buff[128] 정도로 선언하는 것이 안전합니다. 

또는 버퍼의 뒷쪽에 임의 변수를 하나 두는 것도 요령인데 
별로 추천은 하고 싶지 않군요


7. 자료구조를 사용한다면 검증된 라이브러리를 사용하라..

프로그램을 작성하다보면 배열보다 리스트와 같은 자료구조를 
이용하는 것이 효율적일때가 있읍니다. 

대표적인 것들이 

스택이나 , 큐, 더블 링크드 리스트 , 리스트, 이진 트리 리스트
등등이 있읍니다. 

이때 많은 분들은 직접 만들어 사용합니다. 

그런데 이런 처리는 포인터를 사용해야 하고 저같이 논리에 약한
사람들이 만들면 버그가 살기에 좋은 환경을 제공합니다. 

그래서 저는 인터넷에 관련 자료 구조용 공개된 소스를 이용하기를 
권장합니다. 

특히 소스포지에 가면 이런 자료 구조체 라이브러리들이 많이 있읍니다.
가급적 이렇게 공개되고 여러사람이 사용하고 있는 것을 이용하기를 
바랍니다.

직접 만들면 피 봅니다... ㅜㅜ

8. 메모리 할당 함수들 

C 에서 메모리를 할당하기 위해서 사용하는 함수들은 다음과 같습니다. 

void *calloc(size_t nmemb, size_t size); // 할당 + 메모리 클리어 
void *malloc(size_t size); // 할당
void free(void *ptr); // 해제 
void *realloc(void *ptr, size_t size); // 재 할당 + 메모리 복사 

이 중에서 가장 많이 사용하는 함수는 

malloc 함수와 free죠...

하지만 malloc 함수보다는 calloc 함수를 사용하기를 권장합니다.
(저역시 malloc 함수를 자주 사용합니다만 ㅜㅜ )
그래도 malloc 함수를 자주 사용하게 되면 다음과 같은 처리를 꼭 해주시기를 바랍니다. 

char *buff = NULL;

buff = malloc( 1000 );
if( buff != NULL )
{
memset( buff, 0, 1000 );
}
else
{
// 에러 처리 ( 보통은 프로그램을 죽인다. )
}

if( buff != NULL ) free( buff );


9. assert 함수


앞에서 malloc 함수를 처리할때 에러가 난 경우에 대한 처리가 귀찮죠?
이런 경우 사용하거나 기타 등등에 사용하면 좋은 함수가 assert 함수입니다.


이 함수는 #include <assert.h> 를 포함하고 사용하면 되는데 
사용 문법은 간단합니다. 

assert( 논리식 );

이 함수는 논리식이 참이면 특별한 것을 하지 않습니다. 
그러나 거짓이면 에러를 표현 합니다. 
파일명과 함수명 그리고 라인번호와 함께 문제가 된 값을 표현합니다. 
그리고 프로그램을 죽입니다.( 으으 살벌.. )

보통은 포인터 변수의 값이 0이 되는 것을 방지하기 위해서 사용합니다. 

assert 함수는 NDEBUG 가 정의 되어 있으면 아무런 것도 하지 않는 
함수이므로 소스를 수정하지 않고서도 함수의 에러 처리를 무효화 
할 수 있는 무척 좋은 함수 입니다.



modaless의 경우 대화상자가 떠있는 상태에서 얼마든지 다른 작업을 할 수 있다.

modal의 경우 대화상자가 닫히기 전까지 다른 작업을 할 수 없다.


modal 형태로 창을 띄우는 것은 


CShapeDlg dlg;

dlg.DoModal(); // 이런 형태로 창을 띄우는 것이다.


modal로 창을 띄우는 경우, 이 창을 먼저 해결해야 부모 창으로 돌아갈 수 있다.




그리고 modaless 형태로 창을 띄우는 것은


CShapeDlg *dlg;


dlg = new CShapeDlg(); //혹은 CShapeDlg(this); 등으로 초기화 한 후

dlg->Create(IDD_SHAPE, NULL) //혹은 Create(IDD_SHAPE, DesktopWindow()); 등을 써서 만들어주고,

dlg->ShowWindow(SW_SHOW) //혹은 dlg->ShowWindow(SW_SHOWNA); 등등 으로 창을 띄워주면 된다.


//initDialog등을 처리해주고싶다면 

dlg->PostMessage(WM_INITDIALOG)

dlg->SendMessage(WM_CLOSE) //등을 넣어서 창을 닫아 줄 수 있다.




CDialog에 있는 OnTimer


OnTimer라는 메세지 핸들러는 SetTimer(타이머ID, 발생간격, 실행하려는 함수명)​과 함께 사용한다

// ​여기서 함수명에 NULL을 넣어주면 OnTimer를 사용


일정 시간 뒤에 원하는 업무를 처리하기 사용한다.

 

비슷한 기능으로 Sleep( millisecond ) 를 입력하는데

Sleep을 이용할 경우 해당 쓰레드 전체가 멈추어 버리고 그 멈춤에 따라 외부 인터럽트입력(키보드, 마우스, 등등)이

주어지게 되면 버퍼에 쌓였다가 한번에 처리되는 일이 발생하게 된다.

 

그래서 그걸 방지하기 위해 Timer를 사용하는데 사용방법은 다음과 같다.

 

 

 

등록

 BEGIN_MESSAGE_MAP(CGroupChatWindow, CDialog)

...

ON_WM_TIMER()

...

 END_MESSAGE_MAP()

메세지 맵에 'ON_WM_TIMER()'를 등록

 

 

타이머 시작

 SetTimer(1394,1000,NULL);

  파라매터

1) Timer의 ID값

2) SetTimer후 몇 millisecond후 마다 실행 (반복실행)

3) 함수명, NULL 사용시 오버라이드 함수 'OnTimer()'로 작동된다.

  반환값 : 핸들러

 

 

 

선언

 afx_msg void OnTimer(UINT nIDEvent);

헤더파일에 선언

 

 

 

오버라이드 함수 내부

 void CGroupChatWindow::OnTimer(UINT nIDEvent)
 {
    switch(nIDEvent)
    {
        case 1394:

             limitLatencyClickAble = TRUE;   
             mCheckSendVoice.EnableWindow(TRUE);

             break;
    }

    KillTimer(1394);
}

switch문을 사용하여 Timer의 ID를 구분하여 처리

SetTimer에 첫 파라매터에 1394를 적었기에 OnTimer( )에서는 switch문에 case로 1394

 

 

 

타이머 중지

 KillTimer(1394);

타이머 아이디를 입력하면 끝


+ Recent posts