넌 나만 바라봐, 선풍기

작성자
홍권
작성일
2021-04-27 04:12
조회
1849
안녕하세요, '넌 나만 바라봐, 선풍기' 세미나 진행을 맡게 된 싸이어 부회장 홍권입니다.  시작하기에 앞서 진행 과정을 간략히 말씀드리면,
  1. 라즈베리파이 설정
  2. Pi Camera 연결 및 사용
  3. 라즈베리파이를 통한 모터 제어 및 pwm제어와 모터드라이버에 대한 간략한 설명
  4. pip를 통한 opencv 설치 및 대략적인 사용 방법 숙지
  5. opencv를 통한 얼굴 추적 및 인식된 얼굴 좌표에 따른 서보모터 제어
  6. 선풍기 틀의 3d모델링 및 3d프린팅(선택)
와 같은 순서로 진행이 될 것입니다. 사실 가이드를 보고 그대로 따라하기만 한다면 어려울 것은 없겠지만, 이해할 것은 꽤나 많을 것 같습니다. 최대한 삽질을 해보면서 리눅스, 터미널이랑 친해지시길 바랍니다. 윈도우처럼 뭐 잘못 설치했으면 제어판에서 삭제하면 끝! 이 아니라 이것저것 해결하려고 설치도 해보고 설정도 건드렸다가 전부 초기화해야할 상황에 맞닥뜨릴 수도 있습니다. 그럴 때마다 인내심을 갖고 초기화하면서 진행하시길 바랍니다.

그리고 이 세미나에서는, opencv에 대한 이해 및 상세한 사용법은 다루지 않으며, 단순히 얼굴을 인식하는 예제에 얼굴 좌표에 따른 모터 제어를 추가하는 정도로 마무리가 될 예정입니다.

마지막 3d프린팅은, 제가 미숙한 부분도 있고 이 세미나에서 크게 비중을 둔 부분이 아니라 선택사항으로 남겨뒀습니다. 그 전까지 배울 사항이 더 많기에 3d프린팅에 관해서는 동아리 톡방에서 질문하며 완성해주시길 바랍니다. 마지막에 저의 대략적인 모델링을 공유해드리겠습니다.

p.s. 라즈베리파이는 값이 꽤 나가기에, 소중히 다뤄주시길 바랍니다!

1. 라즈베리파이 설정

1) https://www.raspberrypi.org/software/operating-systems/ 에서 Raspberry Pi OS를 받아줍니다.


위와 같은 3가지 이미지 중, GUI가 포함된 두번째 이미지를 권장합니다.

2) 압축을 풀어 나온 이미지 파일을 sd카드에 씌워줘야 합니다. sd카드를 리더기 등을 이용하여 컴퓨터에 연결한 후에, https://sourceforge.net/projects/win32diskimager/ 이 소프트웨어를 이용하여 이미지를 sd카드에 씌워줍니다. Read는 지정된 드라이브를 img파일로 읽어오는 옵션으로 백업이 필요할 경우에 사용하는 것이고, 우리에게 필요한 것은 img파일을 드라이브에 씌워주는 Write 옵션입니다.

3) 이제 sd카드를 라즈베리파이에 꽂고, 모니터와 키보드를 연결 한 후에 전원을 인가하시면 됩니다. 전원으로는 5V 3A를 권장하므로, 키트에 동봉된 충전기를 이용하시는 게 좋습니다. 물론 15W 이상의 출력을 내는 고속충전기는 사용해도 무방하지만, 많이 보이는 5V 2A 충전기는 사용하시면 전원 부족으로 문제가 생길 수 있습니다.

4) 혹여 문제가 생겨서 라즈베리파이를 초기화 해야할 상황이면, https://www.sdcard.org/downloads/formatter/sd-memory-card-formatter-for-windows-download/ 이 소프트웨어를 사용하여 sd카드를 포맷하는 것을 권장합니다.

라즈베리파이를 모니터와 키보드를 연결해서 쓰는 게 가장 쉽지만, 키보드가 따로 없어서 힘든 경우도 있고, 있다해도 매번 사용 시에 연결하는 게 꽤나 번거롭습니다.

