Python-only scene, task, and robot-model foundation for the decanting-cell base-placement study. The runtime is independent of Isaac Sim and ROS/MoveIt: one validated SceneSnapshot feeds Pinocchio FK/IK/Jacobians, Coal collision checks, conservative SR task-workspace screening, and the reusable base-candidate evaluator.
For continuation work, read the code architecture, the project handoff, and the Korean run-command guide. The handoff records confirmed process requirements, current results, unresolved decisions, and the fresh-machine checklist.
Implemented in this stage:
- direct USD/USDA/USDC environment loading with triangle-mesh visualization and collision, including parent transforms, units and instances (see USD scenes);
- fixed conveyor, worktable, and camera-support proxies extracted from
cu_usd_simplified.usd; - a synthesized
World Z = 0floor; - the
1.350 x 1.000 x 0.150 mpallet and0...0.760 mlift range; - all five real box sizes and the four alternative lowest-layer corner samples;
- one representative
0.660 x 0.440 x 0.300 mtote; - independently movable UR20 and SR-12iA bases, including mounting height and pedestal;
- the official mesh-based UR20 description as the visualization default;
- a local FANUC/Isaac SR-12iA mesh preparation path and a corrected conservative primitive fallback;
- a provisional UR20 suction proxy; the SR-12iA currently has no process tool;
- an SR-12iA
tool0cutting-task footprint tied to the support-cube top; - nominal display poses aimed toward the pallet (UR20), with the SR-12iA
tool0on the opposite-elbow branch, 0.10 m beyondUncasingLoadFrametoward the worktable, 0.02 m inside the configured task workspace's conveyor-side edge, and aligned toUncaseGroup +Y; - a representative tote rotated 90 degrees so its long axis follows the worktable's short axis;
- the eight-step UR20 process as phase-specific poses and moving-object states;
- fixed-base, arm-only Pinocchio IK and Jacobian-ellipsoid metrics;
- deterministic IK branch retry when a numerically valid endpoint branch has a colliding local joint interpolation segment;
- explicit robot/self/environment/payload collision pairs (the URDFs load with geometry but no collision-pair list);
- separate simultaneous and sequential scenarios, with a conservative Pinocchio-derived SR concurrent exclusion OBB for steps 3 and 4;
- conservative SR box-top checks against the configured footprint, planar 2R joint ranges, and the selected 300/450 mm J3 range;
- an objective-free candidate API and compact JSON report CLI ready for a later optimization loop;
- an explicit finite case-grid cache containing replayable Pinocchio
q, TCP poses, collision diagnostics, and manipulability-ellipsoid axes; - an immutable cache-playback panel that performs no IK during replay;
- a separate on-demand control panel that evaluates one parameter set in the local Python process and immediately presents its in-memory result in Meshcat.
Pixi is not used. Clone the repository at any filesystem location. BGF is reference material rather than a runtime dependency; initialize only its root submodule because the current BGF revision contains an incomplete nested submodule declaration:
git clone REPOSITORY_URL
cd Pinocchio_based_workspace_optimization
git submodule update --init BGF
Create the named Miniconda environment and install this checkout in editable mode:
conda env create -n pinocchio-workspace --file environment.yml
conda activate pinocchio-workspace
python -m pip install -e . --no-deps --no-build-isolation
Prepare the licensed SR mesh and validate both portable robot bundles, the scene, the tracked reports, and both tracked result caches:
decanting-setup --accept-fanuc-license
python -m pytest
The acceptance flag is intentional: it downloads the pinned source directly
from NVIDIA, verifies SHA-256, converts it locally, and never commits FANUC
geometry. If an authorized geometries.usd is already available, use
decanting-setup --sr12ia-usd path/to/geometries.usd instead. Re-running
decanting-setup without either option only validates an existing bundle.
Without shell activation, use conda run -n pinocchio-workspace COMMAND. On
Windows, use the activated environment or conda run so Conda's DLL paths are
configured. Set PYTHONUTF8=1 when working from a non-ASCII checkout path.
All repository-owned paths are resolved through the installed module rather
than a username, drive, or checkout folder name. If Python is installed
non-editably, set DECANTING_WORKSPACE_ROOT to the complete cloned repository
before running the commands.
For the simplified, editable layout built from USD groups and child elements,
run python -m decanting_workspace.layout_cli --config config/cell_layout.yaml --open.
This mode excludes example SKUs, tote solids and conveyor legs, retains tote dimensions as references, and uses USD robot base positions
with URDF/Pinocchio models, and removes overlapping environment volumes.
See USD layout for translation controls and export.
Download the USD archive and asset files from
http://irol.iptime.org/public/share/fmJieNofpXugLi-i2Juqvw, then place the
downloaded ZIP file and the extracted USD files under the repository's USD/
directory. The full-cell environment should be available as
USD/cu_usd_simplified.usd before running the commands below.
Read an unsimplified polygon-mesh environment directly while retaining the configured task frames and robot URDFs:
decanting-live --usd path/to/environment.usd --openSee USD scenes for environment selection, conveyor/table
bindings, supported geometry, and cache regeneration. The scene-only viewer,
evaluator, precompute, playback, and setup commands also accept --usd.
For this repository's full cell at USD/cu_usd_simplified.usd, use
decanting-live --config config/cell_usd.yaml --open to retain its fixed supports
and omit missing camera/floor payloads and generated moving objects.
--ur20-source official is the default. A portable bundle of the Universal
Robots ROS 2 description release 4.3.1 at immutable commit
ae333289875f9ba5a9ea6649a54036efb5ccabee is stored under
assets/robots/ur20/official. It contains the relative-path URDF, DAE visual
meshes, STL collision meshes, both applicable license texts, and a SHA-256
provenance manifest. The default resolver validates every file and works
offline; it no longer generates a machine-specific URDF under .cache.
For a deterministic mesh-free fallback, use --ur20-source primitive. An explicit model always takes precedence:
python -m decanting_workspace --ur20-urdf path/to/ur20.urdf --open
The suction tool dimensions are provisional and live in config/cell_nominal.yaml. With the official UR20 they are attached to tool0 both as a viewer primitive and as two collision geometries. Loading with suction_proxy=... also creates a suction_tcp operational frame at the configured contact plane; use that frame, not the bare flange tool0, for IK and Jacobians.
FANUC provides product outline CAD to registered MyFANUC users. Isaac Sim 6.0
also contains a link-separated FANUC SR-12iA asset at
Isaac/Robots/Fanuc/sr12ia/sr12ia.usd. Either convert an authorized local
source:
decanting-assets path/to/Fanuc/sr12ia/payloads/geometries.usd
or explicitly accept the linked terms and download the pinned NVIDIA source:
decanting-assets --download --accept-fanuc-license
Both paths write eight OBJ files, a relative-path four-axis URDF, and a
provenance manifest to the ignored directory .cache/robot_assets/sr12ia.
The pinned input digest lives in
assets/robots/sr12ia/download_manifest.json. The bundle can be moved with
the checkout and is revalidated before use. The supplied Isaac asset is the
standard 300 mm J3 model, so its generated URDF is limited to 0...0.300 m.
The scene viewer uses it only for the 300 mm scenario; 450 mm still requires
the corrected provisional model or an explicit matching manufacturer model.
The fallback follows the real 2R-P-R skeleton: L1 = L2 = 0.450 m, the arm plane is 0.336 m above the base, J3 moves downward through 0...0.450 m, and J4/tool0 has no artificial translational offset. At zero joint position, tool0 = [0.900, 0, 0.336] m. Its shaft uses the longer 450 mm-option height for both choices, conservatively over-approximating the 300 mm fallback until the corresponding manufacturer asset is available. No process tool or provisional cutter is attached to the current SR model.
The default SR guide is deliberately not the arm's full 0.9 m mechanical reach or a swept-link cylinder. It is the permitted XY target region for tool0: the oriented top footprint of the SR support cube, inset only by the selected --clearance-mm. Candidate evaluation places the real SKU on UncasingLoadFrame, checks its four top corners against this footprint, verifies a joint-limit-valid planar 2R branch, and checks cutting Z against the selected J3 range. No cutter offset is assumed. During simultaneous UR work, a separate OBB encloses both this configured task region and the SR collision geometry at its designated cutting posture; sequential mode omits that dynamic OBB while retaining the fixed pedestal and camera supports. An exact SR cutting path and final tool collision model remain later inputs.
The Universal Robots graphical documentation and FANUC/Isaac assets have
separate licenses. The UR terms are distributed with the vendored bundle as
required. The FANUC 3D Content Sharing Agreement restricts standalone
redistribution, so derived SR mesh files stay under .cache and are not
committed. A clone is made complete through the explicit setup step rather
than by copying licensed geometry into Git.
Sources: Universal Robots description, FANUC SR-12iA specifications, FANUC CAD download information, Isaac Sim robot asset catalog, and FANUC 3D Content Sharing Agreement.
Open a scene-only interactive Meshcat view. This materializes the selected
environment and nominal robot poses; it is not decanting-live candidate
evaluation and it does not replay a result cache:
python -m decanting_workspace --openOr save a self-contained review snapshot:
python -m decanting_workspace `
--ur20-source primitive `
--sr12ia-source approximate `
--lift-height-mm 380 `
--sku 049995 `
--sr-j3-stroke-mm 450 `
--save-html outputs\cell-lift-380.htmlSelf-contained Meshcat HTML embeds mesh bytes. The CLI therefore rejects
static export when the pinned UR20 or prepared FANUC asset is selected; use the
live --open view for those models. Explicit third-party URDF licensing remains
the caller's responsibility.
Candidate/scenario controls are:
--ur-base X_M Y_M Z_M YAW_DEG
--sr-base X_M Y_M Z_M YAW_DEG
--lift-height-mm 0..760
--sku 123591|430060|180043|049995|000509
--corner all|southwest|southeast|northwest|northeast
--sr-j3-stroke-mm 300|450
--clearance-mm VALUE
--tote-table-offset-mm VALUE
--corner all shows four translucent alternative samples; it does not create four simultaneous collision boxes. Selecting one corner creates one physical box scenario.
--tote-table-offset-mm translates both the representative tote and its
ToteLoadFrame along the supporting worktable's long axis. For the nominal
geometry, positive offset is World +X and the valid range is approximately
-273.355...1086.645 mm; values outside it are rejected so the tote does not
cross a long table edge.
The evaluation command has no viewer in its loop. It uses the pinned official UR20 collision mesh by default and writes a compact report without storing the large per-sample joint vectors or Jacobian matrices:
python -m decanting_workspace.candidate_cli `
--sku 123591 `
--corner all `
--coordination both `
--lift-height-mm 0 `
--sr-j3-stroke-mm 300 `
--output-json outputs\candidate.jsonThe task contract is:
- top-down pick from each selected pallet corner independently;
- top-down placement at
UncasingLoadFrame, with World-Z yaw+180 deg; - front pick of the level-2 empty tote, followed by a vertical lift;
- placement at the movable
ToteLoadFrame; - front pick of the opened box from its UR-nearest vertical face;
- pouring sequence marker only (no pose test yet);
- optional level-3 waste fit and release IK (Soft; never rejects a base);
- push from the conveyor-opposite tote face to the exact
ToteSupplyFrameendpoint, then independently verify the final pushing posture at that endpoint even if the sampled push path stops early.
The independent final-pose sample retains its collision result and manipulability ellipsoid for UI inspection, but is excluded from scenario metric-summary means because the same endpoint is already sampled by the push segment. This prevents accidental double weighting in a later objective.
Steps 1--5 and 8 are Hard. A coordination result is UR-feasible only when all
selected corner alternatives satisfy every Hard check. cell_feasible also
requires the SR cutting-workspace report to pass. Simultaneous and sequential
results are kept separate.
Every target is World_T_suction_tcp. The pallet top-down rotation is obtained
by Pinocchio FK at the candidate base and nominal arm pose. The floating root
is then frozen exactly: IK and manipulability use only the six UR arm columns.
The reported ellipsoid inputs include translational singular values/volume and
a dimensionally normalized 6-D metric using a configurable characteristic
length; no weights, aggregation objective, or candidate ranking are imposed.
Approach segments are sampled in Cartesian space and adjacent IK solutions are
sampled in joint space for collision diagnostics. The solver is deterministic
multi-start damped least squares. Therefore not_found means that this search
did not find a valid solution; it is not a proof of global IK or motion-planning
infeasibility. Long station-to-station global paths, the pour pose, exact SR
cutting trajectory, payload dynamics, suction limits, and push forces are not
claimed by this stage.
The checked nominal report is
outputs/nominal_candidate_all_corners.json.
With the settings recorded inside that file, pallet pick and Uncasing placement
pass for all four corner alternatives. The nominal SR base passes footprint and
planar reach but its arm plane is 0.107659328 m below the SKU 123591 box top,
so its vertical task check fails. Sequential UR handling then fails at the
current tote placement, opened-box side-pick, and tote-push poses because the
reported IK branches collide with the worktable/camera support or robot itself.
Simultaneous mode additionally fails tote pickup against the conservative SR
reserved volume. These are diagnostics for the starting layout, not an
optimized result or a global infeasibility proof.
The live mode is separate from immutable cache playback. It evaluates exactly one parameter set on demand, converts that result to the existing step/check/sample hierarchy in memory, and displays it immediately:
decanting-live --openUse the URL labeled LIVE CONTROL UI (parameters + calculated results),
normally http://127.0.0.1:8766/. The separate Meshcat /static/ URL is only
the embedded 3-D viewer and intentionally has no parameter controls. When
running without an activated shell, preserve the live URL output with
conda run --no-capture-output -n pinocchio-workspace decanting-live --open.
config/case_grid_working.yaml is used only to supply the first parameter
values shown when the page opens. --initial-grid PATH can select another
grid for those initial values, but the command does not load or write a result
cache. Continuous controls cover both robot base poses, lift height, tote
offset, and clearance; SKU, pallet corner, coordination mode, and SR J3 stroke
are explicit choices. After one evaluation, moving among its steps, checks,
and samples only replays that latest in-memory result and does not recompute
IK. The Task pose panel also has an unchecked Wireframe ellipsoid control;
toggling it, ellipsoid visibility, or scale only selects a new display state
and does not run another evaluation. These display controls are separate from
the 15 calculation parameters.
Both live and cached selection APIs accept ellipsoid_wireframe as a strict
JSON boolean. It defaults to false, preserving the translucent solid style.
An unchanged style reuses the existing sphere geometry and material; switching
between solid and wireframe rebuilds the display material but does not repeat
the calculation.
The page starts with automatic calculation enabled. Parameter edits are debounced for 500 ms, only one calculation runs at a time, and edits made while it is running collapse to the latest pending parameter set. Disable auto calculation to stage several edits and use Calculate now explicitly. The backend also serializes evaluate/select requests because the collision checker and mutable SR J3 limit are not shared-worker-safe.
Two profiles are available:
quickis the default responsive diagnostic. It uses endpoint-oriented10 mtranslation and180 degrotation steps with no interior joint samples. It does not establish a continuous collision-free path.fulluses the configured local sampling, by default50 mm,5 deg, and three interior joint-segment samples. It is more detailed thanquick, but remains a local diagnostic and is not a global motion planner or a proof that a station-to-station path exists.
Open with full selected, optionally overriding its sampling settings:
decanting-live `
--default-profile full `
--cartesian-step-mm 25 `
--cartesian-rotation-step-deg 2.5 `
--joint-interpolation-samples 5 `
--openThe official UR20 model is used for evaluation and display by default. The
locally prepared FANUC/Isaac SR-12iA asset is display-only. Live feasibility,
including a selected 450 mm J3 study, uses the configured conservative
300/450 mm proxy (or an explicit --sr12ia-urdf evaluation proxy), never the
prepared mesh. The prepared visual has a native 300 mm J3 range; a displayed
posture outside that range is rejected rather than clamped. Use
--sr12ia-visual-urdf only to change the visual model, or
--allow-sr-visual-fallback to intentionally display the evaluation proxy
when the prepared asset is unavailable.
config/case_grid_working.yaml is the reproducible opening setup for the UI:
- UR20 base:
[-1.300, -7.350, 0.850, 90 deg] - SR-12iA base:
[-0.300, -7.073643684, 0.665, -90 deg] - all five SKUs, pallet lift
0.200 m, tote offset+0.700 m - SR J3 stroke
0.450 m
The generated outputs/precomputed_working_cases.json.gz uses the official
UR20 collision meshes and the normal 50 mm Cartesian, 5 deg rotation, and
three-sample joint-segment checks. All 20 sequential SKU/corner cases pass UR20
HARD steps 1--5 and 8. Twelve also pass the SR cutting-workspace check: all four
corners for SKUs 123591, 430060, and 000509. Step 7 remains SOFT and may
fail IK, as permitted by the process definition. The 20 simultaneous cases are
retained intentionally: tote handling fails against the conservative SR
exclusion box, so they are diagnostic rather than presented as feasible.
Regenerate the verified cache with:
decanting-precompute `
--grid config\case_grid_working.yaml `
--output-json outputs\precomputed_working_cases.json.gzOpen it directly; --cache now defaults to that verified cache:
decanting-playback --openThe panel reports 12 / 40 full-cell-feasible cases and opens the first passing
sequential case. The smallest observed collision clearance is approximately
6.464 mm at the box/SR support boundary, so this is a simulation
commissioning point rather than a robust production clearance. With the
current no-tool-offset SR model and solid floor-to-base pedestal proxy, SKUs
180043 and 049995 cannot satisfy SR vertical reach without the pedestal
entering the box support volume. They must therefore be revisited when the
cutter/tool offset or the mounting/support geometry is defined; the preset does
not hide that model limitation.
Continuous base placement and lift/tote motion do not have a finite set of
"all" values. config/case_grid_nominal.yaml therefore declares every value
that is precomputed. Its current 720 exact cases cover all five known SKUs,
three lift heights, three tote-frame positions, four independent pallet
corners, both coordination modes, and both SR J3 options. UR20 and SR-12iA are
nominal-only because their search bounds have not yet been supplied. Add base
axis values to that YAML when the bounds are fixed; the exact Cartesian-product
count is printed before either robot is loaded.
Create a full path-sampled cache with the official UR20 collision model:
decanting-precompute `
--grid config\case_grid_nominal.yaml `
--output-json outputs\precomputed-cases.json.gzThe checked-in workspace also contains the generated
outputs/precomputed_nominal_cases.json.gz. It is deliberately a task-pose
playback cache: each Cartesian check stores its start/end samples, while joint
interpolation is disabled. Its settings are preserved inside the cache and it
must not be presented as a continuous collision-free path result. It was
generated using:
decanting-precompute `
--grid config\case_grid_nominal.yaml `
--output-json outputs\precomputed_nominal_cases.json.gz `
--cartesian-step-mm 10000 `
--cartesian-rotation-step-deg 180 `
--joint-interpolation-samples 0Open the broader diagnostic cache explicitly:
decanting-playback `
--cache outputs\precomputed_nominal_cases.json.gz `
--openThe playback page combines a finite-case parameter panel and a Meshcat iframe.
Selecting a case, process step, check, or sample performs an exact immutable
cache lookup; it never invokes the live evaluator. A
successful sample displays the stored robot configuration and the
translational ellipsoid reconstructed from its cached World-aligned SVD
directions and singular values. The unchecked Wireframe ellipsoid control
switches that display between translucent solid and wireframe through a cache
selection only; it never invokes IK or recomputes manipulability. A failed IK
sample shows its best diagnostic configuration with a red target frame but
hides the ellipsoid. Step 6 retains
the previous successful posture and is labelled as having no pose check.
Robot visual models are loaded only once. The default playback UI uses the pinned,
repository-owned official UR20 mesh without embedding it in an exported HTML
file. Playback also loads the locally prepared FANUC/Isaac SR-12iA mesh from
.cache/robot_assets/sr12ia/sr12ia_mesh.urdf; this display-only choice does
not replace the SR collision/kinematic model recorded in the cache. Use
--sr12ia-visual-urdf PATH to override only the displayed SR model, and use
--sr12ia-urdf PATH when the evaluation/fingerprint model itself must change.
If the prepared mesh is missing or stale, playback now fails with the setup
command instead of silently showing another robot. The approximate visual can
only be selected intentionally with --allow-sr-visual-fallback.
Cache loading verifies the scene, workflow contract, and actual evaluation
URDF fingerprints. Robot fingerprints replace local geometry paths with the
referenced file content hashes, so identical checkouts at different absolute
paths validate the same cache while changed geometry is rejected. The
prepared Isaac mesh is the 300 mm J3 option;
it may visualize a 450 mm study only while the cached displayed J3 posture is
within 300 mm. Feasibility remains explicitly tied to the 450 mm evaluation
model in that case.
Visual meshes must not be loaded inside a base-optimization iteration. Resolve
and load each robot once before the candidate loop. The live UI follows the
same rule: it loads evaluation bundles and display models once at startup, then
serializes parameter evaluations. For UR20, load_visual=False keeps the
kinematic model and collision STL geometry while omitting the DAE viewer model:
from decanting_workspace import SceneState, evaluate_base_candidate, load_scene_spec
from decanting_workspace.robots import load_robot_bundle, resolve_official_ur20
spec = load_scene_spec()
urdf, package_dirs = resolve_official_ur20()
ur20 = load_robot_bundle(
"ur20",
urdf,
package_dirs=package_dirs,
load_visual=False,
suction_proxy=spec.robots["ur20"].suction_proxy,
)
sr12ia = load_robot_bundle(
"sr12ia",
spec.robots["sr12ia"].urdf_path,
load_visual=False,
)
state = SceneState() # vary SKU/lift/tote offset/J3 outside the base loop
for ur_base, sr_base in base_candidates:
raw_result = evaluate_base_candidate(
spec,
state,
ur_base,
sr_base,
ur20,
sr12ia,
)
# A later optimizer consumes raw_result; this layer does not rank it.evaluate_base_candidate applies the selected SR J3 limit on each call and
rejects a 450 mm study if the supplied SR model supports only 300 mm. The
bundled fallback conservatively supports both choices. config/cell_nominal.yaml
defines task dimensions and the default environment proxies. In USD mode,
selected triangles provide environment geometry and bound support dimensions;
task frames and SKU dimensions remain configured. The remaining optimization work is to choose sampling bounds and
an objective/constraint aggregation over these raw results.