Koh Young Technology

AOI Tutorial

Learn step-by-step Greedy FOV placement with a small example (10 components)
📘 This is a supplementary document. For official contest rules, see Contest Rules ★
1 Input Data
Let's examine the example PCB data. 10 components are placed on a 160×120mm PCB.

PCB Component Layout

Fiducial (type=2)
Barcode (type=1)
Normal (type=0)
Side (side=1)
Side FOV (fov/2)
Step Region (step=1)
🛠
Python 3.11 required for kohyoung_solver
The provided kohyoung_solver package only works in a Python 3.11 environment. Other versions (3.10, 3.12, etc.) may cause import errors, so be sure to set up a virtual environment with Python 3.11.
conda create -n iccas_contest python=3.11 or pyenv install 3.11
tl_xtl_ybr_xbr_ytypesidesteptime
012818142000
113010145221000
2204532550000.3
3353845480000.3
4253535430100.3
5704085520000.3
6805595650000.3
7685078600100.3
811085125950010.3
9128901421000010.3
💡 type: 2=Fiducial, 1=Barcode, 0=Normal  |  side: If 1, Side camera capture is required (must fit within half-size FOV area)  |  step: If 1, the component is in a step region
pcb_xpcb_yfov_xfov_y
1601205050
💡PCB size 160×120mm, FOV size 50×50mm
capture_timerecon_timeside_capture_timemax_corev_xv_ya_xa_y
0.50.50.2581000100098009800
💡Capture 0.5s, Reconstruction 0.5s, Side additional 0.25s, Max 8 cores, Movement speed 1000mm/s, Acceleration 9800mm/s²
Note: v_x, v_y (200–1000) and a_x, a_y (4900–9800) vary per dataset. This example uses v=1000, a=9800.
tl_xtl_ybr_xbr_y
010075160120
💡Step region: bottom-right of PCB (100,75)~(160,120). Components within this region have step=1
2 Step-by-Step Greedy Solution
Follow along as we place FOVs one by one using the Greedy algorithm. Click the buttons below.

Initial State

0
Covered Components
10
Unassigned Components
0
Placed FOVs

Greedy Algorithm Pseudocode

Algorithm: Greedy FOV Placement ──────────────────────────────────── Phase 1: Fiducial-Only FOVs 1. FOR EACH comp WHERE type == 2: 2. Create FOV(center = comp.center, comps = [comp]) // 1 Fiducial per FOV Phase 2: Origin-Distance Greedy Placement 3. remaining ← sort unassigned components by distance to (0,0) 4. FOR EACH seed IN remaining: 5. fov_center ← (seed.tl_x + fov_w/2, seed.tl_y + fov_h/2) // component at FOV top-left 6. fov_center ← clamp to PCB boundary 7. covered ← all same-step unassigned components fully inside FOV 8. Note: side=1 components checked against Side FOV (fov/2) area 9. Create FOV(fov_center, covered) Phase 3: Big Component Tiling 10. FOR EACH comp WHERE size > FOV: 11. Place ceil(w/fov_w) × ceil(h/fov_h) grid of FOVs Phase 4: Path Ordering 12. type descending groups (2 → 1 → 0) × nearest neighbor ordering
3 Coverage Error Demo
What happens when a component is not included in any FOV? See how errors are displayed in the Checker and Viewer.

Correct Result (Cover: PASS)

Combined View — Normal
,x,y,comp_idx 0,15,11,[0] 1,138,16,[1] 2,30,40,"[2, 3, 4]" 3,74,55,"[5, 6, 7]" 4,126,93,"[8, 9]"

Error Result (Cover: FAIL)

