November 24, 2017

디바이스마트 미디어:

[43호]Smart Pad

Cap 2017-10-18 13-15-49-260

2017 

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

Smart Pad

글 | 단국대학교 정의동, 양지현, 최진우

심사평
칩센 유사한 작품이 있었으나, 시제품의 완성도와 시연의 구성이 매우 좋았습니다. 작품과 같은 제품을 기획할 때 운동량 측정 등 여러 부가 기능에 대한 고려를 많이 하느라 메인 기능의 부각을 저하시키는 경우가 많이 있는데, 보고서 및 시연 영상에서 핵심 기능을 강조함으로서 제품의 기획의도를 더 잘 이해하게 만들었습니다. 추가적으로 연동하는 스마트장치의 네비게이터에서 이미 인지한 정보를 다른이에게 공유한다는 내용 또한 흥미를 끌게 하는 작품입니다. 신발 깔창에 제품을 설치하는 방식인데, 좀 더 심플한 액세서리 개념으로 적용하는 방법은 없을지 고민을 해보는 것도 좋아 보입니다.

뉴티씨 당장 제품으로 만들어도 될 만큼 좋은 작품입니다. 그대로 출시해도 될 것 같지만, 이 기술을 따로 VR이나 AR에 적용하면 더 많은 가시적인 효과를 볼 가능성이 있겠습니다.

위드로봇 동영상 데모가 훌륭합니다. GPS 위치 오차가 심한 지역에서는 동영상처럼 적절한 서비스를 제공하기가 어려울 수 있습니다. 이 부분에 대한 대비책이 있다면 매우 훌륭한 서비스를 제공하는 제품이 될 것 같습니다.

작품 개요
최근 웨어러블 디바이스들이 많이 출시가 되고 있고 있습니다. 웨어러블 디바이스는 신체에 부착하여 컴퓨팅 행위를 할 수 있는 모든 것들을 뜻하고, 일부 컴퓨팅 기능 수행이 이용 가능한 애플리케이션까지 포함된 기기를 의미합니다. 인간과 사물 그리고 기기 등 연결대상과 그 범위가 빠른 속도로 확대되고, 사용자를 네트워크와 연결시키는 웨어러블 디바이스의 중요성이 부각되고 있는 시점에서 생각해 낸 웨어러블 디바이스는 SMART PAD입니다.

일반 사람들 ver.
많은 웨어러블 디바이스들이 나오고 있지만 깔창으로 나오고 있는 것들은 대부분 칼로리 측정기, 만보계 기능을 하는 것인데 이것에 착안하였고 길치, 방향치인 사람들도 올바른 방향으로 길을 찾아갈 수 있게 알려주면 좋겠다는 생각과 요새 납치, 유괴에 관해 이슈가 되는 점에 대해 보호자에게 현재위치를 알리는 것이 필요하다는 생각에 더해 Smart PAD란 것을 생각하게 되었습니다.
또한 내 하루 일과를 알 수 있고 여행일기를 쓰면 좋겠다는 생각 또한 발자취 메뉴와 여행일기 메뉴를 생각 하게 되었습니다. 이 스마트 패드로 사람들이 지도에 시간을 할애하는 것이 아닌 주변을 돌아보며 다닐 수 있고 안전한 귀가길, 여행자에게는 여행일기에 더불어 하루종일 나의 발자취 또한 매일 저장할 수 있게 하는 것이 목적입니다.

시각장애인 ver.
깔창에 진동모터를 부착하여 앞, 뒤, 좌, 우를 진동으로 길을 알려주는 웨어러블 디바이스입니다. 그뿐만 아니라 일반 사람들이 걷다가 신호등이 고장난 곳, 공사 중인 곳 등을 3초 이상 멈추게 되면 사용자의 위치를 서버에 보내 지도에 표시합니다. 많은 사용자가 표시한 지역을 활용하여 시각장애인분들이 그 지역을 피할 수 있도록 합니다.

