Files
Mujoco_WASM/test/engine/testdata/actuation/orientation.xml
T
Yuval Tassa 072e963fa0 Add SO3 transmission and native orientation actuator.
https://youtu.be/17XpwnqyCXs

New transmission type mjTRN_SO3: a relative orientation, targeting a ball
joint or a site+refsite pair. It is the first transmission with more than
one force output: its length is the norm of the expmap vector of the
relative rotation and its moment axes are the 3 rows of the
relative rotational Jacobian, without projecting onto per-actuator gears.

New force law mjGAIN_SO3/mjBIAS_SO3: a geodesic PD servo, force =
kp * log(q_current^-1 * q_target) - kv * velocity, exact for arbitrary axis
combinations with a unique equilibrium at every commanded orientation.
Error, moment rows and velocity all live in the child frame (joint or
site): the right-difference error is the gradient of the geodesic
potential in that frame. The parent-frame (left) error is not: driving
child-frame torques with it pumps energy at large angles, settling into
steady-spinning limit cycles (the SO3LargeAngleConvergence test). The
integrator variant stores the 3D orientation setpoint in act (actnum = 3,
re-anchored to a bounded representative at integration time). Exposed in
MJCF as <orientation joint=|site=+refsite= kp kv|dampratio>, or via
<general gaintype="so3" biastype="so3">.

The setpoint input has two charts: an expmap target (3 controls, default)
or a quaternion target (4 controls) -- <orientation input="quat">, the
first actuator with different input and output widths. The signature is
recorded in a new per-actuator field actuator_ctrlspec (mjtCtrlChart),
whose meaning is scoped by the gain type the way gain/bias parameters are;
ctrlnum is derived from it at compile time and remains the layout
authority. An explicit field rather than width inference or a prm slot:
width-as-chart cannot express same-width signatures (upcoming servo input
subsets), and prm slots are the input_mode pattern this stack retires.
The force law normalizes the commanded quaternion, making it scale- and
antipodally-invariant. The all-zero ctrl still maps to the identity via
mju_normalize4, but it is a degenerate point (a nudge of any component
commands a half-turn), so quat inputs reset to the identity quaternion:
new mj_resetCtrl sets neutral ctrl values (zero, except qw = 1), called
by mj_resetData and the viewers' Clear All. The quat chart is
restricted to dyntype 'none': integrating a quaternion setpoint linearly
is not meaningful on the manifold. New mjsActuator.ctrlspec field carries
the signature through the spec and XML round-trip.

Actuator sensors (actuatorpos/vel/frc) now report one value per force
output; dim = 3 on an SO3 actuator.

As the first actuator with nu != nactuator, this commit also makes the
viewers multi-input aware: the control sliders in simulate and studio,
which indexed per-actuator arrays by control index (out of bounds on
this model class), are generated per control and labeled with the
actuator name plus an input suffix ("orient/qw"), via the new
introspection helper mj_actuatorInputName -- the single source of truth
for input names, extended by each new multi-input type (quaternion
components are w-first: qw, qx, qy, qz). Slider ranges now honor a
defined ctrlrange even when ctrllimited is false: range is the UI hint,
limited is the clamp -- wrapped and expmap setpoints are unbounded but
still want finite sliders, while quat components are truly bounded.

The rotational demo model is orientation.xml under
test/engine/testdata/actuation/, upgraded to a three-way contrast:
per-axis wrapped servos vs an expmap-commanded vs a quat-commanded
orientation actuator, on identical checker-textured boxes. It is loaded
by the mixed-axis contrast and input-name tests, and doubles as the
viewer test model (slider groups of 3 independent, 3 grouped, 4 grouped).

PiperOrigin-RevId: 951607063
Change-Id: If235dba8e2f2ca72672e7c62531a27e967c6a373
2026-07-21 11:36:13 -07:00

79 lines
3.1 KiB
XML

<mujoco>
<compiler autolimits="true"/>
<option integrator="implicitfast">
<flag contact="disable" gravity="disable"/>
</option>
<statistic meansize=".05" extent=".9" center="0 0 .15"/>
<visual>
<headlight diffuse=".1 .1 .1" ambient=".15 .15 .15"/>
</visual>
<!--
Three identical ball-jointed boxes, illustrating the three rotational setpoint semantics.
The "per-axis" box has three independent position servos: each setpoint is interpreted on
its own circle (wrapped), exact for rotations about a single gear axis. The "expmap" and
"quat" boxes each have a single orientation actuator: the setpoints are interpreted
jointly as an orientation on SO(3), exact for any combination of axes, commanded in the
exponential-map and quaternion charts respectively.
-->
<asset>
<texture name="checker" type="cube" builtin="checker" rgb1=".75 .55 .28" rgb2=".22 .18 .14" width="32" height="32"/>
<material name="checker" texture="checker"/>
<texture name="grid" type="2d" builtin="checker" width="512" height="512" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
<material name="grid" texture="grid" texrepeat="1 1" texuniform="true" reflectance=".2"/>
</asset>
<default>
<geom type="box" size=".07 .09 .05" material="checker"/>
<position kp="5" dampratio="1"/>
<default class="angle">
<position ctrlrange="-6.2832 6.2832" ctrllimited="false"/>
</default>
<default class="quat">
<position ctrlrange="-1 1" ctrllimited="true"/>
</default>
<site size="1e-5"/>
</default>
<worldbody>
<light pos="0 0 10" type="point" diffuse=".75 .72 .65" intensity="1200000" range="20" attenuation="1 0 .04"/>
<light pos="-.3 -.4 1.3" dir="0 .34 -.94" diffuse=".75 .72 .65" intensity="100000" range="4" cutoff="13" attenuation="1 0 0" exponent="60"/>
<light pos="0 -.4 1.3" dir="0 .34 -.94" diffuse=".75 .72 .65" intensity="100000" range="4" cutoff="16" exponent="60" attenuation="1 0 .5"/>
<light pos=".3 -.4 1.3" dir="0 .34 -.94" diffuse=".75 .72 .65" intensity="100000" range="4" cutoff="16" exponent="60" attenuation="1 0 .5"/>
<light type="point" pos="0 -1.5 2" diffuse=".3 .35 .45" intensity="15000" range="8" castshadow="false" attenuation="1 0 .3"/>
<geom name="floor" type="plane" size="2 2 .025" material="grid"/>
<site name="per-axis" pos="-.3 0 .4"/>
<body name="peraxis" pos="-.3 0 .2">
<joint name="peraxis" type="ball"/>
<geom/>
</body>
<site name="expmap" pos="0 0 .4"/>
<body name="expmap" pos="0 0 .2">
<joint name="expmap" type="ball"/>
<geom/>
</body>
<site name="quat" pos=".3 0 .4"/>
<body name="quat" pos=".3 0 .2">
<joint name="quat" type="ball"/>
<geom/>
</body>
</worldbody>
<actuator>
<position name="rx_peraxis" joint="peraxis" gear="1 0 0" class="angle"/>
<position name="ry_peraxis" joint="peraxis" gear="0 1 0" class="angle"/>
<position name="rz_peraxis" joint="peraxis" gear="0 0 1" class="angle"/>
<orientation name="expmap" joint="expmap" kp="1" dampratio="1" class="angle"/>
<orientation name="quat" joint="quat" input="quat" kp="1" dampratio="1" class="quat"/>
</actuator>
</mujoco>