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
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:
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.
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
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.