From 4d55d2f950bde044ab499ba9b543b6be1ecdbecc Mon Sep 17 00:00:00 2001 From: chyoo Date: Thu, 20 Aug 2026 13:22:00 +0900 Subject: [PATCH] add guide.. --- .../InstructorLearningGuideModal.tsx | 187 ++++++++++++++++++ src/layouts/InstructorLayout.tsx | 6 +- 2 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 src/components/InstructorLearningGuideModal.tsx diff --git a/src/components/InstructorLearningGuideModal.tsx b/src/components/InstructorLearningGuideModal.tsx new file mode 100644 index 0000000..d709243 --- /dev/null +++ b/src/components/InstructorLearningGuideModal.tsx @@ -0,0 +1,187 @@ +import { useEffect } from 'react'; +import { createPortal } from 'react-dom'; +import { BookOpenText, X } from 'lucide-react'; + +type GuideLesson = { + code: string; + title: string; + guidance: string; + tools: string; +}; + +type GuideDay = { + title: string; + lessons: GuideLesson[]; +}; + +const GUIDE_DAYS: GuideDay[] = [ + { + title: 'DAY 1. ROS2 & Simulation', + lessons: [ + { + code: '01', + title: 'ROS2 기초', + guidance: 'Gazebo와 TurtleBot3 또는 MK-mini를 실행하고 /odom, /tf, /scan, /cmd_vel, /map의 역할을 설명합니다. Goal 지정은 Nav2 Action의 실제 사례로 사용하고, TF tree는 터미널에서 map-odom-base_link 관계를 확인합니다.', + tools: '모듈 리스트, ROS 연결상태, 터미널, 시각화, RViz2', + }, + { + code: '02', + title: 'Sensor Processing', + guidance: 'NAV2-Perception을 실행해 LiDAR /scan을 확인하고, 퍼셉션 입력 토픽과 TF, LiDAR 장착 위치 설정을 읽고 변경 전후를 비교합니다.', + tools: 'NAV2-Perception, 코드 편집, 파라미터 설정, 시각화 Sensor', + }, + { + code: '03', + title: 'Simulation 환경 구축 Part 1', + guidance: '기본 월드와 실내 환경을 비교한 뒤 커스텀 맵에서 벽과 장애물을 배치합니다. Gazebo 레이저가 환경을 어떻게 인식하는지 함께 확인합니다.', + tools: '맵 설정, 커스텀 맵 편집, NAV2-Gazebo, 시각화', + }, + { + code: '04', + title: 'Simulation 환경 구축 Part 2', + guidance: '동일한 맵에서 TurtleBot3와 MK-mini를 번갈아 실행해 차동구동과 애커만 조향 차량의 차이를 비교합니다.', + tools: 'NAV2-TurtleBot3Burger, NAV2-MKMini, Gazebo, 시각화', + }, + ], + }, + { + title: 'DAY 2-3. SLAM & Localization', + lessons: [ + { + code: '05', + title: 'LiDAR SLAM', + guidance: '차량 또는 Gazebo, Perception, SLAM, 시각화 순서로 실행합니다. /scan, /odom, /map, /tf 관계를 확인하고 결과 맵을 저장합니다.', + tools: 'NAV2-SLAM, NAV2-Perception, 맵 저장, 시각화, ROS 연결상태', + }, + { + code: '06', + title: 'LiDAR Localization', + guidance: '저장한 맵을 선택하고 Map, Localization, Planning, Control 조합으로 전환합니다. Pose 지정과 Goal 주행으로 map 기준 위치 보정을 확인합니다.', + tools: 'NAV2-Map, NAV2-Localization, NAV2-Planning, NAV2-Control, 시각화 Pose/Goal', + }, + { + code: '07', + title: 'Parameter 튜닝', + guidance: '모듈 파라미터와 설정 파일을 바꾸고 로그, 토픽, 주행 경로의 변화를 비교합니다. 수정 근거와 결과를 Git 변경사항으로 남기게 합니다.', + tools: '파라미터 설정, 코드 편집, 로그, 소스 제어', + }, + ], + }, + { + title: 'DAY 4-5. Planning & Control', + lessons: [ + { + code: '09', + title: 'Occupancy Map', + guidance: 'SLAM으로 생성된 2D 점유맵을 Nav2 장애물 인식 기준으로 설명합니다. 커스텀 월드, LiDAR 스캔, 저장된 맵을 비교해 맵 품질이 주행에 미치는 영향을 확인합니다.', + tools: '커스텀 맵, NAV2-SLAM, NAV2-Map, 시각화', + }, + { + code: '10', + title: 'Planning', + guidance: 'Goal을 지정하고 NAV2-Planning 경로를 확인합니다. TurtleBot3와 MK-mini의 경로 차이, 장애물 회피, 전진 중심과 후진 허용 설정을 비교합니다.', + tools: 'NAV2-Planning, 시각화 Path/Goal, ROS 연결상태', + }, + { + code: '11', + title: 'Control', + guidance: 'NAV2-Control의 /cmd_vel 흐름과 차량 조향을 연결해 설명합니다. 시뮬레이션으로 검증한 뒤 원격 플랫폼 제어권을 배정해 저속 실차 검증을 진행합니다.', + tools: 'NAV2-Control, NAV2-MKMini, 원격 플랫폼 워크스페이스, 시각화', + }, + ], + }, +]; + +type InstructorLearningGuideModalProps = { + open: boolean; + onClose: () => void; +}; + +export function InstructorLearningGuideModal({ open, onClose }: InstructorLearningGuideModalProps) { + useEffect(() => { + if (!open) return; + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Escape') onClose(); + }; + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [onClose, open]); + + if (!open) return null; + + return createPortal( +
{ + if (event.currentTarget === event.target) onClose(); + }} + > +
+
+
+ + +
+

LMS 학습 운영 가이드

+

ROS2, 시뮬레이션, SLAM, Planning 및 Control 실습 운영 기준

+
+
+ +
+ +
+
+

강사 운영 흐름

+
    +
  1. 1. 수업 전 구성커리큘럼, 학습단계, 사용 모듈, 기본 파라미터를 구성하고 그룹에 플랫폼을 배정합니다.
  2. +
  3. 2. 수업 중 검증모듈 실행, ROS 연결상태, 코드 수정, 시각화 순서로 원인을 확인합니다.
  4. +
  5. 3. 수업 후 기록저장 맵, 파라미터, Git 변경사항, 주행 결과를 제출 기준으로 남깁니다.
  6. +
+
+ +
+ {GUIDE_DAYS.map((day) => ( +
+

{day.title}

+
+ {day.lessons.map((lesson, index) => ( +
+
+ {lesson.code} +

{lesson.title}

+
+

{lesson.guidance}

+

LMS 사용 기능{lesson.tools}

+
+ ))} +
+
+ ))} +
+ +
+

팀 프로젝트 운영

+

3~4인 그룹마다 문제 상황과 맵을 정하고, 강사는 그룹별 원격 플랫폼 사용 시간을 배정합니다. 학생은 커스텀 맵에서 먼저 SLAM, Localization, Planning, Control을 검증한 뒤 원격 MK-mini에서 저속 검증합니다. 질의응답 강사 탭에는 주행 실패 원인, 파라미터 변경 근거, 토픽 상태 캡처를 남기게 합니다.

+
+ +
+

현재 과정에서 제외할 항목

+
    +
  • 카메라 기반 SLAM과 딥러닝 객체 인식은 현재 정식 LMS 모듈 흐름으로 구성되어 있지 않습니다.
  • +
  • 저장된 3D SLAM 결과를 로컬 Gazebo의 물리 충돌 월드로 재현하는 실습은 지원하지 않습니다.
  • +
+
+
+ +
+ +
+
+
, + document.body, + ); +} diff --git a/src/layouts/InstructorLayout.tsx b/src/layouts/InstructorLayout.tsx index c4bea5c..b74a1c8 100644 --- a/src/layouts/InstructorLayout.tsx +++ b/src/layouts/InstructorLayout.tsx @@ -23,6 +23,7 @@ import { io } from 'socket.io-client'; import type { Socket } from 'socket.io-client'; import { ConfirmDialog } from '../components/ConfirmDialog'; import { InstructorChatPanel } from '../components/InstructorChatPanel'; +import { InstructorLearningGuideModal } from '../components/InstructorLearningGuideModal'; import { useToast } from '../components/ToastProvider'; import autonomyLogo from '../assets/icon.png'; import { useProfileAvatar } from '../hooks/useProfileAvatar'; @@ -78,6 +79,7 @@ export function InstructorLayout({ session, route, onNavigate, onLogout, courses const [notificationTab, setNotificationTab] = useState<'unread' | 'read'>('unread'); const [notificationPage, setNotificationPage] = useState(1); const [chatOpen, setChatOpen] = useState(false); + const [guideOpen, setGuideOpen] = useState(false); const [hasUnreadChat, setHasUnreadChat] = useState(false); const [notifications, setNotifications] = useState([]); const profileRef = useRef(null); @@ -503,6 +505,8 @@ export function InstructorLayout({ session, route, onNavigate, onLogout, courses onConfirm={() => void handleAvatarDelete()} /> + setGuideOpen(false)} /> + -