얼마전에 youtube에서 재미난 영상을 보았다. "Arduino Radar Project" 인데 참 재밌는 것을 만든것 같다고 생각을 했다. 아래 영상을 한번 보자.

이 영상을 보자마자 한번 만들어 봐야 겠다고 생각을 했다.

Arduino Radar Project는 Arduino Mega를 사용했고 초음파센서 모듈과 서보모터를 이용해서 Radar를 구현을 했다. 그리고 PC에서 Radar의 모습으로 표현을 해줬다. PC에서 사용한 개발 툴은 Processing이다. Arduino에서 각도값과 물체의 거리 값을 Serial 통신으로 PC로 전송을 하고, 그 값을 기반으로 Radar의 모습으로 표현을 한 프로젝트이다. 아래 그림은 이 프로젝트의 블록도이다.


 Processing을 간단하게 알아보면, MIT미디어랩에서 만든 Java기반의 오픈소스 프로그래밍 IDE 이다. 기존의 프로그래밍을 하는 사람이나 일반 사람들도 쉽게 사용할 수 있도록 만들어졌고, 이미지, 애니메이션, 인터렉션등 시각적으로 표현하는 것에 특화가 되어있다. 오픈소스이기 때문에 무료로 사용이 가능하고 다양한 라이브러리나 예제들이 오픈되어 있다. 아두이노를 사용하는 사람이라면 아두이노와 연동을 해서 시각적인 효과를 표현하고 싶을 때 쉽게 표현을 할수 있을 것이다.

아래 URL은 Arduino Radar Project의 원본 페이지 이다.

http://howtomechatronics.com/projects/arduino-radar-project/


Arduino Network Radar

위에 설명했던 Arduino Radar Project는 Arduino와 PC가 USB 케이블로 연결이 되어 서로 통신하는 방식이다. 하지만 내가 만들 것은 Ethernet Shield를 추가해서 Network(Ethernet)에 연결된 Arduino Radar를 만들어 보려고 한다. PC의 Processing도 시리얼 통신으로 구현이 되어 있는 것을 Network 기능을 추가하려고 한다.

Arduino Radar Project는 Arduino와 PC가 한공간에 있어야 하지만, Arduino Network Radar는  Arduino와 PC 서로 떨어져 있는 원격지에서 Radar를 확인할 수 있는 장점이 있다.

Arduino Network Radar를 만들어 보기전에 서보모터와 초음파센서에 대해서 간단히 알아보자.

2016/07/05 - [IT / Development/Arduino] - Arduino로 Servo Motor를 제어해 보자.

2016/07/05 - [IT / Development/Arduino] - Arduino와 초음파센서(HC-SR04)를 이용해서 거리를 측정해 보자.

위 2개의 글을 보면 서보모터와 초음파센서에 대해서 대충 알수 있으며, 어떻게 다루는지 알수 있을 것이다.

아래 그림은 Arduino Network Radar의 구성도 이다.

위 Arduino Radar와는 다르게 Ethernet 케이블로 연결된 것을 볼수 있다. Arduino는 Server로 동작을 할 것이고, PC는 Client로 동작을 할 것이다.

위 구성도를 바탕으로 회로도를 그려 보았다. Arduino에 Ethernet Shield를 Stacking 한 모습니다.

Arduino의 D3에 서보모터의 신호선을 연결하고, D2에 초음파센서의 Trig Pin, D4에 Echo Pin을 연결했다. 이렇게 선만 연결하고 따로 다른 회로를 구성할 필요는 없다.

아래는 실제로 구성한 모습니다. 초음파센서의 브라켓을 3D 프린터로 출력해서 서보모터에 고정을 했다.

아래는 Arduino Code 이다. Client가 Connect 되고 데이터를 아무거나 넘겨주면, Arduino는 Client로 data를 전송 한다. Ethernet으로 전송되는 데이터의 포맷은 "각도,거리." 형태이며 string으로 전송된다. Ex) 55,55.

다른 코드설명은 주석으로 대체한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <Servo.h>        // 서보 라이브러리 추가 
#include <SPI.h>        // SPI 라이브러리 추가
#include <Ethernet2.h>    // 이더넷 라이브러리 추가
 