작품 설명
주요 동작 및 특징
하드웨어
· 양쪽 발에 아두이노 미니 프로, 진동모터, 가속도/지자기/자이로 센서, 블루투스, 압력센서 등을 부착한다.
· 안드로이드에 tmap api를 사용하여 MAP을 보여준다.
· 안드로이드 어플리케이션 내에 두 가지 방법 중(촉각적-진동/청각적방법) 하나를 선택한다.
· 어플리케이션에 현재 방향과 다음 방향을 알려줄 알고리즘을 적용한다.
· 깔창에 달린 가속도, 자이로, 지자기 센서를 사용해 절대적인 현재 위치를 파악한다.
· 길을 가다가 멈출 경우 압력센서로 멈춰있는지 걷고 있는지 판단하여 블루투스 1:N 통신으로 현재 위치를 안드로이드에게 전달한다.
· 안드로이드 어플리케이션에서 위에서 말한 알고리즘으로 다음 방향을 구한다.
· 블루투스 통신으로 다음 방향을 전달한다.
· 깔창에 부착된 진동모터 또는 청각적인 알림으로 방향을 알 수 있다.
· 촉각적 방법일 경우 – 앞이면 양발의 ‘앞’에 달린 진동 모터를 뒤면 양발의 ‘뒤’에 달린 진동모터를 왼쪽 방향이면 ‘왼발’을 오른쪽 방향이면 ‘오른발’ 진동모터를 울리게 한다.
· 청각적 방법일 경우 음성으로 어느 방향으로 가야 하는지 알려준다.
· 음성인식으로 목적지를 정할 수 있다.
· 안드로이드 어플리케이션 메뉴는 아래와 같다.
1. 현재위치부터 목적지까지의 길을 보여주는 메뉴,
2. 길을 상세히 설명한 메뉴,
3. 최근 검색 메뉴,
4. 사용자가 하루 종일 다닌 발자취를 보여주고, 여행 메모(사진,동영상)를 할 수 있는 여행 다이어리 메뉴
5. 하루 종일 얼마나 걸었는지를 알려주는 메뉴
6. 보호자에게 사용자 위치를 알림 메뉴
앞서 말한 4번 메뉴를 통해 사용자의 그 날 하루 발자취와 여행일기를 쓸 수 있다.
현재 있는 곳에서 사진을 찍은 경우, 나중에 그 장소에서 무얼 했는지, 얼마나 그 장소에 있었는지를 알 수 있다.

소프트웨어
길찾기 알고리즘

44 ict smart pad (1)
1. 현재 위치와 그 다음 가야할 곳 (1m 앞)의 위치를 알아냅니다.
2. getAngle이라는 함수를 만들어서 현재위치와 다음 위치까지의 각도를 알아냅니다. bearingTo(destination)을 사용하여 알아낼 수 있습니다.
3.이를 이용하여 방향벡터를 구할 수 있으므로 어느 방향인지 판단을 할 수 있습니다.
4. 그 전에 현재 사용자가 바라보는 방향의 정보를 깔창으로부터 받아와 절대 방향을 생성합니다.
5. 절대 방향을 토대로 진행방향과 비교를 하여 다음 방향을 구합니다.
6. 다음 방향을 깔창에 진동으로 알려줍니다.

44 ict smart pad (2)

7. 현재위치를 계속해서 갱신해야만 사용자가 움직이면서 계속해서 다음 방향을 새로 구할 수 있습니다.

44 ict smart pad (3)

 

어플리케이션 모습

44 ict smart pad (4)

44 ict smart pad (5)

44 ict smart pad (6)

용도 및 사용방법

44 ict smart pad (7)

음성인식 또는 text 방법으로 목적지를 설정합니다. 어플리케이션의 방향설정 알고리즘을 통해 다음 위치를 구하면 위의 그림처럼 발에 진동이 울려 방향을 알 수 있다. 여행일기 메뉴에서는 오늘 하루의 발자취와 일기(사진, 메모)등을 쓸 수 있다.

44 ict smart pad (8)

어플에 나온 화살표 방향이 진동방향을 알려준다. 왼쪽은 사람이 느낀 방향을 손가락으로 나타낸 사진이다.

