본문 바로가기
프로젝트

아두이노 레이더 프로젝트 - 후속편 360도 레이더

by Bugwhale 2020. 2. 1.

이 글은 아두이노 레이더 프로젝트 - 후속편 360도 레이더에 대해 설명합니다.

1. 개요

이전 글에서 레이더 꾸미기에 대한 마무리 작업을 하였다. 레이더 프로젝트를 계획한 시점에서 든 생각이 "레이더면 당연히 360도 회전이지"였다. 막상 구상단계에 들어가다보니 하드웨어에서 문제가 생겼다. 한방향으로만 계속 돌아가면 케이블이 꼬이는 것이다. 물론 DC 모터의 브러쉬처럼 회전은 하여도 케이블은 꼬이지 않도록 하는 방법이 있지만(슬립링 검색) 여기서는 그 방법까지는 적용하지 않겠다. 혹시라도 하드웨어적인 문제를 해결하여 사용하실 분들을 위하여 소스코드는 한방향으로 360도 회전 후 반대방향으로 360도 회전 반복하는 코드와 한 방향으로만 계속 회전하는 코드 두개를 작성하였다.

2. 아두이노 회로 구성 및 소스 코딩

전체적인 구조는 바뀌지 않았으나 스텝모터의 특성때문에 이해하는건 조금 어려울 수 있다. 주석과 소스코드 이후에 자세하게 설명하였다.

2.1 한방향으로만 회전하는 코드

// 스텝모터 라이브러리
#include <Stepper.h>

#define STEP_REVOLUTION 2048
#define STEP 4
// 핀 번호 설정
#define PIN_IN1 2
#define PIN_IN2 3
#define PIN_IN3 4
#define PIN_IN4 5
#define PIN_PSD A0

// 스텝모터 클래스 생성 모터드라이버의 핀 번호는 다음 순서대로 들어간다.(총스텝 수, IN4, IN2, IN3, IN1)
Stepper myStepper(STEP_REVOLUTION, PIN_IN4, PIN_IN2, PIN_IN3, PIN_IN1);

void setup ()
{
	// 시리얼 통신 초기화
	Serial.begin (9600);
	// 핀 모드 설정
	pinMode (PIN_PSD, INPUT);
	// 스텝모터 스피드 설정(setSpeed 메소드는 RPM값을 설정한다. 1분에 15바퀴 = 1바퀴당 4초)
	myStepper.setSpeed(15); 
}

void loop ()
{
	// 1바퀴를 회전시켜야 하니까 2048/4 = 512 번 수행
	for(int i = 0; i < STEP_REVOLUTION/STEP; i++){
		// 4스텝(0.703125°)만큼 회전 후
		myStepper.step(STEP);
		// PSD 센서로부터 거리를 얻어온다
		int distance = getDistance(analogRead(PIN_PSD));
		// 시리얼 통신으로 전달
		Serial.print(i*0.17578125*STEP);
		Serial.print("a");
		Serial.print(distance);
		Serial.print("\n");
	}
}

// PSD 센서값 얻어오는 함수
int getDistance(int sensorValue)
{
  return 27.728 * pow(map(sensorValue, 0, 1023, 0, 5000) / 1000.0, -1.2045);
}

2.2 양방향으로 회전하는 코드

// 스텝모터 라이브러리
#include <Stepper.h>

#define STEP_REVOLUTION 2048
#define STEP 4
// 핀 번호 설정
#define PIN_IN1 2
#define PIN_IN2 3
#define PIN_IN3 4
#define PIN_IN4 5
#define PIN_PSD A0

// 스텝모터 클래스 생성 모터드라이버의 핀 번호는 다음 순서대로 들어간다.(총스텝 수, IN4, IN2, IN3, IN1)
Stepper myStepper(STEP_REVOLUTION, PIN_IN4, PIN_IN2, PIN_IN3, PIN_IN1);