라즈베리파이는 ssh와 vnc를 통한 원격 접속이 가능합니다. pi OS를 설치하고, 부팅 한 후에 와이파이를 잡고 ssh랑 vnc를 활성화 해주면 됩니다. 하지만 문제는, 이 첫 세팅 과정입니다. 결국 처음에는 디스플레이와 키보드가 있어야 하니 한번만 번거로움을 감수하면 편해지는 건 맞습니다. 그런데, 이 첫 세팅과정도 무선으로 진행할 수 있는 방법이 있습니다.

1-1. Headless 세팅법

https://www.tomshardware.com/reviews/raspberry-pi-headless-setup-how-to,6028.html

위의 링크를 참고하셔서 진행하시면 됩니다. 다만 이 중 와이파이 연결이 조금 까다롭습니다. 막히면 이 글을 읽어주시고, 또는 위의 링크에서 랜선을 통한 연결도 소개하고 있으니 이렇게 진행하셔도 괜찮습니다. 특히 노트북으로만 진행하신다면 여기저기 들고다니면서 라즈베리파이를 연결하실 때 이 방법이 가장 괜찮을 것입니다. 다만 노트북에 랜포트가 있거나 c타입 허브가 필요할 것입니다. vnc관련하여 마지막에 설정해줘야하는 부분이 있으니 이 부분 또한 확인해주시길 바랍니다.

1) 와이파이 국가코드 관련 문제



1

2

3

4

5

6

7

8

9


country=US

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev

update_config=1


network={

scan_ssid=1

ssid="your_wifi_ssid"

psk="your_wifi_password"

}


cs

링크에서도 설명되어있는 wpa_supplicant.conf 파일 내용입니다. ssid와 psk는 위의 설명대로 연결하려는 와이파이 이름과 비밀번호를 입력해주시면 됩니다. *참고로 "는 지우시면 안됩니다.*

여기는 한국이므로 국가코드를 KR로 설정해서 한번에 연결이 된다면 그대로 쓰면 됩니다. 라즈베리파이 전원인가 후 와이파이 연결까지는 약 30초는 기다려주셔야 합니다. 참고로 동방 공유기는 KR 국가코드로 한번에 연결이 가능했습니다.

연결이 되었는지 확실히 확인하는 방법은, 공유기 설정페이지에 들어가서 'raspberry pi'라는 기기가 연결되었는지 확인하시면 됩니다. 이 부분은 공유기마다 달라서 제가 쓰고 있는 iptime 공유기 기준으로 말씀드리면,



위의 사진을 참고하여 커넥션 정보에 들어가시면 여러 기기가 연결되어 있는 것을 확인하실 수 있습니다. 이 중 192.168.0.1은 공유기 자신의 ip 주소이며, 224.0.0.1은 https://ko.ipshu.com/ipv4/224.0.0.1 에 자세한 설명이 있습니다. 아무튼 우리가 신경쓸 ip가 아니라는 것입니다! 그 외에 ip주소에는 (기기명)이 붙어있는 것이 확인될 텐데, 이 중 기기명이 raspberrypi인 것을 확인할 수 있으면 정상적으로 연결이 된 것입니다.

하지만 만약 연결이 되지 않는다면 조금 귀찮아집니다. 검색해본 바에 따르면 저를 포함한 iptime 공유기 대부분이 갖고 있는 문제 같은데, 일단 국가코드를 KR로 하고 접속을 할 수가 없습니다. 따라서 공유기 설정에 들어가셔서



위와 같이 지역을 '미국'으로 변경 후 위 conf 파일의 국가코드에도 US를 입력해줘야 연결이 가능합니다. 저는 공유기에 연결된 다른 기기들에 혹시 모를 문제를 일으키지 않기 위하여 쓰지 않는 2.4GHz의 지역만 변경하여 라즈베리파이를 2.4GHz로 연결해주었습니다. 큰 문제가 예상되진 않으나 참고하여 되도록이면 저처럼 쓰시는 것을 권장드립니다.

그리고 텍스트 파일의 이름을 "wpa_supplicant.conf"로 변경해주시면, ssh파일처럼 라즈베리파이가 부팅 시에 이를 인식해서 시스템 파티션에 있던 wpa_supplicant.conf를 우리가 boot 파티션에 넣어둔 conf파일로 변경한 후에 boot 파티션에서 삭제합니다. 따라서 연결이 안 될 경우에 다시 시도해볼 것을 대비해, 작성한 conf파일을 컴퓨터에 저장해둔 후에, 안되면 국가코드만 변경해서 다시 boot파티션에 복붙해보고 시도하는 식으로 진행하는 것을 권장합니다.

