TIKI-MINI로 시작하는 ROS2 실무
목차

2. 시뮬레이션

2.14미니 프로젝트

2.7장과 2.8장에서 만든 부품을 합칠 차례입니다. 이번 과제는 course 월드에서 장애물 세 개를 피해 목표 원까지 가는 노드를 완성하는 것입니다. 정답은 하나가 아니고 점수는 아래 채점표에 따라 나옵니다.

미션

7 x 5 m 코스(x -1~6, y -2.5~2.5)의 원점에서 출발해 (5.3, 0)에 있는 반지름 0.4 m 목표 원에 들어갑니다. 코스 중간에는 반지름 0.3 m 원통 세 개가 서 있습니다.

장애물중심 (x, y)반지름
1(1.5, 0.3)0.3 m
2(3.0, -0.5)0.3 m
3(4.3, 0.5)0.3 m

규칙

  • 제한 시간은 60초입니다(코드 상자의 duration).
  • 속도 제한은 켜져 있습니다. linear.x 0.2 m/s, angular.z 0.85 rad/s로 잘리고, 명령이 0.5초 끊기면 로봇이 섭니다.
  • 출발 자세는 (0, 0, 0)이고 course에서는 /odom 좌표가 월드 좌표와 같습니다.
  • 충돌은 시뮬레이터가 셉니다. 로봇(반지름 0.12 m)이 벽이나 장애물에 닿아 이동이 막히면 1회이고, 막힌 상태가 이어지는 동안은 1회로 칩니다. 이동 명령이 0이 되었다가 다시 막히면 1회 더 셉니다.
  • 도착은 로봇 중심이 목표 원 안에 들어온 순간입니다. 도착 시각이 기록되며, 도착한 뒤 계속 달려도 감점은 없습니다.
  • 사용할 토픽은 /scan, /odom, /cmd_vel입니다. 월드 정보를 코드에 직접 쓰는 것은 허용합니다. 장애물 위치를 아는 경유점 방식이 이 과제에서 가장 쉬운 길입니다.

실행이 끝나면 출력 탭 맨 아래에 미션 결과: 도착 (29.2초), 충돌 0회 같은 줄이 붙습니다. 이 줄이 채점 증거입니다. 예외로 중단된 실행에는 이 줄이 붙지 않으므로 점수가 없습니다.

채점표

항목기준배점
도착미션 결과가 도착50
충돌0회 20점, 1회 15점, 2회 10점, 3회 이상 0점20
시간30초 이하 30점, 45초 이하 20점, 60초 이하 10점30

도착하지 못하면 충돌과 시간 점수도 없습니다. 만점은 100점입니다. 기준 숫자는 이 장의 참고 풀이를 측정한 값을 바탕으로 정했고, 선생님이나 학습 모임에서 바꿔 써도 됩니다.

시작 코드

아래 코드는 그대로 실행해도 오류 없이 돕니다. 하지만 cmd에 아무 값도 넣지 않아서 로봇은 서 있습니다. TODO 세 군데를 채우는 것이 과제입니다. 구독 두 개와 10 Hz 타이머, 각도 정리 함수 wrap, 구간 최솟값 함수 sector_min은 이미 들어 있습니다. 1초마다 현재 위치, yaw, 정면, 왼쪽, 오른쪽 거리를 로그로 남깁니다.

LIVE

시작 코드

그대로 실행하면 미션 결과: 미도달, 충돌 0회가 나옵니다. 첫 로그는 x=0.00 y=0.00 yaw=0.00 | 정면 1.23 왼쪽 2.50 오른쪽 2.50입니다. 정면 1.23 m는 장애물 1까지의 거리입니다.

단계별 목표

단계목표확인 방법
1직진하고 스캔을 읽는다TODO 1에 cmd.linear.x = 0.2를 넣는다. 정면 거리가 줄어드는 로그를 본다
2가장 가까운 장애물을 피한다충돌 0회로 장애물 1을 지난다
3목표에 도착한다미션 결과: 도착
4충돌과 시간을 줄인다채점표 점수를 올린다

단계 1처럼 직진만 하면 약 6.1초에 장애물 1에 부딪혀 충돌 1회로 끝납니다(위치 (1.20, 0.0)). 이 지점에서 정면 거리는 0.15 m로 읽히고, 이때부터 inf 처리가 문제가 됩니다. 2.8장의 사각지대를 떠올리세요.

힌트

전략 A: 경유점 따라가기

