Omni People Standalone Apps broken in Isaac Sim 4.5

Isaac Sim Version

4.5.0
4.2.0
4.1.0
4.0.0
4.5.0
2023.1.1
2023.1.0-hotfix.1
Other (please specify):

Operating System

Ubuntu 22.04
Ubuntu 20.04
Windows 11
Windows 10
Other (please specify):

GPU Information

  • Model: 3080 GTX
  • Driver Version:

Topic Description

Detailed Description

I want to have characters animated via standalone python scripts so I can use them in a larger simulation. For now, being able to only use GoTo command is enough.

In Isaac 4.2, there are a few applications that can use Omni People using a similar logic. For example:

  1. GitHub - cadop/siborg-simulate-walk: An Omniverse extension to quickly add target goal positions for virtual agents · GitHub
  2. API Reference — Pegasus Simulator documentation

In short, they use the commands

import omni.anim.graph.core as ag
omni.kit.commands.execute(
            "RemoveAnimationGraphAPICommand",
            paths=[Sdf.Path(self.skel_root_prim.GetPrimPath())]
        )
        omni.kit.commands.execute(
            "ApplyAnimationGraphAPICommand",
            paths=[Sdf.Path(self.skel_root_prim.GetPrimPath())],
            animation_graph_path=Sdf.Path(animation_graph.GetPrimPath())
        )
character_graph = ag.get_character(skel_root_path)
character_graph.set_variable("Walk", 1.0)

in a world with a person and a Navmesh, to apply a desired pose to a sim person, and it works fine in 4.2.

Those applications are now broken with 4.5.

Steps to Reproduce

To help the discussion, I have created a minimal working example, which works on 4.2 but not on 4.5 (launch with Isaac’s <python.sh>):

from isaacsim import SimulationApp

# Launch Isaac Sim
simulation_app = SimulationApp({"headless": False})

import omni
import carb
import random
import numpy as np
from dataclasses import dataclass
try:
    # Isaac Sim 4.2
    from omni.isaac.core.utils.extensions import enable_extension
except ModuleNotFoundError:
    # Isaac Sim 4.5
    from isaacsim.core.utils.extensions import enable_extension

# Enable the extensions required for animated characters
enable_extension("omni.kit.scripting")
enable_extension("omni.anim.graph.core")
enable_extension("omni.anim.graph.schema")
enable_extension("omni.anim.graph.ui")
enable_extension("omni.anim.people")

from pxr import Gf, Sdf
import omni.client
import omni.anim.graph.core as ag
from omni.anim.people import PeopleSettings
try:
    from omni.isaac.core import World
    from omni.isaac.core.utils import prims
    from omni.isaac.nucleus import get_assets_root_path
except ModuleNotFoundError:
    from isaacsim.core.api.world import World
    from isaacsim.core.utils import prims
    from isaacsim.nucleus import get_assets_root_path
from omni.usd import get_stage_next_free_path

# Make sure extensions are fully loaded
simulation_app.update()

# These lines are needed to restart the USD stage and make sure that the people extension is loaded
import omni.usd
omni.usd.get_context().new_stage()

@dataclass
class PersonState:
    """Pose container for the simulated character."""

    position: np.ndarray = np.array([0.0, 0.0, 0.0])
    orientation: np.ndarray = np.array([0.0, 0.0, 0.0, 1.0])


def _quat_from_yaw(yaw: float) -> np.ndarray:
    """Return [x, y, z, w] quaternion from a yaw angle."""
    half = yaw * 0.5
    return np.array([0.0, 0.0, np.sin(half), np.cos(half)])