2) VNC 연결 시"Cannot currently show the desktop"이 뜨는 경우

putty를 통해 ssh연결을 하고, sudo raspi-config를 입력합니다. 이후 Advanced Options - Resolution에 들어가신 후, 원하시는 해상도를 설정해줍니다. (fhd 27인치 모니터 기준으로는, 1024x768이 가장 괜찮았습니다.)

재부팅 이후에 vnc연결을 다시 시도해보시면 연결이 될 것입니다.

이후의 과정을 진행하면서 그냥 복붙 하시기보단 터미널에 명령어를 입력해보면서 터미널과 친해지도록 노력해보시길 바랍니다!'

2. Pi Camera 연결 및 사용

http://www.3demp.com/community/boardDetails.php?cbID=233

위 글을 참고하여 진행해주시기 바랍니다. 파이썬의 import 관련한 간략한 설명도 있으니, 모르는 부분은 질문 혹은 구글링을 통해 자세히 알아보시는 것을 추천드립니다.

3. 라즈베리파이로 모터 제어하기

우리는 라즈베리파이로 서보모터 1개, 그리고 DC모터 1개를 제어할 것입니다. 둘 다 라즈베리파이로부터 직접 전원을 끌어서 쓸 수도 있습니다. 하지만 모터의 상태에 따라 라즈베리파이에 문제를 일으킬 수도 있으며, 우리가 사용할 모터의 전력 소모가 적지 않기 때문에 적절치 않습니다.

따라서 두 모터를 AA건전지 홀더에 연결하여 전력을 공급할 것이며, 각각의 pwm 제어 신호는 라즈베리파이에서 받게 할 것입니다. 여기서 DC모터를 pwm신호로 제어한다고? 하는 의문이 드실 수도 있는데,  l298n 모터드라이버에 연결하여 사용할 것이므로 아래에서 더 자세히 언급하겠습니다.

1) 서보모터 하나만 제어하기

https://digital-play.tistory.com/13

위 글의 사진을 참고하여 핀을 연결 해줍시다. 위 예제에서는 26번 핀에 연결을 했음을 기억하고,

https://syki66.github.io/blog/2020/09/27/servo-motor.html

위의 서보모터 하나만을 제어하는 예제를 따라해보시길 바랍니다.



위의 핀배열을 참고하셔서 원하는 핀에 연결을 하고, 코드에서도 해당 핀을 지정해주면 잘 작동합니다. 자세히 보시면 핀마다 특별한 역할을 하는 것이 있기 때문에, 이를 잘 확인하시고 사용하시길 바랍니다. 그림을 누르면 공식문서로 이동합니다.

또한 위 그림에서 작게 적혀 있는 번호(보드 번호), 그리고 GPIO 번호(BCM 핀번호)가 다른 걸 볼 수 있는데, 기본적으로는 BCM 핀번호를 사용하는 것으로 설정이 되어있습니다. 하지만

https://m.blog.naver.com/pk3152/221368513358

모드 변경을 통해 보드 번호로도 제어할 수 있습니다.

2) dc모터와 모터 드라이버까지 연결하여 제어하기

https://devicemart.blogspot.com/2019/07/l298n.html

모터 드라이버에 대한 설명을 읽고, 각자 +/- 연결 방향에 주의하며 빵판을 통해 건전지 홀더와 연결해보시길 바랍니다. 연결을 다 한 후에는 서보모터의 pwm핀, 그리고 모터드라이버의 ENA, input1, input2 핀이 라즈베리파이와 연결된 준비를 하고 있어야합니다. 그리고 라즈베리파이의 gnd핀과 건전지 홀더의 -가 꼭 연결 되어있어야 합니다!

다 되셨다면, 서보모터의 pwm핀을 (보드번호기준) 31번, in2는 33번, in1은 35번, ena는 37번에 연결해주고 아래의 코드를 돌려봅시다.


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


import RPi.GPIO as GPIO

from time import sleep


#PWM PIN

ENA = 26  #37 pin

servo = 6 #31 pin


#GPIO PIN

IN1 = 19  #35 pin

