Booil Jung

NVIDIA Isaac Sim에서의 산불 연기 체적 시뮬레이션 및 의미론적 라벨링

NVIDIA Isaac Sim에서 산불 연기와 같은 복잡한 물리 현상을 시뮬레이션하고, 이를 인공지능(AI) 모델 학습을 위한 합성 데이터로 활용하는 과정은 플랫폼의 근간을 이루는 여러 핵심 기술에 대한 깊이 있는 이해를 요구한다. 이 과업의 성공적인 수행은 단순히 특정 기능을 사용하는 것을 넘어, Universal Scene Description (USD), PhysX 물리 엔진, 그리고 RTX 렌더링 파이프라인이 어떻게 상호작용하는지 파악하는 데서 시작된다. 본 파트에서는 Isaac Sim의 기술적 토대를 분석하여, 산불 연기 시뮬레이션 및 자동 라벨링 과정에서 발생하는 고유한 문제들의 근본 원인을 규명하고, 후속 파트에서 제시될 해결책의 기술적 배경을 확립하고자 한다.

USD는 단순히 3D 파일 형식을 넘어, 복잡한 3D 장면을 구성하고 기술하기 위한 강력한 프레임워크이다.1 NVIDIA Omniverse와 Isaac Sim은 이 USD를 핵심 데이터 모델로 채택하여, 가상 환경 내의 모든 요소와 그들 간의 관계를 정의한다.3 시뮬레이션의 관점에서 USD는 논리적, 관계적 컨텍스트를 제공하는 ‘스테이지(Stage)’의 개념을 중심으로 작동한다. 테이블 위에 컵이 놓여있는 관계는 스테이지 상에서 각 프리미티브(primitive, 이하 프림)의 상대적 위치와 각 프림이 가진 속성(attribute)으로 표현된다.3

Isaac Sim 내에서 수행되는 모든 시뮬레이션은 본질적으로 USD 스테이지 상의 프림 속성을 시간의 흐름에 따라 프로그래밍 방식으로 변경하는 과정이다.3 로봇, 센서, 환경 등 시뮬레이션의 모든 구성 요소는 ‘프림’이라는 기본 단위로 존재하며, 각 프림은 고유한 경로(prim path)를 통해 식별된다. 이러한 구조는 시뮬레이션 환경의 모듈성과 확장성을 크게 향상시킨다.

특히 중요한 것은 USD의 스키마(schema)를 통한 확장성이다. 예를 들어, UsdGeom 스키마는 큐브나 구와 같은 기하학적 형태를 정의하는 반면, UsdPhysicsPhysxSchema와 같은 물리 스키마는 특정 프림에 질량, 마찰, 충돌체(collider) 등의 물리적 속성을 부여하는 데 사용된다.3 로봇의 관절(joint)이나 강체(rigid body)는 모두 기하학적 프림에 물리 스키마를 적용함으로써 시뮬레이션 가능한 객체로 정의된다.

이러한 USD의 아키텍처는 산불 연기와 같은 체적 효과(volumetric effect)를 다룰 때 근본적인 기술적 난제를 제시한다. AI 모델 학습을 위한 데이터, 특히 의미론적 분할(semantic segmentation) 데이터는 특정 픽셀이 어떤 객체에 해당하는지를 식별하는 과정이다. Isaac Sim의 데이터 생성 프레임워크인 Replicator는 프림에 부여된 ‘의미론적(semantic)’ 태그를 읽어 이 작업을 수행한다.5 그러나 PhysX Flow와 같은 체적 효과는 표준적인 기하학적 메시(mesh)로 구성된 프림이 아니기 때문에, Replicator의 표준 주석기(annotator)가 인식할 수 있는 의미론적 스키마를 직접 적용하기 어렵다. 즉, USD의 강력한 구조화 및 확장성이 오히려 비정형 데이터인 연기 시뮬레이션의 라벨링을 어렵게 만드는 역설적인 상황이 발생하는 것이다. 이는 특정 기능의 부재나 버그가 아니라, 시뮬레이션 표현 방식(물리 기반 체적)과 주석 시스템의 표현 방식(기하학 기반 시맨틱) 간의 아키텍처적 불일치에서 기인하는 문제이다.

Isaac Sim은 NVIDIA Omniverse Kit SDK 위에 구축된 참조 애플리케이션(reference application)이다.7 Omniverse Kit은 렌더링, 물리 시뮬레이션, UI 등 다양한 기능을 모듈화된 확장(extension) 형태로 제공하는 프레임워크로, 개발자들은 이를 통해 목적에 맞는 3D 애플리케이션을 구축할 수 있다.2 이 모듈식 구조 덕분에 Isaac Sim은 로보틱스 시뮬레이션에 특화된 기능들을 유연하게 통합하고 확장할 수 있다.

물리 시뮬레이션의 핵심은 NVIDIA PhysX 엔진이다. PhysX는 GPU 가속을 통해 대규모의 복잡한 물리 현상을 높은 충실도(high fidelity)로 시뮬레이션할 수 있는 강력한 엔진이다.7 Isaac Sim은 PhysX를 활용하여 강체 및 연체 동역학, 관절 구동, 충돌 감지 등 로봇과 환경의 물리적 상호작용을 정밀하게 계산한다.1 특히 PhysX 5부터는 FLeX 라이브러리의 기술을 통합하여 입자 기반의 유체 및 천 시뮬레이션(Position Based Dynamics, PBD)과 유한요소법(Finite Element Method, FEM) 기반의 연체 동역학을 지원하며, 이는 연기와 같은 가스성 유체(gaseous fluid) 시뮬레이션의 기반이 된다.12

