Asset Inventory and Data Export#

With your factory scene organized, let’s explore it programmatically using Python scripting. This section teaches you to traverse the USD stage, identify matching prims, and export their attributes to CSV for reporting. You will add visual navigation waypoints in the next lesson.

Understanding Stage Traversal#

Stage traversal walks a USD scene hierarchy to query prims. The stage.Traverse() calls used here visit active, loaded, defined, non-abstract prims. They do not descend into scenegraph instances or include unloaded payload contents. The resulting counts describe that traversal, not every object in the source files.

Tip

For a deep dive into Stage traversal and more OpenUSD topics, check out our learning path.

Why Stage Traversal Matters for Digital Twins#

As your factory digital twin grows in complexity, manually tracking assets becomes impractical. Consider these scenarios:

Facility Management Needs

  • “How many safety devices do we have in the facility?”

  • “What types of machines are installed in Production Line A?”

  • “Which assets need maintenance based on their installation dates?”

  • “What’s the total count of each asset type for procurement planning?”

In real facility management, you need to:

  • Generate asset inventories for maintenance planning

  • Create compliance reports showing all safety equipment locations

  • Track component installations across production lines

  • Export data for ERP integration and facility management systems

Manual clicking through hundreds of objects isn’t practical—scripting automates this essential workflow.

How USD Stage Traversal Works#

USD traversal operates by walking through prims in depth-first order

/Factory (Root)
├── /Factory/ProductionLine_A (visited first)
│   ├── /Factory/ProductionLine_A/Machine_001 (visited second)
│   └── /Factory/ProductionLine_A/Machine_002 (visited third)
└── /Factory/ProductionLine_B (visited fourth)
    └── /Factory/ProductionLine_B/Machine_003 (visited fifth)

This visits each prim included by the traversal once in depth-first order. See the OpenUSD stage traversal API for the default predicate and instance behavior.

Python Scripting in Omniverse#

Accessing the Script Editor

  1. Navigate to Developer > Script Editor to open the Script Editor.

Basic Stage Traversal: Listing Traversed Prims#

Let’s start with a fundamental script that traverses your factory stage and lists the paths included by the default traversal.

Script 1: Default Stage Traversal#

Warning

Depending on the size of your scene this may take some time to complete.

Copy and paste this script into the Script Editor

 1# Import necessary USD modules
 2from pxr import Usd
 3import omni.usd
 4
 5# Get the current stage (your opened factory scene)
 6stage = omni.usd.get_context().get_stage()
 7
 8# Check if stage is valid
 9if not stage:
10    print("No stage currently loaded!")
11else:
12    print("=== Factory Scene Traversal ===")
13    print("Traversing matching prims in depth-first order:\n")
14    
15    # Traverse all active, loaded, defined prims
16    prim_count = 0
17    for prim in stage.Traverse():
18        print(f"Path: {prim.GetPath()}")
19        print(f"Type: {prim.GetTypeName()}")
20        print(f"Kind: {prim.GetMetadata('kind') or ''}")
21        print("-" * 40)
22        prim_count += 1
23    
24    print(f"\nTotal prims traversed: {prim_count}")

Execute the script and observe:

Script Editor displaying stage-traversal code and output listing prim paths, types and kinds.
  • The hierarchical structure of your factory scene

  • Prim types (Xform, Mesh, Material, etc.)

  • USD Kind metadata (Component, Assembly, Group)

  • Total count of prims included by this traversal

Understanding the Output#

Your results will show the organized structure of your digital twin:

Output Element

What It Tells You

Why It Matters

Path

Hierarchical location in scene

Navigation and organization

Type

USD primitive type (Mesh, Xform, etc.)

Technical structure understanding

Kind

Asset classification (Component, Assembly)

Logical grouping and filtering

Targeted Asset Identification#

Rather than listing everything, let’s create more focused scripts that identify specific types of assets relevant to facility management.

Script 2: Finding Manufacturing Equipment#

This script identifies machines and equipment specifically

Note

This script lists candidates whose names contain equipment keywords or whose kind is component. Keywords include machine, robot, conveyor, press, router, and feeder. A component can also be a prop, and a matching name can belong to an internal part. Review the paths and count only the roots of the equipment assets you intend to inventory; this heuristic is not a verified equipment count.

 1from pxr import Usd, UsdGeom
 2import omni.usd
 3
 4# Get current stage
 5stage = omni.usd.get_context().get_stage()
 6
 7if stage:
 8    print("=== Manufacturing Equipment Inventory ===\n")
 9    
