News Download Tutorial FAQ Reference Buy

자바 언어 : 센서 두 개를 사용한 라인 트레이서

개요
지난 시간에는 햄스터 로봇의 바닥 센서 한 개를 사용하여 검은색 선을 따라 주행하는 방법을 알아보았습니다. 이번에는 햄스터 로봇의 바닥 센서 두 개를 사용하여 주행하는 방법을 알아보도록 하겠습니다.
첫 번째 방법
A4 용지를 준비하고 햄스터 로봇이 이동할 직선을 검은색 테이프 또는 펜으로 그립니다. 검은색 선의 폭은 양쪽 바닥 센서의 간격보다 약간 작은 정도(0.8cm)로 하는 것이 좋습니다. 미리 제작된 파일을 프린터로 인쇄해도 됩니다.

라인 트레이서 실습판 내려 받기 PDF PPT

기본적으로는 앞으로 이동하면서 햄스터 로봇의 왼쪽 바닥 센서가 검은색 선 위에 있으면 중앙으로 가기 위해 왼쪽으로 움직이고, 오른쪽 바닥 센서가 검은색 선 위에 있으면 중앙으로 가기 위해 오른쪽으로 움직이면 됩니다.

     

원리는 간단한데 이를 실제로 구현하기 위해서는 두 가지가 필요합니다.

우선 왼쪽 바닥 센서와 오른쪽 바닥 센서의 값을 관찰해 봅시다. 바닥 센서가 하얀색 종이 위에 있을 때와 검은색 선 위에 있을 때 센서 값이 어떻게 다른지 확인해 봅시다.

import org.roboid.hamster.Hamster;
import org.roboid.runtime.Runner;

public class Controller {
    public static void main(String[] args) {

        Hamster hamster = new Hamster();

        while(true) {
            System.out.println(hamster.leftFloor() + ", " + hamster.rightFloor());
            Runner.wait(20); // 너무 빨리 반복하지 않도록 한다.
        }
    }
}

측정된 센서 값으로부터 햄스터 로봇의 바닥 센서가 하얀색 종이 위에 있는지, 검은색 선 위에 있는지 판단하기 위해서는 크기를 비교하면 됩니다. 기준이 되는 숫자가 필요한데 앞에서 측정한 센서 값의 중간 값을 사용해 봅시다. 하얀색 종이 위에 있을 때 센서 값이 100, 검은색 선 위에 있을 때 센서 값이 0이었다면 기준이 되는 숫자는 50으로 하면 됩니다. 왼쪽 바닥 센서가 검은색 선 위에 있는 경우를 다음과 같이 표현할 수 있습니다.

hamster.leftFloor() < 50

마찬가지로 오른쪽 바닥 센서가 검은색 선 위에 있는 경우를 다음과 같이 표현할 수 있습니다.

hamster.rightFloor() < 50

이제 햄스터 로봇을 왼쪽 또는 오른쪽으로 움직여야 합니다. 기본적으로는 앞으로 이동하고 있기 때문에 제자리에서 회전하는 방법을 사용해 봅시다. 왼쪽으로 회전하기 위해서는 왼쪽 바퀴를 뒤로 움직이고 오른쪽 바퀴를 앞으로 움직이면 됩니다.

hamster.wheels(-30, 30);

오른쪽으로 회전하기 위해서는 왼쪽 바퀴를 앞으로 움직이고 오른쪽 바퀴를 뒤로 움직이면 됩니다.

hamster.wheels(30, -30);

둘 다 검은색 선 위에 없는 경우가 있기 때문에 왼쪽 바닥 센서가 검은색 선 위에 있는 경우와 오른쪽 바닥 센서가 검은색 선 위에 있는 경우를 모두 검사해 주어야 합니다. 앞으로 이동하면서 왼쪽 바닥 센서가 검은색 선 위에 있으면, 즉 왼쪽 바닥 센서의 값이 50보다 작으면 왼쪽으로 회전하고, 오른쪽 바닥 센서가 검은색 선 위에 있으면, 즉 오른쪽 바닥 센서의 값이 50보다 작으면 오른쪽으로 회전하도록 코드를 작성해 봅시다.

hamster.wheels(30, 30);
if(hamster.leftFloor() < 50) {
    hamster.wheels(-30, 30);
} else if(hamster.rightFloor() < 50) {
    hamster.wheels(30, -30);
}

이제 이 코드를 계속 반복하면 됩니다.

