7차시 : 장애물 회피 RC카

# 7차시 목차

  • 센서와 자율주행

  • [실습 7] 스마트 RC카 장애물 회피하기

실습 7-1. 장애물 회피 RC카 코드 살펴보기

실습 7-2. 장애물 회피 RC카 미션 수행하기

실습 7-3. 블루투스로 RC카 제어하기

  • [마무리] 작동원리 파악하기


  • 센서와 자율주행

우리나라의 대표적인 완성차 기업인 H사는 지난 2021년 미국 로봇 회사인 ‘보스턴 다이내믹스’를 약 1조원에 인수했습니다. 보스턴 다이내믹스가 만드는 로봇들은 걷고 달리는 것은 기본이며, 계단을 오르내리고 제자리에서 뛰고 돌며, 심지어 백덤블링까지도 가능합니다. 또한 세계적인 아이돌 그룹 BTS와 함께 춤을 추는 영상도 화제가 되었습니다. 사람 형태의 로봇 뿐만 아니라 4족 보행, 바퀴로 주행하는 로봇 등 기발한 상상력을 구체적인 로봇의 형태로 구현할 수 있는 기술력을 가진 로봇 회사입니다. 그런데, 자동차 회사인 H사가 왜 로봇 회사를 인수했을까요?

H사는 최근 ‘당신을 향한 모빌리티’라는 주제의 광고 영상을 제작했습니다. 만약, 이 광고를 보는 동안 H사의 로고와 설명이 없었다면, 아마 대부분의 사람들은 로봇 회사가 만든 광고로 착각할 수 있을 것 같습니다. 이 광고를 통해 우리는 H사가 가진 고민과 비전, 즉 자동차라고 하는 탈 것의 제한과 한계를 자율 주행과 로봇 기술을 이용해 확장해가고 있음을 볼 수 있습니다.

자율 주행과 로봇 기술은 모두 첨단 과학 기술의 집약체입니다. 다양한 센서들로 주변을 인식하고, 상황에 따라 적절한 판단을 내릴 수 있는 알고리즘과 이러한 시스템에 의해 전달된 명령들이 일사불란하게 동력 전달 장치들로 전달되어 움직임으로 만들어지는 과정 전반에는 엄청나게 많은 과학의 원리와 기술, 공학의 과정이 담겨 있습니다. 그리고 이러한 과학 기술들의 발전은 우리 사회에 많은 영향을 미치기 때문에 우리는 과학 기술의 발전을 시민의 한 사람으로써 주목할 수 있어야 합니다.

[실습 7 ] 스마트 RC카 장애물 회피하기

[실습 7-1] RC카 장애물 회피 코드 아두이노 업로드하기

#include <Servo.h>

Servo EduServo;

//출력핀(trig)과 입력핀(echo) 설정

int trigPin = 13; // 디지털 13번 핀에 연결

int echoPin = 12; // 디지털 12번 핀에 연결

int Ultra_d = 0;

int val = 0; // 좌우 경로 설정 변수

int RightMotor_E_pin = 5; // 오른쪽 모터의 Enable & PWM

int LeftMotor_E_pin = 6; // 왼쪽 모터의 Enable & PWM

int RightMotor_1_pin = 8; // 오른쪽 모터 제어선 IN1

int RightMotor_2_pin = 9; // 오른쪽 모터 제어선 IN2

int LeftMotor_3_pin = 10; // 왼쪽 모터 제어선 IN3

int LeftMotor_4_pin = 11; // 왼쪽 모터 제어선 IN4

//좌우 모터 속도 조절, 설정 가능 최대 속도 : 255

int L_MotorSpeed = 153; // 왼쪽 모터 속도

int R_MotorSpeed = 153; // 오른쪽 모터 속도

void setup() {

EduServo.attach(2); // 서보모터 PWM 디지털입출력 2번핀 연결

pinMode(echoPin, INPUT); // echoPin 입력

pinMode(trigPin, OUTPUT); // trigPin 출력

pinMode(RightMotor_E_pin, OUTPUT); // 출력모드로 설정

pinMode(RightMotor_1_pin, OUTPUT);

pinMode(RightMotor_2_pin, OUTPUT);

pinMode(LeftMotor_3_pin, OUTPUT);

pinMode(LeftMotor_4_pin, OUTPUT);

pinMode(LeftMotor_E_pin, OUTPUT);

Serial.begin(9600); // PC와의 시리얼 통신 9600bps로 설정

Serial.println("Welcome Eduino!");

}

