Skip to content

Commit 03109e0

Browse files
committed
docs: explain growing CR impl
1 parent 854e77e commit 03109e0

2 files changed

Lines changed: 102 additions & 0 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
(vf-growing-arm)=
2+
# Growing arm (GrowingCR + boundary control)
3+
4+
## Introduction
5+
6+
The **growing arm** setup pairs a storage-backed Cosserat rod (`GrowingCR`) with
7+
boundary forcing that acts like a **turret** at the base of the active segment
8+
and **discrete growth** when the user adds or removes elements. The same idea
9+
backs dual-arm demos in the Virtual Field runtime: fixed bases in the world,
10+
tracked controller orientation, and buttons to extend or retract the simulated
11+
length.
12+
13+
The figures below are taken from that workflow: how the scene is arranged, how
14+
targeting maps to the base, and a short loop of growth in VR.
15+
16+
![Growing-arm scene setup](../_static/growing_arm/grow_Setup.png)
17+
18+
![Targeting](../_static/growing_arm/grow_Targetting.png)
19+
20+
![VR: extending](../_static/growing_arm/VR-grow.gif)
21+
22+
This page explains the **mechanism**: a fixed-capacity rod where only a **suffix**
23+
of elements is simulated, the rest folded as **storage**; `_GrowingCRBoundaryConditions`
24+
applies PD at that suffix base and resizes `current_elements` on triggers.
25+
26+
## Mental model: active suffix
27+
28+
The implementation is `GrowingCR` in
29+
`src/virtual_field/runtime/custom_elastica/rods/growing_cr.py`. Arrays are
30+
allocated for `total_elements`, but `current_elements` controls how many elements
31+
from the **end** of the array participate in internal forces, damping, and
32+
integration. Equivalently, physics uses slices `[-current_elements:]` on
33+
per-element data and the last `current_elements + 1` nodes.
34+
35+
At build time, `growing_cr_allocate` lays out a straight rod so the **inactive**
36+
prefix sits near the base and the **active** segment points along the desired
37+
direction; the **base** of the simulated arm is the first node of the active
38+
suffix, at index `total_elements - current_elements`.
39+
40+
![Schematic](../_static/growing_arm/active_suffix_diagram.png)
41+
42+
Editor and GitHub **Markdown previews** only render ordinary `![](...)` image
43+
links like the lines above. They do not run Sphinx, so fenced `image` directives
44+
would show as raw text. After `sphinx-build` or `make html` in `docs/`, open
45+
`_build/html/custom_elastica/growing_arm.html` in a browser (or use hosted docs)
46+
to see the full themed layout.
47+
48+
## What `_GrowingCRBoundaryConditions` does
49+
50+
The class in
51+
`src/virtual_field/runtime/custom_elastica/boundary_conditions.py` subclasses
52+
PyElastica’s `NoForces` and runs each step as **forcing** on the rod.
53+
54+
1. **Turret tracking**
55+
A callable `controller()` returns a world-frame rotation matrix (and a
56+
position that is currently unused). The base of the active segment is pulled
57+
toward a fixed `target_position` with gain `p_linear_value`, and its
58+
directors are aligned to the controller orientation with gain
59+
`p_angular_value`. The torque uses the rotation vector from the relative
60+
rotation (SO(3) logarithm / inverse Rodrigues construction), including a
61+
stable branch near π.
62+
63+
2. **Growth / shrink**
64+
Two booleans, `trigger_increase_elements` and `trigger_decrease_elements`,
65+
request changing `current_elements`. A short **debounce** (0.3 s) avoids
66+
repeated toggles.
67+
- **Increase** (when `current_elements < total_elements`): increment
68+
`current_elements`, then call `_reset_element_kinematics_and_strains` on the
69+
new base index so the newly exposed segment is snapped to rest length,
70+
inherits directors from its neighbor, and clears spurious velocity/angular
71+
rates at that joint.
72+
- **Decrease** (when `current_elements > min_elements`): decrement
73+
`current_elements` only; the shorter active prefix leaves the folded
74+
storage as-is for the next steps.
75+
76+
3. **Ramp** (artifact from rest of the PyElastica)
77+
`ramp_up_time` scales both linear and angular efforts by
78+
`min(1, time / ramp_up_time)` so startup does not impulse the rod (in typical
79+
setups `ramp_up_time` is chosen very small).
80+
81+
Together, this replaces a classical **fixed** base constraint: the base **moves**
82+
with the user’s aim while the arm length changes on discrete button edges.
83+
84+
## Example wiring
85+
86+
`TwoGCRSimulation` (`src/virtual_field/runtime/two_gcr_simulation.py`) builds two
87+
`GrowingCR` rods with `total_elements = 5 * n_elem` and `current_elements =
88+
n_elem`, then attaches `_GrowingCRBoundaryConditions` with:
89+
90+
- `target_position` at each arm’s fixed base in the scene,
91+
- `controller` returning the tracked controller pose for that arm,
92+
- **primary** button: decrease length,
93+
- **secondary** button: increase length (edge-triggered in `handle_commands`).
94+
95+
That pairing is one concrete UX; the boundary class only needs the triggers and
96+
controller you provide.

docs/index.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ also includes experimental Virtual Field/WebXR runtime.
2323
virtual_field/mesh_publisher
2424
virtual_field/common_utility
2525

26+
.. toctree::
27+
:maxdepth: 2
28+
:caption: Custom Elastica:
29+
30+
custom_elastica/growing_arm
31+
2632
.. toctree::
2733
:maxdepth: 2
2834
:caption: VR:

0 commit comments

Comments
 (0)