주요 사용자 및 수혜자
처음 가는 길에 대한 방향정보를 알려주어 목적지에 도달할 수 있게 하기 때문에 여행자 또는 길치, 방향치에게 편의를 줄 수 있다. 또한 청각적인 도움 또는 촉각적 도움을 주기 때문에 시각장애인에게 또한 편의를 줄 수 있다.

특장점 및 활용계획
지도가 아닌 주변에 시각을 씀으로써 안전하게 목적지에 도달할 수 있을 뿐만 아니라 보호자에게 사용자의 위치를 알릴 수 있고 여행일기 또는 하루 일지를 씀으로써 오늘 하루에 대해 돌아 볼 수 있다.

서버와 빅데이터 활용부문

44 ict smart pad (1)

 

일반 사용자가 신호등이 고장난 곳, 공사중인 곳에서 3-5초 정도 서있으면 사용자의 위치를 모바일에서 GPS 데이터를 받아서 서버로 전송합니다. 그리고나서 서버는 데이터베이스에 위치정보를 저장합니다. 위치 정보가 많이 쌓여서 지도상으로 정보를 조회하고 싶을 때는, 데이터 베이스에서 모아놓은 정보를 조회하여 서버단에서 지도를 조회할 수 있도록 합니다. 위 그림은 서버에서 지도 정보를 조회했을 때 나타나는 대략적인 화면입니다. 사용자가 정지한 지점 여러 군데가 데이터베이스에 저장되어 있는데, 서버는 데이터베이스에서 정보를 조회하여 밀집되어 기록되어 있는 위치가 있는지 확인하게 됩니다. 이때 KNN 알고리즘을 사용하여 밀집되어 있는 위치를 찾고, 밀집도에 따라서 구분을 달리하여 해당 지점을 표시하여 한눈에 개선할 지점을 조회할 수 있습니다.
또한 어플리케이션에서 구현한 알고리즘을 통해 개선되어야할 위치를 피하여 사용자(시각장애인)에게 길을 알려줍니다.

44 ict smart pad (9)

 

전체 시스템 구성

44 ict smart pad (10)

· T map API를 이용해 MAP 구현
· 방향 Algorithm을 구축하여 사용자가 가야 할 방향 구하기
· 길 찾기 중 보호자에게 SMS로 알람
· 아두이노와 Bluetooth 통신을 통한 데이터 송신 및 수신
1) 지자기센서 사용해 절대방향 수신
2) 자이로,가속도 센서를 사용해 운동량, 만보계량 수신
3) 알고리즘에서 구한 방향 송신

44 ict smart pad (11)
· GPS신호 데이터를 이용한 경로 개선

44 ict smart pad (12)

· 작품규격
사이즈 : 220 x 90 x 40(mm)

44 ict smart pad (13)

개발 환경(개발언어, Tool, 사용시스템 등)
· 개발언어 – C언어 / java : 개발 Tool에서 쓰이는 언어가 C언어이기 때문에 자연스레 C언어를 이용하여 코딩 및 소스구현을 하게 되었습니다. 이클립스에서 쓰는 언어는 java이기 때문에 java를 공부하고 소스를 구현하였습니다.
· 개발 Tool – 아두이노 IDE tool : 아두이노 프로미니를 사용하여 아두이노 전용 tool인 IDE tool을 사용하였습니다.
· 개발 Tool – 이클립스 : 안드로이드 스튜디오가 나오기전 이클립스를 사용했었는데 이클립스로 java 공부할 때 사용하여 구현하다보니 이클립스가 더 편해져서 이클립스를 사용하여 app 개발을 했습니다.
· 사용 시스템 – 아두이노 프로미니 : 깔창에 들어가려면 작은 mcu를 사용해야되고 센서와 모터가 많이 들어가기 때문에 핀이 많이 필요하여 타이니두이노 보단 아두이노 프로미니가 적합하다고 생각했습니다. 깔창에 있는 아두이노에선 연산을 많이 요구하지 않기 때문에 속도면에서 빠르지 않아도 된다고 생각하여 아두이노 프로미니를 선택하였습니다.