class Person:
    """Helper that spawns and animates a character."""

    setting_dict = carb.settings.get_settings()
    people_asset_folder = setting_dict.get(PeopleSettings.CHARACTER_ASSETS_PATH)
    character_root_prim_path = setting_dict.get(PeopleSettings.CHARACTER_PRIM_PATH)
    assets_root_path = None

    if not character_root_prim_path:
        character_root_prim_path = "/World/Characters"

    if people_asset_folder:
        assets_root_path = people_asset_folder
    else:
        root_path = get_assets_root_path()
        if root_path is not None:
            assets_root_path = f"{root_path}/Isaac/People/Characters"

    @staticmethod
    def _find_skel_root(stage, path):
        """Locate the SkelRoot prim below ``path``."""
        prim = stage.GetPrimAtPath(path)
        if prim.GetTypeName() == "SkelRoot":
            return prim, path
        for child in prim.GetAllChildren():
            child_prim, child_path = Person._find_skel_root(stage, f"{path}/{child.GetName()}")
            if child_prim:
                return child_prim, child_path
        return None, None

    def __init__(self, world: World, name: str, character: str, init_pos=None, init_yaw: float = 0.0) -> None:
        init_pos = init_pos or [0.0, 0.0, 0.0]
        self._world = world
        self._stage = world.stage

        self._state = PersonState(np.array(init_pos), _quat_from_yaw(init_yaw))
        self._target_position = np.array(init_pos)
        self._target_speed = 0.0
        self._target_yaw = float(init_yaw)

        self._stage_prefix = get_stage_next_free_path(self._stage, f"{Person.character_root_prim_path}/{name}", False)
        self.character_usd_path = self.get_character_usd_path(character)

        self.spawn_character(self.character_usd_path, self._stage_prefix, init_pos, init_yaw)
        self.character_graph = None
        self.add_animation_graph()

        self._world.add_physics_callback(self._stage_prefix + "/state", self.update_state)
        self._world.add_physics_callback(self._stage_prefix + "/update", self.update)

    # ------------------------------------------------------------------
    def update(self, dt: float) -> None:
        if not self.character_graph and self.skel_root_prim:
            self.character_graph = ag.get_character(self.skel_root_path)

        dist = np.linalg.norm(self._target_position - self._state.position)
        if dist > 0.1 and self.character_graph:
            self.character_graph.set_variable("Action", "Walk")
            self.character_graph.set_variable("PathPoints", [carb.Float3(self._state.position), carb.Float3(self._target_position)])
            self.character_graph.set_variable("Walk", self._target_speed)
        else:
            if self.character_graph:
                self.character_graph.set_variable("Walk", 0.0)
                self.character_graph.set_variable("Action", "Idle")
            if dist <= 0.1:
                attr = self.character_prim.GetAttribute("xformOp:orient")
                quat = Gf.Quatf(Gf.Rotation(Gf.Vec3d(0, 0, 1), self._target_yaw).GetQuat()) if isinstance(attr.Get(), Gf.Quatf) else Gf.Rotation(Gf.Vec3d(0, 0, 1), self._target_yaw).GetQuat()
                attr.Set(quat)

    def update_target_pose(self, position, yaw, walk_speed=1.0) -> None:
        self._target_position = np.array(position)
        self._target_yaw = float(yaw)
        self._target_speed = walk_speed
        attr = self.character_prim.GetAttribute("xformOp:orient")
        quat = Gf.Quatf(Gf.Rotation(Gf.Vec3d(0, 0, 1), self._target_yaw).GetQuat()) if isinstance(attr.Get(), Gf.Quatf) else Gf.Rotation(Gf.Vec3d(0, 0, 1), self._target_yaw).GetQuat()
        attr.Set(quat)

    def update_state(self, dt: float) -> None:
        if not self.character_graph and self.skel_root_prim:
            self.character_graph = ag.get_character(self.skel_root_path)
        if self.character_graph:
            pos = carb.Float3(0, 0, 0)
            rot = carb.Float4(0, 0, 0, 0)
            self.character_graph.get_world_transform(pos, rot)
            self._state.position = np.array([pos[0], pos[1], pos[2]])
            self._state.orientation = np.array([rot.x, rot.y, rot.z, rot.w])

    # ------------------------------------------------------------------
    def spawn_character(self, usd_file: str, stage_name: str, init_pos, init_yaw) -> None:
        if not self._stage.GetPrimAtPath(Person.character_root_prim_path):
            prims.create_prim(Person.character_root_prim_path, "Xform")

        if not self._stage.GetPrimAtPath(Person.character_root_prim_path + "/Biped_Setup"):
            setup_prim = prims.create_prim(
                Person.character_root_prim_path + "/Biped_Setup",
                "Xform",
                usd_path=Person.assets_root_path + "/Biped_Setup.usd",
            )
            setup_prim.GetAttribute("visibility").Set("invisible")

        self.character_prim = prims.create_prim(stage_name, "Xform", usd_path=usd_file)
        self.character_prim.GetAttribute("xformOp:translate").Set(Gf.Vec3d(float(init_pos[0]), float(init_pos[1]), float(init_pos[2])))
        orient_attr = self.character_prim.GetAttribute("xformOp:orient")
        quat = Gf.Quatf(Gf.Rotation(Gf.Vec3d(0, 0, 1), float(init_yaw)).GetQuat()) if isinstance(orient_attr.Get(), Gf.Quatf) else Gf.Rotation(Gf.Vec3d(0, 0, 1), float(init_yaw)).GetQuat()
        orient_attr.Set(quat)

        prim, path = Person._find_skel_root(self._stage, stage_name)
        self.skel_root_path = path
        self.skel_root_prim = prim

    def add_animation_graph(self) -> None:
        if not self.skel_root_prim:
            return
        animation_graph = self._stage.GetPrimAtPath(Person.character_root_prim_path + "/Biped_Setup/CharacterAnimation/AnimationGraph")
        omni.kit.commands.execute("RemoveAnimationGraphAPICommand", paths=[Sdf.Path(self.skel_root_prim.GetPrimPath())])
        omni.kit.commands.execute("ApplyAnimationGraphAPICommand", paths=[Sdf.Path(self.skel_root_prim.GetPrimPath())], animation_graph_path=Sdf.Path(animation_graph.GetPrimPath()))
        self.character_graph = ag.get_character(self.skel_root_path)

    # ------------------------------------------------------------------
    @staticmethod
    def get_character_usd_path(agent_name: str) -> str | None:
        agent_folder = f"{Person.assets_root_path}/{agent_name}"
        result, _ = omni.client.stat(agent_folder)
        if result != omni.client.Result.OK:
            carb.log_error("Character folder does not exist.")
            return None
        character_usd = Person.find_usd_file(agent_folder)
        return f"{agent_folder}/{character_usd}" if character_usd else None

    @staticmethod
    def find_usd_file(folder: str) -> str | None:
        result, items = omni.client.list(folder)
        if result != omni.client.Result.OK:
            carb.log_error(f"Unable to read character folder path at {folder}")
            return None
        for item in items:
            if item.relative_path.endswith(".usd"):
                return item.relative_path
        carb.log_error(f"Unable to find a .usd file in {folder} character folder")
        return None