시각적 결과물을 생성하는 역할은 Omniverse RTX 렌더러가 담당한다. RTX 렌더러는 실시간 레이 트레이싱(ray tracing) 및 패스 트레이싱(path tracing) 기술을 통해 사진과 같은 수준의(photorealistic) 이미지를 생성한다.2 산불 연기와 같이 빛의 산란과 흡수가 복잡하게 일어나는 반투명한 체적을 사실적으로 렌더링하기 위해서는 이러한 고급 렌더링 기술이 필수적이다. 특히 RTX 렌더러의 패스 트레이싱 모드는 VDB 파일과 같은 비균일 볼륨(non-uniform volume) 데이터의 렌더링을 지원하므로, 외부 DCC(Digital Content Creation) 툴에서 제작된 고품질 연기 효과를 Isaac Sim으로 가져와 렌더링하는 워크플로우를 가능하게 한다.14

이 세 가지 핵심 요소(Kit, PhysX, RTX 렌더러)는 독립적이면서도 유기적으로 연결되어 Isaac Sim의 시뮬레이션 환경을 구성한다. PhysX는 보이지 않는 물리 상태(연기의 밀도, 속도 등)를 계산하고, RTX 렌더러는 이 물리 상태를 기반으로 시각적 결과물을 만들어내며, Replicator는 렌더링된 이미지와 USD 스테이지의 정보를 바탕으로 학습 데이터를 추출한다. 따라서 산불 연기 라벨링 문제의 본질은 이 세 시스템 간의 인터페이스 문제로 귀결된다. PhysX는 연기를 시뮬레이션하고 RTX 렌더러는 연기를 볼 수 있지만, Replicator는 연기를 ‘이해’하고 라벨링할 수 있는 표준적인 방법을 제공하지 않는다. 이 간극을 메우는 것이 본 보고서의 핵심 과제이며, 이는 각 구성 요소의 개별적인 한계가 아닌, 특정 사용 사례에 대한 통합의 한계를 보여준다.

Isaac Sim은 GUI를 통한 직관적인 조작뿐만 아니라, 파이썬 스크립트를 통한 완전한 프로그래밍 제어를 지원한다.1 이러한 스크립팅은 Isaac Sim 애플리케이션 내의 스크립트 에디터(Script Editor)를 통해 실행하거나, GUI 없이 백그라운드에서 실행되는 독립 실행형(standalone) 애플리케이션 형태로 구동할 수 있다.15 특히 대규모 합성 데이터셋을 생성하는 작업은 수많은 시뮬레이션 반복을 요구하므로, GUI 없이 서버 환경에서 자동화된 파이프라인을 구축할 수 있는 독립 실행형 애플리케이션 방식이 필수적이다.

이러한 독립 실행형 애플리케이션의 진입점은 SimulationApp 클래스이다.16 이 클래스는 시뮬레이션의 시작 및 종료, 렌더링 설정, 물리 엔진 초기화 등 애플리케이션의 전반적인 생명 주기를 관리한다. 개발자는 이 클래스의 인스턴스를 생성함으로써 Isaac Sim의 모든 기능을 스크립트 환경으로 가져올 수 있다.

Isaac Sim Core API는 이러한 파이썬 기반 개발을 용이하게 하기 위해 제공되는 고수준 래퍼(wrapper) 라이브러리다.3 이 API는 복잡한 저수준 USD 및 PhysX API를 로보틱스 애플리케이션에 적합하게 추상화하여, 프림 생성, 속성 수정, 로봇 제어, 센서 데이터 수집과 같은 일반적인 작업을 몇 줄의 코드로 간결하게 수행할 수 있도록 돕는다. 예를 들어,

DynamicCuboid 클래스를 사용하면 단 한 줄의 코드로 물리 속성이 적용된 큐브를 스테이지에 추가할 수 있다.3 본 보고서에서 제시하는 해결책들은 모두 이 Core API와 Replicator API를 활용한 파이썬 스크립트를 기반으로 구현된다. 이는 사용자가 기존의 파이썬 기반 AI 및 데이터 처리 파이프라인에 Isaac Sim을 원활하게 통합할 수 있음을 의미한다.

Isaac Sim에서 사실적인 산불 연기를 구현하는 접근법은 크게 두 가지로 나눌 수 있다. 첫째는 Isaac Sim에 내장된 PhysX Flow 확장 기능을 활용하는 실시간 시뮬레이션 방식이고, 둘째는 Blender나 Houdini와 같은 외부 전문 DCC 툴에서 미리 시뮬레이션된 체적 데이터를 VDB 파일 시퀀스로 가져오는 방식이다. 각 방법론은 시각적 충실도, 실시간 상호작용성, 워크플로우 복잡성 등에서 뚜렷한 장단점을 가지므로, 최종 목표(예: 실시간 로봇-연기 상호작용 테스트 vs. 고품질 학습 데이터 생성)에 따라 적절한 방법을 선택하는 것이 중요하다.

PhysX Flow는 Isaac Sim에 통합된 실시간 희소 복셀(sparse voxel) 유체 시뮬레이션 엔진으로, omni.flowusd 확장을 통해 제공된다.12 이는 주로 화염과 연기 효과를 위해 설계되었으며, 전체 시뮬레이션 공간을 조밀한 그리드(dense grid)로 채우는 대신, 연기나 화염이 존재하는 영역에만 동적으로 복셀 블록을 할당하여 계산 효율성을 극대화한다. 이 시뮬레이션은 GPU에서 실행되어 실시간 성능을 보장한다.19 Flow는 PhysX SDK의 일부로 배포되지만, 주된 강체 동역학 솔버와는 별개의 라이브러리로 작동한다.20

Flow 시뮬레이션의 핵심 개념은 다음과 같다 21:

Isaac Sim에서 Flow 효과를 생성하는 가장 간단한 방법은 사전 설정(preset)을 사용하는 것이다. 메뉴에서 Create > Flow > Add Box Emitter 또는 Add Sphere Emitter를 선택하여 이미터 프림을 스테이지에 추가할 수 있다.22 이미터가 생성되면, 렌더링 설정에서 Flow가 활성화되어야 시각적으로 확인할 수 있다.

연기의 동적인 움직임과 외형은 다양한 파라미터를 통해 정밀하게 제어된다. 특히 산불 연기와 같은 복잡한 현상을 위해서는 연소 모델(combustion model)의 이해가 필수적이다.21