course에서는 /odom이 월드 좌표라서 장애물 사이로 지나는 점을 미리 정해 두고 순서대로 따라가면 됩니다. 로봇 반지름 0.12 m와 장애물 반지름 0.3 m를 더하면 로봇 중심이 장애물 중심에서 0.42 m 이상 떨어져야 합니다. 여유를 두고 0.5 m 정도를 목표로 하세요. 경유점 방식의 뼈대는 2.7장의 폐루프와 같습니다.

  1. atan2로 목표점 방향을 구하고 wrap(방향 - self.yaw)로 각도 오차를 얻습니다.
  2. 각도 오차에 비례하는 angular.z를 주되 ±0.85를 넘기지 않습니다.
  3. 방향이 어느 정도 맞았을 때만(오차 0.3 rad 미만) 전진합니다.
  4. 점에서 0.15 m 이내로 들어오면 다음 점으로 넘어갑니다.

전략 B: 스캔으로 피하며 목표로

경유점이 없어도 풀립니다. 평소에는 목표 방향으로 가고, 정면이 가까워지면 2.8장의 회피 상태로 바뀌는 방식입니다. 주의할 점이 둘 있습니다.

  • 회피를 끝내자마자 다시 목표를 향해 돌면 정면이 곧바로 또 가까워져서 제자리에서 맴돕니다. 회피가 끝난 뒤 얼마 동안은 목표를 무시하고 직진하세요.
  • 정면 부채꼴을 ±15도처럼 좁게 잡으면 장애물 옆구리에 닿습니다. 2.8장처럼 넓게 잡습니다.

디버깅 요령

  • 로그에 현재 상태와 임계값을 찍어 보세요. 멈췄을 때 front 값이 어떤지 보면 대부분 원인이 나옵니다.
  • 코드 상자 아래 캔버스에서 로봇이 어디서 어떻게 막혔는지 보세요. 충돌 위치는 출력 탭에 collision #1 at (x, y)로 나옵니다.
  • 각도는 wrap으로 항상 -π~π로 정리하세요. 오차가 ±π 근처에서 부호가 뒤집혀 로봇이 반대로 도는 경우가 가장 흔한 버그입니다.

참고 풀이

먼저 직접 시도하세요. 막혔을 때 아래에서 두 전략의 완성 코드를 열어 보세요. 둘 다 시작 코드의 뼈대에서 TODO만 채운 것입니다.

막히면 참고 풀이를 확인하세요

전략 A는 경유점 네 개를 순서대로 따라갑니다. 장애물 1 아래(y = -0.5), 장애물 2 위(y = 0.4), 장애물 3 아래(y = -0.3)를 지그재그로 지나서 목표로 갑니다. 실행 중 장애물 중심과 가장 가까워진 거리는 0.64 m였습니다.

LIVE

참고 풀이 A: 경유점

측정 결과는 미션 결과: 도착 (29.2초), 충돌 0회입니다. 채점표로는 도착 50, 충돌 20, 시간 30으로 100점입니다.

전략 B는 목표를 향해 가다가 정면 ±35도 안에서 0.5 m 이내로 장애물이 보이면 서서 더 트인 쪽으로 돕니다. 정면이 0.9 m 넘게 트이면 2초(20틱) 동안 목표를 무시하고 직진한 뒤 다시 목표를 향합니다.

LIVE

참고 풀이 B: 스캔 회피

이 풀이에서 장애물 중심과 가장 가까워진 거리는 0.50 m입니다. 측정 결과는 미션 결과: 도착 (40.3초), 충돌 0회로 도착 50, 충돌 20, 시간 20의 90점입니다. 경유점 방식보다 11초 느립니다. 회피 때문에 우회하고 제자리 회전이 들어가기 때문입니다.

두 풀이 모두 odomNoise: 0.2(20% 노이즈)로 바꿔 돌렸을 때도 도착했습니다. A는 29.4초, B는 39.8초이고 충돌은 둘 다 0회였습니다. 이 코스에서는 오도메트리가 조금 흔들려도 판정에 지장이 없었습니다.

확장 과제

  1. corridor 월드: 2.8장의 복도 예제는 29.9초에 도착합니다. 이 코드를 더 빠르게, 또는 임계값을 덜 의존하게 고쳐 보세요. 모퉁이를 /odom의 yaw로 판단하는 방법도 있습니다.
  2. 코드 상자의 sim에서 odomNoise를 0.5, 1.0으로 올려 두 풀이가 어디서 무너지는지 찾으세요. 20%까지는 둘 다 버텼습니다.
  3. 경유점 좌표를 한 점씩 바꿔 가며 충돌이 처음 생기는 점을 찾으세요. 장애물 중심 거리 0.42 m가 정말 경계인지 확인하는 실험입니다.

