Hyundai Motor Group Innovation Center Singapore · Apr, Jun 2026 · Digital Twin & Simulation Engineering Intern
Action Graph Code Generator
HMGICS is building a digital twin of its electric-vehicle factory on NVIDIA Isaac Sim, working toward a software-defined dark factory by 2032. Wiring one robot subprogram into an Action Graph by hand took roughly an engineering week, and a single cell has forty or more subprograms across multiple robots. Over the last three months of my internship I scoped, researched and built a proof-of-concept generator that does the wiring automatically, so the remaining work is checking it rather than building it.
01Scoped alone from a one-line brief
The brief was a single sentence: automate the authoring of Action Graphs from robot programs. No acceptance criteria, no prior art inside the team, no spec. I started by writing down the existing manual workflow step by step with its pain points, defined the problem boundaries myself, and reviewed them with the team lead before writing any code. That was a different kind of work from my first three months, where the features were already defined.
Before committing to an approach I evaluated seven candidates: deterministic Python generation, a local LLM, an LLM agent reading structured documentation, a custom API pipeline, and three others. I scored them on predictability, auditability, and whether they could run inside an air-gapped network at all. Deterministic Python won. The output drives the motion of a physical factory robot, so being able to explain every generated line matters more than being clever.
02Ground truth first, generation second
I hand-built a correctly wired compound node for the wiper pick sequence and had the senior developer validate it: node structure, joint correction values, fork and join patterns. That became the reference. Every generated graph was diffed against its USD output until the generated version was indistinguishable from the hand-made one.
The diffing is what surfaced four undocumented OmniGraph behaviours. og.Attribute.set() turned out to be runtime only, silently failing to persist to USD on reload. Target and relationship-typed attributes needed CreateRelationship().SetTargets() instead of the documented setter. Make Array input types reset values to zero unless the calls were sequenced in a particular order. And og.Controller.connect() simply does not work for cross-graph compound port connections, so those relationships had to be authored manually. Each one was found by reading the installed extension source, since there was no internet in that environment.
All four workarounds went into a shared helper module, codegen_utils.py, which wraps node creation, wiring, attribute setting and target-prim relationships. Any future generator script gets the fixes without rediscovering them.
03Built to be handed over
The generator was never the whole deliverable. I wrote a reference suite alongside it covering the node catalogue, the API patterns, the AS-to-graph translation rules, worked examples, every technical decision with the alternatives I rejected, and the parts of Kawasaki AS semantics that matter for code generation.
To check the documentation actually worked, I drafted a user acceptance test: an engineer who has never seen the project generates a compound node using the documentation alone, with no verbal guidance. I also filed the internal AI use-case submission for the workflow and left behind a set of diagnostic and inspection scripts for the team.
next article
Digital Twin Simulation Platform →