이러한 파라미터들은 USD 프림의 속성으로 노출되므로, 파이썬 스크립트를 통해 동적으로 제어할 수 있다. 예를 들어, 시뮬레이션 중에 연기의 색상이나 밀도를 변경하는 복잡한 시나리오를 구현할 수 있다.

PhysX Flow는 실시간 상호작용에 최적화되어 있지만, Omniverse의 다른 물리 시스템과의 통합에는 명백한 한계가 존재한다. 가장 대표적인 예가 바람(wind) 효과이다. Omniverse에서 제공하는 표준 물리 역장(force field) 확장은 Flow 시뮬레이션에 영향을 주지 않는다.25 이는 Flow 시뮬레이션이 독립적인 솔버 내에서 실행되기 때문이다.

이 문제에 대한 공식적인 해결책은 이미터 자체의 속도를 애니메이션하는 것이다.25 즉, 바람 효과를 만들기 위해서는 ‘바람 팬’과 같은 역장 객체를 장면에 배치하는 것이 아니라, 파이썬 스크립트나 OmniGraph를 사용하여 Flow 이미터 프림의 velocity 속성을 매 프레임마다 동적으로 변경해야 한다. 이는 복잡하고 가변적인 산불 환경의 바람을 시뮬레이션하기 위해 상당한 구현 노력을 요구한다.

성능 측면에서는 앞서 언급한 Cell Size가 가장 중요한 요소이다.21 또한, 생성되는 파티클의 크기가 장면의 스케일에 비해 너무 클 경우 비현실적인 결과가 나타날 수 있으므로, Flow 팔레트에서 파티클 크기를 장면의 다른 객체(예: 기본 큐브)와 비교하여 적절히 조절해야 한다.26 Flow는 실시간 시각화에는 강력하지만, 이처럼 환경과의 물리적 상호작용이 제한적이고 파라미터 튜닝이 성능과 직결된다는 점을 고려해야 한다. 사용자는 Flow 시뮬레이션을 장면 전체의 물리 시스템과 상호작용하는 요소라기보다는, 특정 속성을 통해 제어되는 독립적인 ‘블랙박스’로 접근해야 한다.

최고 수준의 시각적 충실도가 요구되는 경우, 실시간 시뮬레이션의 제약을 벗어나 Blender나 Houdini와 같은 전문 DCC 소프트웨어를 활용하는 것이 효과적이다.27 이러한 툴들은 비실시간(offline) 렌더링을 전제로 설계되어, 더 정교하고 복잡한 유체 역학 솔버를 통해 극사실적인 연기, 화염, 폭발 효과를 만들어낼 수 있다. 아티스트는 이 툴들을 사용하여 연기의 밀도, 온도, 속도, 색상 등을 미세하게 제어하고, 바람이나 장애물과의 상호작용을 정밀하게 시뮬레이션할 수 있다.

이 워크플로우의 최종 결과물은 VDB 파일 시퀀스이다. VDB는 체적 데이터를 효율적으로 저장하고 처리하기 위한 업계 표준 파일 형식으로, OpenVDB 라이브러리를 통해 널리 사용된다.31 시뮬레이션의 각 프레임은 하나의 .vdb 파일로 저장되며, 이 파일 시퀀스는 연기의 동적인 변화를 담고 있다.

Isaac Sim의 RTX 렌더러는 외부에서 생성된 VDB 파일 시퀀스를 직접 렌더링하는 강력한 기능을 제공한다.14 이 기능은 RTX 렌더러의 패스 트레이싱 모드에서 활성화되며, 비균일 체적(non-uniform volume) 데이터의 렌더링을 가능하게 한다.

여기서 가장 중요한 구현 세부 사항은 VDB 데이터가 적용되는 방식이다. VDB는 새로운 종류의 프림으로 임포트되는 것이 아니라, UsdGeom.Cube와 같은 표준 기하학 프림에 적용되는 볼륨 재질(volume material) 형태로 처리된다.14 즉, 개발자는 장면에 큐브 프림을 생성하고, 이 큐브에 VDB 파일 시퀀스를 가리키는 볼륨 재질을 할당한다. 이 큐브는 VDB 데이터의 경계 상자(bounding box) 역할을 하며, 렌더러는 이 큐브 내부 공간에서 레이 마칭(ray marching)과 같은 기법을 통해 체적을 렌더링한다.

VDB 렌더링의 품질과 성능은 몇 가지 주요 설정에 의해 결정된다 14:

이 VDB 임포트 워크플로우는 실시간 시뮬레이션의 복잡성을 에셋 준비 및 렌더링 단계로 이전시킨다. 실시간 상호작용성은 희생되지만, 비교할 수 없는 수준의 시각적 품질을 얻을 수 있다. 특히 VDB가 표준 큐브 프림의 재질로 처리된다는 점은 매우 중요한 시사점을 가진다. 이는 라벨링 문제에 있어 다루기 힘든 미지의 ‘볼륨 프림’ 대신, Replicator가 이미 잘 알고 있는 UsdGeom.Cube를 다루면 된다는 의미이기 때문이다. 이 사실은 4부에서 제안할 라벨링 솔루션의 핵심적인 기반이 된다.

두 가지 연기 시뮬레이션 방법론의 장단점을 명확히 이해하고 프로젝트의 요구사항에 맞는 최적의 접근법을 선택하기 위해 다음 표와 같이 비교 분석을 수행할 수 있다.

특성 PhysX Flow (내장 실시간) Imported VDB Sequence (외부 사전 제작)
시각적 충실도 실시간 렌더링에 적합한 좋은 품질 오프라인 렌더링 수준의 매우 뛰어난 품질
런타임 성능 실시간 성능, 셀 크기와 파티클 수에 크게 의존 렌더링 부하가 높음, 볼륨 밀도와 산란 바운스 횟수에 의존
상호작용성 높음 (움직이는 이미터, 파라미터 변경에 실시간 반응) 낮음 (사전에 베이크된 애니메이션, 실시간 상호작용 불가)
워크플로우 복잡성 Isaac Sim 내에서 모든 작업이 완결됨 Blender/Houdini 등 외부 DCC 전문 지식 및 추가 작업 필요
라벨링 접근법 간접적인 프록시 지오메트리 동기화 필요 호스트 큐브를 직접 프록시로 활용 가능 (더 간단함)

