월세가 월급의 36퍼센트 소득 3분의 1이 주거비로 사라지는 서울의 현실
지난 글에서 라즈베리 파이와 파이썬 프로그래밍의 기초를 다루며 로봇에 '지능'을 부여하는 첫 단계에 대해 알아보았습니다. 이제 이 지능을 활용하여 로봇이 스스로 주변 환경을 인식하고, 복잡한 지시 없이도 목표를 향해 나아가며, 무엇보다 장애물을 피해서 안전하게 이동하도록 만드는 방법을 배울 차례입니다. 이 글에서는 로봇 자율 주행의 가장 기본적인 단계이자 필수적인 기능인 장애물 회피 알고리즘의 원리를 심층적으로 다룹니다. 초음파 및 적외선 센서를 활용하여 로봇이 장애물을 감지하고, 이를 바탕으로 경로를 변경하는 구체적인 알고리즘과 프로그래밍 예시를 통해 여러분의 로봇이 진정한 '자율 이동체'로 거듭나는 과정을 함께할 것입니다.
로봇 자율 주행(Autonomous Navigation)은 로봇이 외부의 직접적인 제어 없이 스스로 주변 환경을 인식하고, 경로를 계획하며, 목표 지점까지 이동하는 능력을 의미합니다. 이는 단순한 이동을 넘어, 로봇이 환경에 적응하고 스스로 판단하여 행동하는 지능형 로봇의 핵심 기능입니다. 자율 주행의 가장 기본적인 전제는 바로 장애물 회피입니다.
로봇이 아무리 뛰어난 경로 계획 능력을 가지고 있더라도, 주변에 있는 장애물을 인식하고 피하지 못한다면 임무를 완수할 수 없습니다. 심지어 자신이나 주변 환경에 피해를 줄 수도 있습니다. 따라서 장애물 회피는 로봇의 안전하고 효율적인 자율 주행을 위한 가장 기본적인 기능이자 필수적인 알고리즘입니다.
장애물 회피를 위해 가장 일반적으로 사용되는 센서는 다음과 같습니다.
가장 간단한 형태의 장애물 회피 알고리즘은 다음과 같은 논리 흐름을 가집니다.
1. 센서로 전방의 거리(또는 장애물 유무)를 측정한다.
2. 만약 측정된 거리가 '위험 거리'보다 작으면 (즉, 장애물이 너무 가까이 있으면):
a. 로봇을 정지시키거나 후진시킨다.
b. 주변 센서를 다시 측정하거나, 임의의 방향(예: 오른쪽)으로 회전한다.
c. 회전 후 다시 전방을 측정하여 안전하다고 판단되면 전진한다.
3. 만약 '위험 거리'보다 크면 (즉, 장애물이 없으면):
a. 로봇을 전진시킨다.
4. 이 과정을 무한히 반복한다.
지난 7편에서 조립했던 아두이노 기반 이동 로봇에 초음파 센서(HC-SR04)를 추가하여 장애물 회피 기능을 구현해 보겠습니다.
// L298N 모터 드라이버 핀 설정 (7편 코드 참고)
int motor1_in1 = 2;
int motor1_in2 = 3;
int motor1_ena = 9; // PWM 핀
int motor2_in3 = 4;
int motor2_in4 = 5;
int motor2_enb = 10; // PWM 핀
// HC-SR04 초음파 센서 핀 설정
int trigPin = 7;
int echoPin = 6;
// 상수 정의
const long DURATION_TIMEOUT = 23200; // 400cm (200cm 왕복)에 대한 타임아웃 마이크로초 (소리 속도 약 343m/s)
const int OBSTACLE_DISTANCE_CM = 25; // 장애물 회피를 시작할 거리 (cm)
void setup() {
// 모터 핀 모드 설정
pinMode(motor1_in1, OUTPUT);
pinMode(motor1_in2, OUTPUT);
pinMode(motor1_ena, OUTPUT);
pinMode(motor2_in3, OUTPUT);
pinMode(motor2_in4, OUTPUT);
pinMode(motor2_enb, OUTPUT);
// 초음파 센서 핀 모드 설정
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
Serial.begin(9600); // 시리얼 통신 시작 (디버깅용)
Serial.println("Obstacle Avoidance Robot Ready!");
}
void loop() {
long duration, distanceCm;
// 1. 초음파 센서로 거리 측정
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Echo 핀으로부터 펄스 지속 시간을 읽습니다. (최대 DURATION_TIMEOUT까지 기다림)
duration = pulseIn(echoPin, HIGH, DURATION_TIMEOUT);
// 거리를 cm로 변환 (소리의 속도: 343m/s = 0.0343cm/us. 왕복 거리이므로 2로 나눔)
distanceCm = duration * 0.0343 / 2;
Serial.print("Distance: ");
Serial.print(distanceCm);
Serial.println(" cm");
// 2. 장애물 감지 및 회피 로직
if (distanceCm < OBSTACLE_DISTANCE_CM && distanceCm > 0) { // 장애물이 너무 가까이 있다면
Serial.println("Obstacle detected! Avoiding...");
stopMotors(); // 일단 정지
delay(500); // 잠시 대기
moveBackward(100); // 잠시 후진
delay(700);
stopMotors();
delay(300);
// 오른쪽으로 회전 (로봇마다 회전 방향은 다를 수 있음)
// 한쪽 바퀴만 돌리거나, 양쪽 바퀴를 반대 방향으로 돌립니다.
turnRight(150); // 예: 오른쪽 바퀴는 정지, 왼쪽 바퀴만 전진
delay(1000); // 1초간 회전
stopMotors();
delay(300);
// 회전 후 다시 전방 확인을 위해 loop()가 다시 시작됨
} else { // 장애물이 없다면 전진
moveForward(150);
Serial.println("Moving forward...");
}
delay(50); // 짧은 지연 (센서 안정화 및 루프 속도 조절)
}
// === 로봇 움직임 제어 함수 ===
void moveForward(int speed) {
digitalWrite(motor1_in1, HIGH);
digitalWrite(motor1_in2, LOW);
analogWrite(motor1_ena, speed);
digitalWrite(motor2_in3, HIGH);
digitalWrite(motor2_in4, LOW);
analogWrite(motor2_enb, speed);
}
void moveBackward(int speed) {
digitalWrite(motor1_in1, LOW);
digitalWrite(motor1_in2, HIGH);
analogWrite(motor1_ena, speed);
digitalWrite(motor2_in3, LOW);
digitalWrite(motor2_in4, HIGH);
analogWrite(motor2_enb, speed);
}
void turnLeft(int speed) {
digitalWrite(motor1_in1, LOW); // 왼쪽 바퀴 후진
digitalWrite(motor1_in2, HIGH);
analogWrite(motor1_ena, speed);
digitalWrite(motor2_in3, HIGH); // 오른쪽 바퀴 전진
digitalWrite(motor2_in4, LOW);
analogWrite(motor2_enb, speed);
}
void turnRight(int speed) {
digitalWrite(motor1_in1, HIGH); // 왼쪽 바퀴 전진
digitalWrite(motor1_in2, LOW);
analogWrite(motor1_ena, speed);
digitalWrite(motor2_in3, LOW); // 오른쪽 바퀴 후진
digitalWrite(motor2_in4, HIGH);
analogWrite(motor2_enb, speed);
}
void stopMotors() {
analogWrite(motor1_ena, 0);
analogWrite(motor2_enb, 0);
}
loop() 함수:
digitalWrite(trigPin, ...): 짧은 펄스를 트리거 핀에 보내 초음파를 발사합니다.pulseIn(echoPin, HIGH, DURATION_TIMEOUT): 에코 핀이 HIGH 신호를 유지하는 시간(초음파가 돌아오는 시간)을 측정합니다. DURATION_TIMEOUT은 너무 멀리 있거나 센서가 응답하지 않을 때 무한 대기하는 것을 방지합니다.distanceCm = duration * 0.0343 / 2;: 측정된 시간(duration)을 바탕으로 거리를 계산합니다. 소리의 속도와 왕복 시간을 고려한 공식입니다.if (distanceCm < OBSTACLE_DISTANCE_CM && distanceCm > 0): 측정된 거리가 미리 정의된 OBSTACLE_DISTANCE_CM(예: 25cm)보다 작고, 유효한 값일 때 (0보다 클 때) 장애물 회피 동작을 시작합니다.loop()의 시작으로 돌아가 전방을 확인합니다.moveForward() 함수를 호출하여 로봇을 전진시킵니다.moveForward, moveBackward, turnLeft, turnRight, stopMotors 함수는 로봇의 움직임을 추상화하여 코드를 더 읽기 쉽게 만듭니다. (7편 코드에서 가져옴)이 예제는 가장 기본적인 장애물 회피 알고리즘입니다. 더 똑똑한 로봇을 만들려면 다음과 같은 방법으로 알고리즘을 개선할 수 있습니다.
이번 글에서는 로봇 자율 주행의 가장 기본적인 단계인 장애물 회피 알고리즘의 원리와 아두이노를 이용한 구현 예시를 살펴보았습니다. 초음파 센서를 활용하여 장애물을 감지하고, 로봇이 스스로 경로를 변경하여 안전하게 이동하도록 만드는 것이 얼마나 중요한지 이해하셨을 겁니다. 이 기본적인 알고리즘을 바탕으로 다중 센서 활용, 스마트 회전, 속도 제어 등 다양한 방식으로 로봇의 지능을 더욱 향상시킬 수 있습니다.
다음 편에서는 좀 더 정교하고 복잡한 움직임을 제어하는 로봇 팔 만들기에 도전해 보겠습니다. 서보 모터와 키네마틱스 기초를 통해 로봇 팔의 각 관절을 제어하는 방법을 배우게 될 것입니다. 이제 여러분의 로봇에 초음파 센서를 장착하고, 위 예제 코드를 수정하여 자신만의 장애물 회피 로봇을 만들어 실제 환경에서 테스트해 보세요!
댓글
댓글 쓰기