// 초음파 센서 하드웨어 핀 정의
const int trigPin = 2;
const int echoPin = 4;
// 서보 모터 하드웨어 핀 정의
const int servoPin = 3;
// MAC Addr 초기화
byte mac[] = {
    0xDE0xAD0xBE0xEF0xFE0xED
};
// 고정 아이피를 사용한다면, 사용할 IP 정의
IPAddress ip(1921681177);
IPAddress gateway(19216811);
IPAddress subnet(25525500);
 
EthernetServer server(5000);        // 이더넷 서버 객체 선언, port는 5000
Servo myServo;        // 서보 모터 객체 선언
 
long duration;        // 초음파 반사 시간 저장 변수 선언
int distance;        // 물체와의 거리 저장 변수 언
char sendData[6= "0,";    // 데이터 버퍼 선언
boolean alreadyConnected = false// Connect 상태 변수 선언
 
void setup() {
    Serial.begin(115200);        // 시리얼 초기화
    // 이더넷 초기화, 아래 둘중에 하나만 사용.
    Ethernet.begin(mac, ip, gateway, subnet);    //고정 IP를 사용
    //Ethernet.begin(mac);        //DCHP 사용
    server.begin();        //서버로 동작 시작
 
    pinMode(trigPin, OUTPUT); // trigPin을 Output으로 설정
    pinMode(echoPin, INPUT); // echoPin을 Input으로 설정
    myServo.attach(servoPin); // 서보 모터 초기화
    // 디바이스의 IP 출력
    Serial.print("Arduino Network Radar server address:");
    Serial.println(Ethernet.localIP());
}
void loop() {
    // 클라이언트 객체 선언, 서버가 받은 데이터가 있는지 확인
    EthernetClient client = server.available();
    // 클라이언트로 붙터 받은 데이터가 있으면
    if (client)    {
        Serial.println("Hello client");
        alreadyConnected = true;    // 클라이어트가 접속 했다고 판단.
    }
    if (alreadyConnected == true) {
        // 서보 모터는 15도 -> 165도로 회전
        for (int i = 15; i <= 165; i++) {
            myServo.write(i);
            delay(30);
            distance = calculateDistance();        // 물체와의 거리 산출
            int j = 180 - i;    // 디스플레이를 위한 각도 반전
            sprintf(sendData, "%d%s%d%s", j, ",", distance, ".");    // 각도 값과 거리 값을 합침
            server.print(sendData);        //클라이언트로 data 전송
            Serial.print(sendData);        //시리얼로 data 전송
 
        }
        // 서보 모터는 165도 -> 15도로 회전
        for (int i = 165; i > 15; i--) {
            myServo.write(i);
            delay(30);
            distance = calculateDistance();
            int j = 180 - i;
            sprintf(sendData, "%d%s%d%s", j, ",", distance, ".");
            server.print(sendData);
            Serial.print(sendData);
        }
    }
}
// 초음파 센서 동작, 거리 산출 함수
int calculateDistance() {
    // 10us 펄스 발생
    digitalWrite(trigPin, LOW);
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
 
    duration = pulseIn(echoPin, HIGH);    // 반사된 초음파 시간 측정
    distance = duration*0.034 / 2;        // 츨정된 시간을 바탕으로 거리 계산
    return distance;    // 거리 값 리턴
}
cs

아래 그림은 PC에서 Ethernet Client Terminal을 이용해서 Arduino와 통신을 해본 것이다.

Client에서 접속을 하고, "Hi" 라고 메시지를 보내면 Arduino에서 각도 데이터와 거리 데이터를 보내주는 것을 볼 수 있다. 위 그림에는 23번 포트이지만 최종 코드에서는 5000번 포트로 변경 했다.

아래는 Processing Code이다. 기본적인 code는 Arduino Radar와 동일하고, 시리얼 인터페이스 부분을 Network 인터페이스로만 수정했다. 

여기서 약간 헤맨 부분이 있었다. clientEvent 함수인데, 패킷단위로 이벤트가 발생이 될줄 알았는데 바이트 단위로 이벤트가 발생하는 것을 볼수 있었다. 그래서 별도로 String으로 데이터를 받지 않고 한 바이트씩 받아 버퍼에 따로 저장을 했다. 수정한 부분은 한글로 따로 주석처리를 했으니 참고하길 바란다. 나머지 코드는 조만간 다시 분석해서 한글로 주석을 달겠다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import processing.net.*;    // network 라이브러리 import
Client myClient;        // client 객체 생성
// defubes variables
char dataIn;        // ethernet data 임시 버퍼 선언
String sData="";        // ethernet data를 string 형태로 저장할 버퍼 선언
 
String angle="";
String distance="";
String data="";
String noObject;
float pixsDistance;
int iAngle, iDistance;
int index1=0;
int index2=0;
PFont orcFont;
void setup() {
  
 size (960540); // ***CHANGE THIS TO YOUR SCREEN RESOLUTION***
 //size (1920, 1080);
 smooth();
 myClient = new Client(this"192.168.1.177"5000);     // Client로 초기화, 아두이노 IP 192.168.1.177 Port번호 5000으로 접소.
 myClient.write("Hi");        // 아두이노에 접속이 되면 "Hi" 전송
 
 orcFont = loadFont("OCRAExtended-30.vlw");
}
void draw() {
  
  fill(98,245,31);
  textFont(orcFont);
  // simulating motion blur and slow fade of the moving line
  noStroke();
  fill(0,4); 
  rect(00, width, height-height*0.065); 
  
  fill(98,245,31); // green color
  // calls the functions for drawing the radar
  drawRadar(); 
  drawLine();
  drawObject();
  drawText();
}
// 데이터가 수신되면 호출되는 함수. 패킷단위로 호출될 줄 알았는데 바이트 단위로 호출 됨.
void clientEvent(Client myClient) {
  dataIn = myClient.readChar();        // 수신된 데이터를 dataIn 변수에 저장.
  sData += dataIn;        // dataIn 변수의 데이터를 sData string 변수에 누적해서 저장.
  
  // "."(점)이 전송될 때까지 sData에 저장만 한다.
  if(dataIn == '.')
  {
    sData = sData.substring(0,sData.length()-1);        // End 데이터인 "."을 제외하고 sData에 저장한다.
    String[] list = split(sData, ",");        // ","를 기준으로 데이터를 나누어 list 배열에 저장한다.
    
    angle=list[0];        // list[0]는 각도 데이터 이고, angle 변수에 저장한다.
    distance=list[1];    // list[1]은 거리 데이터 이고, distance 변수에 저장한다.
 
    // 파싱된 데이터를 processing 콘솔창에 표시한다.
    print("angle     : ");
    println(angle);
    print("distance  : ");
    println(distance);
    println("------------------");
    
    iAngle = int(angle);        // angle변수를 int 형으로 변환하고, iAngle 변수에 저장한다. iAngle변수는 화면에 표시될때 사용이 된다.
    iDistance = int(distance);    //distance변수를 int 형으로 변환하고, iDistance 변수에 저장한다. iDistance변수는 화면에 표시될때 사용이 된다.
    sData = "";        // sData를 초기화 한다.
  }
}
void drawRadar() {
  pushMatrix();
  translate(width/2,height-height*0.074); // moves the starting coordinats to new location
  noFill();
  strokeWeight(2);
  stroke(98,245,31);
  // draws the arc lines
  arc(0,0,(width-width*0.0625),(width-width*0.0625),PI,TWO_PI);
  arc(0,0,(width-width*0.27),(width-width*0.27),PI,TWO_PI);
  arc(0,0,(width-width*0.479),(width-width*0.479),PI,TWO_PI);
  arc(0,0,(width-width*0.687),(width-width*0.687),PI,TWO_PI);
  // draws the angle lines
  line(-width/2,0,width/2,0);
  line(0,0,(-width/2)*cos(radians(30)),(-width/2)*sin(radians(30)));
  line(0,0,(-width/2)*cos(radians(60)),(-width/2)*sin(radians(60)));
  line(0,0,(-width/2)*cos(radians(90)),(-width/2)*sin(radians(90)));
  line(0,0,(-width/2)*cos(radians(120)),(-width/2)*sin(radians(120)));
  line(0,0,(-width/2)*cos(radians(150)),(-width/2)*sin(radians(150)));
  line((-width/2)*cos(radians(30)),0,width/2,0);
  popMatrix();
}
void drawObject() {
  pushMatrix();
  translate(width/2,height-height*0.074); // moves the starting coordinats to new location
  strokeWeight(9);
  stroke(255,10,10); // red color
  pixsDistance = iDistance*((height-height*0.1666)*0.025); // covers the distance from the sensor from cm to pixels
  // limiting the range to 40 cms
  if(iDistance<40){
    // draws the object according to the angle and the distance
  line(pixsDistance*cos(radians(iAngle)),-pixsDistance*sin(radians(iAngle)),(width-width*0.505)*cos(radians(iAngle)),-(width-width*0.505)*sin(radians(iAngle)));
  }
  popMatrix();
}
void drawLine() {
  pushMatrix();
  strokeWeight(9);
  stroke(30,250,60);
  translate(width/2,height-height*0.074); // moves the starting coordinats to new location
  line(0,0,(height-height*0.12)*cos(radians(iAngle)),-(height-height*0.12)*sin(radians(iAngle))); // draws the line according to the angle
  popMatrix();
}
void drawText() { // draws the texts on the screen
  
  pushMatrix();
  if(iDistance>40) {
  noObject = "Out of Range";
  }
  else {
  noObject = "In Range";
  }
  fill(0,0,0);
  noStroke();
  rect(0, height-height*0.0648, width, height);
  fill(98,245,31);
  textSize(25);
  
  text("10cm",width-width*0.3854,height-height*0.0833);
  text("20cm",width-width*0.281,height-height*0.0833);
  text("30cm",width-width*0.177,height-height*0.0833);
  text("40cm",width-width*0.0729,height-height*0.0833);
  textSize(25);
  text("Object: " + noObject, width-width*0.875, height-height*0.0277);
  text("Angle: " + iAngle +" °", width-width*0.48, height-height*0.0277);
  text("Distance: ", width-width*0.26, height-height*0.0277);
  if(iDistance<40) {
  text("        " + iDistance +" cm", width-width*0.225, height-height*0.0277);
  }
  textSize(25);
  fill(98,245,60);
  translate((width-width*0.4994)+width/2*cos(radians(30)),(height-height*0.0907)-width/2*sin(radians(30)));
  rotate(-radians(-60));
  text("30°",0,0);
  resetMatrix();
  translate((width-width*0.503)+width/2*cos(radians(60)),(height-height*0.0888)-width/2*sin(radians(60)));
  rotate(-radians(-30));
  text("60°",0,0);
  resetMatrix();
  translate((width-width*0.507)+width/2*cos(radians(90)),(height-height*0.0833)-width/2*sin(radians(90)));
  rotate(radians(0));
  text("90°",0,0);
  resetMatrix();
  translate(width-width*0.513+width/2*cos(radians(120)),(height-height*0.07129)-width/2*sin(radians(120)));
  rotate(radians(-30));
  text("120°",0,0);
  resetMatrix();
  translate((width-width*0.5104)+width/2*cos(radians(150)),(height-height*0.0574)-width/2*sin(radians(150)));
  rotate(radians(-60));
  text("150°",0,0);
  popMatrix(); 
}
cs

이제 Arduino Code와 Processing Code가 완료 됬기 때문에 Network으로 연결하고 테스트를 하면된다.

나는 공유기에 PC를 연결하고, Arduino도 같은 공유기에 연결을 했다. Arduino는 DHCP를 사용하지 않고 static IP로 설정을 했다. PC의 IP는 공유기를 통해서 DHCP로 받아 192.168.1.xxx 로 잡혔고, Arduino의 IP도 192.168.1.177로 같은 네트워크망이기 때문에 통신이 될 것이다.

이제 동작하는 영상을 보자!!!

아주 잘 동작하는 것을 볼수 있다. 물체가 인식이 되면 PC 화면에는 빨간색으로 표시가 된다. processing을 아주 조금 다뤄보니 참 재밌는 것 같다. 좀더 공부해 봐도 좋을 것 같다.

+ Recent posts