void loop() {

Ultra_d = Ultrasonic();

Serial.println(Ultra_d);

motor_role(HIGH, HIGH); // 직진

if(Ultra_d < 250) {

if (Ultra_d < 150) {

Serial.println("150 이하.");

motor_role(LOW, LOW); // 후진

delay(1000);

analogWrite(RightMotor_E_pin, 0);

analogWrite(LeftMotor_E_pin, 0);

delay(200);

}

else {

analogWrite(RightMotor_E_pin, 0);

analogWrite(LeftMotor_E_pin, 0);

delay(200);

Serial.println("150 이상.");

val = Servo_con();

if (val == 0) {

Serial.println("우회전.");

analogWrite(RightMotor_E_pin, 0);

analogWrite(LeftMotor_E_pin, 0);

delay(500);

motor_role(LOW, LOW); // 후진

delay(500);

motor_role(LOW, HIGH); // 우회전

delay(800);

}

else if (val == 1) {

Serial.println("좌회전.");

analogWrite(RightMotor_E_pin, 0);

analogWrite(LeftMotor_E_pin, 0);

delay(500);

motor_role(LOW, LOW); // 후진

delay(500);

motor_role(HIGH, LOW); // 좌회전

delay(800);

}

}

}

}

void motor_role(int R_motor, int L_motor){

digitalWrite(RightMotor_1_pin, R_motor);

digitalWrite(RightMotor_2_pin, !R_motor);

digitalWrite(LeftMotor_3_pin, L_motor);

digitalWrite(LeftMotor_4_pin, !L_motor);

analogWrite(RightMotor_E_pin, R_MotorSpeed); // 우측 모터 속도값

analogWrite(LeftMotor_E_pin, L_MotorSpeed); // 좌측 모터 속도값

}

int Ultrasonic(){

long duration, distance;

digitalWrite(trigPin, HIGH); // trigPin에서 초음파 발생(echoPin도 HIGH)

delayMicroseconds(10);

digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH); // echoPin 이 HIGH를 유지한 시간을 저장 한다.

distance = ((float)(340 * duration) / 1000) / 2;

//Serial.print("DIstance:"); // 물체와 초음파 센서간 거리를 표시.

//Serial.println(distance);

return distance;

}

int Servo_con(){

EduServo.write(30);

delay(300);

int Ult_30 = Ultrasonic();

delay(700);

EduServo.write(150);

delay(300);

int Ult_150 = Ultrasonic();

delay(700);

if(Ult_30 > Ult_150){

val = 1;

}

else{

val = 0;

}

EduServo.write(90);

return val;

}

  • 장애물 회피 코드 분석하기


[코드 작동 원리 이해]

- If와 else는 조건문에 쓰이는 영어 단어입니다. 그렇다면, 초음파 센서로 거리를 측정해서 RC카를 구동 동작하게 하는 코드에서는 어떻게 활용되고 있나요?

‣ 일정 거리 이하 일 때와 그렇지 않을 때에 따른 구동 동작을 구분하기 위해 활용


[코드 작동 원리 이해]

- If와 else는 조건문에 쓰이는 영어 단어입니다. 그렇다면, 초음파 센서로 거리를 측정해서 RC카를 구동 동작하게 하는 코드에서는 어떻게 활용되고 있나요?

‣ 일정 거리 이하 일 때와 그렇지 않을 때에 따른 구동 동작을 구분하기 위해 활용


‣ 정지(0.2초) → Servo_con() 함수 실행

‣ Servo_con() 함수: 장애물 감지하였을 때 실행하는 함수

- Servo_con함수 실행 val == 0 일 때는 어떻게 구동 동작 하나요?

‣ 정지(0.5초) → 후진(0.5초) → 회전(0.8초)

- Servo_con함수 실행 val == 1 일 때는 어떻게 구동 동작 하나요?