Combined View — Error
,x,y,comp_idx 0,15,11,[0] 1,138,16,[1] 2,30,40,"[2, 3, 4]" 3,95,46,"[5, 6]" ← #7 missing! 4,126,93,"[8, 9]"
Component #7 is not included in any FOV.
When the Checker's cover_all_components() detects uncovered components:
• The component's error flag is set to 1
• The component is displayed in red (#d63031) in the Viewer
• The error FOV is shown with a red border (#cd4547) and red background (rgba(205,99,71,0.2))
• The Cover result is shown as FAIL
💡
Other error cases:
• If components with different step values are mixed in the same FOV → Cover FAIL
• If a component assigned to an FOV is outside the FOV area → Cover FAIL
• If a side=1 component is outside the Side FOV (fov/2 = 25×25) area → Cover FAIL
• If the FOV order is not in descending type order → Order FAIL (e.g., Barcode → Fiducial order)
4 Final Result
Review the final output_fov.csv from the Greedy solution and the pipeline inspection process.

output_fov.csv

x (center)y (center)comp_idxFOV TypeSide Component?
01511[0]2 (Fid)-
113816[1]1 (BC)-
23040[2, 3, 4]0side=1 (#4)
37455[5, 6, 7]0side=1 (#7)
412693[8, 9]0-
💡 FOV Type is not explicitly specified in the output. The Checker automatically determines it as the maximum type value among components in comp_idx.
Inspection order rule: FOV 0(type=2) ≥ FOV 1(type=1) ≥ FOV 2(type=0) ≥ FOV 3(type=0) ≥ FOV 4(type=0) → Order: PASS

Pipeline Inspection Process (Concept)

The AOI equipment uses a pipeline structure to perform capture, reconstruction, and inspection simultaneously. FOVs are processed in order, with reconstruction/inspection of the previous FOV overlapping with the capture of the next FOV.

💡 The exact CT is automatically calculated by the Checker, considering movement time (trapezoidal velocity profile based on v and a), Side capture time, inspection time distribution, etc. Run python 2_run.py --solver greedy to compute the CT. Then run python 3_view.py --solver greedy --open to generate and open the result Viewer (result.html).

Verification Summary

PASS
Cover
PASS
Order
5
FOVs
10
Components
All 10 components are covered with 5 FOVs. Greedy is a simple but reasonable baseline solution. For better CT, consider fine-tuning FOV positions, optimizing movement paths, and using component clustering algorithms.
5 Penalty Solver — Constraint Violation Behavior
When components are not included in any FOV or the inspection order is violated, the Checker automatically runs penalty_solver to recalculate the CT.

How the Penalty Solver Works

1. Constraint violation detected: If cover_all_components() or fov_inspection_order() returns FAIL, the penalty_solver is triggered.

2. Solver output discarded: The participant's output_fov.csv is completely ignored, and FOVs are re-placed from scratch using the greedy baseline solution below.

3. Greedy baseline solution: Fiducial-only FOVs → origin-distance sorted component placement at FOV top-left → absorb same-step neighbors → big component tiling → nearest neighbor path ordering.

Algorithm: Penalty Solver (Greedy Baseline) ──────────────────────────────────── 1. Fiducial(type=2) → individual FOV each // 1 per FOV 2. remaining ← unassigned components, sorted by dist to (0,0) 3. FOR EACH seed IN remaining: 4. fov_center ← (seed.tl + fov_size/2) // component at FOV top-left 5. covered ← all same-step unassigned comps inside FOV 6. Big components → tiling grid FOVs 7. type descending (2→1→0) × nearest neighbor order 8. RETURN ordered FOVs → Checker recalculates CT
⚠ Penalty Effect
• Your optimization results are completely discarded and replaced with the greedy baseline solution.
• The greedy baseline is reasonable but far from optimal, so CT will be significantly higher than a well-tuned solver.
• Finding better FOV placements while satisfying all constraints is the core challenge of this contest.
💡
Behavior in code: In 2_run.py, if cover or order is False, c.penalty_solver() is automatically called.
c = Checker(path) cover = c.cover_all_components() order = c.fov_inspection_order() if not cover or not order: c.penalty_solver() # Discard solver output → replace with greedy baseline ct = c.compute_ct() # Recalculate CT based on penalty FOVs
Always verify Cover: PASS and Order: PASS! When penalty_solver is applied, your optimization is discarded. Be sure to check the Cover/Order results in the 2_run.py output.
Koh Young Technology

AOI 문제풀이 튜토리얼

소규모 예제(10개 부품)로 배우는 Greedy FOV 배치
📘 이 문서는 보조 자료입니다. 공식 대회 규칙은 대회규칙 ★을 참고하세요.
1 입력 데이터 확인
예제 PCB 데이터를 살펴봅니다. 160×120mm PCB 위에 10개 부품이 배치되어 있습니다.

PCB 부품 배치도

Fiducial (type=2)
Barcode (type=1)
Normal (type=0)
Side (side=1)
Side FOV (fov/2)
단차 영역 (step=1)
🛠
kohyoung_solver 사용 시 Python 3.11 필요
제공되는 kohyoung_solver 패키지는 Python 3.11 환경에서만 동작합니다. 다른 버전(3.10, 3.12 등)에서는 import 오류가 발생할 수 있으니, 반드시 Python 3.11로 가상환경을 구성하세요.
conda create -n iccas_contest python=3.11 또는 pyenv install 3.11
tl_xtl_ybr_xbr_ytypesidesteptime
012818142000
113010145221000
2204532550000.3
3353845480000.3
4253535430100.3
5704085520000.3
6805595650000.3
7685078600100.3
811085125950010.3
9128901421000010.3
💡 type: 2=Fiducial, 1=Barcode, 0=Normal  |  side: 1이면 Side 카메라 촬영 필요 (FOV의 절반 크기 영역에 들어와야 함)  |  step: 1이면 단차 영역 부품
pcb_xpcb_yfov_xfov_y
1601205050
💡PCB 크기 160×120mm, FOV 크기 50×50mm
capture_timerecon_timeside_capture_timemax_corev_xv_ya_xa_y
0.50.50.2581000100098009800
💡촬영 0.5s, 복원 0.5s, Side 추가 0.25s, 최대 8코어, 이동속도 1000mm/s, 가속도 9800mm/s²
참고: v_x, v_y (200~1000), a_x, a_y (4900~9800)는 데이터셋마다 다릅니다. 이 예시는 v=1000, a=9800 기준입니다.
tl_xtl_ybr_xbr_y
010075160120
💡단차 영역: PCB 우하단 (100,75)~(160,120). 이 영역 안의 부품은 step=1
2 Step-by-Step Greedy 풀이
Greedy 알고리즘으로 FOV를 하나씩 배치하는 과정을 따라가 봅니다. 아래 버튼을 클릭하세요.

초기 상태

0
커버된 부품
10
미할당 부품
0
배치된 FOV

Greedy 알고리즘 의사 코드

Algorithm: Greedy FOV Placement ──────────────────────────────────── Phase 1: Fiducial 단독 FOV 1. FOR EACH comp WHERE type == 2: 2. FOV 생성(center = comp.center, comps = [comp]) // 1 Fiducial per FOV Phase 2: 원점 거리순 Greedy 배치 3. remaining ← 미할당 부품을 (0,0) 거리 오름차순 정렬 4. FOR EACH seed IN remaining: 5. fov_center ← (seed.tl_x + fov_w/2, seed.tl_y + fov_h/2) // 부품이 FOV 좌상단 6. fov_center ← PCB 경계 내로 클램핑 7. covered ← FOV 안에 완전히 들어가는 같은 step 미할당 부품 8. 주의: side=1 부품은 Side FOV(fov/2) 영역 기준 체크 9. FOV 생성(fov_center, covered) Phase 3: Big Component 타일링 10. FOR EACH comp WHERE size > FOV: 11. ceil(w/fov_w) × ceil(h/fov_h) 격자 FOV 배치 Phase 4: 경로 결정 12. type 내림차순 그룹(2 → 1 → 0) × nearest neighbor 순서
3 커버리지 에러 시연
부품이 FOV에 포함되지 않으면 어떻게 될까요? Checker와 Viewer에서의 에러 표시를 확인합니다.

정상 결과 (Cover: PASS)

Combined View — 정상
,x,y,comp_idx 0,15,11,[0] 1,138,16,[1] 2,30,40,"[2, 3, 4]" 3,74,55,"[5, 6, 7]" 4,126,93,"[8, 9]"

에러 결과 (Cover: FAIL)

Combined View — 에러
,x,y,comp_idx 0,15,11,[0] 1,138,16,[1] 2,30,40,"[2, 3, 4]" 3,95,46,"[5, 6]" ← #7 누락! 4,126,93,"[8, 9]"
부품 #7이 어떤 FOV에도 포함되지 않았습니다.
Checker의 cover_all_components()가 미커버 부품을 감지하면:
• 해당 부품의 error 플래그가 1로 설정됩니다
• Viewer에서 해당 부품이 빨간색(#d63031)으로 표시됩니다
• 에러 FOV는 빨간 테두리(#cd4547)빨간 배경(rgba(205,99,71,0.2))으로 표시됩니다
• Cover 결과가 FAIL로 표시됩니다
💡
기타 에러 케이스:
• FOV 안에 step이 다른 부품이 섞여 있으면 Cover FAIL
• FOV에 할당된 부품이 FOV 영역 밖에 있으면 Cover FAIL
side=1 부품이 Side FOV(fov/2 = 25×25) 영역 밖에 있으면 Cover FAIL
• FOV 순서가 type 내림차순이 아니면 Order FAIL (예: Barcode → Fiducial 순서)
4 최종 결과
Greedy 풀이의 최종 output_fov.csv와 파이프라인 검사 과정을 확인합니다.

output_fov.csv

x (중심)y (중심)comp_idxFOV TypeSide 부품?
01511[0]2 (Fid)-
113816[1]1 (BC)-
23040[2, 3, 4]0side=1 (#4)
37455[5, 6, 7]0side=1 (#7)
412693[8, 9]0-
💡 FOV Type은 output에 명시하지 않습니다. Checker가 comp_idx의 부품 type 중 최대값으로 자동 결정합니다.
검사 순서 규칙: FOV 0(type=2) ≥ FOV 1(type=1) ≥ FOV 2(type=0) ≥ FOV 3(type=0) ≥ FOV 4(type=0) → Order: PASS

Pipeline 검사 과정 (개념)

AOI 장비는 파이프라인 구조로 촬상·복원·검사를 동시에 진행합니다. FOV가 순서대로 처리되며, 이전 FOV의 복원/검사와 다음 FOV의 촬상이 겹칩니다.

💡 정확한 CT는 이동 시간(v, a 기반 사다리꼴 속도 프로파일), Side 촬영 시간, 검사 시간 분배 등을 고려하여 Checker가 자동으로 계산합니다. python 2_run.py --solver greedy로 CT를 산출하고, python 3_view.py --solver greedy --open으로 결과 Viewer(result.html)를 생성·확인할 수 있습니다.

검증 요약

PASS
Cover
PASS
Order
5
FOVs
10
Components
5개 FOV로 10개 부품을 모두 커버했습니다. Greedy는 간단하지만 합리적인 기본 풀이입니다. 더 나은 CT를 위해서는 FOV 위치 미세 조정, 이동 경로 최적화, 부품 클러스터링 알고리즘 등을 고려해 보세요.
5 Penalty Solver — 제약 위반 시 동작
부품이 FOV에 포함되지 않거나 검사 순서를 위반하면, Checker가 penalty_solver를 자동 실행하여 CT를 재계산합니다.

Penalty Solver 동작 방식

1. 제약 위반 감지: cover_all_components() 또는 fov_inspection_order()FAIL이면 penalty_solver가 동작합니다.

2. Solver 출력 폐기: 참가자의 solver가 출력한 output_fov.csv완전히 무시하고, 아래 그리디 기본 풀이로 처음부터 FOV를 재배치합니다.

3. 그리디 기본 풀이: Fiducial 단독 FOV → 원점 거리순으로 부품을 FOV 좌상단에 배치 → 같은 FOV에 들어가는 부품 흡수 → Big component 타일링 → Nearest neighbor 경로 결정.

Algorithm: Penalty Solver (Greedy Baseline) ──────────────────────────────────── 1. Fiducial(type=2) → 각각 단독 FOV // 1 per FOV 2. remaining ← 미할당 부품, (0,0) 거리순 정렬 3. FOR EACH seed IN remaining: 4. fov_center ← (seed.tl + fov_size/2) // 부품이 FOV 좌상단 5. covered ← FOV 안 같은 step 미할당 부품 전부 흡수 6. Big component → 타일링 격자 FOV 7. type 내림차순(2→1→0) × nearest neighbor 순서 8. RETURN ordered FOVs → Checker가 CT 재계산
⚠ Penalty 효과
• 참가자의 최적화 결과가 전부 폐기되고 기본 그리디 풀이로 대체됩니다.
• 그리디 기본 풀이는 합리적이지만 최적과는 거리가 있으므로, 잘 만든 solver 대비 CT가 크게 증가합니다.
• 제약 조건을 만족하면서 더 좋은 FOV 배치를 찾는 것이 대회의 핵심입니다.
💡
코드에서의 동작: 2_run.py에서 cover 또는 order가 False이면 자동으로 c.penalty_solver()가 호출됩니다.
c = Checker(path) cover = c.cover_all_components() order = c.fov_inspection_order() if not cover or not order: c.penalty_solver() # solver 출력 폐기 → 그리디 기본 풀이로 대체 ct = c.compute_ct() # penalty FOV 기준 CT 재계산
항상 Cover: PASS, Order: PASS를 확인하세요! penalty_solver가 적용되면 정상 최적화 대비 CT가 수배~수십배 증가합니다. 2_run.py 출력에서 Cover/Order 결과를 반드시 점검하세요.