이 분석을 통해, 만약 로봇이 실시간으로 연기와 상호작용하는 시나리오를 테스트하는 것이 목적이라면 PhysX Flow가 적합하다. 반면, AI 모델 학습을 위해 최고의 시각적 품질을 가진 대규모 데이터셋을 생성하는 것이 목적이라면, 상호작용성을 포기하더라도 외부 DCC 툴에서 VDB 시퀀스를 제작하여 가져오는 방식이 더 유리하다. 라벨링의 용이성 측면에서도 VDB 워크플로우가 더 직관적인 해결책을 제공할 가능성이 높다.

Omniverse Replicator는 NVIDIA Isaac Sim의 핵심 기능 중 하나로, AI 인식 모델 학습에 필요한 대규모 합성 데이터셋을 프로그래밍 방식으로 생성하기 위한 프레임워크이다.5 실제 세계에서 수집하기 어렵거나 위험하며, 비용이 많이 드는 데이터를 가상 환경에서 완벽하게 라벨링된 형태로 생성함으로써 AI 개발의 병목 현상을 해결한다. 특히 산불과 같은 재난 상황은 실제 데이터 확보가 거의 불가능하므로, Replicator를 통한 합성 데이터 생성(Synthetic Data Generation, SDG)이 매우 효과적인 대안이 된다.34

합성 데이터의 가장 큰 가치는 ‘완벽한 정답(ground truth)’을 제공한다는 점에 있다.5 예를 들어, 여러 객체가 서로 겹쳐 있는 복잡한 장면에서 각 객체의 가려진 부분까지 포함하는 정확한 분할 마스크(amodal segmentation mask)를 사람이 수동으로 제작하는 것은 거의 불가능하다.37 하지만 시뮬레이션 환경에서는 모든 객체의 위치와 형태 정보를 완벽하게 알고 있으므로, 이러한 정밀한 라벨을 자동으로 생성할 수 있다.

또한, SDG는 ‘롱테일(long-tail)’ 문제, 즉 실제 세계에서는 드물게 발생하여 데이터 수집이 어려운 예외적인 상황들을 의도적으로 생성하여 데이터셋에 포함시킬 수 있다.5 이를 통해 AI 모델은 더욱 다양한 상황에 대한 강건함(robustness)을 갖추게 되며, 일반화 성능이 향상된다. Replicator는 이러한 SDG의 원리를 Isaac Sim의 물리적으로 정확한 시뮬레이션 및 사실적인 렌더링과 결합하여, 실제와 유사하면서도(sim-to-real) 완벽하게 라벨링된 데이터를 대규모로 생성하는 강력한 도구를 제공한다.

Replicator는 크게 여섯 가지 핵심 구성 요소로 이루어져 있으며, 이들은 유기적으로 작동하여 데이터 생성 파이프라인을 구성한다.5

  1. Semantics Schema Editor (의미론적 스키마 편집기): GUI 기반 도구로, USD 스테이지 상의 특정 프림에 ‘class’와 같은 의미론적 정보를 태깅하는 데 사용된다. 예를 들어, 특정 큐브 프림에 class: 'box'라는 시맨틱을 부여할 수 있다. 주석기는 이 정보를 참조하여 해당 프림을 라벨링 대상으로 인식한다.5
  2. Visualizer (시각화 도구): 생성된 라벨 데이터를 뷰포트에서 직접 시각적으로 확인하고 디버깅할 수 있게 해준다. 2D/3D 경계 상자, 의미론적 분할 마스크 등을 렌더링된 이미지 위에 오버레이하여 보여준다.5
  3. Randomizers (랜더마이저): 도메인 무작위화(Domain Randomization, DR)를 수행하는 핵심 요소이다. 에셋의 위치, 회전, 크기, 재질, 조명, 카메라 위치 등을 무작위로 변경하여 데이터셋의 다양성을 확보하고 모델의 일반화 성능을 높인다.5
  4. omni.syntheticdata: Replicator의 가장 낮은 수준의 백엔드 확장 기능이다. RTX 렌더러의 출력(예: AOV, Arbitrary Output Variables)을 주석기 시스템으로 전달하는 다리 역할을 한다.5
  5. Annotators (주석기): 데이터 생성의 심장부이다. omni.syntheticdata를 통해 전달받은 렌더러 출력과 USD 스테이지 정보를 가공하여, 경계 상자, 깊이 맵, 표면 법선 벡터, 의미론적/인스턴스 분할 마스크 등 AI 모델 학습에 필요한 특정 형태의 정답 데이터를 생성한다.5
  6. Writers (작성기): 주석기가 생성한 이미지와 라벨 데이터를 받아, TensorFlow나 PyTorch와 같은 딥러닝 프레임워크에서 바로 사용할 수 있는 특정 형식(예: KITTI, COCO, 또는 간단한 폴더 구조)으로 디스크에 저장한다.5

이 구성 요소들은 파이썬 API를 통해 완벽하게 제어되므로, 개발자는 전체 데이터 생성 과정을 스크립트로 자동화하고 기존의 MLOps 파이프라인에 통합할 수 있다.

Replicator를 사용하여 표준적인 강체(rigid body) 객체를 라벨링하는 워크플로우는 다음과 같은 단계로 이루어진다. 이 과정은 4부에서 다룰 체적 데이터 라벨링의 문제점을 이해하기 위한 기준선 역할을 한다.

다음은 평면 위에 놓인 큐브를 라벨링하는 완전한 파이썬 스크립트 예시의 개념적 흐름이다.

1단계: 프림 생성 및 의미론적 라벨 적용