class SimpleApp:
    """Application that drives a single person."""

    def __init__(self) -> None:
        self.timeline = omni.timeline.get_timeline_interface()
        self.world = World(stage_units_in_meters=1.0)
        self.world.scene.add_default_ground_plane()

        self.person = Person(
            self.world,
            "person",
            "original_male_adult_construction_02",
            init_pos=[0.0, 0.0, 0.0],
            init_yaw=0.0,
        )

        self.world.reset()

    def run(self) -> None:
        self.timeline.play()
        AREA = 5.0
        while simulation_app.is_running():
            if random.random() < 0.01:
                pos = [random.uniform(-AREA, AREA), random.uniform(-AREA, AREA), 0.0]
                speed = random.uniform(0.5, 2.0)
                self.person.update_target_pose(pos, 0.0, speed)
            self.world.step(render=True)
        self.timeline.stop()
        simulation_app.close()


def main() -> None:
    app = SimpleApp()
    app.run()


if __name__ == "__main__":
    main()

Screenshots or Videos

On Isaac Sim 4.2:

Error Messages

On Isaac Sim 4.5:


2025-06-10 13:45:10 [33,731ms] [Warning] [omni.usd] Coding Error: in HasAPI at line 164 of /builds/omniverse/usd-ci/USD/pxr/usd/usd/prim.cpp -- HasAPI: Invalid unknown schema type (TfType::_Unknown) 

2025-06-10 13:45:10 [33,871ms] [Error] [asyncio] Task exception was never retrieved
future: <Task finished name='Task-133' coro=<VariablesService._pending_dirty_handler() done, defined at /home/edson/.local/share/ov/pkg/isaac-sim-4.5.0/extscache/omni.anim.graph.core-106.5.1+106.5.0.lx64.r.cp310/omni/anim/graph/core/scripts/variables_service.py:115> exception=ErrorException(Error in 'pxrInternal_v0_22__pxrReserved__::UsdPrim::HasAPI' at line 170 in file /builds/omniverse/usd-ci/USD/pxr/usd/usd/prim.cpp : 'HasAPI: provided schema type ( AnimGraphSchemaAnimationGraphAPI ) is not an applied API schema type.')>
Traceback (most recent call last):
  File "/home/edson/.local/share/ov/pkg/isaac-sim-4.5.0/extscache/omni.anim.graph.core-106.5.1+106.5.0.lx64.r.cp310/omni/anim/graph/core/scripts/variables_service.py", line 130, in _pending_dirty_handler
    if prim.HasAPI(AnimGraphSchema.AnimationGraphAPI):
pxr.Tf.ErrorException: 
        Error in 'pxrInternal_v0_22__pxrReserved__::UsdPrim::HasAPI' at line 170 in file /builds/omniverse/usd-ci/USD/pxr/usd/usd/prim.cpp : 'HasAPI: provided schema type ( AnimGraphSchemaAnimationGraphAPI ) is not an applied API schema type.'

Additional Information

What I’ve Tried

I have followed the migration guides and none of the related extensions are listed as renamed or deprecated:

enable_extension("omni.anim.graph.core")
enable_extension("omni.anim.graph.schema")
enable_extension("omni.anim.graph.ui")
enable_extension("omni.anim.people")

Additional Context

I understand there is a new way of using Omni People with Isaacsim.Replicator.Agent, but there aren’t any instructions on how to achieve this in a more flexible way through python scripts. I would like help on that.
Ideally, I would like the attached standalone script to be modified in a way that works in Isaac Sim 4.5 (and future releases) or any guidance on how to do so.

Isaacsim.Replicator.Agent:
https://docs.isaacsim.omniverse.nvidia.com/latest/replicator_tutorials/ext_replicator-agent/agent_control.html#add-a-custom-command

Thanks!

Any help/guidance appreciated! Thanks!

You can use the following methods from isaacsim.replicator.agent.core.simulation.SimulationManager:

  • spawn_character_by_idx(spawn_location, spawn_rotation, idx): Spawns a character at a specified location and rotation.
  • setup_all_characters(): Sets up all spawned characters for animation and control.

At this time, we do not have comprehensive documentation for the IRA Python API, as we expect most users to use the GUI or provided example scripts. However, we recognize the need for more detailed API documentation and will look into providing this in future releases.

Please try running your application with the omni.anim.graph.schema extension added into the [dependencies] section of the used .kit file you are using