November 19, 2024

디바이스마트 미디어:

[66호] 원하는 색상으로 제어가 가능한 아두이노 IoT 스마트 무드등 키트 -

2021-06-25

★2021 ICT 융합 프로젝트 공모전 결과 발표! -

2021-05-12

디바이스마트 국내 온라인 유통사 유일 벨로다인 라이다 공급! -

2021-02-16

★총 상금 500만원 /2021 ICT 융합 프로젝트 공모전★ -

2021-01-18

디바이스마트 온라인 매거진 전자책(PDF)이 무료! -

2020-09-29

[61호]음성으로 제어하는 간접등 만들기 -

2020-08-26

디바이스마트 자체제작 코딩키트 ‘코딩 도담도담’ 출시 -

2020-08-10

GGM AC모터 대량등록! -

2020-07-10

[60호]초소형 레이더 MDR, 어떻게 제어하고 활용하나 -

2020-06-30

[60호]NANO 33 IoT보드를 활용한 블루투스 수평계 만들기 -

2020-06-30

라즈베리파이3가 드디어 출시!!! (Now Raspberry Pi 3 is Coming!!) -

2016-02-29

MoonWalker Actuator 판매개시!! -

2015-08-27

디바이스마트 레이저가공, 밀링, 선반, 라우터 등 커스텀서비스 견적요청 방법 설명동영상 입니다. -

2015-06-09

디바이스마트와 인텔®이 함께하는 IoT 경진대회! -

2015-05-19

드디어 adafruit도 디바이스마트에서 쉽고 저렴하게 !! -

2015-03-25

[29호] Intel Edison Review -

2015-03-10

Pololu 공식 Distributor 디바이스마트, Pololu 상품 판매 개시!! -

2015-03-09

[칩센]블루투스 전 제품 10%가격할인!! -

2015-02-02

[Arduino]Uno(R3) 구입시 37종 센서키트 할인이벤트!! -

2015-02-02

[M.A.I]Ahram_ISP_V1.5 60개 한정수량 할인이벤트!! -

2015-02-02

[42호]세상에 하나밖에 없는 전자시계 만들기, “I.Watch Shield” 출시

I.O. Tech

세상에 하나밖에 없는 전자시계 만들기,

“I.Watch Shield” 출시

시계는 스마트폰과 함께 일상 생활에서 가장 많이 사용한다고 해도 과언이 아니다. 이번 기회에 일상생활에서 활용 가능한 실용적인 DIY를 도전해보는 것은 어떨까?