‣ 정지(0.5초) → 후진(0.5초) → 회전(0.8초)


[ 실습 7-2 ] 장애물 설치하고 동작 테스트 하기

[미션] 팀별로 주어진 우드락을 활용하여 장애물을 설치 후 장애물을 잘 회피하여 목적지에 잘 도달하는지 스마트 RC카 동작테스트를 해보자.

  • 추가 과제 : 주어진 장애물을 더 효과적으로 피하기 위해 코드를 일부 수정해 보자.

  • 힌트 : 딜레이값, 회전량, 후진거리, 속도 등을 조정하여 최적화 할 수 있다.

[과제 제출] 장애물 회피를 하는 스마트 RC카 영상을 촬영하여 2023 충남대 RC제작 캠프 오픈채팅방에 영상 제출하기

[실습 7-3] 블루투스로 RC카 제어하기

  1. 블루투스 회로 추가하기

https://drive.google.com/file/d/1kvjnvNQocmwrUXLIh8wCEc3zPrpWpJBH/view?usp=sharing

2. 블루투스 제어 코드 업로드 하기

* 스마트폰 어플의 블루투스 통신을 통해 스마트카의 동작을 무선 제어하는 예제입니다.

*

* 명령

* 1. 블루투스 g

* -> Go 전진 명령

* 2. 블루투스 b

* -> Back 후진 명령

* 3. 블루투스 l

* -> left 좌회전 명령

* 4. 블루투스 r

* -> right 우회전 명령

* 5. 블루투스 s

* -> stop 정지 명령

* 6. 블루투스 q

* -> left 제자리 좌회전 명령

* 7. 블루투스 w

* -> right 제자리 우회전 명령

*/

#include <SoftwareSerial.h> //가상 시리얼 통신을 위한 라이브러리 선언

#define BT_RXD 3 // 아두이노의 4번핀을 RX(받는 핀)로 설정

#define BT_TXD 4 // 아두이노 3번핀을 TX(보내는 핀)로 설정

SoftwareSerial bluetooth(BT_RXD,BT_TXD); //블루투스 통신을 위한 설정

int RightMotor_E_pin = 5; // 오른쪽 모터의 Enable & PWM

int LeftMotor_E_pin = 6; // 왼쪽 모터의 Enable & PWM

int RightMotor_1_pin = 8; // 오른쪽 모터 제어선 IN1

int RightMotor_2_pin = 9; // 오른쪽 모터 제어선 IN2

int LeftMotor_3_pin = 10; // 왼쪽 모터 제어선 IN3

int LeftMotor_4_pin = 11; // 왼쪽 모터 제어선 IN4

//좌우 모터 속도 조절, 설정 가능 최대 속도 : 255

int L_MotorSpeed = 185; // 왼쪽 모터 속도

int R_MotorSpeed = 160; // 오른쪽 모터 속도

int R_Motor = 0;

int L_Motor = 0;

int mode = 0;

void setup() {

pinMode(RightMotor_E_pin, OUTPUT); // 출력모드로 설정

pinMode(RightMotor_1_pin, OUTPUT);

pinMode(RightMotor_2_pin, OUTPUT);

pinMode(LeftMotor_3_pin, OUTPUT);

pinMode(LeftMotor_4_pin, OUTPUT);

pinMode(LeftMotor_E_pin, OUTPUT);

Serial.begin(9600); //PC와 아두이노간 시리얼 통신 속도를 9600bps로 설정

bluetooth.begin(9600); //블루투스와 아두이노간 시리얼 통신 속도를 9600bps로 설정

Serial.println("Welcome Eduino!");

}

void loop() {

if(bluetooth.available()){

char Blue_Val = bluetooth.read();

/*

Serial.print("Input Signal : ");

Serial.print("Bluetooth[ "); Serial.print(Blue_Val); Serial.print(" ], ");

*/

control_SmartCar(Blue_Val);

if(mode == 0){

motor_role(R_Motor, L_Motor);

}

else if(mode == 1){

Right_role(R_Motor, L_Motor);

}

else if(mode == 2){

Left_role(R_Motor, L_Motor);

}

else if(mode == 4){

left_rotation(R_Motor, L_Motor);

}

else if(mode == 5){

right_rotation(R_Motor, L_Motor);

}

else{

analogWrite(RightMotor_E_pin, 0);

analogWrite(LeftMotor_E_pin, 0);

}

}

}