import org.roboid.hamster.Hamster;
import org.roboid.runtime.Runner;

public class Controller {
    public static void main(String[] args) {

        Hamster hamster = new Hamster();

        while(true) {
            hamster.wheels(30, 30);
            if(hamster.leftFloor() < 50) {
                hamster.wheels(-30, 30);
            } else if(hamster.rightFloor() < 50) {
                hamster.wheels(30, -30);
            }

            Runner.wait(10); // 너무 빨리 반복하지 않도록 한다.
        }
    }
}

다음 그림과 같이 햄스터 로봇의 중앙이 검은색 선 위의 중앙에 있도록 올려 놓고 작성한 코드를 실행해 봅시다.

크기를 비교하는 기준이 되는 숫자 50을 변경하면 햄스터 로봇의 동작이 어떻게 달라지는지 관찰해 봅시다.

첫 번째 방법으로 하였을 때 햄스터 로봇이 검은색 선을 따라 비교적 잘 이동하는 것처럼 보입니다. 하지만 다음 그림과 같이 중앙에서 벗어나 출발하는 경우와 약간 비스듬하게 출발하는 경우, 곡선을 주행하는 경우에는 햄스터 로봇의 움직임이 부드럽지 않습니다. 특히 90도 꺾어진 길을 주행할 때는 90도 꺾어진 부분에서 검은색 선을 잘 따라가지 못하고 선을 벗어나게 됩니다. 곡선을 따라 주행하거나 90도 꺾어진 길도 잘 달려갈 수 있어야 물건을 이곳저곳으로 운반할 수 있는 멋진 햄스터 로봇이 될 수 있습니다.

두 번째 방법
첫 번째 방법에서 햄스터 로봇의 움직임이 부드럽지 않은 이유는 왼쪽 바닥 센서 또는 오른쪽 바닥 센서의 위치에 관계 없이 일정한 속도로 움직이기 때문입니다. 중앙의 검은색 선을 약간 벗어났을 때는 조금만 움직여도 되지 않을까요?

검은색 선을 따라 주행하는 원리는 같습니다. 다른 점은 왼쪽 바닥 센서와 오른쪽 바닥 센서의 값에 따라 왼쪽 바퀴와 오른쪽 바퀴의 속도를 변경한다는 것입니다. 규칙을 한번 생각해 봅시다.

이를 왼쪽 바퀴와 오른쪽 바퀴의 관점에서 정리하면 다음과 같습니다.

즉, 왼쪽 바퀴의 속도는 왼쪽 바닥 센서의 값과 정비례 관계를 가지고, 오른쪽 바퀴의 속도는 오른쪽 바닥 센서의 값과 정비례 관계를 가집니다. 바닥 센서의 값은 0 ~ 100의 범위이고 바퀴의 속도는 -100 ~ 100의 범위인데, 앞으로 이동해야 하기 때문에 바퀴의 속도도 0 ~ 100의 범위가 되도록 해야 합니다. 이를 수식으로 표현하면 다음과 같습니다.

햄스터 로봇의 속도가 너무 빠르면 곡선을 주행하거나 90도 꺾어진 길에서 급하게 회전하기가 어려워지기 때문에 최대 속도가 50이 되도록 0.5를 곱하여 다음과 같이 코드를 작성해 봅시다.

import org.roboid.hamster.Hamster;
import org.roboid.runtime.Runner;

public class Controller {
    public static void main(String[] args) {

        Hamster hamster = new Hamster();

        double leftSpeed, rightSpeed;
        while(true) {
            leftSpeed = hamster.leftFloor() * 0.5;
            rightSpeed = hamster.rightFloor() * 0.5;
            hamster.wheels(leftSpeed, rightSpeed);

            Runner.wait(10); // 너무 빨리 반복하지 않도록 한다.
        }
    }
}

어떤가요? 우선 직선을 주행해 보도록 합시다. 중앙에서 벗어나 출발하는 경우와 약간 비스듬하게 출발하는 경우, 곡선을 주행하는 경우 모두 실험해 보고 햄스터 로봇의 움직임이 어떻게 달라졌는지 확인해 보도록 합시다. 곱한 숫자 0.5를 변경하면 햄스터 로봇의 움직임이 어떻게 달라지는지도 살펴 보도록 합시다.

