yqi19 commited on
Commit
68162c7
Β·
verified Β·
1 Parent(s): 4e9c8d6

diagnostics: every non-obvious failure and the measurement that settled it

Browse files
Files changed (1) hide show
  1. DIAGNOSTICS.md +157 -0
DIAGNOSTICS.md ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Diagnostics log
2
+
3
+ Every failure in this suite that took more than one attempt to understand, with the *measurement*
4
+ that settled it. Written down because the diagnosis is worth more than the fix: almost all of
5
+ these look like tuning problems and are not.
6
+
7
+ The recurring lesson: **when a grasp or a joint does not behave, measure the asset offline
8
+ (trimesh on the source mesh, or the URDF's own numbers) before touching a parameter.**
9
+
10
+ ---
11
+
12
+ ## Asset geometry
13
+
14
+ ### The mesh origin is often at the bbox EDGE, not the centre
15
+ `object_pos()` returns the rigid body's origin. For several RoboTwin meshes the geometry is not
16
+ centred on it, so the grasp lands beside the object. Symptom: `gap 0.0 cm` (jaws fully closed on
17
+ nothing) or `object did not rise` β€” never anything that points at geometry.
18
+
19
+ | asset | offset from origin | how it was found |
20
+ |---|---|---|
21
+ | `083_brush` | **+3.7 cm in y** (its Y runs 0..0.46, not βˆ’0.23..0.23) | trimesh: centroid of the handle slice |
22
+ | `091_kettle` | **+4.7 cm in y** | trimesh: per-slice centroid along the tall axis |
23
+
24
+ Fix: measure the centroid of the part you intend to grip and pass `grasp_offset`.
25
+
26
+ ### Slice the mesh to find the graspable feature
27
+ Both the brush and the kettle were being gripped in the wrong place entirely. Slicing the mesh
28
+ along its long axis and printing each slice's cross-section shows where the thin part is:
29
+
30
+ * `083_brush` β€” 0–40 % of its length is a **1.0 cm handle**, 40–100 % is the 2.7–3.6 cm head.
31
+ * `091_kettle` β€” top 25 % is a **1.5 Γ— 3.5 cm neck**; the rest is a smooth 7 cm shell.
32
+
33
+ (The kettle's neck stalls the jaws but slips out under load β€” the body grip works once the y
34
+ offset is corrected. A stall is not a grip.)
35
+
36
+ ### The bbox is pose-independent β€” `object_size()` ignores yaw
37
+ `handover_basket` sets `yaw=90` so the basket's short axis faces the jaws, but `auto_jaw()` reads
38
+ the **authored** bbox and so chose the wrong closing axis. Anything that yaws an object must
39
+ state its jaw axis and its rotated half-extent explicitly.
40
+
41
+ ### RoboTwin GLBs are Y-up β€” and so are the SAPIEN articulations
42
+ Long known for the rigid meshes (`rpy: (90,0,0)`). It is **also** true of the articulated assets,
43
+ which was not handled and broke every one of them:
44
+
45
+ * `036_cabinet` β€” prismatic axis `(0,0,1)` with drawers stacked along **Y** β‡’ the drawers were
46
+ being asked to slide straight **up**.
47
+ * `044_microwave` β€” hinge axis `(0,-1,0)` β‡’ a microwave door hinging about a **horizontal** axis.
48
+ * `060_kitchenpot` β€” lid slides along **Y**, i.e. vertically. Consistent.
49
+
50
+ `place_articulation` now applies `roll=90` by default, mapping asset `(x,y,z)` β†’ sim `(x,-z,y)`.
51
+
52
+ ### Seat articulations from `bounding_box.json`, never from the USD
53
+ `ComputeWorldBound` on an articulation root returns an **empty range**, whose min is `-3.4e38`.
54
+ Using it as a seat height threw every fixture to negative infinity. The shipped
55
+ `bounding_box.json Γ— scale` is deterministic and correct.
56
+
57
+ ---
58
+
59
+ ## Asset conversion
60
+
61
+ ### A hyphen in a mesh filename silently produces an empty USD
62
+ SAPIEN names collision meshes `original-7.obj`. Hyphens are illegal in USD prim paths. Isaac's
63
+ URDF importer half-handles it: renames the prim to `original_7`, then still tries to create
64
+ `</colliders/base/original-7>`, fails to parse, dies with `Used null prim` β€” **writes a 492-byte
65
+ stub and returns normally**. The breakage only surfaces much later as
66
+ `Failed to find an articulation ... ArticulationRootAPI`.
67
+
68
+ Fix: `scripts/sanitize_urdf_asset.py`, plus a payload-size check in the converter that FAILS.
69
+
70
+ ### Renaming `.mtl` files without rewriting `mtllib` inside the `.obj`
71
+ An OBJ names its material library **inside the file**. Renaming the `.mtl` on disk leaves every
72
+ mesh material-less and the whole asset imports as featureless grey β€” the microwave rendered as a
73
+ plain white cube. The sanitizer now rewrites `mtllib` / `usemtl` / `map_*` lines too.
74
+
75
+ ### `UrdfConverterCfg.collider_type` defaults to `convex_hull`
76
+ That collapses a whole microwave into one box, swallowing the door, and the door's collider then
77
+ interpenetrates the body's so the hinge cannot move. Forced to `convex_decomposition`.
78
+
79
+ ### A carried vessel cannot have an interior
80
+ PhysX will not simulate a hollow triangle mesh as a **dynamic** body, so anything liftable is
81
+ convex and solid inside. Beads placed "in" the kettle were pushed onto the table before the
82
+ episode started (measured at `z=0.459`, i.e. table height, every run). Static containers can be
83
+ hollow β€” convert them with `--collision none --suffix _mesh`.
84
+
85
+ Consequence: `pour_kettle` is scored on the **pour** (vessel over the target, tipped past the
86
+ angle its contents would leave it), not on contents landing.
87
+
88
+ ---
89
+
90
+ ## The checking code lying
91
+
92
+ Three false positives, all of which reported SUCCESS for episodes that did nothing. These were
93
+ worse than the task failures, because they hide them.
94
+
95
+ | check | why it lied | fix |
96
+ |---|---|---|
97
+ | `count_in_region` | no height bound β€” a bead still inside the carried vessel counted as poured, so an episode whose **grasp failed** reported SUCCESS | added `below_top`, plus a per-object position print |
98
+ | `joint_moved` | baselined at build time, so the fixture settling under gravity counted as progress β€” `switch_toggle` "passed" on 0.44 rad of falling and 0.008 rad of robot work | `joint_driven`: the delta the solver measured across the action itself |
99
+ | `joint_driven` (first version) | still passed a joint that had blown to **βˆ’7.99 rad** against a `[βˆ’2.339, 0.754]` limit | also require the final value to be inside the joint's limits |
100
+
101
+ ### `ast.parse` does not catch duplicate keyword arguments
102
+ My syntax check passed a call with `grip_tilt=` written twice; it only failed at run time. Use
103
+ `compile()`.
104
+
105
+ ### Verifying only with `--no-randomize` hides pose bugs
106
+ `lid_food` placed the lid at the class constant `self.pot_xy` while the pot itself was jittered β€”
107
+ so with randomization on, the lid landed beside the pot. Invisible for as long as every check run
108
+ used nominal poses. Always derive from the value the builder **returned**.
109
+
110
+ ---
111
+
112
+ ## Robot workspace
113
+
114
+ Shoulders are at `(-0.2, +0.2)` and `(-0.2, -0.2)`; the wrist parks at `[0.078, -0.19]`.
115
+
116
+ * **Comfortable reach is ~0.25 m; ~0.34 m still works; 0.38–0.40 m stalls.** Several "grasp
117
+ failed" bugs were only this β€” `sweep_debris` (0.38 m), `cupboard_store` (0.40 m),
118
+ `pour_kettle` (0.39 m).
119
+ * **Nearest arm is not the best arm.** `sort_cubes` chose the right arm for a pad 9 cm from its
120
+ own shoulder; folded up that far it could not servo (0.54 m tracking error). Score on
121
+ `|reach βˆ’ 0.25|` instead.
122
+ * **Each arm can only push away from its own shoulder.** To push in βˆ’y the hand must stand on the
123
+ block's +y side, which the right arm (at y=βˆ’0.2) can only do by reaching over the block β€”
124
+ the approach swipes it off the table. `push_relay` is an L-shaped route so each arm pushes its
125
+ own natural direction.
126
+ * **The home pose sits inside the only reachable placement band.** This is why `cupboard_store`
127
+ is parked: moved in, the unit's walls block the arm before it can reach the object; moved out,
128
+ the shelf is past reach.
129
+
130
+ ## Motion
131
+
132
+ * **The jaw opens 9.4 cm and clips above ~7.8 cm.** `check_graspable()` warns.
133
+ * **Push: the loop must be parameterised by CONTROL STEPS, not distance.** Sizing it as
134
+ `span/advance` gave 25 iterations = 25 sim steps for a 22 cm push, which reads exactly like
135
+ "the block will not slide".
136
+ * **Push penetration is capped at 10 mm.** 17 mm into a 25 mm half-block launched it 1.8 m.
137
+ * **Re-aim the pusher every step from the live pose.** A line fixed at t=0 has no authority to
138
+ steer a block that has skidded (13 cm off in x).
139
+ * **Grip height for anything resting on a fixture must come from physics** (`live_z=True`), not
140
+ from the table. A lid on a 7.5 cm pot rim was grasped 7.5 cm too low.
141
+ * **`env.render()` needs warm-up.** A single render straight after `build()` returns black β€” all
142
+ 29 tiles of the first overview sheet were black. Step and render repeatedly, keep the last.
143
+
144
+ ## Still open
145
+
146
+ * `rope_straighten` β€” the rope never leaves its off-scene spawn. It is the only **floating-base**
147
+ articulation (the fixed-base ones all move). Try `write_root_state_to_sim()`. Separately, the
148
+ chain interleaves massless spacer links, so body index 13 is **not** `seg13` β€” use
149
+ `scene.link_index()`.
150
+ * `microwave_door`, `drawer_open`, `drawer_store`, `laptop_close`, `switch_toggle` β€” all load,
151
+ seat and report their joints correctly now; the remaining problem is the grip pose against a
152
+ flat, handle-less panel, and (for the switch) that the button's link frame **is** the hinge, so
153
+ pressing there has zero moment arm.
154
+ * `handover_basket` β€” both hands grip, but the basket is empty by the time the second hand
155
+ arrives. 0.062 scale (8.8 cm short axis) is the largest this basket can be and still be
156
+ graspable at all.
157
+ * `ring_post`, `push_relay`, `pour_cup`, `cupboard_store` β€” see the notes in each task file.