void control_SmartCar(char Blue_val){

if( Blue_val == 'g' ){ // "g" 버튼, 명령 : 전진

R_Motor = HIGH; L_Motor = HIGH; mode = 0;

//Serial.print("Forward : ");

}

else if( Blue_val == 'r' ){ // "r" 버튼, 명령 : 우회전

mode = 1;

//Serial.print("Turn Right : ");

}

else if( Blue_val == 'l' ){ // "l" 버튼, 명령 : 좌회전

mode = 2;

//Serial.print("Turn Left : ");

}

else if( Blue_val == 'b' ){ // "b" 버튼, 명령 : 후진

R_Motor = LOW; L_Motor = LOW; mode = 0;

//Serial.print("Backward : ");

}

else if( Blue_val == 's' ){ // "s" 버튼, 명령 : 정지

R_Motor = HIGH; L_Motor = HIGH; mode = 3;

//Serial.print("Stop : ");

}

else if( Blue_val == 'q' ){ // "q" 버튼, 명령 : 제자리 좌회전

mode = 4;

//Serial.print("Left Rotation : ");

}

else if( Blue_val == 'w' ){ // "w" 버튼, 명령 : 제자리 우회전

mode = 5;

//Serial.print("Right Rotation : ");

}

else{

//Serial.print("Not Defined : "); // 지정하지 않은 주소입력.

}

}

void motor_role(int R_motor, int L_motor){

digitalWrite(RightMotor_1_pin, R_motor);

digitalWrite(RightMotor_2_pin, !R_motor);

digitalWrite(LeftMotor_3_pin, L_motor);

digitalWrite(LeftMotor_4_pin, !L_motor);

analogWrite(RightMotor_E_pin, R_MotorSpeed); // 우측 모터 속도값

analogWrite(LeftMotor_E_pin, L_MotorSpeed); // 좌측 모터 속도값

}

void Right_role(int R_motor, int L_motor){

digitalWrite(RightMotor_1_pin, R_motor);

digitalWrite(RightMotor_2_pin, !R_motor);

digitalWrite(LeftMotor_3_pin, L_motor);

digitalWrite(LeftMotor_4_pin, !L_motor);

analogWrite(RightMotor_E_pin, max(R_MotorSpeed*0.4,90)); // 우측 모터 속도값

analogWrite(LeftMotor_E_pin, 255); // 좌측 모터 속도값

}

void Left_role(int R_motor, int L_motor){

digitalWrite(RightMotor_1_pin, R_motor);

digitalWrite(RightMotor_2_pin, !R_motor);

digitalWrite(LeftMotor_3_pin, L_motor);

digitalWrite(LeftMotor_4_pin, !L_motor);

analogWrite(RightMotor_E_pin, 255); // 우측 모터 속도값

analogWrite(LeftMotor_E_pin, max(L_MotorSpeed*0.4,90)); // 좌측 모터 속도값

}

void left_rotation(int R_motor, int L_motor){

digitalWrite(RightMotor_1_pin, HIGH);

digitalWrite(RightMotor_2_pin, LOW);

digitalWrite(LeftMotor_3_pin, LOW);

digitalWrite(LeftMotor_4_pin, HIGH);

analogWrite(RightMotor_E_pin, R_MotorSpeed); // 우측 모터 속도값

analogWrite(LeftMotor_E_pin, L_MotorSpeed); // 좌측 모터 속도값

}

void right_rotation(int R_motor, int L_motor){

digitalWrite(RightMotor_1_pin, LOW);

digitalWrite(RightMotor_2_pin, HIGH);

digitalWrite(LeftMotor_3_pin, HIGH);

digitalWrite(LeftMotor_4_pin, LOW);

analogWrite(RightMotor_E_pin, R_MotorSpeed); // 우측 모터 속도값

analogWrite(LeftMotor_E_pin, L_MotorSpeed); // 좌측 모터 속도값

}

