Add trajectory variants between fixed waypoints - #651
Open
Yuan-Xinyi wants to merge 1 commit into
Open
Yuan-Xinyi wants to merge 1 commit into
Yuan-Xinyi wants to merge 1 commit into
Conversation
Yuan-Xinyi
force-pushed
the
claude/trajectory-augmentation-multimodal-51a454
branch
from
September 17, 2026 14:14
9e5f087 to
34535e8
Compare
|
Affordance expansion varies where the robot makes contact. This adds the other half: once a task's waypoints are settled, produce several different ways to execute them, so imitation learning and reinforcement-learning post-training see more than one solution per task. Operators (expansion/operators.py): - via_points routes a free phase through sampled interior knots using clamped cubic Hermite segments, so several knots change the shape of the path rather than only its amplitude. - nullspace_residual projects a residual onto the null space of task Jacobians supplied by the caller, changing arm posture while holding the declared task rows to first order. A fully constrained task raises instead of silently returning the reference. - retime gains a bounded within-phase profile (uniform, ease_in, ease_out) that changes the velocity profile without changing the path or the phase duration. The uniform path is arithmetically unchanged. - perturb_approach_direction places standoff poses on a cone while leaving the contact transform exact. Every qpos operator uses an envelope that is zero with zero derivative at both phase endpoints, and contact and hold phases are never touched, so annotated waypoints stay bit-identical. expansion/variants.py gives the existing expansion contracts their first caller: plan_trajectory_variants lists the enabled factor combinations with ordinal zero as the unmodified reference, expand_trajectory_variants deduplicates variants for one fixed scene on measured geometry and timing, and expand_row_variants assigns one variant per independent execution row with an ordinal_offset so repeated rollouts start further along the list. Every rejection is counted and reported rather than dropped silently. Configuration replaces the disabled ik and approach factor stubs with real settings and adds spatial.via_count and timing.profiles. contact, contact_timing and recovery remain unimplemented and are still rejected. StackBlocksTwo-v1 is the packaged host integration, opt-in through extensions.trajectory_variants and shipped disabled so its default behavior is unchanged. It reads phase boundaries from the atomic actions' own named trajectory segments rather than re-deriving their step arithmetic, and retimes only phases at or after the lift so the shared clear_dynamics() step index stays aligned across environments. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Yuan-Xinyi
force-pushed
the
claude/trajectory-augmentation-multimodal-51a454
branch
from
September 17, 2026 14:32
34535e8 to
3da4783
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Affordance expansion (#637) varies where the robot makes contact. This PR covers the other half: once a task's waypoints are settled, produce several different ways to execute them, so downstream imitation learning and reinforcement-learning post-training see more than one solution per task instead of many copies of one.
The two compose. An affordance variation produces a new set of waypoints, and variant expansion then produces several ways of executing that set.
This also gives
motion/expansion/its first caller. The package already shipped candidate contracts, coverage bookkeeping andGenerationSession, but nothing in the repository used them, and only two of its seven declared augmentation factors were implemented.Main changes
Operators (
expansion/operators.py)via_pointsroutes an allowed free phase through sampled interior knots, interpolated with clamped cubic Hermite segments.joint_residualadds one fixed-shape bump per phase; with two or more knots this changes the shape of the path, not only its amplitude.nullspace_residualprojects a residual onto the null space of task Jacobians supplied by the caller, changing arm posture while holding the declared task rows. A fully constrained task raises rather than silently returning the reference.retimegains a bounded within-phaseprofile(uniform,ease_in,ease_out) that redistributes time inside a phase without changing the path or the phase's total duration, so one path yields several velocity profiles. Theuniformpath is arithmetically unchanged from the existing operator.perturb_approach_directionplaces standoff poses on a cone around a nominal approach direction while leaving the contact transform exact.Every qpos operator uses an envelope that is zero, with zero derivative, at both endpoints of the phase it modifies, and
contactandholdphases are never touched. Annotated waypoints, contact windows and dwell durations stay bit-identical to the reference.Variant generation (
expansion/variants.py, new)plan_trajectory_variantslists the enabled factor combinations. Ordinal zero is always the unmodified reference; later ordinals cycle through the enabled joint-path operators, then the duration scales, then the time warps.expand_trajectory_variantscollects variants for one fixed scene, rejecting proposals that an operator refuses, that fail sampled motion limits, or whose measured geometry and timing duplicate an accepted row.expand_row_variantsassigns one variant per independent execution row, meaning one per parallel environment with its own randomized scene. Nothing is deduplicated across rows because geometries from different scenes are not comparable, and a rejected row falls back to its own reference rather than leaving an environment without a command stream. Itsordinal_offsetlets repeated rollouts start further along the list instead of repeating the first combinations.CoverageIndex.family_ofexposes the resolved geometry family, sogeometry_family_idreflects measured grouping rather than a fresh digest.Configuration (
expansion/cfg.py)ikandapproachwere_DisabledFactorCfgstubs that raised when enabled; they are now real settings.spatialgainsvia_countandtiminggainsprofiles.contact,contact_timingandrecoveryremain unimplemented and are still rejected when enabled.Packaged host integration
StackBlocksTwo-v1is opt-in throughextensions.trajectory_variantsand ships disabled, so its default behavior is unchanged. Two points worth review attention:TrajectorySegmentranges rather than from re-deriving their internal step arithmetic. A guard fails loudly if the compiledclosesegment stops lining up with_insert_grasp_hold's insertion index, instead of silently mis-annotating a contact phase.Each demonstration segment records the episode index, the variant assigned to every row, per-row sample lengths, per-row resolved phase boundaries, and fallback counts under
metadata["trajectory_variants"].What this does and does not guarantee
Stated explicitly because these are easy to overstate:
nullspace_residualholds the declared task rows to first order only. Phase endpoints stay exact because the envelope vanishes there, but interior samples drift with linearization error and need forward-kinematics verification by the host.BaseSolver.get_jacobianreturns a base-frame Jacobian, so dropping its angular-z row removes rotation about base z, not about the tool axis. Those coincide for the stack-blocks top-down grasp, which is why it declarestask_rows: [0, 1, 2, 3, 4]; a side-grasping task must choose differently.GenerationSession's episode budgets, commit receipts and durable persistence are not used by this path. Persistence stays with the environment's own dataset manager.Example commands
Run these in the environment where EmbodiChain is installed. Set
enabledtotrueinembodichain_tasks/configs/tasks/manipulation/tableware/stack_blocks_two/env.json, then:Each parallel environment receives a different variant, so
--num_envssets how many variants one rollout collects. Append--record_trajectory --trajectory_save_dir ./outputs/trajectory_variantsto save the joint trajectories.Documentation
docs/source/overview/sim/motion/trajectory_variants.md: the factor table, what is and is not guaranteed, the runnable example, how to choose between the APIs, the Jacobian frame caveat, and the limitations.docs/source/api_reference/embodichain/embodichain.lab.sim.motion.expansion.rst: the new operators, the variants section, and corrected configuration prose.agent_context/topics/motion-planning/motion-planning.mdandagent_context/MAP.yaml: ownership, boundaries and routing keywords.Type of change
Validation
black --check ./python docs/scripts/check_api_docs.pycontext.py checkand routingpytest tests/sim/motion(full, 36 min)pytest tests/sim/motion/expansionpytest tests/gym/envs/test_stack_blocks_two_trajectory_variants.pypytest tests/gym/envs/test_official_task_layout.py tests/gym/utils/test_gym_utils.pypytest tests/test_agent_context_*.py tests/docsRebased onto
mainafter #648 and #650 landed. #648 rewroteagent_context/topics/motion-planning/motion-planning.mdinto a terser "find the owner" format specifically to prevent PR-driven content growth, so the context update here was re-resolved onto that format as a minimal delta (+17 lines) rather than the longer section originally written. Neither #648 nor #650 touches Python runtime code, so thetests/sim/motionresult above still applies; the gates and affected tests were re-run after the rebase and again after the rename described below.Not run: the Sphinx docs build. Sphinx is not installed in the development environment used here, so the new page's cross-references were checked for syntax and relative paths only. The CI
buildjob covers it.The task integration is tested without a simulator: the tests drive the task's own planning helpers with stand-in compiled segments, pinning where fixed phases land, that contacts and dwells survive augmentation bit-identically, that shorter rows hold their final command, that successive episodes move further along the combination list, and that the solver Jacobian columns are permuted into the task's joint order.
Naming
An earlier revision of this branch called these "trajectory modes". That word was invented for this change; the expansion package already says "variant" (
CoverageIndexdocuments "timing variants per geometry") and #637 says "variations". The API, configuration key, module, docs and tests were renamed tovariantso the change reuses the repository's existing vocabulary instead of adding a parallel term.Checklist
black .command to format the code base.python docs/scripts/check_api_docs.py), if applicable🤖 Generated with Claude Code