먼저, Isaac Sim Core API를 사용하여 스테이지에 기하학적 프림을 생성한다. 그리고 omni.isaac.core.utils.semantics 모듈의 add_update_semantics 함수를 사용하여 각 프림에 의미론적 정보를 부여한다. 이 정보는 ‘클래스’ 이름과 ‘인스턴스’ ID를 포함할 수 있다.

Python

import omni.isaac.core.utils.prims as prim_utils
from omni.isaac.core.utils.semantics import add_update_semantics

# 스테이지에 큐브 프림 생성
cube_prim = prim_utils.create_prim("/World/MyCube", "Cube", position=(0, 0, 50))

# 'MyCube'라는 클래스 이름으로 의미론적 정보 추가
add_update_semantics(cube_prim, "MyCube")

3

2단계: 렌더 제품 및 작성기 설정

Replicator API를 사용하여 데이터를 캡처할 카메라에 렌더 제품(render product)을 연결한다. 그 다음, 데이터를 저장할 작성기(writer)를 초기화하고, 필요한 주석기(annotator)를 활성화한다. 예를 들어, RGB 이미지와 의미론적 분할 마스크를 얻고 싶다면 rgb=True, semantic_segmentation=True로 설정한다.

Python

import omni.replicator.core as rep

# 뷰포트 카메라에 렌더 제품 생성
render_product = rep.create.render_product("/OmniverseKit_Persp", (1024, 1024))

# BasicWriter를 사용하여 RGB 및 의미론적 분할 데이터 저장 설정
writer = rep.get.writer("BasicWriter")
writer.initialize(
    output_dir="_output_directory",
    rgb=True,
    semantic_segmentation=True
)

# 렌더 제품을 작성기에 연결
writer.attach([render_product])

6

3단계: 데이터 캡처 트리거

rep.orchestrator.step() 함수를 호출하여 데이터 생성을 트리거한다. 이 함수가 호출되면, Replicator는 현재 프레임을 렌더링하고, 활성화된 주석기를 통해 데이터를 생성한 후, 작성기를 통해 디스크에 저장한다.

Python

# 현재 프레임의 데이터를 캡처하고 저장
await rep.orchestrator.step_async()

43

이 표준 워크플로우를 실행하면, _output_directory 폴더에 RGB 이미지와 함께 의미론적 분할 마스크 이미지가 생성된다. 분할 마스크 이미지에서 큐브가 차지하는 픽셀들은 MyCube 클래스에 해당하는 고유한 색상이나 ID 값으로 채워져 있을 것이다. 이 과정이 매끄럽게 작동하는 이유는 UsdGeom.Cube가 명확한 기하학적 형태를 가진 프림이고, Replicator의 semantic_segmentation 주석기가 이러한 기하학적 프림과 그에 적용된 의미론적 태그를 해석하도록 설계되었기 때문이다.

앞서 살펴본 바와 같이, Omniverse Replicator는 기하학적 메시를 가진 프림에 대한 라벨링 워크플로우를 강력하게 지원한다. 그러나 산불 연기와 같은 체적 효과는 이러한 표준적인 접근법이 통하지 않는다는 근본적인 문제를 안고 있다. 본 파트에서는 이 문제의 기술적 원인을 깊이 분석하고, 이를 해결하기 위한 두 가지 구체적이고 실행 가능한 솔루션을 제시하며, 각 솔루션의 장단점을 비교하여 최적의 접근법을 도출한다.

NVIDIA 개발자 포럼의 사용자 보고와 관련 문서들은 PhysX Flow로 생성된 화염이나 연기 객체에 Semantics Schema Editor를 통해 의미론적 라벨을 적용하려는 시도가 실패했음을 일관되게 보여준다.44 이는 단순한 버그나 기능 누락이 아니라, Replicator와 렌더링 파이프라인의 근본적인 작동 방식에 기인한다.

Replicator의 표준 주석기들(예: semantic_segmentation, instance_segmentation, bounding_box_2d_tight)은 렌더링된 이미지의 각 픽셀이 어떤 USD 프림에 의해 생성되었는지를 알아야 작동한다.6 이 과정은 일반적으로 렌더러가 “프림 ID”와 같은 정보를 담은 추가적인 버퍼(AOV, Arbitrary Output Variable)를 생성하고, 주석기가 이 버퍼를 참조하여 픽셀과 프림을 연결하는 방식으로 이루어진다. 이 메커니즘은 폴리곤 메시(polygon mesh)와 같이 명확한 표면과 경계를 가진 기하학적 객체에 최적화되어 있다.

그러나 PhysX Flow나 VDB 파일로 표현되는 체적 효과는 이러한 전통적인 메시 기반의 객체가 아니다. 이들은 RTX 렌더러 내에서 레이 마칭(ray marching)이나 델타 트래킹(delta tracking)과 같은 특수한 볼륨 렌더링 기법을 통해 렌더링된다.14 이 과정에서 생성되는 픽셀 색상은 단일 표면에서 반사된 결과가 아니라, 광선이 볼륨을 통과하며 누적된 밀도와 산란의 결과물이다. 따라서 특정 픽셀을 단 하나의 ‘연기 프림’과 직접적으로 연결하는 명확한 1:1 매핑이 존재하지 않는다. 결과적으로, 표준 주석기는 연기 효과를 의미 있는 객체로 인식하지 못하고, 라벨링을 수행할 수 없게 된다.

이러한 근본적인 문제를 우회하기 위한 가장 실용적이고 강력한 해결책은 ‘프록시 지오메트리’를 사용하는 것이다. 이 방법의 핵심 아이디어는 시뮬레이션과 라벨링을 분리하는 것이다. 즉, 눈에 보이는 실제 연기 시뮬레이션은 그대로 두고, 이와 별개로 Replicator가 인식할 수 있는 단순한 기하학적 프림(프록시)을 생성하여 연기 볼륨의 위치와 크기를 대리하도록 하는 것이다. 의미론적 라벨은 실제 연기가 아닌 이 프록시 프림에 적용된다. 이 개념은 다른 사용 사례에서 텍스처 기반 결함을 라벨링하거나 45, 볼륨 데이터를 직접 조작하기 위해 제안된 연구에서 영감을 얻었다.46