단계별 제작 과정

44 ict smart pad (14)

44 ict smart pad (2)

44 ict smart pad (3)

44 ict smart pad (4)

 

44 ict smart pad (5)

44 ict smart pad (6)

 

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

44 ict smart pad (15)

44 ict smart pad (16)
소스코드
이클립스

public synchronized float getAngle(){
TMapPoint point1 = point_st ;
//TMapPoint pOINT2 = Point();

Location here = new Location(“Start”);
here.setLatitude(point1.getLatitude());
here.setLongitude(point1.getLongitude());

Location destination = new Location(“End”);
//destination.setLatitude(point2.getLatitude());
///destination.setLongitude(point2.getLongitude());
destination.setLongitude(Destlatitude);
destination.setLongitude(Destlongitude);

if(point1==null || destination == null
|| (point1.getLatitude() == 0 && point1.getLognitude() == 0)
|| (Destlatitude == 0 && Destlongitude == 0)) {

return -1;
; else {
mAngle = here.bearingTo(destination); //Angles: -180 ~ 180
}
/* if(point1 == null || point2 == null
|| (point2.getLatitude() == 0 && point2.getLongitude() == 0)
|| (Destlatitude == 0 && Destlongitude == 0)) {
return -1;
; else {
mAngle = here.bearingTo(destination); //Angles: -180 ~ 180
} * //도착지점
if(mAngle < 0) {
mAngle += 360; //convert to 0~360 range
}
//mangle.setText(Float.toString(mAngle));
return mAngle;
}
private void Battery…Check(float battery)

if(gpsLocation ! = null) {
updateCooridiate(gpsLocation);
} else if (networkLocation !=null) {
updateCooridiate(networkLocation);
}else{
updateCooridiate(passiveLocation);
}
}
@override
prodected void onResume() {
// TODO Auto-generated method stub
super.onResume();
setup();
}
/////////
@override
public void onStart() {
// TODO Auto-generated method stub
super.onstart();
Location location = mLocationManager/getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
Location gpsLocation = null;
Location networkLocation = null;
Location passiveLocation = null;

// gps가 항상 정확하지 않음으로, 프로바이더 두개를 사용하는 것이 가장 정확도가 높다.

gpsLocation = requestUpdatesFromProvider(LocationManager.GPS_PROVIDER);
networkLocation = requestUpdatesFromProvider(LocationManager.NETWORK_PROVIDER);
passiveLocation = requestUpdatesFromProvider(LocationManager.PASSIVE_PROVIDER);

if (gpsLocation ! = null) {
updateCooridiate(gpsLocation);
} else if(networkLocation ! = null) {
updateCooridiate(networkLocation);
} elsle {
updateCoorkdiate(passiveLocation);

방향 알고리즘 부분

public void checkAngle1()
{
if((rpAngle >= 270) && (rpAngle <= 360))
{
compass(‘F’);
}
else if((rpAngle >= 0) && (rpAngle <= 89))
{
compass(‘R’);
}
else if((rpAngle >= 90) && (rpAngle <= 180))
{
compass(‘B’);
}
else
{
compass(‘L’);
}
}

public class httprequest extends Thread
{

double v1 = 0;
double v2 = 0;
public httprequest(double v1, double v2)
{
this.v1 = v1;
this.v2 = v2;
}
public void run() {
// TODO Auto-generated method stub

String url = “http://ec2-54-148-213-31.us-west-2.compute.amazonaws.com:5000/”;

String str1 = Double.toString(v1);
String str2 = Double.toString(v2);

uri += str1;
uri += ‘/’;
uri += str2;

RequestBundle rb = new RequestBundle();
rb.setUrl(uri);
rb.setHttpMethod(HttpMethod.GET);
Mat<String, Object > mmap = new HashMap<String, Object>();
APIRequest api = new APIRequest();

try {
api.request(rb);
} catch (malformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {

아두이노 소스
왼쪽 발

#include <SoftwareSerial.h>
SoftwareSerial blue(2,3);//rx tx
#include <Wire.h> //I2C Arduino Library
#include <String.h>
int Stream_control =8; //20
int Stream_status =7;

#define address 0x1E //0011110b, I2C 7bit address of HMC5883
float thetan=0;
float m[10]={0,};
float sum_theta=0;
float sum_thetan=0;
char dir;
char dir2=0;
int dir_sum=0;
int FSR_Pin = A0;
int FSRReading;
float Rfsr;
int Vibrate_pins[4] = {5,0,4,6}; // LED 연결 핀
boolean Vibrate_state[4];
int Battery_Pin = A1;
int Battery_Check;
float Battery;
int step = 0;

void setup(){
//Initialize Serial and I2C communications
Serial.begin(9600);
blue.begin(9600);
Wire.begin();

//Put the HMC5883 IC into the correct operating mode
Wire.beginTransmission(address); //open communication with HMC5883
Wire.write(0×02); //select mode register
Wire.write(0×00); //continuous measurement mode
Wire.endTransmission();

for(int i = 0; i < 3; i++){
pinMode(Vibrate_pins[i], OUTPUT);
Vibrate_state[i] = false;
digitalWrite(Vibrate_pins[i], Vibrate_state[i]);}
pinMode(Stream_control,OUTPUT); //40
pinMode(Stream_status,INPUT);
}

void loop(){

int x,y,z;
Wire.beginTransmission(address);
Wire.write(0×03);
Wire.endTransmission();

Wire.requestFrom(address, 6);
if(6<=Wire.available()){
x = Wire.read()<<8; //X msb
x |= Wire.read(); //X lsb
z = Wire.read()<<8; //Z msb
z |= Wire.read(); //Z lsb
y = Wire.read()<<8; //Y msb
y |= Wire.read(); //Y lsb
}

FSRReading = analogRead(FSR_Pin);
Rfsr = ((9.78 * FSRReading)/(1-(FSRReading/1023.0)));
delay(250);

Battery_Check = analogRead(Battery_Pin);
Battery = Battery_Check/4.2;
if (blue.available()){

byte data = blue.read();
digitalWrite(Stream_control,HIGH);

blue.write(65);
blue.write(84);
blue.write(79);
blue.write(50);
blue.write(13);

int a=digitalRead(Stream_status);
// Serial.println(a);
if(a==0)
{
digitalWrite(Stream_control,LOW);
// delay(800);
Serial.println(data);
blue.write(data);
blue.write(13);
//Serial.print(“ATO1:”);
// Serial.println(data);
delay(800);
}/*
if(data >= ’1′ && data <= ’5′)
{

if((data==’4′)||(data==’1′)||(data==’2′))
{
digitalWrite(Stream_control,HIGH);

blue.write(65);
blue.write(84);
blue.write(79);
blue.write(49);
blue.write(13);

int a=digitalRead(Stream_status);
// Serial.println(a);
if(a==0)
{
digitalWrite(Stream_control,LOW);
// delay(800);
Serial.println(data);
blue.write(data);
blue.write(13);
//Serial.print(“ATO1:”);
// Serial.println(data);
delay(800);
}
}*/
if(data==’5′)
{
step = 1;
}
if((Rfsr>1000)&&(step==1))
{
step=0;
theta(x,y);
}
Serial.println((char)data);
int index = data – ’0′ – 1;
Vibrate_state[index] = !Vibrate_state[index];
digitalWrite(Vibrate_pins[index], Vibrate_state[index]);
delay(2000);
Vibrate_state[index] = !Vibrate_state[index];
digitalWrite(Vibrate_pins[index], Vibrate_state[index]);
}
//}
theta(x,y);
}