돌아보기: 시뮬에서 되던 코드를 실제 로봇에서 돌리면

이 과제의 두 풀이는 이상적인 세계에서 측정한 값입니다. 실기기로 옮길 때 달라질 항목을 정리해 두면 4.5장과 4.6장이 쉬워집니다.

시뮬레이션실기기에서는참고
/scan 정면이 인덱스 180laser가 yaw π라서 정면이 배열의 양 끝, 좌우 인덱스도 뒤바뀜2.5장, 2.8장, 4.5장
노이즈 없는 라이다위치는 같지만(URDF에서 laser의 x, y는 0) 값이 흔들리고 inf가 섞임2.8장, 4.6장
로봇은 반지름 0.12 m 원실제 로봇 형상은 다르다. 장애물 여유를 더 둔다4.6장
속도 변화 즉시 적용바퀴 PID와 마찰로 지연이 있다2.6장, 4.6장
/odom이 이상적바퀴 미끄러짐으로 오차가 쌓이고 헛돌아도 값이 늘 수 있다2.7장, 4.6장
시간이 가상 시간실제 시간이라 타이머 주기가 흔들린다4.6장

옮기기 전에 코드부터 준비해 두세요. 인덱스는 상수로 박지 말고 각도와 angle_min, angle_increment에서 계산하면 정면이 어디든 한 줄 고치면 됩니다. 임계값(STOP_DIST 등)도 파라미터로 빼 두면 실기기에서 다시 맞추기 쉽습니다(1.5장). 실기기에서 처음 돌릴 때는 속도를 0.1 m/s 이하로 낮추고 로봇을 언제든 멈출 수 있는 상태로 두세요. 이 순서의 점검표는 4.5장, 차이의 정리는 4.6장에 있습니다.

한눈에 정리

  1. 과제는 course 월드에서 장애물 세 개를 피해 (5.3, 0) 반지름 0.4 m 목표 원에 60초 안에 도착하는 것이다.
  2. 채점 증거는 출력 탭의 미션 결과 줄이다. 도착 50점, 충돌 20점, 시간 30점으로 계산한다.
  3. 시작 코드는 /scan, /odom 구독과 10 Hz /cmd_vel 발행이 들어 있고 TODO 세 군데를 채운다.
  4. 직진만 하면 6.14초에 장애물 1에 충돌한다(위치 1.20, 0.0).
  5. 경유점 풀이는 29.2초, 충돌 0회로 100점이었다.
  6. 스캔 회피 풀이는 40.3초, 충돌 0회로 90점이었다. 회피 직후 2초 직진하는 장치가 제자리 맴돌기를 막는다.
  7. 실기기로 옮길 때는 스캔 인덱스, 센서 노이즈, 속도 지연, 오도메트리 오차를 다시 점검한다(4.5장, 4.6장).

확인 문제

Q1. course에서는 /odom 좌표를 월드 좌표처럼 써도 되는데 corridor나 obstacles에서는 왜 안 되나요?

/odom은 로봇의 시작 자세를 (0, 0, 0)으로 잡은 좌표입니다. course는 시작이 월드 원점이라 두 좌표가 같습니다. corridor는 (0.5, 0), obstacles는 (-2, 0)에서 출발하므로 /odom 값에 시작 위치를 더해야 월드 좌표가 됩니다.

Q2. 로봇 반지름 0.12 m와 장애물 반지름 0.3 m가 있을 때 경유점은 장애물 중심에서 최소 얼마나 떨어져야 하나요?

0.12 + 0.3 = 0.42 m 이상입니다. 로봇 중심이 이보다 가까워지면 원이 겹쳐서 충돌로 셉니다. 경로 중간에는 로봇이 완벽히 직선으로 가지 못하므로 0.5 m 정도의 여유를 둡니다.

Q3. 회피 상태를 끝낸 로봇이 제자리에서 맴돌기만 합니다. 원인과 해결책은?

회피를 끝내고 목표 쪽으로 돌면 정면이 다시 장애물을 향해 임계값 아래로 내려가고, 곧바로 회피 상태로 돌아가기 때문입니다. 회피가 끝난 뒤 일정 시간(풀이 B는 2초) 목표를 무시하고 직진해서 장애물 곁을 벗어나게 하면 해결됩니다.

다음 장

2부는 여기까지입니다. 4.1장에서 TIKI-MINI의 하드웨어 구성과 안전수칙을 확인한 뒤, 4.5장에서 이 프로젝트의 코드를 실제 로봇으로 옮깁니다.