프록시 지오메트리 방법의 워크플로우는 다음과 같다.

  1. 실제 효과 시뮬레이션: PhysX Flow 또는 VDB 임포트를 통해 사실적인 연기 효과를 생성한다. 이 객체는 렌더링 시 최종 이미지에만 기여하며, 라벨링 과정에는 직접 관여하지 않는다.
  2. 프록시 지오메트리 생성: 연기 볼륨을 감싸는 단순한 UsdGeom.CubeUsdGeom.Sphere와 같은 표준 기하학 프림을 생성한다.
  3. 프록시 동기화: 매 시뮬레이션 프레임마다 연기 볼륨의 위치와 크기(경계 상자)를 계산하고, 프록시 프림의 변환(transform)과 크기(scale)를 이와 일치하도록 업데이트한다.
  4. 프록시 라벨링 및 가시성 제어: 의미론적 라벨(예: class: 'wildfire_smoke')을 이 프록시 프림에 적용한다. 동시에, 이 프록시 지오메트리가 최종 렌더링 결과물에 나타나지 않도록 가시성(visibility)을 끈다. 중요한 점은, 프록시가 주 렌더링에서는 보이지 않더라도 Replicator의 주석기는 이를 감지할 수 있어야 한다는 것이다.
  5. 데이터 생성: Replicator를 실행하면, 주석기는 눈에 보이지 않는 프록시 지오메트리를 기준으로 분할 마스크를 생성한다.

프록시 지오메트리를 구현하는 구체적인 방법은 연기 생성 방식에 따라 달라진다.

프록시 지오메트리가 준비되면, 표준 워크플로우와 동일하게 라벨을 적용하고 데이터를 생성한다.

Python

# 프록시 프림(proxy_prim)에 의미론적 라벨 적용
add_update_semantics(proxy_prim, "wildfire_smoke")

#... Replicator writer 및 annotator 설정...
writer.initialize(
    output_dir="_output_smoke_dataset",
    rgb=True,
    semantic_segmentation=True
)
#...

# 데이터 캡처 실행
await rep.orchestrator.step_async()

이 코드가 실행되면, semantic_segmentation 주석기는 wildfire_smoke 라벨이 붙은 proxy_prim을 인식하고, 이 프록시가 차지하는 화면상의 픽셀 영역에 대해 정확한 분할 마스크를 생성한다.

프록시 지오메트리 방법의 가장 명백한 한계는 정확도이다. 생성된 분할 마스크는 연기의 복잡하고 세밀한 형상(wispiness)을 포착하지 못하고, 프록시의 형태(예: 큐브 또는 구)에 해당하는 단순한 볼록 껍질(convex hull) 형태가 된다. 이는 픽셀 단위의 정밀한 마스크가 필요한 작업에는 단점이 될 수 있다.

그러나 이러한 부정확성은 오히려 모델의 강건성을 높이는 긍정적인 요소로 작용할 수 있다. 실제 세계의 데이터 라벨링 역시 완벽하지 않으며, 종종 노이즈가 많거나 부정확하다.47 AI 모델이 지나치게 완벽하고 깨끗한 합성 데이터에만 학습될 경우, 실제 데이터의 불완전성에 대처하지 못하고 성능이 저하되는 ‘과적합(overfitting)’ 문제가 발생할 수 있다. 프록시 지오메트리를 통해 생성된 다소 거친(coarse) 라벨은 일종의

라벨 공간에서의 도메인 무작위화(label-space domain randomization) 역할을 한다. 즉, 모델이 연기의 완벽한 가장자리에 의존하는 대신, 연기의 전반적인 위치, 형태, 밀도와 같은 더 본질적인 특징을 학습하도록 유도한다. 이는 실제 환경에서 마주할 불완전하고 모호한 연기 형태에 대한 모델의 일반화 성능을 향상시키는 데 기여할 수 있다.48 이처럼 기술적 한계를 모델 강건성 향상을 위한 전략으로 재해석하는 것은 이 방법론의 중요한 가치 중 하나이다.

성능 측면에서, 프록시 지오메트리 방법은 매우 효율적이다. 단순한 지오메트리를 추가하고 매 프레임 변환을 업데이트하는 작업은 시뮬레이션 전체의 계산 부하에 미미한 영향을 미친다.

가장 이상적이지만 기술적 난이도가 매우 높은 해결책은 체적 데이터에 직접 접근하는 커스텀 주석기(custom annotator)를 개발하는 것이다. 이 접근법은 공식적으로 문서화되어 있지 않으며, Omniverse SDK의 저수준 API에 대한 깊은 이해를 요구한다.49

커스텀 주석기의 목표는 픽셀 단위로 정확한 분할 마스크를 생성하는 것이다. 이를 위해 주석기는 렌더러가 사용하는 것과 동일한 체적 데이터에 접근할 수 있어야 한다.

커스텀 주석기는 Replicator의 기존 주석기 구조를 따라야 한다 (/omni/replicator/core/scripts/annotators_default.py 참조).49 주석기의 대략적인 로직은 다음과 같을 것이다.

  1. 카메라의 뷰-프로젝션 행렬(view-projection matrix)을 얻는다. 이는 camera_params 주석기를 의존성으로 추가하여 얻을 수 있다.49
  2. 앞서 설명한 방법으로 체적 데이터(Flow 그리드 또는 VDB 데이터)에 접근한다.
  3. 출력 이미지의 각 픽셀에 대해, 카메라 위치에서 해당 픽셀 방향으로 광선(ray)을 투사한다.
  4. 투사된 광선을 따라 일정한 간격으로 체적 데이터를 샘플링하여 밀도 값을 누적한다.
  5. 누적된 밀도 값이 특정 임계치(threshold)를 초과하면, 해당 픽셀을 ‘wildfire_smoke’ 클래스로 판단하고 분할 마스크 버퍼에 해당 의미론적 ID를 기록한다.

