커뮤니티
- 제 목 [기초] 아두이노 - 빛을 비추면 따라가는 자동차 만들기
- 작성자 주피터 조회 : 9,041 좋아요 : 0 댓글 : 0
- 작성일 2019-10-23 오후 4:02:48
- 첨부파일 sketch_LightCar.ino
-
두개의 DC모터를 이용해서 방향을 바꿀 수 있도록 하겠습니다. 광센서와 랜턴을 사용해서 자동차를 쉽게 조종할 수 있을 것 같습니다.
랜턴이 비추는 쪽으로 방향을 바꾸어 따라가는 자동차.
ⓞ 직류모터와 바퀴
바퀴를 직류 모터의 회전축에 직접 연결하면 너무 빠르게 움직이므로 전압을 낮춰서 속도를 낮춰져야 됩니다. 전압을 낮추면 당연히 속도도 낮춰지기는 하지만, 모터에 공급하는 전압을 낮추게 되면 모터의 회전력도 동시에 약해지게 됩니다. 회전력이 너무 낮아지면 돌지 못하게 됩니다.
그래서 모터와 바퀴 사이에 기어박스를 사용하는 것이 좋습니다. 기어 박스를 사용하여 속도를 낮추게 되면 회전력은 더욱 강해지고 속도는 낮출 수 있습니다.
ⓞ 직류 기어 모터
직류 기어 모터는 직류 모터에 기어박스를 결합하여 하나로 만든 것입니다. 4.8볼트에서 6볼트의 전압을 공급하여 동작시키고 6볼트를 공급할 때 1분에 300회전을 합니다. 전류는 부하가 없을 때 120mA 내외를 소모합니다. 부하가 가해졌을 경우에는 제조 업체의 사양이 나와 있지는 않지만 약 2 암페어 내외를 소모하는 것 같습니다.
ⓞ 회로도
아두이노의 5번 단자와 6번 단자에 각각 330오옴의 저항을 연결하고, 거기에 트랜지스터의 베이스를 연결합니다. 각 트랜지스터의 에미터는 접지(0V)에 연결하고 직류모터를 컬렉터에 연결합니다. 직류 모터의 다른 쪽은 5V 전원에 연결 합니다. 모터를 보호하기 위한 플라이백 다이오드도 모터에 병렬로 연결해줍니다. 아두이노의 9번과 10번 단자에는 330오옴의 저항을 각각 연결하고 발광다이오드의 애노드를 연결해줍니다. 발광다이오드의 캐소드는 접지(0V) 쪽으로 연결을 해주어야 합니다.
광센서 두개를 각각 한쪽 다리를 5볼트에 연결하고 다른 쪽 다리는 4.7킬로오옴의 저항에 연결합니다. 저항의 다른쪽 다리는 접지(0V)에 연결합니다. 그리고 광센서와 저항이 연결된 중간점에서 선을 내어 아두이노 아날로그 입력 단자 A0, A1에 연결합니다.
ⓞ 광센서 회로
광센서에 연결하는 저항을 지난번에는 10킬로오옴을 사용했었는데, 광센서는 밝을 때는 10킬로오옴, 어두울 때는 1메가오옴의 값을 가지는 것이었기 때문에 10킬로오옴의 저항을 사용해서 밝을 때 2.5 볼트, 어두울 때 0.05 볼트를 얻도록 했던 반면에 이번에는 밝을 때 6 킬로오옴, 어두울 때 60 킬로오옴이 되는 광센서를 사용하려고 합니다.
따라서 6 킬로옴에 가까운 4.7 킬로오옴 저항을 사용하면, 밝을 때 2.2 볼트, 어두울 때 0.07 볼트의 전압을 얻을 수 있습니다.
ⓞ 트랜지스터 회로
트랜지스터의 베이스에 연결하는 저항은 왜 330 오옴을 합니다. 모터에 부하가 걸렸을 때 2 암페어 정도의 전류를 소모할 수 있기 때문에 트랜지스터의 증폭률이 100이므로 베이스에는 20 밀리암페어의 전류를 흘려줘야 합니다.
따라서 5 볼트를 0.02 암페어로 나누어 주면 250 오옴의 저항을 연결해야 합니다. 그런데 시중에서 250 오옴의 저항을 구하기는 쉽지가 않고, 250 오옴 보다 작은 값의 저항을 사용하면 전류가 더 많이 흘러 모터의 수명을 단축시킬 수도 있어서, 250 오옴 보다 큰 저항 중에서 가장 비슷한 값의 저항을 찾은 것이 330 오옴이어서 330 오옴 저항을 사용하는 것입니다.
ⓞ 필요한 부품 목록 1
자동차를 만드는데 필요한 재료들은 우선 부품들을 꽃을 브레드 보드 1개,부품들을 연결할 점퍼선 1세트, 아두이노 보드 1개, 컴퓨터와 연결할 USB 케이블 1개, 컴퓨터가 1세트, 다이오드가 2개, 트랜지스터가 2개, 광센서가 2개 필요합니다.
ⓞ 필요한 부품 목록 2
그 다음 4.7 킬로오옴 저항이 2개, 330 오옴 저항이 4개 필요하고, 발광다이오드가 2개 필요하고, 그 다음 바퀴가 2개, 직류 기어 모터가 2개, AA건전지 4개를 넣을 수 있는 건전지홀더가 1개 필요한데 슬라이드 스위치가 부착되어 있고, 아두이노 전원잭에 맞는 전원 플러그가 부착되어 있어야 합니다. 아크릴판이 하나 필요한데 브레드보드 크기에 아두이노의 구멍과 호환되는 구멍이 미리 뚫려 있어야 합니다. 아두이노 보드 위에 브레드보드를 고정하기 위해 필요한 것입니다.
그 다음 자동차 몸체용 기판이 필요한데, 80x120mm크기에, 아두이노, 모터 지지대 호환 구멍이 뚫려 있어야 합니다.
ⓞ 필요한 부품 목록 3
그다음 직류 기어 모터를 기판에 고정하기 위한 지지대가 2개, 10 밀리미터 크기의 기판지지대 8개, 15 밀리미터 크기의 기판지지대 4개, 25 밀리미터 크기의 기판지지대 1개가 필요합니다. 10mm 길이의 볼트 19개, 5mm 길이의 접시머리 볼트 4개, 너트 23개가 필요합니다. 그리고 AA크기 건전지 4개가 필요합니다.
ⓞ 필요한 부품 목록 4
그 다음 벨크로 테잎이 1세트 필요합니다. 벨크로 테잎이란 흔히 찍찍이라고 하는 재료로 브레드보드를 아두이노보드에서 떼어낼 수 있도록 하기 위해 사용하는 것입니다. 그리고 랜턴이 필요합니다. 그 다음 공구가 몇 가지 필요한데요. 나사를 조이는데 필요한 십자드라이버, 나사를 조일 때 너트가 돌아가지 않도록 잡거나, 점퍼선을 끼울 때도 사용할 수 있는 라디오 펜치, 롱노우즈 플라이어라고도 합니다. 벨크로 테잎을 자를 때 사용할 가위가 필요합니다.
ⓞ 자동차 몸체 기판에 지지대를 이용하여 모터를 고정
제일 먼저 직류 기어 모터를 자동차 몸체에 고정시켜 보도록 합시다.우선 직류 기어 모터의 양쪽에 10밀리미터 볼트와 너트를 사용하여 모터 지지대를 고정한 다음 모터 지지대를 로봇 모체 기반에 고정합니다.
볼트를 조일 때는 십자 드라이버를 사용하고, 너트는 라디오 펜치로 잡아주면 됩니다, 지지대의 모서리부분은 안쪽에 있는 볼트부터 고정해야 고정하기가 쉽습니다.
ⓞ 모터와 바퀴가 모두 고정
두개의 모터를 마주보도록 자동차 몸체 기판에 고정한 후 모터의 회전축에 바퀴를 끼워 봅시다. 자동차 몸체 기판을 뒤집어 보면 이렇게 좌우에 너트 네 개씩 각각 고정된 모습이 보일 것입니다.
ⓞ 앞바퀴 대용 지지대 부착
앞바퀴는 기판 지지대를 이용하여 앞바퀴를 대신합니다. 25 밀리미터 길이의 기판 지지대를 자동차 몸체용 기판의 앞부분, 중앙에 그림과 같이 너트를 이용하여 고정하고, 밑 부분에는 10 밀리미터 길이의 볼트를 끼워 높이를 조절할 수 있도록 해줍니다.
ⓞ 건전지 홀더를 몸체에 고정
이번에는 건전지 홀더를 고정해 봅니다. 우선 10밀리미터 지지대를 기어 모터 안쪽의 두 곳에 끼워 줍니다. 반대편에는 너트를 끼울 공간이 없으므로 그대로 둡니다. 그 다음에는 건전지 홀더의 구멍 두 곳에 10밀리미터 지지대를 반대편에서 10밀리미터 볼트로 고정합니다. 그 다음에는 건전지 홀더를 자동차 몸체의 위쪽에 그림과 같이 끼우고 너트로 조여 줍니다.
ⓞ 회로 구성
완성된 자동차 몸체 위에 준비한 재료들을 가지고 회로를 구성해 봅시다. 브레드보드 위에 부품들을 배치하고 점퍼선을 이용하여 연결을 해줍시다. 모두 연결한 뒤에는 부품의 방향과 다리 순서 등을 다시 한번 확인 합니다.
ⓞ 1단계 스케치
각 단자 번호를 용도에 따라 상수로 정의합니다.
#define이라는 것이 상수를 정의하면 #define 뒤에 써준 이름을 스케치 안에서 사용하면 그 다음에 따라오는 값으로 대치해주게 됩니다. 그 다음은 모터를 동작시키는 속도를 두 단계로 SPEED_LOW와 SPEED_HIGH로 정의했습니다.
이 값은 실제 자동차를 동작시키면서 적절한 속도로 조절을 해줄 필요가 있을 것입니다.
모터에 단자에 255가 출력되면 5볼트가 공급되고, 100이 출력되면 2 볼트 정도의 전압이 출력됩니다. 그 다음 셋업 함수에서는 우선 모터 좌,우와 발광다이오드 좌,우의 단자를 출력모드로 설정한 후 testRobot()이라는 함수를 호출하였습니다. testRobot()함수에서는 실제 발광다이오드와 모터의 동작을 시험하는 부분이 있어야 합니다
ⓞ 1단계 스케치의 testRobot() 함수
testRobot() 함수에서는 반복문을 사용하고 반복 블럭안에서, 모터 좌, 우를 켠 후 0.5초간 지연, 다시, 모터 좌,우를 끈 후 0.5초간 지연시킵니다. 위의 반복 블럭을 2회 반복하도록 합니다.
loop() 함수에서는 아무것도 해 줄 필요가 없습니다. 하지만 loop()함수 자체는 반드시 있어야 합니다. 작성된 스케치를 아두이노 보드에 전송하여 시험을 해 봅니다.
ⓞ 직렬통신으로 광센서 값 읽기
setup함수의 testRobot() 호출 부분 밑에 직렬포트 초기화를 추가하고 testCDS() 함수 호출 부분을 추가합니다. setup() 함수 다음에 testCDS() 함수를 작성해줍니다. testCDS() 함수에서는 정수 변수 valueLeft를 정의하고 왼쪽 광센서로 부터 측정값을 읽어들여 저장합니다. 그 다음 정수 변수 voltLeft를 정의하고 valueLeft의 값을 볼트로 환산해 줍니다. 환산을 할 때는 map() 함수를 사용하는데, map()함수의 첫번째 인자로 주어진 값이 두번째와 세번째 인자로 주어진 값사이에서 변할 때 세번째와 네번째 인자로 주어진 값으로 대응하여 변환을 하게 됩니다. 변환된 값은 voltLeft에 저장되고 valueLeft와 voltLeft의 값을 직렬 포트로 전송합니다. 그 다음에는 오른쪽 센서에 대해 왼쪽과 같은 작업을 해줍니다.
작성된 스케치를 아두이노에 전송하여 시리얼 모니터로 전송되는 값을 확인해 봅니다.
valueLeft와 valueRight의 값은 0~1023, voltLeft와 voltRight의 값은 0~5000이 나타나야 합니다.
그리고 광센서를 손으로 가렸을 때, 또 랜턴으로 비추었을 때의 값을 확인합니다.
ⓞ 광센서로 일정세기 이상 감지되면 바퀴 동작 순서도
처음 setup() 함수에는, 출력모드를 설정하고, 모터, 발광다이오드, 센서의 동작을 시험합니다. 센서값의 좌우 평균을 구해서 그 보다 조금 높은 값을 기준치로 정합니다.
loop() 함수에서는 두 광센서 값을 읽어들여 전압으로 변환하고, 왼쪽 센서값과 오른쪽 센서값이 모두 기준치를 넘으면 양쪽 모두 고속으로 진행하고 왼쪽 센서값만 기준치를 넘으면 왼쪽은 고속, 오른쪽은 저속으로 진행합니다. 오른쪽 센서값만 기준치를 넘으면 왼쪽은 저속, 오른쪽은 고속으로 진행합니다. 두 센서값 모두 기준치 미만이면 양쪽 모두 멈춥니다.
왼쪽 센서가 기준치 이상 빛을 감지했을 때 오른쪽 바퀴를 저속으로 진행시키는 이유는 한쪽으로 너무 급선회하는 것을 방지하기 위한 것입니다.
이전 단계까지 작성한 스케치에다가 여유치를 선언해 줍니다.
여유치는 광센서 시험시 감지된 광센서의 평균값에 더해서 기준치를 설정하기 위한 값입니다. 여유치를 MARGIN으로 200의 값을 선언했습니다. 실제 시험을 해보고 조절을 할 필요가 있을 것입니다. 다음은 CDS_INTERVAL을 1000으로 선언했는데 이 값은 몇 밀리초마다 광센서의 값을 읽을지를 선언한 것입니다.
그 다음, prevMillis 변수를 선언했는데 이 변수는 시간이 얼마나 경과했는지를 알기 위해서 직전의 시간값을 저장해두기 위한 변수입니다. 그 다음, threshold 변수를 선언했는데 이 변수는 기준치를 가지고 있기 위한 것입니다. 이 기준치는 광센서 시험 시 얻은 평균값에 여유치를 더해서 가지고 있읍니다.
나중에 광센서의 값을 측정할 때 마다 이 기준치와 비교해서 모터의 진행 동작 여부를 결정하게 했습니다.
ⓞ 스케치 광센서 여유치
testCDS() 함수에는 threshold 값을 설정하는 부분을 추가해줍니다. 광센서 좌, 우에서 읽은 값의 평균을 구한 다음 여유치를 더해서 threshold 변수에 저장합니다.
ⓞ 스케치 loop
loop() 함수에서는 먼저 millis()함수를 써서 현재 시간값을 구한 다음 currentMillis 변수에 저장합니다. 그 다음 현재 시간값과 이전 시간값을 비교하여 CDS_INVERVAL보다 크면 광센서의 값을 읽어 처리합니다. 왼쪽과 오른쪽의 광센서 값을 읽어 전압으로 변환하여 각각의 변수에 저장하고 디버깅을 위해 직렬포트로도 값들을 전송합니다.
ⓞ 스케치 기준치 설정
읽어들인 양쪽 광센서의 전압이 모두 기준치를 초과하면 양쪽 모터 모두 고속으로 진행시킵니다. 왼쪽 값만 기준치를 초과하면 왼쪽 모터는 고속, 오른쪽 모터는 저속으로 진행시킵니다. 오른쪽 값만 기준치를 초과하면 오른쪽 모터는 고속, 왼쪽 모터는 저속으로 진행시킵니다. 양쪽 모두 기준치 이하면 양쪽 모두 정지 시킵니다. 마지막으로 prevMillis의 시간값을 현재의 시간값으로 갱신합니다.
ⓞ 완성된 사진
완성된 스케치를 아두이노 보드에 올려서 실행을 해 보겠습니다. 처음 자동차에 전원이 투입되면 모터가 짧게 두 번 회전하는 것을 볼 수 있고, 시리얼 모니터를 켜보면 좌우 광센서의 값을 1초 마다 표시하는 것을 볼 수 있습니다. 랜턴을 이용하여 좌, 우 양쪽 모두에 빛을 비추면 자동차의 왼쪽, 오른쪽 모터가 진행하고, 전진하는 것을 확인할 수 있습니다. 광센서가 너무 민감하거나 덜 민감하면 여유치 즉, MARGIN값을 조절하여 민감한 정도를 조절할 수 있습니다.
자신의 스케치를 자신의 자동차에 올려서 시험을 해보고 민감도를 조절해 보세요.
동작이 이상하거나 동작을 하지 않을 때는 원인을 찾아서 수정을 해봅시다.
ⓞ 오류 사례
모터가 돌지 않을 때에는 우선 트랜지스터의 단자 순서가 제대로 연결되었는지 확인을 합니다.
우리가 사용하는 P2N2222AG는 다른 대부분의 트랜지스터와는 단자 배치가 다르기 때문에 혼동하기 쉽습니다.
또한 트랜지스터의 베이스에 연결된 저항이 330오옴인지도 확인해 보세요. 저항값이 너무 크면 모터가 돌지 않습니다.
모터가 거꾸로 돌 때는 모터가 거꾸로 돌 때는 모터의 연결선을 반대로 연결해 주시면 됩니다.
빛을 비춰도 모터가 돌지 않을 때는 스케치의 MARGIN값이 너무 클 수 있으니 이 값을 줄여서 해보시고, 광센서가 제대로 연결되어 있는지를 확인합니다.
광센서의 한 쪽 끝은 5 볼트에, 다른 한쪽은 3.7 킬로오옴의 저항에 연결되어 있어야 하고 저항의 다른 쪽 끝은 0 볼트에 연결되어 있어야 합니다. 그리고, 광센서와 저항이 연결된 지점에 점퍼선으로 아두이노 아날로그 입력 0번 (왼쪽)과 1번 (오른쪽)이 연결되어 있어야 합니다. 또한 저항값도 올바른지 확인해 보아야 합니다. 3.7 킬로오옴 저항은 초록-보라-빨강 색띠를 가지고 있습니다.
#define CDS_LEFT 0 #define CDS_RIGHT 1 #define MOTOR_LEFT 5 #define MOTOR_RIGHT 6 #define LED_LEFT 9 #define LED_RIGHT 10 #define MARGIN 200 #define CDS_INTERVAL 1000 #define SPEED_LOW 100 #define SPEED_HIGH 255 unsigned long prevMillis; int threshold; void setup(void) { pinMode(MOTOR_LEFT, OUTPUT); pinMode(MOTOR_RIGHT, OUTPUT); pinMode(LED_LEFT, OUTPUT); pinMode(LED_RIGHT, OUTPUT); testRobot(); Serial.begin(9600); testCDS(); } void testRobot() { for(int i=0; i < 2; i++){ analogWrite(MOTOR_LEFT, SPEED_HIGH); analogWrite(MOTOR_RIGHT, SPEED_HIGH); digitalWrite(LED_LEFT, HIGH); digitalWrite(LED_RIGHT, HIGH); delay(500); analogWrite(MOTOR_LEFT, 0); analogWrite(MOTOR_RIGHT, 0); digitalWrite(LED_LEFT, LOW); digitalWrite(LED_RIGHT, LOW); delay(500); } } void testCDS() { int valueLeft = analogRead(CDS_LEFT); int voltLeft = map(valueLeft,0,1023,0,5000); Serial.print("Left CDS = "); Serial.print(valueLeft); Serial.print(", "); Serial.println(voltLeft); int valueRight = analogRead(CDS_RIGHT); int voltRight = map(valueRight,0,1023,0,5000); Serial.print("Right CDS = "); Serial.print(valueRight); Serial.print(", "); Serial.println(voltRight); } void loop() { unsigned long currentMillis = millis(); if( currentMillis - prevMillis > CDS_INTERVAL){ int valueLeft = analogRead(CDS_LEFT); int voltLeft = map(valueLeft, 0, 1023, 0, 5000); Serial.print("Left CDS = "); Serial.print(valueLeft); Serial.print(", "); Serial.println(voltLeft); int valueRight = analogRead(CDS_RIGHT); int voltRight = map(valueRight, 0, 1023, 0, 5000); Serial.print("Right CDS = "); Serial.print(valueRight); Serial.print(", "); Serial.println(voltRight); Serial.println("====================="); if((voltLeft > threshold) && (voltRight > threshold)){ Serial.println("Both High"); analogWrite(MOTOR_LEFT, SPEED_HIGH); analogWrite(LED_LEFT, SPEED_HIGH); analogWrite(MOTOR_RIGHT, SPEED_HIGH); analogWrite(LED_RIGHT, SPEED_HIGH); }else if ( voltLeft > threshold){ Serial.println("Left High"); analogWrite(MOTOR_LEFT, SPEED_HIGH); analogWrite(LED_LEFT, SPEED_HIGH); analogWrite(MOTOR_RIGHT, SPEED_LOW); analogWrite(LED_RIGHT, 0); }else if (voltRight > threshold){ Serial.println("Right High"); analogWrite(MOTOR_LEFT, SPEED_LOW); analogWrite(LED_LEFT, 0); analogWrite(MOTOR_RIGHT, SPEED_HIGH); analogWrite(LED_RIGHT, SPEED_HIGH); }else{ Serial.println("Both Low"); analogWrite(MOTOR_LEFT, 0); analogWrite(LED_LEFT, 0); analogWrite(MOTOR_RIGHT, 0); analogWrite(LED_RIGHT, 0); } prevMillis = currentMillis; } }