3. 스마트폰 어플로 RC카 블루투스 신호 보내기

블루투스 동작 영상 참고.

  • 팀별로 토의 후 패들렛에서 작성하세요.

[코드 분석] 동작 원리를 나타낸 그림입니다. 그림과 위의 코드를 연결하여 어떤 원리로 동작하는지 설명하시오,

[질문 1] 초음파 센서를 쓰지 않고, 장애물을 회피하려면 어떤 센서들을 활용할 수 있을까요?


[질문 2] 센서를 활용하지 않고 장애물을 회피하려면 어떤 과학 기술을 이용하면 좋을까요?


[생각 정리] 오늘 학습한 내용을 키워드 중심으로 정리해봅시다.


  • 패들렛 링크

라인트레이서 코드


int RightMotor_E_pin = 5; // 오른쪽 모터의 Enable & PWM

int LeftMotor_E_pin = 6; // 왼쪽 모터의 Enable & PWM

int RightMotor_1_pin = 8; // 오른쪽 모터 제어선 IN1

int RightMotor_2_pin = 9; // 오른쪽 모터 제어선 IN2

int LeftMotor_3_pin = 10; // 왼쪽 모터 제어선 IN3

int LeftMotor_4_pin = 11; // 왼쪽 모터 제어선 IN4


int L_Line = A5; // 왼쪽 라인트레이서 센서는 A5 핀에 연결

int C_Line = A4; // 가운데 라인트레이서 센서는 A4 핀에 연결

int R_Line = A3; // 오른쪽 라인트레이서 센서는 A3 핀에 연결


//좌우 모터 속도 조절, 설정 가능 최대 속도 : 255

int L_MotorSpeed = 153; // 왼쪽 모터 속도

int R_MotorSpeed = 153; // 오른쪽 모터 속도


int SL = 1;

int SC = 1;

int SR = 1;


void setup() {

// declare the ledPin as an OUTPUT:

pinMode(RightMotor_E_pin, OUTPUT);

pinMode(LeftMotor_E_pin, OUTPUT);

pinMode(RightMotor_1_pin, OUTPUT);

pinMode(RightMotor_2_pin, OUTPUT);

pinMode(LeftMotor_3_pin, OUTPUT);

pinMode(LeftMotor_4_pin, OUTPUT);


Serial.begin(9600); // PC와의 시리얼 통신 9600bps로 설정

Serial.println("Welcome Eduino!");

}


void loop() {

int L = digitalRead(L_Line);

int C = digitalRead(C_Line);

int R = digitalRead(R_Line);


Serial.print("digital : ");

Serial.print(L);

Serial.print(", ");

Serial.print(C);

Serial.print(", ");

Serial.print(R);

Serial.print(" ");


if ( L == LOW && C == LOW && R == LOW ) { // 0 0 0

L = SL; C = SC; R = SR;

}


if ( L == LOW && C == HIGH && R == LOW ) { // 0 1 0

motor_role(HIGH, HIGH);

Serial.println("직진");

}

else if (L == LOW && R == HIGH ){ // 0 0 1, 0 1 1

motor_role(LOW, HIGH);

Serial.println("우회전");

}

else if (L == HIGH && R == LOW ) { // 1 0 0, 1 1 0

motor_role(HIGH, LOW);

Serial.println("좌회전");


}

else if ( L == HIGH && R == HIGH ) { // 1 1 1, 1 0 1

analogWrite(RightMotor_E_pin, 0);

analogWrite(LeftMotor_E_pin, 0);

Serial.println("정지");

}

SL = L; SC = C; SR = R;

}


void motor_role(int R_motor, int L_motor) {

digitalWrite(RightMotor_1_pin, R_motor);

digitalWrite(RightMotor_2_pin, !R_motor);

digitalWrite(LeftMotor_3_pin, L_motor);

digitalWrite(LeftMotor_4_pin, !L_motor);


analogWrite(RightMotor_E_pin, R_MotorSpeed); // 우측 모터 속도값

analogWrite(LeftMotor_E_pin, L_MotorSpeed); // 좌측 모터 속도값

}