void theta(int mx, int my)
{
thetan = atan2(my,mx)*(180/3.14159265) +180;
compass(thetan);
// Serial.println(dir);
}

void compass(float sum_theta)
{
if ((sum_theta>=53) && (sum_theta <=160))
{
dir = ‘E’;
}
else if ((sum_theta>=6) && (sum_theta <=52))
{
dir = ‘F’;
}
else if (((sum_theta>=322) && (sum_theta <=360))||((sum_theta>=0) && (sum_theta <=5)))
{
dir = ‘G’;
}
else if ((sum_theta>=161) && (sum_theta <=320))
{
dir = ‘H’;

}
compass_print(dir);
}
void compass_print(char dir)
{
//Serial.println(dir);
//if(Rfsr>1000)
// {
//blue.println(sum_theta);

if(dir != dir2)
{
digitalWrite(Stream_control,HIGH);
delay(800);
// BTSerial.println(“AT”);
blue.write(65);
blue.write(84);
blue.write(79);
blue.write(50);
blue.write(13);
// BTSerial.write((char)’T');
//Serial.print(“okay”);
delay(10);

int a=digitalRead(Stream_status);
// Serial.println(a);
// if(a==0)
// {
digitalWrite(Stream_control,LOW);
delay(800);
blue.write(dir);
blue.write(13);
//Serial.println(BTSerial.println());
// }
}

dir2 = dir;
// }
}