90도 꺾어진 길을 주행할 때는 어떻게 되는지 실험해 봅시다. 여전히 90도 꺽어진 길은 힘겨운가 봅니다. 더 큰 문제는 90도 꺾어진 부분에서 회전하다가 양쪽 바닥 센서 모두 검은색 선 위에 올라갈 수 있다는 것입니다. 이 코드에서 바퀴의 속도는 바닥 센서의 값과 정비례 관계를 가지기 때문에 양쪽 바닥 센서의 값이 모두 아주 작은 값이 되면 햄스터 로봇이 정지하게 될 수도 있습니다. 또한, 검은색 선의 폭이 양쪽 센서의 간격보다 넓은 경우에는 마찬가지로 양쪽 바닥 센서의 값이 모두 아주 작은 값이 되어서 햄스터 로봇이 움직이지 않을 수 있습니다. 다른 방법을 생각해 보아야겠습니다.
세 번째 방법
다시 원점으로 돌아가서 아래 그림을 보고 다르게 생각해 봅시다. 기본적으로는 앞으로 이동하면서 햄스터 로봇의 왼쪽 바닥 센서가 검은색 선 위에 있으면 중앙으로 가기 위해 왼쪽으로 움직이고, 오른쪽 바닥 센서가 검은색 선 위에 있으면 중앙으로 가기 위해 오른쪽으로 움직이면 됩니다.
그런데 왜 바닥 센서가 검은색 선 위에 있다는 것을 기준이 되는 숫자와 크기를 비교하여 판단할까요? 바닥 센서의 값이 50보다 작으면 검은색 선 위에 있다고 판단할 필요가 있을까요?
아래 그림을 자세히 살펴보면 햄스터 로봇이 중앙에서 오른쪽에 있는 경우에는 오른쪽 바닥 센서의 값이 왼쪽 바닥 센서의 값보다 커지게 됩니다. 반대로 햄스터 로봇이 중앙에서 왼쪽에 있는 경우에는 왼쪽 바닥 센서의 값이 오른쪽 바닥 센서의 값보다 커지게 됩니다. 즉, 기준이 되는 숫자를 사용하지 않고 왼쪽 바닥 센서 값과 오른쪽 바닥 센서 값의 크기를 비교하면 됩니다.

     

첫 번째 방법의 코드를 약간 수정해 봅시다.

import org.roboid.hamster.Hamster;
import org.roboid.runtime.Runner;

public class Controller {
    public static void main(String[] args) {

        Hamster hamster = new Hamster();

        int leftFloor, rightFloor;
        while(true) {
            leftFloor = hamster.leftFloor();
            rightFloor = hamster.rightFloor();
            hamster.wheels(30, 30);
            if(leftFloor < rightFloor) { // 중앙에서 오른쪽에 있으면 왼쪽으로 회전한다.
                hamster.wheels(-30, 30);
            } else if(leftFloor > rightFloor) { // 중앙에서 왼쪽에 있으면 오른쪽으로 회전한다.
                hamster.wheels(30, -30);
            }

            Runner.wait(10); // 너무 빨리 반복하지 않도록 한다.
        }
    }
}

조금 더 수정하면 다음과 같이 됩니다.

import org.roboid.hamster.Hamster;
import org.roboid.runtime.Runner;

public class Controller {
    public static void main(String[] args) {

        Hamster hamster = new Hamster();

        int diff;
        while(true) {
            diff = hamster.leftFloor() - hamster.rightFloor();
            hamster.wheels(30, 30);
            if(diff < 0) { // 중앙에서 오른쪽에 있으면 왼쪽으로 회전한다.
                hamster.wheels(-30, 30);
            } else if(diff > 0) { // 중앙에서 왼쪽에 있으면 오른쪽으로 회전한다.
                hamster.wheels(30, -30);
            }

            Runner.wait(10); // 너무 빨리 반복하지 않도록 한다.
        }
    }
}

왼쪽 바닥 센서 값과 오른쪽 바닥 센서 값의 차이인 diff가 음수이면 햄스터 로봇이 중앙에서 오른쪽에 있고, 양수이면 왼쪽에 있다고 할 수 있습니다. 이제 햄스터 로봇의 위치에 따라 바퀴의 속도를 다르게 해봅시다. 규칙은 다음과 같습니다.

왼쪽 바퀴와 오른쪽 바퀴의 관점에서 정리하면 다음과 같습니다.

이를 수식으로 표현하면 다음과 같습니다.