IN2 = 13  #33 pin


#dc speed and direction

def setDC(pwm,speed):

    if speed==0:

        GPIO.output(IN1,0)

        GPIO.output(IN2,0)

    else:

        GPIO.output(IN1,1)

        GPIO.output(IN2,0)

        pwm.ChangeDutyCycle(speed)



#0-180 degree to dutycycle 2-12

def setservo(pwm,pos):

    pwm.ChangeDutyCycle(pos/18+2)


GPIO.setmode(GPIO.BCM)


GPIO.setup(ENA, GPIO.OUT)

GPIO.setup(IN1, GPIO.OUT)

GPIO.setup(IN2, GPIO.OUT)

GPIO.setup(servo, GPIO.OUT)


#pwm init

pwmdc = GPIO.PWM(ENA,100)

pwmservo=GPIO.PWM(servo,50)


#start pwm

pwmdc.start(0)

pwmservo.start(0)


setservo(pwmservo,0)

sleep(3)


i=0

while (i<180):

    i+=20

    setservo(pwmservo,i)

    sleep(1)



pwmdc.stop()

pwmservo.stop()

GPIO.cleanup()



cs

위의 코드에선 서보모터를 20도부터 160도까지 20도 간격으로 움직여보는 명령만 했는데, 정의해둔 setDC함수를 이용하여 모터를 제어해보시길 바랍니다. 또한 해당 함수의 in1과 in2를 바꿔서 방향을 조절할 수도 있습니다.

4-1. Opencv 설치

https://www.pyimagesearch.com/2019/09/16/install-opencv-4-on-raspberry-pi-4-and-raspbian-buster/

step 0까지는 라즈베리파이 초기 설정과정으로 건너뛰셔도 됩니다.

이후 step 1의 expand filesystem 과정부터 차근차근 따라해주시길 바랍니다.

step 3에서는'cv'라는 이름의 virtual environment를 만들게 될 것입니다. 가상환경에 들어가게 되면, 커맨드라인 가장 왼쪽에 (cv)가 적힌 것을 확인할 수 있습니다. deactivate를 입력해주시면 가상환경에서 나올 수 있으며, 이후 다시 가상환경에서 작업하고 싶을땐 어디서든 'workon cv'라고 입력해주시면 됩니다. virtualenv의 자세한 사용법은 https://hidekuma.github.io/python/virtualenv/virtualenvwrapper/python-virtualenv-wrapper/를 확인해주시길 바랍니다. 또한 step3 마지막에 picamera사용을 위한 과정도 진행해주시기 바랍니다.

https://learnopencv.com/object-tracking-using-opencv-cpp-python/

위 링크에는 opencv의 여러 알고리즘에 대한 설명이 있습니다. 상황에 따라 다양한 알고리즘을 채택하여 우리가 원하는 결과물을 얻어낼 수 있다는 것을 알고가면 좋겠습니다.

4-2. Opencv를 통한 얼굴 추적 및 모터 제어

얼굴을 따라다니는 알고리즘은 다음과 같습니다.



위 알고리즘 중에서 모터 제어는 이미 위에서 구현했으니, 얼굴 위치를 분석하는 알고리즘을 살펴보면,

https://iot4beginners.com/object-tracking-camera-using-raspberry-pi-and-opencv/

서보모터 두개와 파이카메라를 이용해 얼굴을 따라다니는 카메라를 제작한 예시입니다. 위 링크의 루프문과 우리가 위에서 작성했던 모터제어 코드를 결합하여 얼굴을 따라다니는 코드를 작성해보면,


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


from picamera.array import PiRGBArray

from picamera import PiCamera

import cv2

import RPi.GPIO as GPIO

from time import sleep


#PWM PIN

ENA = 26  #37 pin

servo = 6 #31 pin


#GPIO PIN

IN1 = 19  #35 pin

IN2 = 13  #33 pin


#dc speed and direction

def setDC(pwm,speed):

    if speed==0:

        GPIO.output(IN1,0)

        GPIO.output(IN2,0)

    else:

        GPIO.output(IN1,1)

        GPIO.output(IN2,0)

        pwm.ChangeDutyCycle(speed)



#0-180 degree to dutycycle 2-12

def setservo(pwm,degree):

    pwm.ChangeDutyCycle(degree/18+2)