void setup ()
{
	// 시리얼 통신 초기화
	Serial.begin (9600);
	// 핀 모드 설정
	pinMode (PIN_PSD, INPUT);
	// 스텝모터 스피드 설정(setSpeed 메소드는 RPM값을 설정한다. 1분에 15바퀴 = 1바퀴당 4초)
	myStepper.setSpeed(15); 
}

void loop ()
{
	// 1바퀴를 회전시켜야 하니까 2048/4 = 512 번 수행
	for(int i = 0; i < STEP_REVOLUTION/STEP; i++){
		// 4스텝(0.703125°)만큼 회전 후
		myStepper.step(STEP);
		// PSD 센서로부터 거리를 얻어온다
		int distance = getDistance(analogRead(PIN_PSD));
		// 시리얼 통신으로 전달
		Serial.print(i*0.17578125*STEP);
		Serial.print("a");
		Serial.print(distance);
		Serial.print("\n");
	}
	// 1바퀴는 2048/4 = 512 번 수행
	for(int i = 0; i < STEP_REVOLUTION/STEP; i++){
		// 반대방향으로 4스텝(0.703125°)만큼 회전 후
		myStepper.step(-STEP);
		// PSD 센서로부터 거리를 얻어온다
		int distance = getDistance(analogRead(PIN_PSD));
		// 시리얼 통신으로 전달
		Serial.print(-i*0.17578125*STEP);
		Serial.print("a");
		Serial.print(distance);
		Serial.print("\n");
	}
}

// PSD 센서값 얻어오는 함수
int getDistance(int sensorValue)
{
  return 27.728 * pow(map(sensorValue, 0, 1023, 0, 5000) / 1000.0, -1.2045);
}

다른부분은 이해할만 하더라도 제일위에 #define STEP_REVOLUTION 2048 #define STEP 4 이 부분이 이해하기 어려울 것이다. 데이터 시트 기준 28BYJ-48 스텝모터는 64 스텝당 5.625° 만큼 회전한다. 즉 4096 스텝이 한바퀴임을 파악할 수 있는데 데이터 시트는 하프스텝 기준으로 아두이노 개발환경에서 사용하는 Stepper.h 라이브러리는 풀스텝 기준으로 작성되었기에 한바퀴는 2048 스텝으로 잡아야 한다.(풀스텝 = 하프스텝/2) 그렇기에 총 스텝값을 의미하는 STEP_REVOLUTION 값은 2024 로 설정하였다. 서보모터로 레이더를 만들었을 때는 1도마다 거리센서로 측정을 해주었다. 그렇기에 당연히 스텝모터를 사용하더라도 1도마다 측정하는게 깔끔하지만 28BYJ-48 스텝모터 특성상 1도씩 딱 맞아 떨어지지 않는다. 왜냐하면 1스텝이 0.17578125° 도이기 때문이다.(이 조차도 시간으로 제어한다면 어떻게든 할 수 있겠지만 그렇게 까지 할 필요는 없다고 본다.) 그래서 너무 크지도 작지도 않은 4스텝마다(0.703125°)로 설정한다. 이것을 의미하는 것이 #define STEP 4 이다.

혹시라도 정밀하게 하거나 덜 정밀하게 하고 싶다면 STEP 값을 다음 값으로 변경하면 된다. 스텝 각도 * 32 5.625° * 16 2.8125° * 8 1.40625° * 4 0.703125° * 2 0.3515625° * 1 0.17578125°
2019.01.16 확인결과 4스텝 이하인 경우에 각도의 틀어짐이 발생한다. 제어가 잘 안된다면 8스텝이상으로 설정하는 것을 추천한다.

3. 프로세싱 소스 코딩하기

프로세싱 소스 코드는 서보모터를 사용할 때와 비교하여 크게 달라지는 점은 없다. 크게 두가지가 바뀌었는데 180도 기준으로 설정된 값들이 360도 기준으로 바뀌었으며 스텝모터 특성상 1도마다(정수) 제어하지 못하니 약 0.7도(실수)로 제어하다보니 int 형 자료형에서 float 형 자료형을 사용하게 되었고 이에 따라 필요한 부분을 바꿔주었다.