다른 게임 엔진인 Unreal Engine의 Niagara VFX 시스템은 파티클이 Custom Depth Buffer에 값을 쓸 수 있도록 하여, 후처리 효과나 분할 마스크 생성에 활용하는 고수준 기능을 제공한다.50 Omniverse에 유사한 고수준의 ‘커스텀 주석 버퍼’ 기능이 없다는 점이 개발자가 이처럼 복잡하고 어려운 저수준의 경로를 택하도록 만든다. 이 비교는 현재 Omniverse 생태계에서 이 특정 사용 사례에 대한 기능적 성숙도의 간극을 보여주며, 프록시 지오메트리 방법이 왜 대부분의 사용자에게 더 현실적인 해결책인지를 뒷받침한다.

두 가지 제안된 해결책의 특성을 요약하면 다음과 같다.

특성 프록시 지오메트리 방법 커스텀 주석기 개발
구현 노력 낮음-중간 (파이썬 스크립팅) 매우 높음 (C++/저수준 API)
주석 정확도 낮음 (거친 볼록 껍질 형태) 높음 (픽셀/복셀 단위)
런타임 성능 높음 (최소한의 오버헤드) 잠재적으로 느림 (프레임당 많은 계산 필요)
안정성/유지보수성 높음 (공개된 API 사용) 낮음 (비공식 API 의존, 업데이트 시 파손 위험)

결론적으로, 픽셀 단위의 완벽한 정확도가 반드시 필요하고 이를 위해 상당한 연구 개발 자원을 투입할 수 있는 전문 팀이 아니라면, 프록시 지오메트리 방법이 안정성, 구현 용이성, 성능 측면에서 압도적으로 우수하며, 대부분의 AI 모델 학습 시나리오에 충분히 효과적인 해결책이다.

이전 파트에서 논의된 개념들을 종합하여, 산불 연기 생성부터 자동 라벨링, 그리고 데이터셋 생성에 이르는 전체 워크플로우를 시연하는 완전한 파이썬 스크립트와 함께, 생성된 데이터의 품질과 모델의 성능을 극대화하기 위한 모범 사례들을 제시한다.

다음은 독립 실행형 애플리케이션(SimulationApp) 환경에서 PhysX Flow를 사용하여 연기를 생성하고, 프록시 지오메트리 방법으로 이를 라벨링하여 데이터셋을 생성하는 전체 파이썬 스크립트의 구조와 핵심 로직이다.

Python

# run_smoke_sdg.py

import asyncio
import os
import numpy as np
from isaacsim import SimulationApp

# 시뮬레이션 앱 초기화 (헤드리스 모드)
CONFIG = {"headless": True, "disable_viewport_updates": True}
simulation_app = SimulationApp(CONFIG)

# SimulationApp 초기화 후에 필요한 모듈들을 임포트
import omni.replicator.core as rep
from omni.isaac.core import World
from omni.isaac.core.utils.prims import create_prim, get_prim_at_path
from omni.isaac.core.utils.semantics import add_update_semantics
from omni.physx.scripts import physicsUtils
from pxr import Gf, UsdGeom

class SmokeSimulation:
    def __init__(self):
        self._world = World(stage_units_in_meters=1.0)
        self._world.scene.add_default_ground_plane()
        
        # 1. PhysX Flow 이미터 및 프록시 지오메트리 생성
        self.flow_emitter = create_prim(
            prim_path="/World/FlowEmitter",
            prim_type="FlowEmitterSphere",
            attributes={"flow:radius": 2.0, "flow:velocity": (0, 0, 5.0)}
        )
        self.proxy_cube = create_prim(
            prim_path="/World/SmokeProxy",
            prim_type="Cube",
            position=(0, 0, 5.0),
            scale=(4, 4, 4),
            visible=False  # 렌더링에서는 보이지 않게 설정
        )
        
        # 2. 프록시에 의미론적 라벨 적용
        add_update_semantics(self.proxy_cube, "wildfire_smoke")

        # 3. 물리 스텝 콜백 등록
        self._physics_callback = self._world.add_physics_callback(
            "sync_proxy_with_smoke", self.sync_proxy_callback
        )

    def sync_proxy_callback(self, step_size):
        """매 물리 스텝마다 호출되어 프록시 위치를 동기화"""
        # 실제 구현에서는 Flow 시뮬레이션의 경계 상자를 얻어오는 로직이 필요.
        # 여기서는 예시로 이미터 위치를 따라가도록 단순화.
        emitter_pos = self.flow_emitter.GetAttribute("xformOp:translate").Get()
        
        # 프록시 큐브의 위치를 업데이트
        proxy_xform = UsdGeom.Xformable(self.proxy_cube)
        proxy_xform.ClearXformOpOrder()
        proxy_xform.AddTranslateOp().Set(Gf.Vec3d(emitter_pos))
        # 실제로는 크기(scale)도 동기화해야 함

async def run_replicator_pipeline():
    # 4. Replicator 설정
    camera_prim = create_prim("/World/Camera", "Camera", position=(0, -20, 10))
    camera_prim.GetAttribute("xformOp:orient").Set(Gf.Quatd(0.707, 0.707, 0, 0))

    render_product = rep.create.render_product(camera_prim.GetPath(), (1280, 720))

    writer = rep.get.writer("BasicWriter")
    output_dir = os.path.join(os.getcwd(), "_wildfire_smoke_dataset")
    writer.initialize(
        output_dir=output_dir,
        rgb=True,
        semantic_segmentation=True
    )
    writer.attach([render_product])
    
    # 5. 시뮬레이션 및 데이터 생성 실행
    await rep.orchestrator.run_async(num_frames=100)
    
    print(f"데이터셋 생성 완료: {output_dir}")

