프로젝트

나도 메이커! 메이커스 여러분들의 작품/프로젝트를 공유하는 공간입니다.

제스처 인식 제어 로봇팔 만들기

2019-10-31 11:43:23

제스처란?


 

제스처는 비언어적 의사소통 수단 중 하나로 손이나 얼굴 표정, 몸짓을 이용해 상대방에게 의미를 전달하는 것을 말합니다. 

제스처는 언어가 달라 말이 통하지 않는 상대와 의사소통을 하거나 대화시 대화의 내용을 강조하거나 보안할 때 자주 사용합니다.

 

제스처 인식은 컴퓨터가 인간의 제스처를 인식하기 위해 수학적 알고리즘 등 다양한 방법을 통해 제스처를 해석하는 기술입니다.

 

 




 

제스처 센서 사용방법


 

이번에 사용할 센서는 Sparkfun APDS-9960 모델로 RGB 및 제스처 센서 모듈입니다.

(https://learn.sparkfun.com/tutorials/apds-9960-rgb-and-gesture-sensor-hookup-guide#resources-and-going-further)

 

 

APDS-9960는 내부에 UV 및 IR, 서로 다른방향으로 4개의 다이오드가 장착되어 있습니다. 

 

이를 이용해서 아래의 기능을 사용할 수 있습니다.

1. 제스처 인식

2. 주변환경의 밝기 - 근접 센서

3. 주변환경의 색을 측정 - 칼라 센서

 

이번 가이드에서는 위 기능 중 1번 제스처 인식 기능을 사용하겠습니다.

 

제스처 인식은 IR센서에서 반사된 에너지를 4방향의 다이오드로 검출하여 동작을 측정합니다.

 

제스처 센서는 아래와 같은 제스처를 인식할 수 있습니다.

- LEFT : 센서의 오른쪽에서 왼쪽으로 지나가는 경우

- RIGHT : 센서의 왼쪽에서 오른쪽으로 이동한 경우

- UP : 센서의 아래쪽에서 위쪽으로 이동한 경우

- DOWN : 센서의 위쪽에서 아래쪽으로 이동한 경우

- FAR : 센서 가까이서 1초 이상 있다가 센서 위로 멀어지는 경우

- NEAR : 센서 위 멀리에서 가까이 온 후 1초 이상 있다가 감지 범위 밖에서 사라진 경우 

 

 

 

 

 

부품 목록

NO 부품명 수량 상세설명
1 오렌지보드 + 확장보드 1  
2 제스처 센서 1 APDS-9960
3 점퍼 케이블 5  

 

 

부품명 오렌지보드 + 확장보드 제스처 센서 점퍼 케이블
파트

 

 

 

 

 

하드웨어 making


 

1. 제스처 센서의  SCL핀을 오렌지보드 확장실드 A5에 연결합니다.

2. 제스처 센서의  SDA핀을 오렌지보드 확장실드 A4에 연결합니다.

3. 제스처 센서의  INT핀을 오렌지보드 확장실드에 연결합니다.

4. 제스처 센서의  GND핀을 오렌지보드 확장실드 GND에 연결합니다.

5. 제스처 센서의  VCC핀을 오렌지보드 확장실드 3.3V에 연결합니다.

 

 

 

* 센서의 전원은 3.3V 를 넣어야 합니다!

 

소프트웨어 coding

 

 

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
#include <Wire.h>
#include <SparkFun_APDS9960.h>
 
// Pins
#define APDS9960_INT    2 // Needs to be an interrupt pin
 
// Constants
 
// Global Variables
SparkFun_APDS9960 apds = SparkFun_APDS9960();
int isr_flag = 0;
 
void setup() {
 
  // Set interrupt pin as input
  pinMode(APDS9960_INT, INPUT);
 
  // Initialize Serial port
  Serial.begin(9600);
  Serial.println();
  Serial.println(F("--------------------------------"));
  Serial.println(F("SparkFun APDS-9960 - GestureTest"));
  Serial.println(F("--------------------------------"));
  
  // Initialize interrupt service routine
  attachInterrupt(0, interruptRoutine, FALLING);
 
  // Initialize APDS-9960 (configure I2C and initial values)
  if ( apds.init() ) {
    Serial.println(F("APDS-9960 initialization complete"));
  } else {
    Serial.println(F("Something went wrong during APDS-9960 init!"));
  }
  
  // Start running the APDS-9960 gesture sensor engine
  if ( apds.enableGestureSensor(true) ) {
    Serial.println(F("Gesture sensor is now running"));
  } else {
    Serial.println(F("Something went wrong during gesture sensor init!"));
  }
}
 
void loop() {
  if( isr_flag == 1 ) {
    detachInterrupt(0);
    handleGesture();
    isr_flag = 0;
    attachInterrupt(0, interruptRoutine, FALLING);
  }
}
 
void interruptRoutine() {
  isr_flag = 1;
}
 
void handleGesture() {
    if ( apds.isGestureAvailable() ) {
    switch ( apds.readGesture() ) {
      case DIR_UP:
        Serial.println("UP");
        break;
      case DIR_DOWN:
        Serial.println("DOWN");
        break;
      case DIR_LEFT:
        Serial.println("LEFT");
        break;
      case DIR_RIGHT:
        Serial.println("RIGHT");
        break;
      case DIR_NEAR:
        Serial.println("NEAR");
        break;
      case DIR_FAR:
        Serial.println("FAR");
        break;
      default:
        Serial.println("NONE");
    }
  }
}
 

 

 

TIP 위 예제코드를 사용하기 위해서는 SparkFun_APDS9960 라이브러리가 필요합니다. 이 라이브러리는 외부 라이브러리이기 때문에 라이브러리 추가 작업을 진행해야 합니다. 아래 사진을 참고하여 라이브러리를 추가한후 코드를 업로드하세요.

 

 

 

 

 

 

 

라이브러리를 설치 후 위 소스 코드를 업로드하신후 시리얼 모니터를 열어보시면 센서에서 측정된 제스처가 아래 사진과 같이 시리얼 모니터에 표현해줍니다.

 

 

 

소프트웨어 설명

이 센서의 제스처 인식은 인터럽트 기능을 이용합니다.

(인터럽트 참고 : https://kocoafab.cc/tutorial/view/634)

 

1
 attachInterrupt(0, interruptRoutine, FALLING);            
 

 

제스처 센서에 움직임이 인식되면 인터럽트 핀(D2번핀)에 FALLING 신호를 보내고, 오렌지보드에선 이 신호를 받으면 interruptRoutine() 함수가 실행됩니다. 

 

 

1
2
3
4
5
6
  if( isr_flag == 1 ) {
    detachInterrupt(0);
    handleGesture();
    isr_flag = 0;
    attachInterrupt(0, interruptRoutine, FALLING);                   
  }
 

 

 

이 함수가 실행되면 isr_flag 를 0에서 1로 바꾸고, 그럼 loop문 안에 있는 이 조건문이 동작하게 됩니다.

 

이 조건문이 동작하면 인식 결과가 나올동안 인터럽트를 잠깐 꺼주고 동작을 인식하는 함수를(handleGesture()) 실행합니다.

 

handleGesture() 함수의 동작이 끝나면 다시 isr_flag를 0으로 바꿔주면서 인터럽트를 다시 켜줍니다.

 

 

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
void handleGesture() {
    if ( apds.isGestureAvailable() ) {                   
    switch ( apds.readGesture() ) {
      case DIR_UP:
        Serial.println("UP");
        break;
      case DIR_DOWN:
        Serial.println("DOWN");
        break;
      case DIR_LEFT:
        Serial.println("LEFT");
        break;
      case DIR_RIGHT:
        Serial.println("RIGHT");
        break;
      case DIR_NEAR:
        Serial.println("NEAR");
        break;
      case DIR_FAR:
        Serial.println("FAR");
        break;
      default:
        Serial.println("NONE");
    }
  }
}
 

 

handleGesture() 함수는 실제로 제스처 센서에 인식된 움직임을 구분하는 함수입니다.

 

apds.readGesture() 함수를 실행하면 제스처 센서로부터 인식된 움직임을 구분한 결과가 나옵니다.

 

이 결과를 이용하여 Switch문을 통해 시리얼 모니터에 구분한 결과를 출력합니다.

 

 

제스처 인식 제어 로봇팔 만들기


 

제스처 센서에 인식된 결과에 따라 움직이는 로봇팔을 만들어 보겠습니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

제스쳐 센서 인식 결과에 따른 서보모터 움직임

(* 모터 번호는 https://kocoafab.cc/make/view/786# 을 참고하세요)

 

제스쳐 센서 인식 서보모터 번호 및 움직임
RIGHT 모터1(밑판) -> 각도 DOWN
LEFT 모터1(밑판) -> 각도 UP
UP 모터2(옆면1) -> 각도 UP
DOWN 모터2(옆면1) -> 각도 DOWN
NEAR

모터3(옆면2) -> 각도 DOWN

FAR 모터3(옆면2) -> 각도UP

 

 

소프트웨어 코딩

 

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
#include <Wire.h>
#include <SparkFun_APDS9960.h>
#include <Servo.h>
 
#define APDS9960_INT    2 
 
Servo bottom;
Servo arm1;
Servo arm2;
Servo grip;
 
int target_B = 90;
int target_A1 = 90;
int target_A2 = 90;
 
SparkFun_APDS9960 apds = SparkFun_APDS9960();
int isr_flag = 0;
 
 
void setup() {
  bottom.attach(8);
  arm1.attach(9);
  arm2.attach(10);
  grip.attach(11);
 
  bottom.write(target_B);
  arm1.write(target_A1);
  arm2.write(target_A2);
  grip.write(100);
  // Set interrupt pin as input
 
  pinMode(APDS9960_INT, INPUT);
 
  Serial.begin(9600);
  Serial.println();
  Serial.println(F("--------------------------------"));
  Serial.println(F("SparkFun APDS-9960 - GestureTest"));
  Serial.println(F("--------------------------------"));
 
  attachInterrupt(0, interruptRoutine, FALLING);
 
  if ( apds.init() ) {
    Serial.println(F("APDS-9960 initialization complete"));
  } else {
    Serial.println(F("Something went wrong during APDS-9960 init!"));
  }
 
  if ( apds.enableGestureSensor(true) ) {
    Serial.println(F("Gesture sensor is now running"));
  } else {
    Serial.println(F("Something went wrong during gesture sensor init!"));
  }
}
 
void loop() {
  if ( isr_flag == 1 ) {
    detachInterrupt(0);
    handleGesture();
    isr_flag = 0;
    attachInterrupt(0, interruptRoutine, FALLING);
  }
 
  bottom.write(target_B);
  arm1.write(target_A1);
  arm2.write(target_A2);
  grip.write(100);
}
 
void interruptRoutine() {
  isr_flag = 1;
}
 
void handleGesture() {
  if ( apds.isGestureAvailable() ) {
    switch ( apds.readGesture() ) {
      case DIR_UP:
        Serial.println("UP");
        target_A1 += 20;
        break;
      case DIR_DOWN:
        Serial.println("DOWN");
        target_A1 -= 20;
        break;
      case DIR_LEFT:
        Serial.println("LEFT");
        target_B += 40;
        break;
      case DIR_RIGHT:
        Serial.println("RIGHT");
        target_B -= 40;
        break;
      case DIR_NEAR:
        Serial.println("NEAR");
        target_A2 -= 20;
        break;
      case DIR_FAR:
        Serial.println("FAR");
        target_A2 += 20;
        break;
      default:
        Serial.println("NONE");
    }
  }
}
 

 

 

 

소프트웨어 설명

 

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
void handleGesture() {                                                                      
  if ( apds.isGestureAvailable() ) {
    switch ( apds.readGesture() ) {
      case DIR_UP:
        Serial.println("UP");
        target_A1 += 20;
        break;
      case DIR_DOWN:
        Serial.println("DOWN");
        target_A1 -= 20;
        break;
      case DIR_LEFT:
        Serial.println("LEFT");
        target_B += 40;
        break;
      case DIR_RIGHT:
        Serial.println("RIGHT");
        target_B -= 40;
        break;
      case DIR_NEAR:
        Serial.println("NEAR");
        target_A2 -= 20;
        break;
      case DIR_FAR:
        Serial.println("FAR");
        target_A2 += 20;
        break;
      default:
        Serial.println("NONE");
    }
  }
}
 

 

handleGesture() 함수는 기본 코드에서 사용한 제스처 센서에 움직임이 인식되면 그 움직임이 무엇인지 확인하는 코드에 추가로 해당 움직임에 맞게 서보모터의 각도를 변경하는 코드를 추가하였습니다.

 

미리 전역변수 target_A1, target_A2, target_B를 생성하고(기본값 90도) 제스처 센서 인식 결과에 따라 이 변수 값을 +/- 해줍니다.

 

 

1
2
3
4
  bottom.write(target_B);        
  arm1.write(target_A1);
  arm2.write(target_A2);
  grip.write(100);
 

 

위 handleGesture() 함수에서 각도를 정하면 loop문 안에 있는 위 명령어를 통해 로봇팔의 각도를 정해줍니다.

 

 

 

 

 

 

 

 

 

kocoafab