2014년부터 소프트웨어 교육의 대중화에 힘쓰고 있는 국내기업 I.O. Tech는 I.Watch Shield를 새롭게 출시했다. I.O. Tech는 한국형 아두이노 기초 키트, 쉴드 등의 제품을 출시하고 있어 국내 입문 메이커들이 보다 손쉽게 접근이 가능하다.
이번에 출시한 I.Watch Shield는 DS1307 칩을 사용하며 I2C 통신 방식으로 작동하는 제품이다. 아두이노 우노 보드, 아두이노 호환 보드로 사용이 가능하며 RTC(Real Time Clock)와 4자리 7세그먼트를 이용하여 전자시계를 구현하는 프로젝트형 아두이노 쉴드이다. 일반 메이커뿐만 아니라 학교 아두이노 활용 교육용으로도 매력적인 제품이다.
현재 디바이스마트에서 구매가 가능하며, I.O. Tech 공식 홈페이지(http://www.io-tech.co.kr/) 자료실에 유의사항 및 회로도, 예제코드가 친절하게 마련되어 있다.

 

제품 특징
· 아두이노(Arduino)의 아두이노 스케치와 연결 사용 가능
· 아두이노 우노 보드, 아두이노 호환 보드로 사용 가능
· RTC는 DS1307 칩을 사용하며 I2C 통신 방식 동작
· 하나의 택트스위치가 풀업저항 방식으로 연결되어 있음

제품 구매하러 가기

www.io-tech.co.kr

 

[42호]제 6의 손가락 : 4절 링크를 이용한 수평 유지 로봇 SRF

42 ict 4절 링크를 이용한 (8)

2016 ictmain

2016 ICT 융합 프로젝트 공모전 우수상

제 6의 손가락 : 4절 링크를 이용한

수평 유지 로봇 SRF

(Supernumerary Robotic Finger)

 

글 | 고려대학교 최주헌

 

심사평

JK전자 기능적으로는 어느 정도 구현이 되었으니 힘이 있는 모터를 사용해서 조금 더 무거운 것을 올려도 균형을 잡을 수 있다면 인체에 부착하여 무거운 짐을 옮길 때 사용하면 편리하겠네요.
뉴티씨 균형 유지라는 매우 어려운 주제이지만, 누구나 쉽게 이해할 수 있도록 프로젝트를 구성하여 좋은 작품으로 평가된다. 알고리즘 등에 보다 집중하여 구현하고 설명을 한다면 더 좋은 작품이 될 것으로 보이며, 움직임이 보다 빠르게 구현될 수 있도록 하여, 빠른 움직임에서도 빠르게 대처될 수 있도록 개선하는 작업이 필요해 보이며, 앞으로 좀 더 연구하여 매우 훌륭한 작품을 만들기를 바라며, 좋은 점수를 받았다.

칩센 좋은 아이디어와 훌량한 완성도를 가지고 있다. 장애를 가진 사람에게도 도움이 될 아이디어 같다.

위드로봇 아이디어와 작품의 완성도가 무척 높습니다. 제어기를 좀 더 세심하게 설계하여 평판의 기울기 정도를 좀 더 잘 제어할 수 있으면 재미있는 상품이 나올 수 있을 것 같습니다.

작품 개요

42 ict 4절 링크를 이용한 (1)

전지와 MCU(Micro Controller Unit)의 소형화 추세에 따라 웨어러블 로봇이 각광받고 있다. 최근에는 “사람의 팔, 다리가 여러 개이면 어떨까?” 라는 아이디어에서 나온 여분의 신체부위 로봇 SRL(Supernumerary Robotic Limbs)이 개발되고 있다. 하지만 이러한 SRL은 사람의 의도를 파악하기 위해, 추가하고자 하는 신체부위에 많은 센서를 부착해야 하는 문제가 있다. 이 작품은 여분의 보조 손가락 SRF (Supernumerary Robotic Finger)를, 신체에 별도의 센서를 부착하지 않고도 신체 정보를 얻어 조절할 수 있다. SRF는 사람의 팔에 부착되어 쟁반과 같은 면적이 넓은 물체를 손으로 들 때, 물체가 균형을 잃지 않도록 보조해 주는 것을 목적으로 한다. 이때 필요한 신체 정보는 SRF End Point에 부착된 센서를 통해 4절링크 모델로 분석하여 얻어낸다. 제작한 SRF로 수평 유지 실험결과 수평 유지에 큰 도움을 주는 것을 확인하였다. 위 작품을 통해 로봇과 인간사이의 조화 연구에 새로운 접근 방향을 제시할 것으로 본다.

작품 설명
주요 동작 및 특징

42 ict 4절 링크를 이용한 (2)

본 작품은 쟁반과 같은 넓은 평면 물체의 균형을 잡아주는 여분의 보조 손가락(SRF) 웨어러블 로봇으로, 손목에 로봇이 부착된다. 로봇의 센서가 쟁반에 부착되어 쟁반의 지면과의 기울기를 측정하고 이에 따라 로봇의 서보 모터를 제어하여 쟁반의 균형을 유지한다. 본 작품은 기존의 SRF로봇들이 수행하는 물건을 잡는 기능이 아니라 물체의 균형을 잡는 새로운 기능을 시도하여 SRF 연구에 새로운 방안을 제시한다.

전체 시스템 구성

42 ict 4절 링크를 이용한 (3)

본 작품은 물체의 균형을 잡아주는 2개의 서보모터(AX-12)와 물체의 기울기를 측정하는 IMU 센서(EBIMU-9DOFV3), 이들의 상호작용을 일으키는 연결 Joint들, 모터 제어와 센서 값을 받아들이는 MCU(아두이노MEGA)로 구성된다. 이와 같은 로봇 디자인이 나오게 된 이유는 단계별 제작과정에 구체적으로 서술하였으며 작동방식은 첨부한 영상을 통해 보다 이해하기 쉽게 하였다.

SRF 전체 구성
SRF 로봇은 사람 손목에 부착되며 2개의 흡착판은 평면 물체에 부착된다. 각각의 서보 모터는 로봇과 부착된 물체의 Roll, Pitch를 조절하여 지면과의 수평을 조절할 수 있다. MCU는 IMU 센서 값에 측정되는 Roll Pitch 값을 받아들여 지평면과의 수평을 유지하도록 모터를 제어한다. 제어방식은 PID 제어 방식이며, 처음에 모터 각속도에 따른 평면 물체의 각속도 변화를 통해 사람 손가락과 로봇 End Point간의 거리를 측정, 제어 상수를 결정한다.
이러한 과정을 통해 SRF 로봇은 손가락에 별도의 센서를 부착하지 않고도 손의 위치를 파악하여 손위의 평면 물체 기울기를 유지할 수 있도록 보조해준다.

개발 환경(개발 언어, Tool, 사용 시스템 등)
· C언어를 이용하여 아두이노 MEGA 사용(아두이노 스케치)
· SRF 손목 부착 방식 (구버전)
· 서보모터와 IMU 센서는 시리얼통신을 이용

단계별 제작 과정
SRF 모델 설정
손위의 평판 기울기를 조절하기 위해서는 평판과 만나는 최소 3개의 점으로 평면이 필요하며, 3점 간의 거리를 조절하여 3차원 상의 기울기 Roll. Pitch, Yaw의 각도를 조절할 수 있다.
3차원 상에서 지면과 수평을 이루기 위해서는 평판과 지면 사이의 각이 전부 0이 되면 된다. 즉 수평면 사이의 각인 Roll, Pitch가 0이 되도록 3개의 점을 조절하면 된다. 그렇기에 손의 변화에 맞춰 Roll, Pitch 값이 0에 수렴하도록 SRF를 2자유도 모델로 설정하여 설계하였다.

42 ict 4절 링크를 이용한 (2)

위 모델을 통해 사람 손을 단순화 시켜보면 평판과접하는 5개의 손가락은 그림(Fig 1)처럼, 평판과 수평면 사이 각을 조절하는데 필요한 3개의 점 중 하나의 점으로 볼 수 있다. 이렇게 할 경우 손의 점을 기준으로 나머지 점 2개의 위치를 조절하여 손위의 평판과 수평면과 평행하게 조절할 수 있다. 본 작품은 이러한 모델을 통해 하드웨어 설계를 진행 하였다.

SRF 하드웨어 디자인
2자유도의 SRF는 2개의 서보모터(Dynamixel AX-12), 4개의 링크, 조인트, End Point로 구성되어 있으며 아래 그림(Fig 2)과 같은 형태로 설계하였으며 이에 따른 D-H 변수는 표 1과 같다.

42 ict 4절 링크를 이용한 (3)

42 ict 4절 링크를 이용한 (4)

이때 θ1과 θ1만 서보 모터를 이용하여 제어하도록 설계하였는데, 그 이유는 2차원 상에서 보았을 때 그림(Fig 3)의 형태처럼 손위의 평판과 SRF의 End Point로 결합할 경우 2자유도 모델이 되기 때문이다.
이때 SRF와 평판과 손의 관계가 4절 링크 형상을 띄기 때문에 이러한 관계를 이용하여 평판의 수평 조절을 제어할 수 있다. Joint 1은 End point와 함께 Universal Joint의 역할을 하여 Motor2 제어시 손목의 꺾임을 막는다. 평판의 수평 조절을 위해서는 손의 형상에 따른 임의로 주어진 평판 위 1개의 점을 통해 2개의 점을 조절하면 되기때문에, End Point 설계시 평판과 2점이 만나 부착될 수 있도록 하였다. 또한 End Point는 평판에 부착되어 평판의 Roll, Pitch 값과 End Point의 Roll, Pitch 값은 동일하기 때문에 End Point에 IMU Sensor를 부착하여 평판의 기울기 정보를 얻어내도록 하였다. (Fig 4)

42 ict 4절 링크를 이용한 (4)

이러한 설계를 통해 NX 3D모델링 프로그램과 3D프린터를 이용하여 제작한 SRF는 위 그림(Fig 5)과 같다. 부품 제작 과정은 아래에 기재하였다.

42 ict 4절 링크를 이용한 (5)

 

42 ict 4절 링크를 이용한 (5) 42 ict 4절 링크를 이용한 (6)

SRF 제어
설계한 SRF를 이용하여 평판이 수평을 유지하도록 하기 위해서는 End Point에 부착된 IMU 센서가 측정하는 Roll, Pitch 값을 0에 수렴하도록 모터 2개를 제어해야 한다. 각각의 모터는 Roll과 Pitch를 제어할 수 있는데 Motor1의 경우 그림(Fig 6.)처럼 Pitch 값을 제어할 수 있다.
SRF는 그림(Fig 7)처럼 팔에 부착되어 있으며 초록색 사각형은 모터, 노란색 원은 Revolute-joint를 의미한다. SRF의 링, 팔, 평판의 움직임은 4절 링크와 같기 때문에, 표 1. D-H변수를 사용하면 θ1을 통해 θ3와 θ5가 정해진다. θ1통해 IMU센서로 측정되는 Roll 값을 제어 할 수 있으므로 Pitch가 0이 되도록 Motor1을 PID 제어로 제어하였다.
하지만 θ1과 Pitch값을 조절하는 시스템이 일정하지 않고 손의 길이 a0 평판의 길이 a4에 따라 관계가 바뀌기 때문에 컨트롤러의 Kp, Ki, Kd 상수 값은 IMU로 측정되는 각속도 값과 θ1 사이의 관계를 통해 지속적으로 변화시키도록 하였으며 그 과정은 이와 같다.

42 ict 4절 링크를 이용한 (6)

4절 링크 관계식을 이용하기위한 링크 별 길이 정보는 표 2와 같으며 팔 길이 a0 손목에서 손까지의 일반적인 길이 20cm로 정하였다. 이때 손과 SRF 사이의 평판 길이 a4가 잡는 방식에 따라 변하기 때문에, w1이 일정할 경우 a4의 변화와 링크a1의 회전 상태인 θ1에 따른 각속도 w6의 값의 변화를 알아야 한다. 4절 링크 관계식으로 이를 계산하면 아래 그림(Fig 7)과 같이 선형적인 관계임을 볼 수 있다. 이를 통해 측정된 w6와 θ1값을 통하여 w4를 측정 할 수 있으며 이를 이용하여 Kp, Ki, Kd 상수 값을 변경 시킨다.

42 ict 4절 링크를 이용한 (7)

Pitch 값의 수평 제어 후 Roll의 수평 제어의 경우, Motor2의 회전축과 Roll의 회전축이 일치하여 Motor2의 움직임이 Roll 값의 변화와 같다. 이러한 선형적인 관계이므로 PID 제어를 통해 Roll 값이 0이 되도록 제어하였다. Motor2를 제어하면 그림(Fig. 8)과 같이 평판과 손 SRF 사이의 간격이 “r”로 일정하기 때문에 Motor2의 회전축이 변하게 되고 손목을 꺾게 된다.

42 ict 4절 링크를 이용한 (8)

그렇기 때문에 하드웨어 디자인에서 Joint 1을 넣어 End point와 함께 Universal Joint역할을 하게하여 손목 꺾임을 방지하였다. 대신 Motor2의 제어는 2D에서본 4절링크의 링크 길이를 변화시키기 때문에 평형에 맞추어진 Pitch 값에 변화를 주게 된다.
Motor2에 의한 Pitch 변화에 바로 반응하기 위해 그림(Fig. 9)와 같은 제어 시스템을 사용하였다.

42 ict 4절 링크를 이용한 (9)

SRF 성능 실험
로봇 설계 후 손의 위치 변화에 따른 평판의 각도 측정값은 다음과 같다.

42 ict 4절 링크를 이용한 (10)

외부에서 각도 변화가 주어졌을 때 SRF의 대응을 확인하기 위해, 일정한 주기로 평판을 변화시키며 이에 따른 SRF End Point 값을 측정한 결과. 그림(Fig 10, Fig 11)과 같이 SRF는 손의 움직임에 의한 평판의 진폭을 2배 이상 감소시키며 이를 통해 SRF의 목표인 수평 보조 기능을 잘 수행함을 확인할 수 있으며 충분히 빠른 시간에 반응하는 것을 확인할 수 있다.

결론
본 작품은 물체의 수평 유지를 목적으로 하는 SRF에 대한 것으로, SRF가 사용자의 의도를 파악하는 방식으로는 로봇과 신체를 하나의 기구학적 모형으로 해석하고 이로부터 신체 정보를 얻음으로써 신체에 별도의 센서를 부착하지 않고도 수평유지라는 목적을 달성하는 방법을 제시하였다.
이러한 방식의 SRF는 착용자에게 센서를 장착하는 대신 로봇 자체의 센서를 이용하기 때문에 착용자로부터 아무런 조건의 제한을 받지 않는다. 이 점을 이용하여 사람뿐만 아니라 향후 구조물 등에 부착되도록 하여 물체의 수평을 유지하는 역할을 수행할 수 있다. 또한 기존의 웨어러블 로봇의 인체 의도 파악 연구에 새로운 해결 방안을 제시하였다.

42 ict 4절 링크를 이용한 (7) 42 ict 4절 링크를 이용한 (8)

기타(회로도, 소스코드, 참고문헌 등)
회로도

42 ict 4절 링크를 이용한 (11)

소스코드

#define Roll_initial 512
#define Pitch_initial 512
#define Yaw_initial 512
#define Angle_MarginP 0.5
#define Angle_Margin 0.5
#define Kd 0.01 // D 제어
#define Ky 0.3 // Roll 제어에 따른 Pitch 제어
#define Ki 0.05 // i제어
#define Motor_Speed 1000
#define Speed_High 700
#define Speed_Low 300
#define Kp1 0.5
#define ID_Roll 8
#define ID_Pitch 4
#define ID_Yaw 2
int flag=1;
int sequence1 =1;
double Kp=2.5; // P제어
int sa=0;
double wr=0;
double wp=0;
char *str;
char *p;
int temp;
double fRoll;
double fPitch;
double Roll, Pitch, Yaw;
long Motor_R, Motor_P, Motor_Y;
double ip=0;
double ir=0;
double Time = 0;
double dt = 0.025;
double maxw=0;
void setup()
{
Serial.begin(115200); //Serial Monitor
Serial1.begin(1000000); // Serial for Motor
Serial2.begin(115200); // Serial for IMU

Motor_R = Roll_initial;
Motor_P = Pitch_initial;
Motor_Y = Yaw_initial;
sequence1 =1;
Motor(512,512, 512, 100);
delay(1000);
Motor(513,600,513, 100);
delay(10);
}void loop()
{
//시리얼 통신으로 IMU 센서값 받아들임
temp=Serial2.available();
if(temp>40)
{
char b[60]={0};
char c[60]={0};
for(int i=0; i<temp; i++)
{
b[i]=Serial2.read();
}
for(int i=0; i<temp; i++)
{
if(b[i]==42)
{
for(int j=i; j<temp; j++)
{
if(b[j]==13)
{
for(int k=0; k<j-i-1; k++)
{
c[k]=b[k+i+1];
}
break;
}
}
}
}
str = strtok_r(c,”,”,&p);
Roll = atof(str);
str = strtok_r(NULL,”,”,&p);
Pitch = atof(str);
Time += dt;

Serial.print(fRoll);
Serial.print(“,”);
Serial.print(fPitch);
Serial.print(“,”);
Serial.print(Time);
Serial.println();
//시간에 따른 IMU 센서값 출력
delay(10);
}else{
Serial.begin(115200); //Serial Monitor
Serial1.begin(1000000); // Serial for Motor
Serial2.begin(115200); // Serial for IMU
Serial.println(“Loop Start”);
}
//0값 노이즈 제거
if(Pitch!=0){
wp=(fPitch-Pitch)/0.03;
ip+=fPitch*0.03;
fPitch=Pitch;

}
if(Roll!=0){

wr=(fRoll-Roll)/0.03; //Rol 각속도 측정
Serial.print(“w is – “);
Serial.print(wr);
if(maxw<abs(wr)&&abs(wr)<100){ //초기 값 세팅에서 손가락 길이 알기위한 w가 측정 노이즈 보정위해 100이상은 무시
maxw=abs(wr);

}
Serial.print(“max w is – “);
Serial.print(maxw);
ip+=fRoll*0.03;

fRoll=Roll;
}
if(sequence1){ //전원 연결시 스캐닝과정 초기 Roll Pitch 값이 0이 되도록 단순제어
if (0<abs(maxw)&&abs(maxw)<25){//w값에 따른 Kp값 변경
Kp=3;
}else if(26<abs(maxw)&&abs(maxw)<50){
Kp=2,3;
}else if(51<abs(maxw)&&abs(maxw)){
Kp=1.7;
}
if (abs(fPitch) > Angle_MarginP)
{

if(Pitch > 0){
if(Motor_P<824){
Motor_P+=1;
}
}
else if(fPitch < 0)
{
if(Motor_P>200){

Motor_P-=1;

}
}
}

if (abs(fRoll) > Angle_Margin)
{

if(fRoll < 0){
if(Motor_R<700){

Motor_R+=3;

}
}
else if(fRoll > 0)
{
if(Motor_R>300){
Motor_R-=3;

}
}
}
Motor(Motor_R,Motor_Y, Motor_P, 100);

}else{

//스캐닝 후 PID 제어

if (abs(fPitch) > Angle_MarginP)
{
Motor_P+=wp*Kd+Ki*ip;
if(Pitch > 0){
if(Motor_P<824){
Motor_P+=abs(fPitch)*Kp1;
}
}
else if(fPitch < 0)
{
if(Motor_P>200){

Motor_P-=abs(fPitch)*Kp1;

}
}
Motor_R+=Ky*abs(fPitch);
}

if (abs(fRoll) > Angle_Margin)
{

if(fRoll < 0){
if(Motor_R<700){
Motor_R+=wr*Kd+ip*Ki;
Motor_R+=abs(fRoll)*Kp;

}
}
else if(fRoll > 0)
{
if(Motor_R>300){
Motor_R-=abs(fRoll)*Kp;
Motor_R+=wr*Kd+ip*Ki;
}
}
}
Motor(Motor_R,Motor_Y, Motor_P, Motor_Speed); //모터와 시리얼 통신
}
if((abs(fRoll)<Angle_MarginP)&&(abs(fPitch)<Angle_MarginP)){
sequence1=0;
Motor_Y-=1;

}
Serial.println (sequence1);
delay(15);
}
void Motor(unsigned int Pos_1, unsigned int Pos_2, unsigned int Pos_3, unsigned int Speed) //모터 시리얼 통신 함수
{ unsigned char Check_Sum;
int Num_Actuator = 3;
unsigned char ID_1 = 8;
unsigned char ID_2 = 2;
unsigned char ID_3 = 4;
unsigned char Length = 5*Num_Actuator+4;

Serial1.write(0xFF);
Serial1.write(0xFF);
Serial1.write(0xFE); //Broadcast ID
Serial1.write(Length); //Length
Serial1.write(0×83); //Instruction
Serial1.write(0x1E); //Address
Serial1.write(4); //Data Length

Serial1.write(ID_1);
Serial1.write(Pos_1);
Serial1.write(Pos_1>>8);
Serial1.write(Speed);
Serial1.write(Speed>>8);
Serial1.write(ID_2);
Serial1.write(Pos_2);
Serial1.write(Pos_2>>8);
Serial1.write(Speed);
Serial1.write(Speed>>8);
Serial1.write(ID_3);
Serial1.write(Pos_3);
Serial1.write(Pos_3>>8);
Serial1.write(Speed);
Serial1.write(Speed>>8);
Check_Sum = ~(0xFE + Length + 0×83 + 0x1E + 4 + (ID_1+ID_2+ID_3) + (Pos_1+Pos_2+Pos_3) + ((Pos_1>>8)+(Pos_2>>8)+(Pos_3>>8)) + 3*(Speed+(Speed>>8)));
Serial1.write(Check_Sum);
}

 

참고문헌
[1] C. Davenport, F. Parietti, and H. H. Asada, “Design and biomechanicalanalysis of supernumerary robotic limbs,” in ASME 2012 5th Annual Dynamic Systems and Control Conference joint with the JSME 2012. 11th Motion and Vibration Conference, pp. 787-793, 2012.
[2] Domenico Prattichizzo, Monica Malvezzi, Infan Hussain and Gionata Salvietti “The Sixth-Finger: a Modular Extra-Finger to Enhance Human Hand Capabilites”in The 23rd IEEE International Symposium on Robot and Human Interactive Communication 2014
[3] Arthur G.Erdman and George N.Sandor, Sridhar Kota Mechanism Design Analysis and Synthesis 4Edition, Prentice Hall

 

 

 

 

[42호]ESP8266 와이파이를 내장한 아두이노 Uno, Arduino UNO WiFi 출시

  Arduino  

ESP8266 와이파이를 내장한

아두이노 Uno, Arduino UNO WiFi 출시

Arduino(Arduino.org 및 Arduino.cc)는 Atmega328P 마이크로컨트롤러와 ESP8266 WiFi 통합모듈을 탑재해 Arduino UNO와 와이파이를 하나의 보드로 통합한 “Arduino Uno WiFi”를 출시했다.
이 제품은 14개의 디지털 입/출력 핀헤더, 6개의 아날로그 입력 핀, 16MHz 세라믹 공진기(공명기), USB 포트, 전원 잭, ICSP 헤더, 그리고 Reset 버튼을 내장하고 있다. 또한 USB 케이블을 통해 간편하게 컴퓨터와 연결하거나, AC-DC 아답터 또는 배터리를 활용해 쉽게 전원을 공급할 수 있는 등 마이크로컨트롤러를 활용하는데 필요한 것들을 모두 내장하고 있다.
특히 Arduino Uno Wi-Fi의 가장 유용한 기능 중 하나는 OTA (over-the-air) 프로그래밍을 지원해 케이블없이 온라인으로 Arduino 스케치나 Wi-Fi 펌웨어 작업이 가능한 점이다.
제품에 대한 더 자세한 사항은 디바이스마트 홈페이지(http://www.devicemart.co.kr/)에서 확인할 수 있다.

제품 특징
· 보드에 장착된 센서나 엑츄에이터에 와이파이로 통신하여 IoT 시스템을 쉽고 빠르게 만들 수 있음
· USB를 통하거나 외부 전원을 통한 작동이 모두 가능
· USB가 아닌 외부의 전원은 AC-DC 아답터(DC잭 활용) 또는 배터리(Gnd. Vin핀 활용)로 공급

Features

Arduino Uno Wi-Fi는 ATmega328P (datasheet)와 ESP8266 Wi-Fi 통합 모듈(datasheet) 기반의 새로운 Arduino Uno 입니다. 14개의 디지털 입/출력 핀헤더(6개는 PWM 출력으로 사용 가능), 6개의 아날로그 입력 핀, 16MHz 세라믹 공진기(공명기), USB 포트, 전원 잭, ICSP 헤더, 그리고 Reset 버튼을 내장하고 있습니다. 또한 USB 케이블을 통해서 간편하게 컴퓨터와 연결하거나, AC-DC 아답터 또는 배터리를 활용해 쉽게 전원을 공급할 수 있는 등 마이크로컨트롤러를 활용하는데 필요한 모든 것들을 포함하고 있습니다.

ESP8266 Wi-Fi 모듈은 자기제어(Self-contained) SoC 칩셋으로, 와이파이 네트워크에 접속하기 위한 통합 TCP/IP protocol stack을 내장하고 있습니다. (또는 액세스포인트로도 활용 가능합니다) Uno Wi-Fi의 가장 유용한 기능 중 하나는, OTA (over-the-air) 프로그래밍을 지원해 온라인으로(케이블 없이) Arduino sketches나 Wi-Fi firmware의 데이터 이동이 가능합니다.

ARDUINO MICROPROCESSOR
Processor ESP8266
Architecture Tensilica Xtensa LX106
Operating Voltage 3.3 V
Flash Memory 4 MB
RAM 8 MB instruction, 12 MB data
Clock Speed 80 MHz
WiFi 802.11 b/g/n 2.4 GHz
Wake up time < 2 ms
ARDUINO MICROCONTROLLER
Microcontroller ATmega328
Architecture Atmel AVR 8-bit
Operating Voltage 5 V
Flash memory 32 KB
SRAM 2 KB
Clock Speed 16 MHz
Analog I/O Pins 6
EEPROM 1 KB
DC Current per I/O Pins 40 mA
GENERAL
Digital I/O Pins 20
PWM Output 6
Power Consumption 93 mA
PCB Size 53 x 68.5 mm
Weight 0.025 Kg
Product Code A000133

 

PowerTechnical Specifics
Arduino Uno Wi-Fi 제품은 USB를 통하거나 외부 전원을 통한 작동이 모두 가능합니다(자동 선택). USB가 아닌 외부의 전원은 AC/DC 아답터 또는 배터리로 공급할 수 있습니다. 아답터의 경우에는 아답터의 2.1mm 플러그와 보드의 DC잭을 연결해 사용 가능합니다. 배터리로 전원을 공급하는 경우에는 ‘Gnd’와 ‘Vin’ 핀헤더에 전선을 연결하는 형태로 가능합니다. 

외부 전원을 통해 전원을 공급할 때는 6~20V 전압으로 제품을 작동할 수 있습니다. 하지만 7V 미만의 전원만이 공급되는 경우에는 5V 핀에서도 5V 이하의 전원이 공급될 수 있으며, Uno Wifi 보드 역시도 불안정한 작동을 할 수 있습니다. 또한 12V를 초과하는 전압을 공급하는 경우에는 전압 레귤레이터가 과열되거나 보드에 손상을 입힐 수도 있습니다. 즉 7V에서 12V의 전압 공급을 권장합니다.

전원 Pin 상세 설명 :

  • VIN. 외부 전원을 사용할 때 아두이노 보드에 입력되는 전압. (USB 연결 혹은 기타 조정된 전원 소스의 5 전압과는 반대)
  • 5V. 보드의 레귤러에이터로부터 조정된 5V로 출력되는 핀. 보드는 DC 전원잭 (7 – 12V), USB 커넥터 (5V)나 보드의 VIN pin (7-12V) 5V나 3.3V 핀을 통한 공급 전압은 레귤레이터를 우회하고, 보드는 손상될 수 있습니다. 권장하지 않습니다.
  • 3V3. 온보드 레귤레이터에 의해 발생된 A 3.3 전압 공급. 최대 전류는 1 A 입니다. (파워 입력 소스에 따른)
  • GND. 접지 핀.
  • IOREF. 아두이노 보드의 이 핀은 마이크로컨트롤러가 작동하는 전압 기준을 제공합니다. 올바르게 구성된 쉴드는 IOREF 핀 전압을 읽고 적절한 전원 소스을 선택하거나 5V 또는 3.3V 전원 공급 장치로 작업하기 위해 출력에서 전압 변환기를 활성화하십시오.
Memory
ATmega328는 32 KB 플래시입니다. (부트로더용으로 0.5 KB 사용) 또한 SRAM 2 KB,  EEPROM 1 KB 이 있습니다. (EEPROM 라이브러리로 읽고 쓸 수 있음)
Input and Output
우노의 각 14 디지털 핀은 pinMode(), digitalWrite() and digitalRead() 입출력에 사용됩니다. 5 전압으로 작동됩니다. 각 핀은 최대 40 mA으로 제공되거나 수신할 수 있으며 내부 풀업 저항(기본적으로 연결되지 않음)은 20-50kOhms 입니다. 게다가 몇몇의 핀은 다음과 같은 기능으로 특화됩니다.
  • Serial: 0 (RX), 1 (TX). TTL 시리얼 데이터를 수신 (RX) 및 전송(TX) 하는 데 사용됩니다. 이 핀은 ATmega8U2 USB-to-TTL 시리얼 칩의 해당 핀에 연결됩니다.
  • 외부 인터럽트: 2, 3. 이 핀은 낮은 값, 어떤값의 상승, 하락할 때 인터럽트가 발생하도록 할 수 있습니다. 자세한 사항은 attachInterrupt() 기능을 참조 하십시오.
  • PWM: 3, 5, 6, 9, 10 and 11. analogWrite() 을 사용하여 8-bit PWM 출력을 제공합니다.
  • SPI: 10 (SS), 11 (MOSI), 12 (MISO), 13 (SCK). 이 핀은 . SPI 라이브러리를 사용하는 SPI 통신을 지원합니다.
  • LED: 디지털 핀 13으로 연결된 LED가 내장되어 있습니다. 이 핀이 높은 값일 때, LED는 켜집니다. 핀이 낮은 값을 때에는 꺼집니다. 우노는 6 아날로그 입력이며, 10비트의 분해능(1024 다른 값)이 제공되는 A0 부터 A5까지 표시됩니다. AREF 핀, analogReference()를 사용하여 범위의 상단을 변경할 수 있지만 기본적으로 접지에서 5V까지 측정됩니다. 게다가 몇몇의 핀은 기능적으로 특화되어 있습니다.
  • TWI: TWI: A4 혹은 SDA 핀, A5 혹은 SCL 핀. 와이어 라이브러리를 사용하여 TWI 통신 지원. 참조: A4 혹은 SDA 핀, A5 혹은 SCL 핀은 IO 확장기 SC16IS750IBS에서 사용중입니다. 보드에 다른 두개의 핀이 있습니다:
  • AREF. Reference voltage for the analog inputs. Used with analogReference(). 아날로그 입력을 위한 기준 전압. analogReference() 사용.
  • RESET. 라인을 LOW로 가져와서 마이크로컨트롤러를 리셋하십시오. 일반적으로 보드의 차단하는 쉴드에 리셋버튼을 추가합니다.
Communication

The Arduino Uno Wi-Fi 는 컴퓨터, 다른 아두이노, 다른 마이크로컨트롤러와 통신하는 많은 시설을 갖추고 있습니다. ATmega328는 디지털 핀 0 (RX), 1 (TX) 로 사용 가능한 UART TTL (5V) 시리얼 통신을 제공합니다. 보드상의 ATmega16U2 채널의 USB 연결된 시리얼 통신은 컴퓨터 소프트웨어에 대한 가상 컴포트로 나타납니다. 16U2 펌웨어는 표준화된 USB COM 드라이버를 사용하며, 외부 드라이버가 필요 없습니다. 그러나 윈도우에서는, inf 파일이 요구됩니다. 아두이노 소프크웨어는 아두이노 보드로부터 온 간단한 문자 데이터가 허용된 시리얼 모니터를 포함합니다. 보드의 RX, TX LEDs는 데이터가 USB 시리얼 칩, USB 연결을 통해 전송될 때, 깜박입니다. (그러나 핀0, 1은 시리얼 통신이 아님).

소프트웨어 시리얼 라이브러리는 어떤 우노의 디지털 핀의 시리얼 통신에도 허용됩니다.

ATmega328은 I2C (TWI)와 SPI 통신을 물론 지원합니다. 아두이노 소프트웨어는 I2C 버스의 사용을 간단하게 하는 와이어 라이브러리를 포함합니다; SPI 라이브러리 사용에는 SPI 통신을 사용하십시오.

Arduino Uno Wi-Fi에는 AtMega 16u2, AtMega 328p 및 ESP8266EX 사이의 통신을 허용하는 I2C 버스/ SPI 인터페이스가 있는 SC16IS750IBS IO 확장기 단일 UART가 있습니다.

The Arduino Uno Wi-Fi를 사용하면 보드에 장착된 센서나 엑츄에이터에 와이파이로 통신하여 IoT 시스템을 쉽고 빠르게 만들 수 있습니다. Arduino Uno Wi-Fi를 Wi-Fi network 클라이언트로 다른 장치와 연결하는 서버로 사용하거나 ad’hoc Wi-Fi 연결을 만들 수 있습니다. Arduino Uno Wi-Fi를 통해 인터넷으로 접속하는 완벽한 방법은 Ciao Library이며 REST connector를 사용하는 것입니다. Arduino Uno Wi-Fi에는 브라우저를 통해 즉시 보드를 명령할 수 있는 RestServer 스케치가 미리 업로드되어 있습니다: Arduino Uno Wi-Fi SSID를 연결하십시오, http://192.168.240.1/arduino/digital/13/1 링크로 이동하여 LED L을 켜십시오.

다른 가능한 명령들은 다음과 같습니다: 

* “/arduino/digital/13″     -> digitalRead(13)
* “/arduino/digital/13/1″   -> digitalWrite(13, HIGH)
* “/arduino/analog/2/123″   -> analogWrite(2, 123)
* “/arduino/analog/2″       -> analogRead(2)
* “/arduino/mode/13/input”  -> pinMode(13, INPUT)
* “/arduino/mode/13/output” -> pinMode(13, OUTPUT)

 

Programming
 The Arduino Uno Wi-Fi는 아두이노 소프트웨어가 있는 고전적인 아두이노 우노로 프로그래밍할 수 있습니다. (download). the Tools > Board menu (보드의 마이크로컨트롤러와 맞는) Arduino Uno Wi-Fi를 클릭하십시오.
Arduino Uno Wi-Fi의 새로운 특징은 Wi-Fi를 통해(Arduino Yun의 같은 방식으로) 스케치를 업로드할 수 있습니다.
 Arduino Uno Wi-Fi의 ATmega328에는 외부 하드웨어 프로그래머 없이 새로운 코드를 업로드할 수 있는 부트 로더가 미리 설치되어 있습니다.
 물론 부트로더를 우회시킬 수 있으며 Arduino ISP를 사용하거나 비슷한 것을 사용하는 ICSP((In-Circuit Serial Programming) 헤더 마이크컨트롤러를 프로그래밍할 수 있습니다.

 

Automatic (Software) Reset
Arduino Uno Wi-Fi는 업로드하기 전에, 리셋 버튼을 물리적으로 누르지 않고 연결된 컴퓨터에서 실행되는 소프트웨어로 리셋할 수 있도록 설계되었습니다. ATmega8U2/16U2의 하드웨어 흐름 제어 라인 (DTR) 중 하나는 100 nanofared capacitor를 통해 ATmega328의 리셋 라인에 연결됩니다. 아두이노 환경에서 아두이노 소프트웨어는 이 기능을 사용하여 업로드 버튼을 누르기만 하면 코드가 업로드 됩니다. 즉, DTR을 낮추면 업로드 시작과 잘 맞을 수 있으므로 부트 로더의 시간이 더 짧아질 수 있습니다. 

이 설정에는 다른 의미가 있습니다. Uno Wifi가 Mac OS X 또는 Linux를 실행하는 컴퓨터에 연결되면 소프트웨어가 USB를 통해 연결될 때마다 리셋됩니다. 0.5초 동안, 부트로더는 Uno에서 실행중입니다. 형식이 잘못된 데이터(새 코드를 업로드하는 것 이외에)를 무시하도록 프로그래밍 되어있지만, 연결이 되면 보드에 전송된 데이터의 처음 몇 바이트는 차단됩니다. 보드에서 스케치가 시작될 때 일회성 구성이나 다른 데이터를 수신하는 경우 해당 데이터를 전송하기 전에 연결하고나서 통신하는 소프트웨어가 대기하고 있는지 확인하십시오.

Uno Wifi는 자동 리셋을 사용할 수 없도록 차단하는 trace가 포함되어 있습니다. trace의 양쪽 패드를 함께 납땜하여 다시 활성화시킬 수 있습니다. 이것은 “RESET-EN”라고 부릅니다. 물론 110V 저항을 5V에서 리셋 라인에 연결하여 자동 리셋을 비활성화할 수도 있습니다.

 

USB Overcurrent Protection
Arduino Uno Wi-Fi는 컴퓨터 USB 포트의 합선, 과전류로부터 자체 보호되는 리셋 가능한 폴리퓨즈가 있습니다. 대부분의 컴퓨터가 자체 보호를 제공하지 않더라도, 퓨즈는 보호 외부 레이어를 제공합니다. USB 포트에 500 mA 이상이 적용되면, 퓨즈는 합선이나 과전류가 제거될 때까지 자동적으로 연결이 차단됩니다.

 

Physical Characteristics
Uno Wi-Fi PCB의 최대 길이 및 넓이는 각각 2.7, 2.1 인치이며, USB 커넥터와 파워잭은 이전 치수에서 벗어나서 확장됩니다. 네 개의 나사 구멍으로 보드를 표면이나 케이스에 부착할 수 있습니다. 참조로 디지털 핀 7, 8 사이의 거리는 160 mil (0.16″) 이며, 다른 핀의 100mil 간격의 배수가 아닙니다.

Documentation


Arduino를 시작
하거나 보드에 지금 바로 실행해보세요. Arduino UNO WiFi 시작Getting Started

Tutorials

Advanced

 

Arduino UNO WiFi 제품 상세 보러가기

[42호]아두이노, 상상을 현실로 만드는 JUNE KIT

10MAKE

아두이노, 상상을 현실로 만드는 JUNE KIT

사람들은 끊임없이 무언가를 만들어낸다. 그리고 나이, 성별, 국적 상관없이 아이디어를 가진 누구나 상상을 현실로 만들 수 있다.

아두이노는 현재 전 세계에서 가장 널리 사용되고 있는 오픈소스 기반의 피지컬 컴퓨팅 플랫폼으로 하드웨어나 소프트웨어의 전문 지식이 없어도 누구나 쉽게 배우고 사용할 수 있도록 개발되어 많은 사람들에게 널리 보급되어 사용되고 있다.

JUNE KIT는 아두이노를 처음 접하는 초보자들이 쉽게 배울 수 있도록 구성한 키트이다. 아두이노를 단순히 책으로 배우는 것이 아니라, 저자가 직접 구성한 키트와 직접 제작한 동영상 강의를 모두 담아 쉽고 편리하게 마스터할 수 있도록 준비된 제품이다. 아두이노 호환보드, 브레드보드, 초음파센서, 서보모터, 조도센서, LED 등 필요한 모든 부품이 JUNE KIT 안에 포함되어 있다.
아두이노를 어디서부터 시작해야 할지 고민하시는분, 전기·전자에 대한 배경지식이 없어 망설이시는 분, 아두이노 프로젝트를 진행해보고싶으신 분들을 위한 제품이다
상상을 현실로 만드는 메이커를 위한 키트, 아두이노, 상상을 현실로 만드는 프로젝트 Kit Only (키트 단품)은 디바이스마트 홈페이지(http://www.devicemart.co.kr)에서 구매 가능하다.

 

아두이노, 상상을 현실로 만드는 JUNE KIT 제품 상세 구경하러가기

 

[42호]실내 위치 인식용 마커의 무결성 검사 로봇

42 ict 실내위치 (3)

2016 ictmain

2016 ICT 융합 프로젝트 공모전 우수상

실내 위치 인식용 마커의 무결성 검사 로봇

글 | 한국과학기술원 김규광, 최덕규, 임휘준

 

심사평

뉴티씨 실내 위치 인식 등에 관한 여러가지 연구들이 그동안 많이 진척되었는데, 그 중 한가지 아이템에 집중하여 결과를 낸 부분이 인상적이다. 실제로 로봇을 제작하여, NFC와 QR 코드를 인식하고, 자신의 위치를 보정하는 등 현재 위치를 확인하고, 이를 통하여 로봇의 위치 인식으로 물류 시스템 등에서 사용할 수 있도록 하려고 노력한 점이 돋보인다. 다만, 실제 상황에서의 고려 사항인 안전, 전원문제, 기구적인 토크 문제, 거리 문제 등은 고려되지 않은 점이 아쉽다. 실질적인 연구로 이어질 수 있을 것 같아서 좋은 점수를 주었다.

칩센 이미 구현된 기술에서 새로운 실용적인 아이디어를 도출했다, 실제 완성도를 조금만 더 높일 수 있고 마커를 활용한 물류 로봇들과 운용에서 문제를 없앨 수 있다면 상품화에 도전해도 될 만큼 좋은 아이디어 같다.

위드로봇 이동 로봇의 자기 위치 추정 문제는 꽤 오랫동안 로봇공학자들이 풀려고 고생하고 있는 난제입니다. 해당 작품은 QR 코드와 RFID 및 엔코더를 누적한 DR로 제한된 환경 내에서 위치 추정 문제를 풀려고 시도한 작품입니다. 아쉬운 부분은 융합 알고리즘을 너무 단순화하여, 전체 시스템의 성능이 더 나은 결과를 낼 수 있음에도 불구하고 그만큼 나오지 않는 부분입니다. 그리고 부수적이긴 하지만 QR 코드는 코드 자체적으로 오염에 의한 손상 문제에 대해 값을 복원할 수 있는 오류 정정 코드를 삽입할 수 있는 기능이 있습니다. 이러한 부분도 같이 고려되면 완성도가 훨씬 높아질 것 같습니다. 실제 로봇을 만들어 아이디어를 확인하는 과정이 쉽지 않는데, 결과를 도출해 낸 점에 박수를 보냅니다.

작품 개요
다양한 목적에 로봇을 적용하려는 시도가 늘어나고 있으며 실제로 물류 창고 등에서 로봇이 사용되기도 하는 등 실내 환경에서의 로봇의 임무 수행을 위한 위치 인식과 추정에 대한 관심이 증가하고 있다. 간단한 Monte Carlo Localization에서부터 레이저 스캐너, 카메라 등을 이용한 SLAM 기술, 전파 신호의 삼각 측량이나 fingerprinting을 이용한 위치 측위 등 다양한 기술이 개발되었으나 센서의 가격으로 인한 다수의 로봇 제작 비용의 증가, 전파 신호 측정값의 불안전성 등 여러 이유로 인해 일정 간격으로 마커를 부착하고 이를 로봇이 인식해 엔코더 정보 등과 조합 함으로서 위치를 인식하는 방식 또한 꾸준히 사용되어 왔다. 특히 마커 설치에 대한 미관상의 부담이 없으며 다수의 로봇을 안정적이고 효율적으로 운영해야 하는 물류 창고에 보통 적용되며 아마존 사의 KIVA 로봇의 경우 이러한 부착식 마커 시스템을 사용하는 대표적인 성공 사례이다. 하지만 이러한 부착식 마커의 경우 로봇 주행 및 기타 원인에 의한 물리적인 충격이나 먼지 등의 오염물질에 의해 손상될 수 있다. 대형 물류 창고의 경우 이러한 손상을 사람이 일일이 검사한다는 것은 쉬운 일이 아니며 발생할 경우 물류 체계에 비효율성을 불러올 수 있다. 본 프로젝트에서는 차륜 형태의 실내에서 주행 가능하고 QR 코드 마커를 인식해 로봇의 엔코더/센서를 이용한 위치와 비교하여 해당 위치에 설치된 QR 코드가 정상 상태인지를 확인하는 로봇을 개발하고자 한다. 또한 RFID/NFC 태그를 QR 코드와 같이 사용하여 QR 코드 손상 시에도 위치 인식을 도와줄 수 있는 시스템을 제안하고자 한다.

작품 설명
주요 동작 및 특징
Differential Wheel 형태의 차량형 주행 로봇이며 실내 주행을 돕기 위해 Wi-Fi를 이용하여 카메라 영상과 엔코더, 나침반 센서의 데이터를 전송하며 웹기반 조종 인터페이스를 제공한다. 로봇에는 스마트폰이 장착되어 있어 이를 이용하여 QR 코드와 NFC 태그를 인식한다.

전체 시스템 구성
로봇의 주 제어장치는 라즈베리파이 보드를 사용하였으며 센서와 하드웨어 인터페이스를 위해 아두이노 보드를 부착하였다. 로봇 프레임에 장착된 서보모터를 사용해 문턱 등의 장애물을 손쉽게 넘어갈 수 있으며 모터에 부착된 엔코더와 지자기 센서를 사용해 현재 위치를 계산한다. 지자기센서는 모터와 회로 등의 자기장에 영향을 받지 않도록 앞으로 길게 빼서 설치하였으며 제작 후에는 360도 회전시켜 heading 각을 계산하기 전 보정을 해 주었다. 아두이노는 센서 데이터를 모아 라즈베리파이 보드로 전달하며 모터 제어 명령이 내려올 경우 해당하는 모터를 움직인다. (그림 1.)

42 ict 실내위치 (1)
그림 1

 

라즈베리파이에서는 시리얼 통신을 통해 아두이노와 통신하며 제어용 웹 서버를 구동한다. 통상적으로 블루투스나 RC 수신기를 이용한 1대 1통신을 사용하며 많은 DIY프로젝트에서는 안드로이드 어플리케이션을 조종 인터페이스로 사용하나 본 프로젝트에서는 web 인터페이스를 사용해보고자 하였다. Wi-Fi를 통해 연결하므로 영상 전송 등에 필요한 넓은 대역폭과 빠른 속도가 보장되며 소켓 핸들을 계속 관리해 줘야 하는 TCP/IP통신과는 다르게 비동기식 통신인 request 방식을 사용해 일시적인 연결 장애에도 더 강인한 특징이 있다. 또한 안드로이드에서만 구동 가능한 앱과는 달리 웹 브라우저가 구동 가능하고 같은 ip대역 내에만 들어 있으면 접속하는 장비의 기종에 상관 없이 사용이 가능하다는 장점을 갖고 있다. (그림 2.)

42 ict 실내위치 (2)
그림 2

또한 jQuery Mobile을 사용하여 웹에서 폰의 앱과 비슷한 UI를 구현하였다. 조종 버튼을 누르거나 슬라이드가 움직이면 javascript 코드에 의해 POST request가 웹서버로 보내지며 서버는 수신받은 값을 아두이노로 보내 사용자의 요청에 맞춰 구동한다. (그림 3,4)

42 ict 실내위치 (3)
그림 3
42 ict 실내위치 (4)
그림 4

통상의 리니어 5V 출력 레귤레이터로는 라즈베리파이와 서보, 구동용 모터 등에 전력을 공급하기 힘들어 직접 전원 모듈을 제작하였다. 주 전력인 DC 5V는 스위칭 레귤레이터를 사용하여 큰 발열 없이 수 암페어 단위의 출력을 낼 수 있도록 하였으며 코일이나 커패시터 등의 회로 소자도 해당 전류를 견딜 수 있는 소자를 사용하였다. 지자기센서에 사용되는 3.3V는 많은 전력을 소비하지 않아 회로 단순화를 위해 리니어 레귤레이터를 사용하였다. 11.1V 리튬 폴리머 배터리를 사용하여 시스템 구동에 필요한 충분한 전력을 넣어주었다. (그림 5.)

42 ict 실내위치 (5)
그림 5

안드로이드 스마트폰을 부착시키기 위해 홀더를 직접 설계하였으며 QR 코드를 읽을 적절한 초점거리를 벌리면서도 NFC 태그를 읽을 수 있는 거리를 맞추기 위해 높이 조절이 가능하도록 3D 프린터의 히트베드 위치를 조절하는데 쓰이는 것과 같은 구조의 스프링과 나사를 같이 사용하였다. 설계한 홀더는 3D 프린터를 사용하여 제작하였으며 로봇에 부착하였다. (그림 6, 7) QR 코드와 NFC 태그를 읽을 수 있는 앱 또한 개발하였다 (그림 8).

 

42 ict 실내위치 (1)
그림 6
42 ict 실내위치 (2)
그림 7
42 ict 실내위치 (3)
그림 8

위치 인식은 엔코더 데이터와 지자기센서를 사용하여 진행하였으며 QR 코드에 비해 손상되기가 힘들고 인식이 편리한 NFC 태그를 우선하여 위치 보정을 하였다. NFC 태그에는 QR 코드를 부착하였으며 QR 코드와 동일한 데이터를 NFC 태그에 입력하였다. (그림 9)

42 ict 실내위치 (6)
그림 9

꺾어진 모양의 주행 코스를 선정하고 가운데 점과 양 꼭지점에 태그를 붙여 총 9장의 태그를 설치하였다. (그림 10)

42 ict 실내위치 (4)
그림 10

주행 코스를 따라 로봇을 조종하여 이동하였으며 NFC 태그 인식 데이터와 모터 입력값, 엔코더 제어값을 모두 저장하여 off line 분석에 사용하였다. 위치 보정에는 EKF SLAM, RFID SLAM 등의 사용을 고려했으나 SLAM 특유의 연산량과 marker의 위치를 모두 연산에 포함해야 하는 문제점(EKF SLAM의 경우) 등이 있어 좀 더 간단한 방식으로 보정계산을 진행하였다. NFC 태그가 인식될 경우 로봇의 현재 좌표를 기존의 이동 궤적을 모두 무시하고 NFC 태그 정중앙으로 이동시켜 엔코더를 사용함으로써 생기는 문제 중 하나인 오차 누적에 의한 drift를 해결하고자 하였다. 아래 그림의 differential wheel 로봇 모델을 사용하여 odometry를 우선적으로 계산하였다. (그림 11)

 

42 ict 실내위치 (7)
42 ict 실내위치 (8)
그림 11

로봇이 바라보는 방향(Heading)의 경우 엔코더를 사용하지 않고 지자기센서를 사용한 자북극 기준 heading 방향을 사용하였다. 실제 이동한 위치를 좌표평면으로 표현하여 의도된 주행 궤적대로 위치 추정 결과가 나오는지를 확인하였다. 수집된 데이터는 Python 언어와 matplotlib 라이브러리를 사용하여 그림 12에 시각화하였다.

42 ict 실내위치 (9)
그림 12

파란 선이 Ground Truth, 노란 점이 NFC 태그의 위치이며 녹색 선이 엔코더로만 계산한 궤적, 붉은 선이 NFC 태그를 이용해 보정한 이동 궤적이다. 엔코더만 사용하여 이동 궤적을 계산한 경우 엔코더의 drift 오차, 좌우 엔코더의 인식률 차이, 나침반의 heading 오류 등에 의해 한번의 이동 궤적 오류가 전체적인 이동 궤적에 누적된 오차를 주는 drift 를 볼 수 있다. 반면 NFC 태그를 이용하여 NFC 태그를 밟은 경우 바로 위치를 NFC 인식 지점으로 옮겨버리는 경우 이러한 기존의 오차의 영향을 받지 않고 ground truth에 맞는 위치로 옮겨감으로서 위치가 보정되는 것을 볼 수 있다. 정확한 위치를 알아 냄으로서 QR 코드/NFC 태그의 위치 또한 알 수 있고 이를 이용하여 QR 코드나 NFC 태그가 손상되더라도 상호 검증을 통해 손상된 marker를 알려줄 수 있다.

본 프로젝트에서는 로봇의 실내 위치 인식에 사용되는 로봇의 marker의 정상 여부를 검증하기 위해 QR 코드(혹은 apriltag 등의 visual 마커) 와 NFC/RFID 태그를 동시에 사용하여 한 쪽이 손상되더라도 다른 마커를 사용하여 오류를 검사하는 방식을 제안하였으며 이 임무를 수행할 수 있는 로봇을 제작하였다. 제작된 로봇은 엔코더와 나침반을 사용해 자신의 이동 위치를 알 수 있으며 안드로이드 폰을 사용하여 NFC와 QR 코드를 인식할 수 있다. 마커 인식을 통해 자신의 위치를 보정할 수 있으며 이를 이용해 “다른 마커가 인식되어야 할 자리에 QR코드/NFC 중 하나만 인식되거나 둘 다 인식되지 않는 경우 이를 사용자에게 알려 줌으로서 대형 물류창고 등에서 로봇의 위치인식을 이용한 process가 원할하게 구동되도록 도와줄 수 있을 것이라 기대된다.
또한 실내 위치 인식의 정확도를 올리기 위해 Wi-Fi, Zigbee 등의 무선 비콘 및 여러 방식이 사용되고 fingerprinting 방식 등 모든 구역에서의 전파 신호 세기를 수집하여 기계학습을 이용해 실내 위치를 인식하려는 시도 또한 사용되고 있다. 이때 사람이 직접 이러한 신호를 수집하는 것은 매우 불편하며 이미 QR코드 등의 기존 마커가 사용될 경우 이 로봇을 이용하여 각 위치 별 전파 신호 수집 등을 수월하게 진행하는 등의 추가적인 활용 또한 기대된다.

개발 환경
아두이노와 아두이노 IDE를 사용하였으며 라즈베리파이 보드에 전용 Debian 리눅스를 올려 사용하였으며 vim 에디터 개발 환경에서 python을 사용하여 프로그램하였다. 인터페이스 웹서버는 python의 BaseHTTPServer 모듈을 사용하여 구동하였으며 HTML과 jQuery Mobile을 사용하여 웹 인터페이스를 제작하였다. 영상 전송에는 OpenCV를 사용하였다.

기타 (회로도/소스코드)
아두이노 소스코드

#include <Servo.h>
#include <Wire.h>
#include <HMC5883L.h>
#include <string.h>
#define MAX_LEN 30
char cmd_array[MAX_LEN];
Servo servo_arm;
HMC5883L compass;
int error = 0;
int rignt_motor_A = 10;
int rignt_motor_B = 11;
int rignt_motor_power = 6;
volatile int right_wheel_count = 0;
int left_motor_A = 7;
int left_motor_B = 8;
int left_motor_power = 5;
volatile int left_wheel_count = 0;
int servo_arm_port = 9;
void setup()
{
Serial.begin(9600);
Wire.begin();

compass = HMC5883L();
error = compass.SetScale(1.3); //Set compass scale
if(error != 0) { Serial.println(“Compass ERR 1″);}
error = compass.SetMeasurementMode(Measurement_Continuous); //Set cont. measure mode
if(error != 0) { Serial.println(“Compass ERR 2″);}

attachInterrupt(0, right_wheel_encoder, CHANGE); // Right Wheel Encoder
attachInterrupt(1, left_wheel_encoder, CHANGE); // Left Wheel Encoder

servo_arm.attach(servo_arm_port);
servo_arm.write(140); //180 (max down) 140 (midpoint) 0(original)

pinMode(rignt_motor_A,OUTPUT);
pinMode(rignt_motor_B,OUTPUT);
pinMode(rignt_motor_power,OUTPUT);

pinMode(left_motor_A,OUTPUT);
pinMode(left_motor_B,OUTPUT);
pinMode(left_motor_power,OUTPUT);
}
void set_servo_angle(int angle)
{
servo_arm.write(angle);
delay(15);
}
void set_motor_value(int right_dir, int left_dir, int right_speed, int left_speed, int delay_time)
{

if(right_dir == 1) //front
{
digitalWrite(rignt_motor_A,HIGH);
digitalWrite(rignt_motor_B,LOW);
}
if(right_dir == 0) //back
{
digitalWrite(rignt_motor_A,LOW);
digitalWrite(rignt_motor_B,HIGH);
}

if(left_dir == 1) //front
{
digitalWrite(left_motor_A,HIGH);
digitalWrite(left_motor_B,LOW);
}
if(left_dir == 0) //back
{
digitalWrite(left_motor_A,LOW);
digitalWrite(left_motor_B,HIGH);
}

analogWrite(rignt_motor_power,right_speed);
analogWrite(left_motor_power,left_speed);

delay(delay_time);

digitalWrite(rignt_motor_power,0);
digitalWrite(left_motor_power,0);
}
float read_compass()
{
MagnetometerScaled scaled = compass.ReadScaledAxis();
int MilliGauss_OnThe_XAxis = scaled.XAxis;
float heading = atan2(scaled.YAxis+136, scaled.XAxis+40);
//float declinationAngle = 0.0457;
//heading += declinationAngle;

if(heading < 0) {heading += 2*PI;}
if(heading > 2*PI) {heading -= 2*PI;}

float deg_heading = ((heading * 180)/PI)-9;
if(deg_heading < 0) {deg_heading += 360;}

return deg_heading;
}
void right_wheel_encoder() {right_wheel_count++;}
void left_wheel_encoder() {left_wheel_count++;}
void send_all_sensor_val()
{
float heading = read_compass();

Serial.print(“r,”);
Serial.print(heading);
Serial.print(“,”);
Serial.print(right_wheel_count);
Serial.print(“,”);
Serial.print(left_wheel_count);
Serial.print(“,”);

MagnetometerRaw raw = compass.ReadRawAxis();
Serial.print(raw.XAxis);
Serial.print(“,”);
Serial.print(raw.YAxis);
Serial.print(“,”);
Serial.print(raw.ZAxis);
Serial.print(“,”);

Serial.println(“”);
right_wheel_count = 0;
left_wheel_count = 0;
}
void loop()
{
if(Serial.available()>0)
{
String cmd = Serial.readStringUntil(‘\n’);
//s,servo_angle,\n <- servo cmd
//s,140,\n
//m,right_fb,left_fb,right_pw,left_pw,time,\n <- move cmd
//m,1,0,125,125,500,\n
//r,0\n <- request cmd
char *ptr;
cmd.toCharArray(cmd_array,MAX_LEN);

char * header = strtok(cmd_array, “,”);
if(strcmp(header,”s”) == 0) //data request
{
int servo_val = atoi(strtok(NULL, “,”));
set_servo_angle(servo_val);
}
if(strcmp(header,”m”) == 0) //move command
{

int right_direction = atoi(strtok(NULL, “,”));
int left_direction = atoi(strtok(NULL, “,”));
int right_power = atoi(strtok(NULL, “,”));
int left_power = atoi(strtok(NULL, “,”));
int power_time = atoi(strtok(NULL, “,”));

set_motor_value(right_direction, left_direction, right_power, left_power, power_time);
}

if(strcmp(header,”r”) == 0) //data request
{
send_all_sensor_val();
}
}
delay(200);
}

웹서버 코드

#!/usr/bin/python
import BaseHTTPServer
import SimpleHTTPServer
import SocketServer
import json
import serial
STOP_CODE = 0
HEADING = 0
L_ENCODER = 0
R_ENCODER = 0
#com = serial.Serial(“/dev/ttyACM0″)
com = serial.Serial(“COM11″)
#LOG_FILE = open(“rover_log.txt”,’w')
class ThreadServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
pass
#
class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
#
def do_POST(self):
global STOP_CODE
global HEADING
global L_ENCODER
global R_ENCODER
global LOG_FILE

length = int(self.headers.getheader(‘content-length’))
var = self.rfile.read(length) #code=f&data=125&delay=500, code=s&data=57
packet = var.split(“&”)
cmd = packet[0].split(“=”)[1]

if cmd == “f”: #Front
header = “m,”
right_dir = “1,”
left_dir = “1,”
right_pw = packet[1].split(“=”)[1] left_pw = packet[1].split(“=”)[1] time_delay = packet[2].split(“=”)[1] ser_cmd = header + right_dir + left_dir + right_pw + “,” + left_pw + “,” + time_delay + “,\n”
print ser_cmd
com.write(ser_cmd)
#”"”
com.write(‘r,0\n’)
raw_sensor_str = com.readline() #r,heading,rencoder,lencoder,mag_x,mag_y,mag_z,\n
#”"”
sensor_data = raw_sensor_str.split(“,”)
HEADING = sensor_data[1] L_ENCODER = sensor_data[3] R_ENCODER = sensor_data[2] #LOG_FILE.write(ser_cmd)
#LOG_FILE.write(raw_sensor_str)

if cmd == “b”: #Back
header = “m,”
right_dir = “0,”
left_dir = “0,”
right_pw = packet[1].split(“=”)[1] left_pw = packet[1].split(“=”)[1] time_delay = packet[2].split(“=”)[1] ser_cmd = header + right_dir + left_dir + right_pw + “,” + left_pw + “,” + time_delay + “,\n”
print ser_cmd
com.write(ser_cmd)
#”"”
com.write(‘r,0\n’)
raw_sensor_str = com.readline() #r,heading,rencoder,lencoder,mag_x,mag_y,mag_z,\n
#”"”
sensor_data = raw_sensor_str.split(“,”)
HEADING = sensor_data[1] L_ENCODER = sensor_data[3] R_ENCODER = sensor_data[2] #LOG_FILE.write(ser_cmd)
#LOG_FILE.write(raw_sensor_str)
if cmd == “r”: #Right
header = “m,”
right_dir = “0,”
left_dir = “1,”
right_pw = packet[1].split(“=”)[1] left_pw = packet[1].split(“=”)[1] time_delay = packet[2].split(“=”)[1] ser_cmd = header + right_dir + left_dir + right_pw + “,” + left_pw + “,” + time_delay + “,\n”
print ser_cmd
com.write(ser_cmd)
#”"”
com.write(‘r,0\n’)
raw_sensor_str = com.readline() #r,heading,rencoder,lencoder,mag_x,mag_y,mag_z,\n
#”"”
sensor_data = raw_sensor_str.split(“,”)
HEADING = sensor_data[1] L_ENCODER = sensor_data[3] R_ENCODER = sensor_data[2] #LOG_FILE.write(ser_cmd)
#LOG_FILE.write(raw_sensor_str)
if cmd == “l”: #Left
header = “m,”
right_dir = “1,”
left_dir = “0,”
right_pw = packet[1].split(“=”)[1] left_pw = packet[1].split(“=”)[1] time_delay = packet[2].split(“=”)[1] ser_cmd = header + right_dir + left_dir + right_pw + “,” + left_pw + “,” + time_delay + “,\n”
print ser_cmd
com.write(ser_cmd)
#”"”
com.write(‘r,0\n’)
raw_sensor_str = com.readline() #r,heading,rencoder,lencoder,mag_x,mag_y,mag_z,\n
#”"”
sensor_data = raw_sensor_str.split(“,”)
HEADING = sensor_data[1] L_ENCODER = sensor_data[3] R_ENCODER = sensor_data[2] #LOG_FILE.write(ser_cmd)
#LOG_FILE.write(raw_sensor_str)
if cmd == “s”: #Servo
servo_angle = packet[1].split(“=”)[1] header = “s,”
ser_cmd = header + servo_angle + “,\n”
print ser_cmd
com.write(ser_cmd)
sensor_data_dict = {‘heading’:HEADING,’l_encoder’:L_ENCODER,’r_encoder’:R_ENCODER}

self.send_response(200)
self.send_header(“Content-type”, “text/html”)
self.end_headers()
self.wfile.write(json.dumps(sensor_data_dict))
#
#
if __name__ == “__main__”:
Handler = MyRequestHandler
server = ThreadServer((’0.0.0.0′,8080), Handler)

while not STOP_CODE == 1:
server.handle_request()
#LOG_FILE.close()
server.server_close()