햄스터 로봇이 앞으로 이동하는 것은 우리가 자동차를 운전할 때 가속 폐달을 밟는 것과 같고, diff 값에 따라 왼쪽, 오른쪽으로 회전하는 것은 자동차의 운전대를 돌리는 것과 같습니다. 이 두 가지를 결합하면 왼쪽 바퀴의 속도와 오른쪽 바퀴의 속도는 다음과 같이 됩니다.

여기서 고민이 한 가지 있습니다. 가속 폐달과 운전대, 두 가지를 어느 정도 비율로 결합해야 할까요? 가속 폐달 값은 30으로 유지한다고 했을 때 운전대를 얼마나 빨리 돌려야 할까요? 햄스터 로봇이 길에서 조금 벗어났다고 해서 운전대를 너무 빨리 돌리면 좌우로 왔다갔다 불안정하게 움직일 것 같습니다. 그렇다고 운전대를 너무 천천히 돌리면 90도 꺾어진 길에서 빠르게 회전하지 못할 것 같습니다. 적절한 값이 필요한데 우선 0.4로 해보도록 하겠습니다.

코드로 작성하면 다음과 같이 됩니다.

import org.roboid.hamster.Hamster;
import org.roboid.runtime.Runner;

public class Controller {
    public static void main(String[] args) {

        Hamster hamster = new Hamster();

        int diff;
        while(true) {
            diff = hamster.leftFloor() - hamster.rightFloor();
            hamster.wheels(30 + diff * 0.4, 30 - diff * 0.4);

            Runner.wait(10); // 너무 빨리 반복하지 않도록 한다.
        }
    }
}

90도 꺾어진 길을 잘 따라가는지 실험을 통해 확인해 보도록 합시다. 90도 꺾어진 부분에서 회전을 잘 하지 못하면 0.4의 값을 조금 더 크게 하고, 직선을 주행할 때 좌우로 왔다갔다 하면 0.4의 값을 조금 더 작게 하면 됩니다. 곱하는 숫자 0.4를 변경하면서 햄스터 로봇의 움직임이 어떻게 달라지는지 살펴 보도록 합시다.
정지선 지키기
다음 그림과 같이 검은색 선의 마지막에 정지선을 그리고, 햄스터 로봇이 검은색 선을 따라 주행하다가 정지선을 만나면 정지하도록 해봅시다.

라인 트레이서 실습판 내려 받기 PDF PPT

검은색 선을 따라 주행하는 방법은 앞서 설명한 것과 같고, 정지선을 감지하여 정지하는 것만 추가하면 됩니다. 햄스터 로봇이 정지선을 만나면 양쪽 바닥 센서가 모두 검은색 선 위에 있게 되므로, 양쪽 바닥 센서의 값이 모두 50보다 작으면 정지선 위에 있는 것으로 판단할 수 있습니다. 하지만 90도 꺾이는 부분에서 순간적으로 양쪽 바닥 센서가 모두 검은색 선 위에 올라갈 수도 있기 때문에, 양쪽 바닥 센서의 값이 모두 50보다 작다고 해서 무조건 정지하면 90도 꺾이는 부분을 따라 돌다가 햄스터 로봇이 정지할 수도 있습니다.

정지선 근처에 햄스터 로봇을 올려 놓고 정지선을 지나 갈 때 양쪽 바닥 센서가 모두 50보다 작은 상태를 얼마나 오래 동안 유지하는지 세어 봅시다.

import org.roboid.hamster.Hamster;
import org.roboid.runtime.Runner;

public class Controller {
    public static void main(String[] args) {

        Hamster hamster = new Hamster();

        int leftFloor, rightFloor, diff;
        int count = 0;
        while(true) {
            leftFloor = hamster.leftFloor();
            rightFloor = hamster.rightFloor();

            if(leftFloor < 50 && rightFloor < 50) { // 정지선 위에 있으면
                ++ count;
                System.out.println(count);
            } else { // 정지선이 아닌 경우 카운트를 0으로 초기화한다.
                count = 0;
            }

            diff = leftFloor - rightFloor;
            hamster.wheels(30 + diff * 0.4, 30 - diff * 0.4);

            Runner.wait(10); // 너무 빨리 반복하지 않도록 한다.
        }
    }
}