10    machines = []
11    
12    for prim in stage.Traverse():
13        # Look for prims with "machine" in the name or specific kinds
14        prim_name = prim.GetName().lower()
15        prim_kind = prim.GetMetadata("kind") or ""
16        
17        # Identify potential manufacturing equipment
18        if (any(keyword in prim_name for keyword in ['machine', 'robot', 'conveyor', 'press', 'router', 'feeder']) 
19            or prim_kind == 'component'):
20            
21            # Get additional information
22            transform = UsdGeom.Xformable(prim)
23            
24            machine_data = {
25                'name': prim.GetName(),
26                'path': str(prim.GetPath()),
27                'type': prim.GetTypeName(),
28                'kind': prim_kind
29            }
30            
31            machines.append(machine_data)
32            
33            print(f"Equipment: {machine_data['name']}")
34            print(f"Location: {machine_data['path']}")
35            print(f"Type: {machine_data['type']}")
36            print(f"Classification: {machine_data['kind']}")
37            print("-" * 50)
38    
39    print(f"\nTotal manufacturing equipment found: {len(machines)}")

Script 3: Props and Safety Equipment Inventory#

This script focuses on props, safety equipment, and environmental objects

Note

This script is searching prims similar to the previous one, but it will categorize them by type.
Such as: Safety Equipment, Storage/Transport, Lighting

 1from pxr import Usd
 2import omni.usd
 3
 4# Get current stage
 5stage = omni.usd.get_context().get_stage()
 6
 7if stage:
 8    print("=== Props and Safety Equipment Inventory ===\n")
 9    
10    props = []
11    safety_equipment = []
12    
13    for prim in stage.Traverse():
14        prim_name = prim.GetName().lower()
15        prim_path = str(prim.GetPath()).lower()
16        
17        # Categorize based on name patterns
18        if any(keyword in prim_name for keyword in ['cone', 'barrier', 'sign', 'safety', 'warning']):
19            safety_equipment.append({
20                'name': prim.GetName(),
21                'path': str(prim.GetPath()),
22                'category': 'Safety Equipment'
23            })
24            
25        elif any(keyword in prim_name for keyword in ['pallet', 'container', 'box', 'rack', 'shelf']):
26            props.append({
27                'name': prim.GetName(),
28                'path': str(prim.GetPath()),
29                'category': 'Storage/Transport'
30            })
31            
32        elif any(keyword in prim_name for keyword in ['light', 'lamp', 'fixture']):
33            props.append({
34                'name': prim.GetName(),
35                'path': str(prim.GetPath()),
36                'category': 'Lighting'
37            })
38    
39    # Display results
40    print("SAFETY EQUIPMENT:")
41    for item in safety_equipment:
42        print(f"  {item['name']} at {item['path']}")
43    
44    print(f"\nPROPS BY CATEGORY:")
45    for item in props:
46        print(f"  {item['category']}: {item['name']} at {item['path']}")
47    
48    print(f"\nSummary:")
49    print(f"Safety Equipment: {len(safety_equipment)}")
50    print(f"Props: {len(props)}")

CSV Export: Asset Data Export#

Export Matching Scene Attributes to CSV#

Keep prims with at least one target attribute, build a record for each match, and write those records to CSV. The illustrated tags and rows explain the filter; they are not verified course export results.

Traverse the stage

Filter by attributes

Lower branch: Skip prims with no matching attributes.

Build records

Write CSV.

A tagged Delta router and cart pass through an attribute filter into records and CSV rows; an untagged cube is skipped.
Factory viewport with the Script Editor open to an inventory export script and its console output.

Now let’s export the attributes taught in this course from matching prims to CSV.

This script targets prims with at least one of the custom attributes added in the metadata and production-line lessons. It does not export untagged assets or all properties of each matching prim.

Key Features of this Export Script

Targeted Filtering Only exports assets that contain your custom metadata attributes, making the process faster and the results more relevant for facility management.

Smart Categorization Automatically categorizes assets based on naming patterns, grouping them into meaningful categories like “Manufacturing Equipment,” “Safety Equipment,” and “Storage/Transport.”

Structured Output Creates a standardized CSV format that can be easily imported into facility management systems, maintenance databases, or spreadsheet applications.

Performance Optimization Uses efficient USD methods to check for specific attributes without processing every prim’s complete attribute list.

Note

To change the location that the file will save to, search for the comment in the code:

# Set your CSV file save location here
CSV_SAVE_PATH = "C:/Users/YourName/Desktop/factory_inventory.csv"

Understanding the TARGET_ATTRIBUTES Configuration

The script searches specifically for these custom attributes:

  • Unique_ID — The integer identifier added during asset metadata review.

  • ProductionLine_ID — The string identifying a production line.

  • Capacity_UnitsPerHour — The integer production capacity of an assembly.

Only prims containing at least one of these attributes are included. A blank field means the attribute is absent or has no default value; it does not mean zero capacity. Read errors are reported as Error reading value and must be investigated.

Before interpreting totals, confirm that the intended payloads are loaded and the intended asset or line roots carry these attributes. Default traversal does not descend into instances, so tags authored only on their descendants will not appear. Review the exported paths for internal parts or nested assemblies before treating the row count as an equipment count.

Interpreting Your Export Results

Earlier export example reporting 27 assets: 4 manufacturing equipment, 2 other and 17 safety equipment.

This screenshot shows an earlier export example. Use the attribute names and illustrative table below for the current script; do not expect identical counts or columns.