오른쪽 발

#include <SoftwareSerial.h>
SoftwareSerial blue(2,3);//rx tx
// 데이터(문자열)을 받을 버퍼의 크기.
#define BUFF_SIZE 20
// 데이터 버퍼
uint8_t buffer[BUFF_SIZE];
uint8_t index = 0;
uint8_t data;
int Vibrate_pins[4] = {8,7,0,4};
int Vibrate_state[4];// LED 연결 핀
int FSR_Pin = A0;
int FSRReading;
float Rfsr;
int cnt;
int i;

void setup(){
//Initialize Serial and I2C communications
Serial.begin(9600);
blue.begin(9600);
//digitalWrite(8, HIGH);
// digitalWrite(4, HIGH);
for(int i = 0; i < 3; i++){
pinMode(Vibrate_pins[i], OUTPUT);
Vibrate_state[i] = false;
digitalWrite(Vibrate_pins[i], Vibrate_state[i]);}
}
void loop(){

if (blue.available()){

//byte data = blue.read();

data = blue.read();
// if(data == 13)
// {
// Serial.println(data);

if(data >= ’1′ && data <= ’4′)
{
Serial.print(“buffer:”);
Serial.println(data);
int index = buffer[0] – ’0′ – 1;
Vibrate_state[index] = !Vibrate_state[index];
digitalWrite(Vibrate_pins[index], Vibrate_state[index]);
delay(2000);
Vibrate_state[index] = !Vibrate_state[index];
digitalWrite(Vibrate_pins[index], Vibrate_state[index]);
// for(i=0;i<index;i++)
// buffer[i]=0;
// index=0;

}

/*buffer[index++] = data;
// 버퍼가 꽉 찼거나 문자열이 끝났을 때,
// 루프에서 나간다.
Serial.print((char)data);
if(index == BUFF_SIZE || data == 13)
{Serial.println(data);
Serial.print(“buffer:”);
Serial.println(buffer[0]);
if(buffer[0] >= ’1′ && buffer[0] <= ’4′)
//if(data >= ’1′ && data <= ’4′)
{

int index = buffer[0] – ’0′ – 1;
Vibrate_state[index] = !Vibrate_state[index];
digitalWrite(Vibrate_pins[index], Vibrate_state[index]);
delay(2000);
Vibrate_state[index] = !Vibrate_state[index];
digitalWrite(Vibrate_pins[index], Vibrate_state[index]);
for(i=0;i<index;i++)
buffer[i]=0;
index=0;

}

// 9600bps 기준으로 delay 를 1ms 을 줘야 한다.
delay(1);
//}
FSRReading = analogRead(FSR_Pin);
Rfsr = ((9.78 * FSRReading)/(1-(FSRReading/1023.0)));
delay(250);
if(Rfsr>1000)
{
cnt++;
if(cnt>1000){
blue.println(“5″);
cnt=0;
}
}
}
}

참고문헌
· 카페 [임베디드 공작소] · 200개의 단계별 예제로 배우는 안드로이드드 4.0 [한동호 저자] · 그 외 네이버 지식인, 네이버 카페 “당근이의 AVR 갖구 놀기”

 

 

 

 

Leave A Comment

*