햄스터 로봇이 주행하는 속도에 따라 다를 수 있는데 실험 결과 12로 측정되었다고 하면 2로 나누어 6을 기준으로 정지선을 판단하도록 합시다. 이제 정지선으로 판단되면 햄스터 로봇을 정지하고 break문으로 무한 반복문을 빠져 나와 실행을 종료하면 됩니다. 햄스터 로봇을 다시 출발 지점에 올려 놓고 실행해 봅시다.

import org.roboid.hamster.Hamster;
import org.roboid.runtime.Runner;

public class Controller {
    public static void main(String[] args) {

        Hamster hamster = new Hamster();

        int leftFloor, rightFloor, diff;
        int count = 0;
        while(true) {
            leftFloor = hamster.leftFloor();
            rightFloor = hamster.rightFloor();

            if(leftFloor < 50 && rightFloor < 50) { // 정지선 위에 있으면
                ++ count;
            } else { // 정지선이 아닌 경우 카운트를 0으로 초기화한다.
                count = 0;
            }

            if(count > 6) {
                hamster.stop();
                break;
            } else {
                diff = leftFloor - rightFloor;
                hamster.wheels(30 + diff * 0.4, 30 - diff * 0.4);
            }

            Runner.wait(10); // 너무 빨리 반복하지 않도록 한다.
        }
    }
}
브레이튼버그의 로봇으로 라인 트레이서 만들기
브레이튼버그의 로봇 중에서 내성적인 로봇을 약간 수정하면 라인 트레이서를 만들 수 있습니다.

라인 트레이서 실습판 내려 받기 PDF PPT

먼저 근접 센서를 바닥 센서로 바꾸도록 합시다. 바닥 센서의 값은 하얀색 종이 위에서 100에 가까운 값이므로 바퀴의 속도가 너무 빠르게 됩니다. 속도를 3으로 나누도록 합시다.

import org.roboid.hamster.Hamster;
import org.roboid.runtime.Runner;

public class Controller {
    private static int denoise(int value) {
        return (value < 10) ? 0 : value; // 값이 작으면 무시한다.
    }

    public static void main(String[] args) {

        Hamster hamster = new Hamster();

        int leftSpeed, rightSpeed;
        while(true) {
            // 왼쪽 바닥 센서 값으로 왼쪽 바퀴의 속도를, 오른쪽 바닥 센서 값으로 오른쪽 바퀴의 속도를 설정한다.
            leftSpeed = denoise(hamster.leftFloor()) / 3;
            rightSpeed = denoise(hamster.rightFloor()) / 3;

            hamster.wheels(leftSpeed, rightSpeed);

            Runner.wait(10); // 너무 빨리 반복하지 않도록 한다.
        }
    }
}

햄스터 로봇의 중앙이 검은색 선 위의 중앙에 있도록 올려 놓고 작성한 코드를 실행해 봅시다. 라인 트레이서가 가능한 이유를 생각해 봅시다.
목차
Hardware
  1. Hamster robot
  2. USB dongle
Preparation for Class
  1. Software installation
  2. Robot to PC connection
  3. Run example files
Basic
  1. Create a new project
  2. Work in the cloud PC environment (optional)
  3. Move on a board #1 (sequence, repetition)
  4. Move and turn
  5. Turn on LEDs and make a sound
  6. Command in order repeatedly
  7. Use proximity sensors
  8. Move on a board #2 (repeat while)
  9. Use floor sensors
  10. Use light and acceleration sensors
  11. Braitenberg's robot
Advanced
  1. Create a board game
  2. Keyboard events
  3. Graphical interface
  4. Line following with one sensor
  5. Line following with two sensors
  6. Follow hamster friends (2 people 1 pair)
  7. Follow a wall
  8. Imitating a robot cleaner
  9. Line following at intersection
  10. Escape a maze
Extension Kit
  1. Assembling
  2. Pin/socket layout
  3. Digital input - Press button to beep
  4. Digital output - LED is on when it is dark
  5. Digital output - flashing twinkling LED
  6. Digital output - LED is on in tilting direction
  7. Analog input - Rotating the potentiometer changes the pitch.
  8. Analog input - please do not let it get hot
  9. Analog Input - Move along the light
  10. PWM output - LED light gently brighter and darker
  11. PWM output - LED candle shakes in the wind
  12. Analog servo output - hamster robot has tail
More Advanced
  1. Behavior based robot control
  2. Path navigation
  3. Swap
Copyright Robot Software Education Institute. All rights reserved.
Please contact prof. Kwang-Hyun Park (akaii@kw.ac.kr) if you have any problem.