async def main():
    smoke_sim = SmokeSimulation()
    await smoke_sim._world.initialize_simulation_async()
    await smoke_sim._world.reset_async()
    
    # Replicator 파이프라인 실행
    await run_replicator_pipeline()

    simulation_app.close()

if __name__ == "__main__":
    asyncio.run(main())

16

이 스크립트는 헤드리스 모드로 Isaac Sim을 실행하고 17, Flow 이미터와 보이지 않는 프록시 큐브를 생성한다.

add_physics_callback을 통해 등록된 sync_proxy_callback 함수는 매 물리 스텝마다 호출되어 프록시의 위치를 연기 볼륨(예시에서는 이미터)의 위치와 동기화한다. Replicator는 wildfire_smoke 라벨이 붙은 프록시 큐브를 기준으로 의미론적 분할 마스크를 생성하고, 이를 RGB 이미지와 함께 지정된 폴더에 저장한다.

단순히 데이터를 생성하는 것을 넘어, 생성된 데이터로 학습한 모델이 실제 환경에서도 잘 작동하도록 하려면 도메인 무작위화(Domain Randomization, DR)가 필수적이다.40 DR은 시뮬레이션 환경의 다양한 요소들을 무작위로 변경하여 모델이 특정 시뮬레이션 환경에 과적합되는 것을 방지하고, 실제 세계의 다양성에 대한 강건성을 확보하는 기법이다.

Replicator의 랜덤화 API를 사용하여 매 데이터 생성 시점마다 연기의 외형과 움직임을 다양하게 만들 수 있다. 이는 PhysX Flow 이미터의 속성을 동적으로 변경함으로써 달성된다.

Python

# Replicator 랜덤화 그래프 내에서 속성 변경
with rep.trigger.on_frame():
    with rep.create.group([flow_emitter_path]):
        rep.modify.attribute(
            "flow:velocity",
            rep.distribution.uniform((-5, -5, 2), (5, 5, 10))
        )
        rep.modify.attribute(
            "flow:density",
            rep.distribution.uniform(0.5, 5.0)
        )

5

모델이 연기 자체의 특징을 학습하도록 하려면, 배경과 조명 등 주변 환경을 적극적으로 무작위화해야 한다.52

이러한 무작위화 기법들은 산불 탐지 관련 연구에서 제안된 합성 데이터 생성 원칙과도 일치하며 34, 모델의 실제 환경 적용 성능을 크게 향상시킬 수 있다.

수만에서 수십만 장에 이르는 대규모 데이터셋을 효율적으로 생성하기 위해서는 시뮬레이션 성능을 최적화하는 것이 중요하다.

이러한 최적화 기법들을 적용하면, 강력한 서버나 클라우드 환경에서 24시간 내내 데이터 생성 파이프라인을 가동하여 AI 모델 학습에 필요한 대규모의 고품질 데이터셋을 효율적으로 구축할 수 있다.

본 보고서는 NVIDIA Isaac Sim 환경에서 산불 연기와 같은 복잡한 체적 효과를 시뮬레이션하고, 이를 AI 학습용 데이터로 활용하기 위해 자동으로 라벨링하는 방법에 대해 심도 있게 고찰했다. 이 과정에서 플랫폼의 근본적인 아키텍처를 분석하고, 표준적인 방법으로는 해결할 수 없는 기술적 난제를 식별했으며, 이에 대한 실용적인 해결책과 고급 연구 경로를 제시했다.

연구 결과, Isaac Sim에서 체적 효과에 대한 직접적인 의미론적 라벨링 기능이 부재하다는 핵심적인 문제를 확인할 수 있었다. 이는 Replicator의 주석 시스템이 기하학적 메시 기반의 프림을 대상으로 설계되었기 때문이며, 볼륨 렌더링으로 처리되는 연기 효과와는 근본적인 아키텍처적 불일치가 존재한다.

이 문제를 해결하기 위해 본 보고서는 두 가지 해결책을 제안하고 분석했다.

  1. 프록시 지오메트리 방법: 실제 연기 볼륨을 대리하는, 렌더링되지 않는 단순한 기하학적 프림(프록시)을 생성하고 여기에 의미론적 라벨을 적용하는 방식이다.
  2. 커스텀 주석기 개발: 체적 데이터에 직접 접근하여 픽셀 단위의 마스크를 생성하는 저수준의 커스텀 주석기를 개발하는 방식이다.

두 방법을 비교 분석한 결과, 프록시 지오메트리 방법이 대부분의 사용자와 프로젝트에 가장 적합한 권장 워크플로우라는 결론을 내렸다. 이 방법은 다음과 같은 명확한 장점을 가진다.

물론 이 방법은 픽셀 단위의 완벽한 정확도를 제공하지 못한다는 한계가 있지만, 이러한 트레이드오프는 대부분의 AI 기반 객체 탐지 및 분할 작업에서 충분히 수용 가능하며 오히려 긍정적인 효과를 낳을 수 있다. 반면, 커스텀 주석기 개발은 이론적으로 가장 이상적인 결과를 낼 수 있지만, 매우 높은 기술적 전문성과 개발 리소스를 요구하며 비공식 API에 의존해야 하는 큰 위험을 감수해야 하므로, 특수한 목적을 가진 소수의 전문가 팀을 위한 연구 경로로 남겨두는 것이 타당하다.

본 연구에서 다룬 기술과 방법론은 현재 시점에서 가장 효과적인 접근법이지만, 관련 기술은 빠르게 발전하고 있으며 향후 다음과 같은 방향으로의 발전이 기대된다.

결론적으로, Isaac Sim에서 산불 연기를 시뮬레이션하고 라벨링하는 과제는 현재의 기술적 한계와 이를 극복하기 위한 창의적인 해결책이 공존하는 흥미로운 영역이다. 본 보고서에서 제시한 프록시 지오메트리 방법은 현재 시점에서 가장 안정적이고 효과적인 솔루션이며, 향후 플랫폼의 발전과 새로운 AI 기술의 등장은 이 분야의 가능성을 더욱 확장시킬 것으로 기대된다.