import processing.serial.*;
 
float iAngle; // 각도값이 실수로 넘어오기 때문에 자료형 변경
int iDistance;
 
void setup() {
	size(500, 500);
	background(0, 0, 0);
	Serial myPort;
	myPort = new Serial(this, "COM3", 9600);
	myPort.bufferUntil('\n');
}
 
void draw() {
	noStroke();
	fill(0, 25);
	rect(0, 0, 500, 500);

	drawRadarValueText();
	drawRadar();
	drawRadarHand();
	drawRadarObject();
	drawRadarInfoText();
}
  
void serialEvent(Serial myPort) {
	String sData, sAngle, sDistance;
	sData = myPort.readStringUntil('\n');
	sData = sData.substring(0, sData.length()-1);
	int seperatorIndex = sData.indexOf("a");
	sAngle = sData.substring(0, seperatorIndex);
	sDistance= sData.substring(seperatorIndex+1, sData.length());
	iAngle = float(sAngle); // 각도값이 실수로 넘어오기 때문에 실수형으로 변환
	iDistance = int(sDistance);
}

void drawRadarValueText() {
	pushMatrix();
	translate(5, 5);
	fill(0);
	stroke(0, 255, 0);
	strokeWeight(1);
	rect(0, 0, 90, 50); // 값이 박스 밖을 벗어나기 때문에 X값을 10 늘려주었다.
	fill(30, 255, 30);
	text("Angle : " + iAngle, 5, 15);
	text("Distance : " + iDistance, 5, 30);
	if(iDistance > 60) {
		text("Not Detected", 5, 45); 
	}
	else {
		text("Detected", 5, 45);
	}
	popMatrix(); 
}

void drawRadar() {
	pushMatrix();
	noFill();
	translate(250, 250);
	strokeWeight(1);
	stroke(80, 230, 25);
	for(int i = 1; i <= 4; i ++) {
		// 반호에서 원을 그려줘야하니 기존에 비해 2배로 그려주자
		// 원으로 그려줘도 상관없으나 이전 코드에서 *2 만 추가해주면 되니 호로 그렸다.
		arc(0, 0, 100*i, 100*i, PI, TWO_PI*2); 
	}
	for(int i = 0; i <= 360; i += 15) { // 180도에서 360도로 그려주자
		line(0,0,-200*cos(radians(i)),-200*sin(radians(i)));
	}
	popMatrix();
}

void drawRadarHand() {
	pushMatrix();
	noFill();
	translate(250, 250);
	strokeWeight(4);
	stroke(80, 255, 25);
	line(0, 0, 200*cos(radians(iAngle)), -200*sin(radians(iAngle)));
	popMatrix();
}

void drawRadarObject() {
	pushMatrix();
	noFill();
	translate(250, 250);
	strokeWeight(4);
	stroke(255, 0, 0);
	if(iDistance < 60) {
		float objectDis = 200.0*(iDistance/60.0);
		float x = objectDis*cos(radians(iAngle));
		float y = -objectDis*sin(radians(iAngle));
		ellipse(x, y, 5, 5);
	}
	popMatrix();
}

void drawRadarInfoText() {
	pushMatrix();
	noFill();
	translate(250, 250);
	for(int i = 1; i <= 4; i ++) {
		text(i*15+"cm", (50*i)-5, 10);
	}
	for(int i = 0; i < 360; i += 15) { // 180도에서 360도로 그려주자
		text(i+"°", -220*sin(radians(i-90))-9, -220*cos(radians(i-90))+5);
	}
	popMatrix(); 
}

4. 동작 결과

 스텝모터와 PSD 센서가 결합될 커넥터 부분과, 스텝모터가 안착될 바디부분이다. 모델링 파일은 다음 경로에 업로드 하였다.

5. 관련 블로그 글

5.1 프로젝트 관련

[series_list_posts] [series_list_related]

5.2 프로젝트 카테고리

프로젝트

6. 참조

아두이노 홈페이지 프로세싱 홈페이지

댓글