After running the script, you’ll see console output showing:

  • Export confirmation with file location and asset count

  • Category breakdown showing how many assets were found in each type

  • File location where you can open the CSV in Excel or your preferred application

The CSV contains only tagged prims included by the traversal. It is a starting point for facility reporting, not proof of a complete inventory.

 1from pxr import Usd
 2import omni.usd
 3import csv
 4import os
 5
 6# ===== CONFIGURATION =====
 7# Set your CSV file save location here
 8CSV_SAVE_PATH = "C:/Users/YourName/Desktop/factory_inventory.csv"
 9
10# Define the specific custom attributes to search for
11TARGET_ATTRIBUTES = [
12    'Unique_ID',
13    'ProductionLine_ID',
14    'Capacity_UnitsPerHour'
15]
16# =========================
17
18stage = omni.usd.get_context().get_stage()
19
20if stage:
21    print("Exporting factory inventory with attributes...")
22    
23    inventory_data = []
24    
25    for prim in stage.Traverse():
26        # Check for target attributes
27        found_attributes = {}
28        for attr_name in TARGET_ATTRIBUTES:
29            if prim.HasAttribute(attr_name):
30                try:
31                    value = prim.GetAttribute(attr_name).Get()
32                    found_attributes[attr_name] = str(value) if value is not None else ''
33                except:
34                    found_attributes[attr_name] = 'Error reading value'
35        
36        # Only include prims with target attributes
37        if found_attributes:
38            # Categorize based on name patterns
39            name_lower = prim.GetName().lower()
40            if any(keyword in name_lower for keyword in ['machine', 'robot', 'press', 'router', 'feeder']):
41                category = 'Manufacturing Equipment'
42            elif any(keyword in name_lower for keyword in ['cone', 'barrier', 'safety', 'warning']):
43                category = 'Safety Equipment'
44            elif any(keyword in name_lower for keyword in ['pallet', 'container', 'box']):
45                category = 'Storage/Transport'
46            elif any(keyword in name_lower for keyword in ['light', 'lamp']):
47                category = 'Lighting'
48            else:
49                category = 'Other'
50            
51            prim_data = {
52                'Name': prim.GetName(),
53                'Path': str(prim.GetPath()),
54                'Type': prim.GetTypeName(),
55                'Kind': prim.GetMetadata("kind") or "",
56                'Category': category
57            }
58            prim_data.update(found_attributes)
59            inventory_data.append(prim_data)
60    
61    if inventory_data:
62        # Write CSV
63        base_columns = ['Name', 'Path', 'Type', 'Kind', 'Category']
64        target_columns = [attr for attr in TARGET_ATTRIBUTES if any(attr in item for item in inventory_data)]
65        columns = base_columns + target_columns
66        
67        with open(CSV_SAVE_PATH, 'w', newline='', encoding='utf-8') as csvfile:
68            writer = csv.DictWriter(csvfile, fieldnames=columns)
69            writer.writeheader()
70            for item in inventory_data:
71                row = {col: item.get(col, '') for col in columns}
72                writer.writerow(row)
73        
74        print(f"✅ Exported {len(inventory_data)} assets to: {CSV_SAVE_PATH}")
75        
76        # Category summary
77        categories = {}
78        for item in inventory_data:
79            cat = item['Category']
80            categories[cat] = categories.get(cat, 0) + 1
81        
82        for category, count in sorted(categories.items()):
83            print(f"   {category}: {count}")
84            
85    else:
86        print("No assets found with target attributes.")
87        
88else:
89    print("No USD stage loaded.")

Factory Inventory Export Results#

The table below is an illustrative example of the updated columns, not a captured course run. With the current name-based rules, CL6_Line_Full is classified as Other. Your rows and included attribute columns depend on the loaded stage and its tags; verify actual output in the course application.

Name

Path

Type

Kind

Category

Unique_ID

ProductionLine_ID

Capacity_UnitsPerHour

CL6_Line_Full

/World/CL6_Line_Full

Xform

assembly

Other

PL_001_ElectronicsAssembly

450

N_05_Assembly

/World/CL6_Line_Full/N_05_Assembly

Xform

assembly

Other

105001

Practical Applications and Next Steps#

Real-World Use Cases#

The CSV export you’ve created enables:

Use Case

Data Application

Business Value

Maintenance Planning

Asset locations and schedules

Preventive maintenance optimization

Compliance Reporting

Safety equipment inventory

Regulatory compliance documentation

Facility Management

Reviewed inventory of tagged prims

Integration with CMMS systems

Change Management

Before/after asset comparisons

Impact assessment and planning

Extending the Scripts#

You can enhance these scripts by:

  • Adding filters for specific production lines or areas

  • Including geometric data like bounding box dimensions

  • Collecting material information for procurement planning

  • Integrating with databases for real-time facility management

  • Generating reports in different formats (JSON, Excel, XML)

Integration With Facility Systems#

This scripting approach transforms your visual digital twin into a data-rich facility management tool, bridging the gap between 3D visualization and operational business systems. The automated inventory generation you’ve learned represents a fundamental capability for industrial digital twin applications.