Spaces:
Running
Running
File size: 8,322 Bytes
0410ac2 9788ac2 0410ac2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | <!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Compression force</title><style>
:root{--bg:#0b1020;--fg:#e8eefb;--dim:#8ea0c2;--line:#1e2a45;--card:#111a2e;
--accent:#ffc46b;--ok:#7be0a0;--bad:#ff8f7a;
--s0:12px;--s1:14px;--s2:16px;--s3:20px;--s4:28px;--s5:40px}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);font-size:var(--s2);
font-family:'IBM Plex Sans',system-ui,sans-serif;line-height:1.6}
.wrap{max-width:1100px;margin:0 auto;padding:0 20px 72px}
a{color:var(--accent)}
h1{font-size:var(--s5);line-height:1.15;margin:40px 0 8px;font-weight:650}
h2{font-size:var(--s4);margin:44px 0 10px;font-weight:600}
h3{font-size:var(--s3);margin:28px 0 6px;font-weight:600}
p{margin:10px 0;max-width:70ch}
.dim{color:var(--dim);font-size:var(--s1)}
.bad{color:var(--bad);font-size:var(--s0)}
nav{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 4px}
nav a{display:inline-block;padding:10px 16px;min-height:44px;line-height:24px;
border:1px solid var(--line);border-radius:999px;text-decoration:none;
background:var(--card)}
nav a[aria-current]{border-color:var(--accent)}
/* built by other scripts and published beside this site */
nav a.ext{border-style:dashed}
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));
gap:12px;margin:20px 0}
.card{background:var(--card);border:1px solid var(--line);border-radius:12px;
padding:16px}
.card b{display:block;font-size:var(--s4);color:var(--accent);font-weight:650}
.card span{color:var(--dim);font-size:var(--s1)}
figure{margin:24px 0}
figure img{width:100%;border-radius:10px;border:1px solid var(--line);
background:#fff}
figcaption{color:var(--dim);font-size:var(--s1);margin-top:8px;max-width:80ch}
table{border-collapse:collapse;width:100%;margin:14px 0;font-size:var(--s1)}
th,td{border-bottom:1px solid var(--line);padding:9px 10px;text-align:right}
th:first-child,td:first-child{text-align:left}
th{color:var(--dim);font-weight:500}
td b{color:var(--ok)}
details{background:var(--card);border:1px solid var(--line);border-radius:10px;
padding:12px 16px;margin:16px 0}
summary{cursor:pointer;color:var(--dim);font-size:var(--s1);min-height:44px;
display:flex;align-items:center}
code{background:#0d1526;padding:2px 6px;border-radius:5px;font-size:var(--s1)}
/* Inline links in prose measured 47x16 and 54x16 at 375 px — a tap target,
because a finger does not know it is "only prose". Padding alone would
break the line box, so the height comes from an inline-block with the
line-height carrying it. */
p a,figcaption a{display:inline-block;min-height:44px;line-height:44px;
padding:0 2px}
/* The <pre> pipeline diagram and the results table are the two things wider
than a phone. Let each scroll inside its own box rather than pushing the
document sideways — a horizontally scrolling PAGE hides content with no
affordance, a scrolling code block is a known idiom. */
pre{background:var(--card);border:1px solid var(--line);border-radius:10px;
padding:14px 16px;overflow-x:auto;font-size:var(--s1);max-width:100%}
.tablewrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
.tablewrap table{min-width:640px}
img{max-width:100%;height:auto}
/* Subscripts default to a fraction of the parent and rendered at 11.7px —
off the --s0..--s5 scale the audit counts. Pinned to the smallest step. */
sub,sup{font-size:var(--s0);line-height:0}
</style></head><body><div class="wrap"><nav><a href="index.html">overview</a><a href="method.html">method</a><a href="results.html">results</a><a href="sensors.html">sensors</a><a href="force.html" aria-current="page">force</a><a href="gallery.html">gallery</a><a href="workbench.html">3D workbench</a><a href="probes/index.html" class="ext">probe clips</a><a href="testset/index.html" class="ext">test set</a><a href="sim/index.html" class="ext">simulator</a></nav>
<h1>The compression force, and which way it pushes</h1>
<p><code>force_{side}_normal_n</code> is a <b>magnitude</b> in newtons, recovered
from the tactile image. It becomes a displacement through one constant:
<b>penetration = F / k</b> with <b>k = 2 N/mm</b>
(<code>dexforce.STIFFNESS_N_PER_M</code> — import it, do not retype it), and a
direction: the gel normal <b>in the sensor's own body frame</b>.</p>
<pre><code>from force_recovery.dexforce import gel_axis, STIFFNESS_N_PER_M
n_hat = R_from_quat(pose[3:7]) @ gel_axis(task, side) # world unit vector
target = pose[:3] + (force_n / STIFFNESS_N_PER_M) * n_hat</code></pre>
<p><b>World vertical is not that direction</b>, under either candidate axis —
quoting only the active one would argue the point with the very axis in dispute.
Over contact frames the gel normal sits a median
7.7°–25.1° from world −z
on motherboard and <b>23.3°–43.2°</b>
on pushT, where 70–98%
of 8,369 frames exceed 15°. Pushing "straight down" is wrong by at
least the smaller of each pair.</p>
<h2>Two candidate axes, and why the choice is not settled</h2>
<p>The default is sensor-local <b>−y</b>: the Mini's sensing face is normal to the
body's y. The calibration files also carry <code>gel_axis_in_rigid</code>
(<code>source="dual_ball"</code>), which is
normalize(gelball − refball) — the line between two calibration ball
centres, from <b>3</b> poses. <b>It never measured the gel
surface</b>; it is the normal only if the fixture held both balls along it. The
two disagree by 21.2° (left) and
22.4° (right).</p>
<div class="tablewrap"><table><thead><tr>
<th>test</th><th>angle from board normal<br><span class="dim">dual_ball / <b>−y</b></span></th>
<th>corr(dF, v·n̂)<br><span class="dim">dual_ball / <b>−y</b></span></th>
<th>image tilt R²<br><span class="dim">dual_ball / <b>−y</b></span></th>
<th>sv₁/sv₂</th></tr></thead><tbody>
<tr><td>left</td><td>7.1° / <b>25.6°</b></td><td>+0.085 / <b>+0.053</b> <span class="dim">(-y better on 3% of 31 eps)</span></td><td>-0.151 / <b>+0.009</b></td><td>1.09</td></tr>
<tr><td>right</td><td>18.1° / <b>7.7°</b></td><td>+0.116 / <b>+0.116</b> <span class="dim">(-y better on 61% of 31 eps)</span></td><td>-0.002 / <b>+0.006</b></td><td>1.04</td></tr>
</tbody></table></div>
<p class="dim">Board normal: pressing >6 N on a level board
(18,054 and 20,166 frames), the gel normal
should point near world −z. corr(dF, v·n̂): pressing in raises force — uses no
world frame and no table. Image tilt: R² of a single linear map from the
pose-predicted tilt to the gel's own deformation gradient.</p>
<h2>What the three tests actually say</h2>
<p><b>They disagree, and the right sensor is the suspect.</b> The left sensor's
dual-ball axis wins both proxies; the right sensor's loses one and ties the other.
The right calibration also reports <code>depth_offset_mm =
0</code> where the left reports
-5 — its ball centre was never backed off by a ball
radius to reach the gel surface.</p>
<p><b>And this dataset cannot settle it.</b> Two independent reasons. Kinematically
the problem is ill-conditioned: sv₁/sv₂ is 1.09 and
1.04, so changing the axis by 60° barely moves any
concentration score. And the gel's own deformation — a <b>real</b> signal, coherent to
10.6° between samples 7 contact rows apart — carries the geometry of whatever was touched
(components, edges, connectors), not the sensor's tilt: R² ≈ 0 for both candidates.
The contacted surface is not a known plane, so the most direct observable is
confounded at the source.</p>
<p><b>What would settle it</b> is a measurement, not more analysis: press the sensor
flat on a known flat plate at ten-plus widely varied orientations. Kinematics
becomes well-conditioned, and the image gives a null test — on a true plane the
deformation gradient vanishes when the press is normal. That same run also answers
whether the OptiTrack rigid body was redefined between calibration epochs, which
the copied gel calibration cannot.</p>
<p class="dim">Cost of the current default: switching from dual_ball to −y moves
<code>force_*_target_pose</code> by a median 0.57 mm
(max 1.53) on motherboard and 0.30 mm
(max 1.45) on pushT, because F/k is itself only a few mm.
Force magnitudes and penetration are unaffected — they are scalars.</p>
</div></body></html> |