GPIO.setmode(GPIO.BCM)


GPIO.setup(ENA, GPIO.OUT)

GPIO.setup(IN1, GPIO.OUT)

GPIO.setup(IN2, GPIO.OUT)

GPIO.setup(servo, GPIO.OUT)


#pwm init

pwmdc = GPIO.PWM(ENA,100)

pwmservo=GPIO.PWM(servo,50)


#start pwm

pwmdc.start(0)

pwmservo.start(0)


#allign servo to center

pos=90

setservo(pwmservo,pos)

sleep(3)



#set pi camaera in an optimised way

width,height=320,240

camera = PiCamera()

camera.resolution = (width,height)

camera.framerate = 30

rawCapture = PiRGBArray(camera, size=(width,height))

sleep(1)


#load Haar cascade file to detect face

face_cascade= cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

#start reading frames from pi camera

for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):

    image = frame.array

    frame=cv2.flip(image,1)

    gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)


    #detect face coordinates x,y,w,h

    faces=face_cascade.detectMultiScale(gray,1.3,5)

    c=0

    for(x,y,w,h) in faces:

        c+=1

        if(c>1):  # we just take care of the first face detected.

            break


        #servo movement

        target_pos=((x+w/2)/320*90+45)//2*2+1

        #limit servo to 45~135 degree, 0~320 is frame horizontal size

        #and rounded quite a lot


        if(pos<target_pos-4):

            pos+=2

            setservo(pwmservo,pos)

            sleep(0.5)

        elif(pos>target_pos+4):

            pos-=2

            setservo(pwmservo,pos)

            sleep(0.5)


        if(c==1):

            frame=cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),6)


    cv2.imshow('frame',frame) #display image


    key = cv2.waitKey(1& 0xFF

    rawCapture.truncate(0)

    if key == ord("q"): #quit when q is entered

        break


cv2.destroyAllWindows()


pwmdc.stop()

pwmservo.stop()

GPIO.cleanup()



cs

위와 같은 코드를 최종적으로 작성할 수 있습니다. DC모터를 제어하는 코드를 적절한 위치에 추가하여 DC모터까지 돌려보시길 바랍니다.

https://dodo-it.tistory.com/30

위 링크는 Haarcascade를 사용한 얼굴 추적의 다른 예시입니다.

또한, 위의 코드를 작동해보면 서보모터가 심히 떨리는 것을 확인할 수 있을겁니다. 이는 얼굴 인식 정확도에 따른 문제보다도, 라즈베리파이의 서보모터 제어의 불안정성에 따른 것인데,

https://luigibox.tistory.com/76

pigpio를 사용하여 해결할 수 있습니다.

 

5. 마무리

저의 3d모델링은 추후에 첨부하도록 하겠습니다. 여유가 되신다면 이번 기회에 모델링과 3d프린터 사용 방법까지 익혀가면 좋겠습니다. 또한 우리가 빵판으로만 핀들을 연결했는데, 이는 부피도 크고 핀이 빠질 우려도 있는 불안정한 상태입니다. 테스트 용도로는 최고이지만, 완성품에 들어갈 수는 없습니다. 회로가 완성이 됐다면 기판에 납땜을 하여 부피도 줄이고, 안정적으로 작동하게 할 수 있습니다. 동방에 납땜을 위한 인두기와 납이 구비되어있으니, 동아리 톡방에 질문해가면서 시간이 되는만큼 많은 것을 얻어가시길 바랍니다. 2학기에 전시회를 할 때 많은 도움이 될 것입니다.

질문은 언제나 환영입니다. 다른 분들도 볼 수 있도록, 이 글에 댓글로 남겨주시길 바랍니다!
전체 0

  • 🚨현재 코로나19 방역지침 준수를 위해 집합금지와 관련하여🚨
    기존 부원들께서는 구글 폼으로 예약 후 사용 부탁드리겠습니다.
  • 동아리방 사용시 항상 마스크 착용, 취식 절대 금지, 자주 환기 부탁드립니다!
  • 동아리방 방문 예약은 전날 24시전까지 부탁드립니다.
시간
12:00     
13:00     
14:00     
15:00상주상주상주 상주
16:00 
17:00 
18:00저녁시간~
19:00상주상주상주 상주
20:00 
21:00 
22:00