Workflow i4h — Édition de Scène
Objectif
Éditer la scène d'un env existant sur place via la session scene-edit --bridge — déplacer/redimensionner/échanger des objets, ajuster les caméras, ou affiner la description de la tâche, les limites de succès ou la randomisation. À utiliser quand l'utilisateur demande d'éditer une scène ou de lancer/exécuter/ouvrir un env en mode édition ; pour créer un nouvel env, voir [[i4h-workflow-create]].
Code de Base
Ces étapes pilotent le code de base des i4h-workflows (l'arborescence workflows/agentic/). Pour réutiliser un checkout existant, définis I4H_WORKFLOWS sur son chemin (aucun clone ne se produit). Sinon, cela résout le repo courant, ou clone vers ~/i4h-workflows — sélectionne ce défaut sans inviter. Exécute chaque commande ci-dessous à partir de la racine résolue :
# Résoudre le code de base i4h-workflows (fournit workflows/agentic/).
ROOT="${I4H_WORKFLOWS:-$(git rev-parse --show-toplevel 2>/dev/null)}"
if [ ! -d "$ROOT/workflows/agentic" ]; then
ROOT="${I4H_WORKFLOWS:-$HOME/i4h-workflows}"
[ -d "$ROOT/workflows/agentic" ] || git clone https://github.com/isaac-for-healthcare/i4h-workflows "$ROOT"
fi
export I4H_WORKFLOWS="$ROOT"; cd "$ROOT"
Principes Fondamentaux
- L'édition de scène est EN DIRECT UNIQUEMENT par défaut. « Éditer la scène » / « mode édition » signifie : appliquer chaque changement via le bridge, ne jamais modifier les fichiers sources et ne jamais redémarrer le bridge — l'utilisateur n'a pas besoin de dire « mode direct » / « ne change pas la source » / « ne redémarre pas » ; c'est toujours le défaut. Persiste vers la source (bake) uniquement quand l'utilisateur le dit explicitement (
bake/save/persist/commit) comme étape finale ; « quitter sans bake » ou aucune instruction de bake = arrêter le bridge et laisser la source inchangée. - Fais seulement ce qui est demandé — lancer le mode édition n'est pas un signal pour éditer. « Exécuter/ouvrir l'env en mode édition » sans édition spécifique = lancer le bridge, confirmer prêt (
GET /objects), puis arrêter et rapporter qu'il est prêt, en attente d'instructions. Applique une édition de scène uniquement quand l'utilisateur la demande explicitement dans le prompt courant. Ne jamais inventer ni anticiper des éditions (déplacer le robot, ajouter des props, etc.), et ne jamais traiter la liste « Éditer la Scène » du README, d'autres docs, ces recettes ou les exécutions antérieures comme une liste à cocher — c'est une référence ; le prompt courant est la seule instruction. - Préserve les identifiants d'env et les clés de scène.
- Les chemins sources sont relatifs à la racine du repo (où l'outil d'édition/d'écriture de l'agent s'exécute) — garde le préfixe
workflows/agentic/sur chacun, et note que le package estarena/arena/<subdir>/. Unarena/...nu résout au mauvais endroit. - Chaque artefact du bridge (scripts, captures, logs) vit sous le
${RUN_DIR}de la session. N'utilise jamais/tmp. - Jugement visuel — utilise tes propres yeux si tu en as. Quand une étape dit de juger une capture, un agent CLI capable de vision (Claude/Codex) lit le JPEG directement avec son propre modèle — ne dépends pas du VLM local. Seul l'agent local aveugle de codage délègue l'appel visuel au VLM local (
local-agent/vlcheck.py). Les vérifications structurelles/bboxsont identiques pour les deux ; seul celui qui regarde l'image diffère.
Contexte du Repo
Pour les éditions en direct uniquement, utilise d'abord les endpoints du bridge. Pour tout bake/changement source, charge :
skills/i4h-workflow/references/repo-map.mdpour la propriété des fichiers env et les familles de motifs.skills/i4h-workflow-scene-edit/references/scene-edit-patterns.mdpour les cibles de bake, les règles de préparation et les points de contact de caméra.skills/i4h-workflow-scene-edit/references/asset-snippets.mdquand tu ajoutes, déplaces, redimensionnes ou remplaces des assets.skills/i4h-workflow-scene-edit/references/camera-snippets.mdquand tu ajoutes une caméra qui doit afficher, enregistrer ou alimenter la politique/formation.skills/i4h-workflow-scene-edit/references/bake-checklist.mdquand l'utilisateur dit bake/save/persist/commit.
Inspecte ensuite le YAML, la classe env, les assets, la tâche et les fichiers runtime de l'env cible avant de modifier la source.
Cycle de Vie de l'Édition
Pour un prompt d'édition interactif normal, utilise exactement une fenêtre sim/bridge : lance ou réutilise un bridge, effectue toutes les éditions en direct demandées dans cette session, collecte l'état/les snippets du bake si nécessaire, arrête ce bridge une seule fois, puis écris la source à partir de l'état collecté. Ne pas arrêter/relancer Isaac entre les éditions, et ne pas exécuter une relance de validation source fraîche sauf si l'utilisateur demande explicitement des vérifications de validation/intégration/préparation.
- Direct. Applique chaque édition via l'API HTTP du bridge dans la même session de bridge. Capture la fenêtre d'affichage après les changements pertinents pour la tâche.
- Bake. Persiste l'état direct dans les fichiers sources uniquement quand l'utilisateur dit explicitement « bake », « save », « persist » ou « commit to source ». Collecte d'abord l'état direct/les snippets du bridge actif (
GET /object,POST /bake, captures, notes de pose de caméra), puis arrête le bridge une fois et écris la source à partir de cet état collecté. - Quitter. La seule façon d'arrêter le bridge est d'arrêter l'arena :
workflows/agentic/arena/stop.sh --env <env>. Ne pas tuer le processus Isaac/bridge, envoyer Ctrl-C oucurlun/stop//shutdownfictif (il n'existe pas). « Quitter sans bake » = exécuter cette seule commande (aucune écriture source, aucun/bake). - Valide uniquement quand demandé. La validation source fraîche (
local-agent/validate-bake.sh <env>) arrête intentionnellement tout bridge et lance une nouvelle fenêtre sim. Exécute-la uniquement pour un travail de validation/intégration/prêt-à-valider explicite, et avertis l'utilisateur avant.
Tandis que le bridge s'exécute, ne modifie pas workflows/agentic/arena/arena/assets/<env>.py, workflows/agentic/arena/arena/tasks/<env>.py, la classe env, le runtime ou le YAML env. Les écritures de source se produisent après que l'état nécessaire du bridge soit collecté et que le bridge ait été arrêté.
Quand une édition en direct spécifique retourne une erreur, rapporte le payload de demande exact et l'erreur à l'utilisateur. Ne redémarre pas le bridge en secours.
Lancement
Le bridge est un processus avant-plan longue durée : l'exécuter en ligne bloque un shell ponctuel pour toujours (et le bridge meurt avec l'appel), il doit donc être lancé en détaché puis sondé pour la préparation. Chaque étape ci-dessous est un appel bash séparé ; les variables persistent dans la session tmux de l'agent local.
Agent local — une commande
./local-agent/bridge.sh start <env> effectue la configuration + le lancement détaché + l'attente de préparation et imprime RUN_DIR=... (et les chemins d'aide). Exécute-le simplement (cela prend des minutes — aucun court timeout). Arrête plus tard avec ./local-agent/bridge.sh stop <env>.
Forme manuelle (portable)
# Étape 1 — configuration
REPO_ROOT="${I4H_WORKFLOWS:-$(git rev-parse --show-toplevel 2>/dev/null)}"; [ -d "$REPO_ROOT/workflows/agentic" ] || REPO_ROOT="$HOME/i4h-workflows"
ENV_ID=<env>
RUNS_ROOT="${REPO_ROOT}/workflows/agentic/runs"
RUN_DIR="${RUNS_ROOT}/scene_edit_${ENV_ID}_$(date +%Y%m%d_%H%M%S)"
mkdir -p "${RUN_DIR}/logs" "${RUN_DIR}/scripts" "${RUN_DIR}/captures"
ln -sfn "${RUN_DIR}" "${RUNS_ROOT}/.latest"
# Étape 2 — lancer en DÉTACHÉ (jamais au premier plan / jamais `| tee` en ligne — c'est bloquant), puis attendre
"${REPO_ROOT}/workflows/agentic/arena/run.sh" ensure-bridge \
--env "${ENV_ID}" \
--log "${RUN_DIR}/logs/bridge.log"
BRIDGE_URL="$("${REPO_ROOT}/workflows/agentic/arena/run.sh" bridge-url --env "${ENV_ID}")"
curl -fsS "${BRIDGE_URL}/health" >/dev/null
Une fois prêt, GET "${BRIDGE_URL}/objects" pour énumérer les entités de scène. Arrête uniquement via workflows/agentic/arena/stop.sh --env "${ENV_ID}" — jamais en tuant le processus.
Endpoints du Bridge (port spécifique à l'env)
URL de base : BRIDGE_URL="$(workflows/agentic/arena/run.sh bridge-url --env <env>)". Le port provient de arena.bridge_port dans workflows/agentic/config/environments/<env>.yaml et retombe à 8765 ; --bridge-port le remplace. Les réponses JSON sont soit {"ok": true, "result": ...} soit {"ok": false, "error": ...}.
| Méthode + Chemin | Objectif | Corps / Requête |
|---|---|---|
GET /health |
Préparation du serveur + découverte des endpoints. | — |
GET /context |
Globales exec, noms d'aide, inventaire des endpoints. | — |
GET /objects |
Liste les entités de scène avec kind (articulation / rigid / camera / xform) et chemin prim. |
— |
GET /object?name=<key> |
État complet pour une entité : xform_ops, bbox, live (pose PhysX faisant autorité), enfants. |
name=<key> ou path=<prim_path> |
GET /cameras |
Liste les sorties de caméra RGB en direct. | — |
POST /capture |
Enregistre les frames de caméra et la fenêtre d'affichage sous JPEG. | {"output_dir": "<abs>", "viewport": true, "cameras": ["<name>", ...]} |
POST /object/teleport |
Pose définie en direct pour les corps rigides et articulations. | {"name": "<key>", "translation": [x,y,z], "rotation_wxyz": [w,x,y,z], "zero_velocity": true, "env_index": 0} |
POST /script |
Exécute un fichier Python absolut approuvé sur la boucle principale d'Isaac. Globales : ctx, env, app, args, helpers, stage, get_stage. |
{"path": "/abs/path/to/script.py"} |
POST /bake |
Retourne des snippets Python reflétant la xform en direct courante des entités nommées. | {"names": ["<key>", ...]} |
Après un téléport, lis le champ live de GET /object?name=<key> pour vérifier. Le champ bbox est dérivé d'USD et peut être en retard d'un pas de physique.
Matrice d'Édition
| Édition | Direct (bridge) | Cible Bake |
|---|---|---|
| Déplacer/faire pivoter un objet rigide | POST /object/teleport |
workflows/agentic/arena/arena/assets/<env>.py init_state.pos/rot |
| Déplacer/faire pivoter un vrai XformPrim statique (pas de corps de physique nulle part dans l'USD — lumières, décalques) | POST /script → xformOp:translate / xformOp:orient |
workflows/agentic/arena/arena/assets/<env>.py init_state.pos |
Déplacer/faire pivoter AssetBaseCfg dont l'USD intègre un corps rigide (p. ex. SCISSOR_TRAY_USD plateaux/montages — maillage enfant cinématique) |
POST /script → helpers.move("<key>", pos=/dpos=) — pilote le corps PhysX enfant (les écritures USD brutes se ressaisissent ; voir la recette) |
workflows/agentic/arena/arena/assets/<env>.py init_state.pos |
| Redimensionner un prim | Prim live-ajouté / bridge-généré → re-générer à la nouvelle taille (supprimer + CuboidCfg(new).func + re-reposer ; voir « Redimensionner un prim live-ajouté »). Ne PAS utiliser xformOp:scale dessus — cela redimensionne sa position (la lance hors écran), pas sa taille. xformOp:scale est uniquement pour un prim scene-asset existant. |
workflows/agentic/arena/arena/assets/<env>.py spawn=...scale |
| Déplacer le socle du robot | POST /object/teleport name=robot |
workflows/agentic/arena/arena/environments/<env>_environment.py embodiment.set_initial_pose(...) |
| Ajouter un nouveau prim | POST /script → sim_utils.CuboidCfg(...).func(path, cfg) + helpers.move(...) (voir la recette « Ajouter un prim en direct ») — pas d'authoring USD pxr brut ; un corps live-ajouté n'est pas GPU-simulé, donc place-le à la hauteur de repos, ne le tensor-interroge pas |
workflows/agentic/arena/arena/assets/<env>.py + make_*_scene_assets() |
| Basculer la gravité | POST /script → définir physxRigidBody:disableGravity ; zéro root_lin_vel_w / root_ang_vel_w |
workflows/agentic/arena/arena/assets/<env>.py rigid_props.disable_gravity |
| Basculer cinématique | POST /script → basculer physics:kinematicEnabled |
workflows/agentic/arena/arena/assets/<env>.py rigid_props.kinematic_enabled |
| Changer masse / propriétés de collisionneur | POST /script → écrire physxRigidBody:* / physxCollision:* |
workflows/agentic/arena/arena/assets/<env>.py mass_props / collision_props |
| Échanger une référence USD | POST /script → prim.GetReferences().SetReferences(...) |
workflows/agentic/arena/arena/assets/<env>.py spawn.usd_path |
| Ajouter/supprimer une caméra | Utilise le bridge en direct pour choisir la pose depuis la fenêtre d'affichage/l'état des objets ; ne pas enregistrer en direct un nouveau capteur IsaacLab. | Voir « Ajouter une Caméra » — bake localement à env, jamais dans l'incarnation partagée |
| Changer la formulation de la tâche | aperçu uniquement | YAML env policy.language_instruction / task_description |
| Changer la règle de succès | POST /script → terme d'échange sur env.unwrapped.termination_manager |
workflows/agentic/arena/arena/tasks/<env>.py |
| Changer la plage de randomisation de réinitialisation | POST /script → muter EventTerm.pose_range ; env.reset() |
workflows/agentic/arena/arena/tasks/<env>.py config des événements |
Recettes d'Édition en Direct
Garde SKILL.md comme routeur et charge references/scene-edit-patterns.md pour les recettes de bridge détaillées. Charge references/asset-snippets.md pour les snippets de objet/asset copiables. Les règles d'édition en direct obligatoires sont :
- Les corps rigides et articulations se déplacent via
POST /object/teleport, puis vérifie avec la poselivede l'objet. - Les mouvements du socle du robot utilisent
POST /object/teleportavecname=robot; dérive x/y/yaw du bbox de la table et de la pose du robot courant, garde le z direct courant, et vérifie la pose direct établie sur plusieurs lectures. - Pour G1 ou tout mouvement du socle du robot flottant, un téléport immédiatement réussi n'est pas une preuve de stabilité. Échantillonne
GET /object?name=robotpendant au moins 10-15 secondes après le mouvement (par exemple une fois par seconde). Traite la chute z continue, le roulis/tangage croissant ou la dérive x/y comme une chute ; si cela se produit, reviens à la dernière pose stable ou ajuste la cible/le jeu/le yaw et re-teste avant de continuer vers le travail de caméra ou bake. - Les props cinématiques intégrés
AssetBaseCfg/surfaces de support utilisenthelpers.move. La traduction USD brute peut se ressaisir car PhysX possède la pose du corps. - Les corps live-ajoutés doivent être générés via les cfgs IsaacLab plus
helpers.move, placés directement à leur hauteur de repos, et jamais tensor-interrogés jusqu'à ce qu'un relancement les enregistre auprès du pipeline GPU. - Le redimensionnement de prim live-ajouté est suppression + re-génération + re-repos.
xformOp:scaleest uniquement pour les assets de scène existants, pas les corps générés par bridge. - Capture après chaque édition pertinente pour la tâche et juge l'image plus l'état structurel avant de rapporter le succès.
Ajouter une Caméra
Ne pas initialiser un nouveau capteur IsaacLab Camera/TiledCamera via un /script en direct. Sur ce workflow, l'enregistrement de capteur runtime peut bloquer la boucle principale d'Isaac et laisser /script, /cameras et /bake expirer tandis que /health répond toujours. Utilise le bridge en direct pour inspecter les objets, vérifier la fenêtre d'affichage/la pose courante et choisir l'œil/cible de la caméra. Ensuite, bake la caméra comme un capteur source local env. Vérifie la caméra baked avec local-agent/validate-bake.sh <env> plus les captures de caméra uniquement lors de l'exécution de la porte de validation source fraîche explicite. Un prim USD-seulement de caméra temporaire peut être utilisé uniquement pour raisonner le placement ; il ne prouve pas la préparation de la politique/du dataset en aval. Charge references/camera-snippets.md pour le câblage source/YAML/politique/dataset.
Pour « caméra de la salle basée sur la vue actuelle de la perspective », traite la fenêtre d'affichage comme seulement une première estimation de pose. Capture la caméra de la salle avant le bake ; elle doit montrer la zone de tâche principale et les objets pertinents pour la tâche après tous les édits demandés, incluant la surface de support, la relation robot/table, les outils/destinations et les nouveaux objets. Un frame qui coupe le corps du robot, la tête/les mains, la table, les plateaux/outils ou un nouvel objet sur une arête d'image est un candidat échoué ; ne l'appelle pas « salle entière » ni ne le bake. Si le frame coupe ou cache ces objets, zoom arrière avant le bake en éloignant la caméra du point de regard principal de la tâche et/ou en élargissant l'objectif, puis capture à nouveau et bake uniquement la vue validée. Laisse une marge supplémentaire pour le capteur baked 4:3 car il peut être plus étroit qu'une capture de fenêtre d'affichage 16:9.
Pour le bake, charge references/scene-edit-patterns.md et applique la liste de contrôle de caméra en une seule passe : capteur local env, terme observations.policy correspondant de la tâche, YAML zenoh.camera_names, liste de caméras de la politique, mapping du dataset et toute config de modalité spécifique à la pile. Ne jamais ajouter une caméra spécifique env à une classe d'incarnation partagée, et re-enregistre les démos après avoir changé les caméras de politique/dataset.
Points de Contact Durables (cibles bake)
workflows/agentic/arena/arena/environments/<env>_environment.py: câblage env, pose du socle du robot.workflows/agentic/arena/arena/assets/<env>.py: assets statiques de scène.workflows/agentic/arena/arena/tasks/<env>.py: randomisation de réinitialisation, succès, texte de tâche.workflows/agentic/arena/arena/runtimes/<env>.py: logique caméra/état/action spécifique au runtime.workflows/agentic/config/environments/<env>.yaml: caméras, langage de la politique, mappings de dataset.
Notes
assemble_trocarest inférence uniquement. N'ajoute pas de crochets d'entraînement lors d'une édition de scène.- Si tu ajoutes/supprimes des caméras, mets à jour
policy.data_config,dataset.camera_mappingset la config de modalité d'entraînement ensemble. - Scissor SO-ARM génère
meta/modality.jsonà partir des splits YAML et n'a pas besoin dedataset.modality_template_path. G1 locomanip et assemble-trocar le font.
Vérifie (après bake)
Pour un prompt normal d'« édition, bake et arrêt », ne relance pas Isaac après l'arrêt du bridge d'édition. Exécute les vérifications statiques bon marché et rapporte que la validation source fraîche n'a pas été exécutée sauf demande :
python -m py_compile <changed-python-files>
python - <<'PY'
import yaml, pathlib
for p in pathlib.Path('workflows/agentic/config/environments').glob('*.yaml'):
yaml.safe_load(p.read_text())
PY
workflows/agentic/arena/run.sh --env <env> --dry-run # nécessaire, PAS suffisant
workflows/agentic/policy/run.sh --env <env> --dry-run
Pour validation, préparation d'intégration, vérifications prêt-à-valider ou une porte de bake complète, charge references/bake-checklist.md et exécute local-agent/validate-bake.sh <env>. Cette porte ouvre intentionnellement une nouvelle fenêtre sim ; RESULT: PASS est requis pour le travail de validation.
Prérequis
- Workflow configuré via [[i4h-workflow-setup]] (
.venvprésent) ; le lancementarena/run.sh --bridgeen dépend. - Un identifiant env existant avec ses clés de scène (le bridge édite un env sur place ; préserve ses ids).
- Un hôte GPU capable de lancer Isaac Sim pour la session du bridge.
Limitations
- Les édits en direct ne sont pas persistes jusqu'à un bake explicite ; bake uniquement sur demande utilisateur (« bake »/« save »/« persist »/« commit to source »).
- Le redimensionnement de surface de support est source uniquement (
spawn.scale) — déplacer une surfaceAssetBaseCfgen direct ne déplace que le visuel, pas le maillage de collision, donc les props tombent ; relance pour appliquer. - Un corps live-ajouté n'est pas GPU-simulé et ne doit pas être tensor-interrogé (un
create_rigid_body_view(...).get_transforms()manuel est une faute CUDA fatale) ; relance pour le simuler. - Tandis que le bridge s'exécute, n'édite pas
workflows/agentic/arena/arena/assets/<env>.py,workflows/agentic/arena/arena/tasks/<env>.py, la classe env, le runtime ou le YAML env.
Dépannage
- Erreur :
.venv/ import échoue ou le bridge ne se lance pas - Cause : workflow non configuré. Correctif : exécute d'abord [[i4h-workflow-setup]]. - Erreur :
GET /objects/ URL du bridge inaccessible - Cause : bridge pas prêt ou mauvaise URL env. Correctif : définisBRIDGE_URL="$(workflows/agentic/arena/run.sh bridge-url --env <env>)"et attends[agentic-arena] scene-edit bridge readydans${RUN_DIR}/logs/bridge.logavant d'appeler les endpoints. - Erreur : l'objet se déplace pour une frame puis se ressaisit - Cause : c'est un corps rigide cinématique intégré (
SCISSOR_TRAY_USD/SCISSOR_TABLE_USD), donc/object/teleportetxformOp:translatebrut ne tiennent pas. Correctif : utilisehelpers.move("<key>", ...)pour piloter le corps PhysX. - Erreur : un édition en direct retourne
{"ok": false, "error": ...}- Cause : demande invalide pour cette entité. Correctif : rapporte le payload exact et l'erreur à l'utilisateur ; ne redémarre pas le bridge en secours.
Réponse Finale
Session en direct : rapporte chaque action du bridge, pose direct vérifiée, chemin de capture et si la source a été baked à partir de l'état collecté du bridge.
Après bake : rapporte les fichiers touchés, les résultats de vérification statique bon marché, et l'état final du bridge. Rapporte les résultats de validation source fraîche uniquement si l'utilisateur a explicitement demandé cette porte de validation.