Engine¶
eden.engine holds Genesis engine components that upstream Genesis does not ship: code that subclasses a Genesis
engine class and runs inside scene.step(). It has two parts, the audio sensors and the MochiSolver. The
engine addons guide explains what belongs here and how each part plugs into stock
Genesis; the physics guide covers when to use Mochi.
Audio sensors¶
Importing eden.engine.sensors installs every sensor below: a Genesis Sensor subclass registers itself with
Genesis' sensor manager on definition, so you pass these options as SensorOptions(sensor=...) like any
gs.sensors.* options. The sensors form one audio pipeline: synthesis (ActuationAudio, ContactAudio,
PianoAudio), propagation (AudioContext) and rendering (SpatialAudio).
from eden.engine.sensors import ContactAudio, SpatialAudio
from eden.options.sensors import SensorOptions
Motor and gearbox noise¶
Motor and gearbox noise: the synthesis stage of the audio pipeline.
Ported from the neuroagents-lab/tactile-genesis fork, where it is a standalone AudioSource
added through a Genesis-side scene.add_audio_source. Eden has no such API and does not want to
add one, so the source is wrapped in a sensor: the sensor declares itself the usual way through
SensorsOptions, hands its voice to :class:~eden.engine.sensors.audio.AudioContext at build, and
exposes the synthesized block through read() as a bonus — per-joint motor audio is a usable
observation in its own right, which the upstream source cannot provide.
The sound model is unchanged: each actuated DOF radiates from its joint's child link, voiced by a velocity-pitched whine (gear mesh), an idle electrical hum, velocity-scaled bearing hiss, and a click on direction reversal, with loudness driven by joint torque and mechanical power.
Provenance
Ported from neuroagents-lab/tactile-genesis @ 3023bcd
(Genesis/genesis/engine/audio/actuation_source.py).
Deviations from upstream: a sensor rather than a standalone source (above); entity_idx is validated against
the scene rather than at options construction, since Eden injects it at build; every oscillator partial is kept
below Nyquist, where upstream bounds only the whine fundamental; a gain tuple longer than n_partials
raises instead of being truncated; the whine fades out below 20 Hz, where upstream's frozen oscillators turn
the holding torque of a still joint into a DC offset; a block is synthesized in closed form rather than sample by
sample (same signal, to float rounding); and the friction_freq / friction_bandwidth / click_freq
defaults are lowered from upstream's 1200 / 800 / 3000 Hz, which the default 4 kHz audio rate cannot carry.
Limitation: parametric synthesis, not a measured motor model. The timbre is plausible rather than calibrated to any real actuator, so treat absolute amplitudes as arbitrary units.
Classes¶
ActuationAudioProperties
¶
Bases: NamedTuple
Timbre of one joint's actuator: how a motor and gearbox sound as a function of torque and speed.
| PARAMETER | DESCRIPTION |
|---|---|
pitch_slope
|
Whine fundamental in Hz per rad/s of joint speed, bundling gear ratio and mesh teeth. The whine
pitch is
TYPE:
|
harmonic_gains
|
Relative gains of the whine partials (fundamental, 2x, 3x, ...). Zero-padded to |
idle_freq
|
Frequency in Hz of the idle electrical hum (PWM, magnetostriction).
TYPE:
|
idle_gain
|
Hum amplitude while the motor is energized, i.e. whenever
TYPE:
|
idle_velocity_gain
|
Adds
TYPE:
|
idle_harmonic_gains
|
Relative gains of the hum's partials at |
friction_gain
|
Amplitude of the velocity-scaled bearing and friction hiss.
TYPE:
|
friction_freq
|
Center frequency in Hz of the friction noise band. Like
TYPE:
|
friction_bandwidth
|
Spectral width in Hz of the friction noise band.
TYPE:
|
load_gain
|
Whine loudness per unit
TYPE:
|
power_gain
|
Loudness per unit mechanical power
TYPE:
|
reversal_click_gain
|
Amplitude of the click emitted when joint velocity changes sign (backlash takeup), scaled by
TYPE:
|
click_freq
|
Center frequency in Hz of the reversal click resonator.
TYPE:
|
click_decay
|
Decay rate in 1/s of the reversal click resonator.
TYPE:
|
slew_coeff
|
One-pole smoothing in
TYPE:
|
ActuationAudio
¶
Bases: RigidEntitySensorOptionsMixin['ActuationAudioSensor'], SimpleSensorOptions['ActuationAudioSensor']
Motor noise radiated by a robot's actuated joints, and readable as a per-joint signal.
read() returns (B, n_dofs, audio_substeps): this step's block per covered DOF, in the order
the joints appear on the entity. The same blocks are registered with the scene's audio registry, so
any :class:~eden.engine.sensors.spatial_audio.SpatialAudio microphone hears them from the right
place — each DOF radiates from its joint's child link.
| PARAMETER | DESCRIPTION |
|---|---|
entity_idx
|
Global entity index of the actuated robot. Required; motor noise needs an actuator.
TYPE:
|
link_idx_local
|
Inherited from Genesis' link-attached sensor options and meaningless here: the sensor covers the whole entity and each DOF radiates from its own joint's child link. Anything but the default raises at build.
|
pos_offset
|
Inherited from Genesis' link-attached sensor options and meaningless here: the sensor covers the whole entity and each DOF radiates from its own joint's child link. Anything but the default raises at build.
|
euler_offset
|
Inherited from Genesis' link-attached sensor options and meaningless here: the sensor covers the whole entity and each DOF radiates from its own joint's child link. Anything but the default raises at build.
|
joints
|
Names of the joints to voice. |
default_properties
|
Timbre applied to every covered joint that
TYPE:
|
properties
|
Per-joint-name timbre overrides.
TYPE:
|
n_partials
|
Number of partials in the whine and hum oscillator banks.
TYPE:
|
audio_substeps
|
Samples emitted per physics step. Must match every other audio participant in the scene.
TYPE:
|
Examples:
Voice an arm's motors, with a louder whine on the shoulder:
>>> from eden.options import SensorOptions
>>> from eden.engine.sensors.actuation_audio import ActuationAudio, ActuationAudioProperties
>>> motors = SensorOptions(
... sensor=ActuationAudio(
... audio_substeps=20,
... properties={"shoulder_pan_joint": ActuationAudioProperties(power_gain=0.4)},
... ),
... attach_entity_name="robot",
... )
Attributes¶
default_properties
class-attribute
instance-attribute
¶
default_properties: ActuationAudioProperties = ActuationAudioProperties()
properties
class-attribute
instance-attribute
¶
properties: dict[str, ActuationAudioProperties] = Field(default_factory=dict)
Methods:¶
validate_scene
¶
Require a real rigid entity, and refuse the link attachment this sensor would ignore.
Runs in scene.add_sensor before the sensor is constructed, which is after Eden has injected
entity_idx / link_idx_local from attach_entity_name / attach_link_name.
| PARAMETER | DESCRIPTION |
|---|---|
scene
|
The scene the sensor is being added to.
TYPE:
|
Source code in eden/engine/sensors/actuation_audio.py
ActuationVoice
¶
ActuationVoice(options: ActuationAudio, solver: RigidSolver, dof_idx, emit_links, properties)
Bases: AudioSource
Synthesis for one :class:ActuationAudioSensor: one emission point per covered DOF.
Owns the oscillator phases and filter memory, which persist across steps, so it is built once and
then driven by :meth:emit from the audio registry each step.
Allocate the synthesis state for the DOFs resolved by the owning sensor.
| PARAMETER | DESCRIPTION |
|---|---|
options
|
The owning sensor's options.
TYPE:
|
solver
|
Solver to read per-DOF torque and velocity from.
TYPE:
|
dof_idx
|
Solver-global DOF indices to voice, in read order. |
emit_links
|
Solver-global index of the link each DOF radiates from, parallel to |
properties
|
Timbre of each DOF, parallel to
TYPE:
|
Source code in eden/engine/sensors/actuation_audio.py
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 | |
Attributes¶
Methods:¶
reset
¶
Zero the oscillator phases, filter memory, and last block for envs_idx.
| PARAMETER | DESCRIPTION |
|---|---|
envs_idx
|
Environments to reset.
TYPE:
|
Source code in eden/engine/sensors/actuation_audio.py
emit
¶
Synthesize this step's block from the current per-DOF torque and velocity.
Source code in eden/engine/sensors/actuation_audio.py
ActuationAudioMetadata
dataclass
¶
ActuationAudioMetadata(voices: list[ActuationVoice] = list(), cache_offsets: list[int] = list(), last_render: int = 0)
Bases: SimpleSensorMetadata
Per-class state of every :class:ActuationAudioSensor.
Each sensor contributes its voice and the offset of its rows in the class read cache, so the per-step classmethod hook can copy every voice's block without reaching back to instances.
ActuationAudioSensor
¶
ActuationAudioSensor(options: ActuationAudio, idx: int, shared_context: AudioContext, shared_metadata: ActuationAudioMetadata, manager: SensorManager)
Bases: SimpleSensor[ActuationAudio, AudioContext, ActuationAudioMetadata]
Registers an :class:ActuationVoice with the audio registry and reads back its block.
The voice does the synthesis, driven once per step by
:meth:~eden.engine.sensors.audio.AudioContext.update. This sensor only copies the result into its
read cache, which is what makes the block available as an observation without synthesizing twice.
Source code in eden/engine/sensors/actuation_audio.py
Methods:¶
build
¶
Build this sensor's voice and register it with the scene's audio registry.
Source code in eden/engine/sensors/actuation_audio.py
Functions:¶
Contact sound¶
Contact microphone: the sound of a link touching, tapping and scraping the things around it.
A source-filter model. The filter is the struck object — a bank of damped modal oscillators whose frequencies and decay rates are its material signature (metal rings long and high, wood thuds). The source is the contact: a tap is an impulse into the bank, sliding is force- and speed-scaled noise shaped by the surface roughness, and a hard tap adds a short high click. The timbre is keyed by the material of the link being struck, not the link carrying the sensor: a fingertip sounds like whatever it touches.
Like :mod:~eden.engine.sensors.actuation_audio the synthesis lives in an
:class:~eden.engine.sensors.audio.AudioSource, so :class:~eden.engine.sensors.audio.AudioContext drives it on
every simulation step and any :class:~eden.engine.sensors.spatial_audio.SpatialAudio microphone hears it from
the sensor's link. read() exposes the same block as a structure-borne vibration signal.
Provenance
Ported from neuroagents-lab/tactile-genesis @ 3023bcd (Genesis/genesis/engine/sensors/contact_audio.py
and Genesis/genesis/options/sensors/audio.py). The fused block kernel and the contact-driver math are upstream's.
Deviations from upstream:
- Synthesis runs from
AudioContext.updaterather than the sensor's own update hook. Eden skips sensor updates on intermediate decimation sub-steps, and a tap lasts one simulation step — synthesized from the sensor hook, three taps in four would never sound atdecimation=4. - Materials are keyed by Eden scene names (
"table","robot/hand") instead of solver link indices, which a declarative config cannot know; an unknown name raises at build. Two sensors giving one name different materials raise instead of the later one winning. - Not ported:
ContactAudioProperties.from_meshand the strike-location mode-shape tables it fills (they need upstream'sgenesis.utils.modal_analysis, which stock Genesis does not have), and the active-acousticExcitationSignalemitter. accel_noise_freqdefaults to 800 Hz, not 5000 Hz, which the default 4 kHz audio rate cannot carry; a mode or click the rate cannot carry warns at build when it would have sounded.
Limitations: one dominant contact per sensor link drives the timbre; slip speed is taken between link origins, ignoring the lever arm of a rotating body; radiation is a point source at the sensor link with no directivity.
Classes¶
ContactAudioProperties
¶
Bases: NamedTuple
How an object sounds when something touches it: its resonances, and the roughness of its surface.
Metal-like materials have high modes that decay slowly (a long ring); wood-like ones have lower modes that decay fast, and a stronger roughness source.
| PARAMETER | DESCRIPTION |
|---|---|
modal_freqs
|
Resonant mode frequencies in Hz. Fewer than the sensor's |
modal_decays
|
Amplitude decay rate of each mode in 1/s. Large is a thud, small a long ring. Same length as
|
modal_gains
|
Output weight of each mode. Same length as |
roughness_gain
|
Amplitude of the sliding-texture noise, scaled at runtime by normal force and slip speed.
TYPE:
|
roughness_spatial_freq
|
Surface bumps per meter. The scrape's pitch is
TYPE:
|
roughness_bandwidth
|
Spectral width in Hz of the scrape around that pitch. Several hundred Hz gives a noisy "shhh"; a narrow band an unrealistic tone.
TYPE:
|
impact_gain
|
Scale of the impulse a tap injects into the modal bank.
TYPE:
|
impact_threshold
|
Force jump in N per simulation step that counts as a tap. A tap fires only when the force crosses this from below, so the force ripple of a sustained slide does not re-ping the modes into a buzz.
TYPE:
|
contact_damping
|
Extra modal decay in 1/s while in contact: a finger pressing on an object deadens it, so the free ring-down is only heard after release.
TYPE:
|
contact_damping_per_force
|
Further in-contact decay per newton of normal force, in 1/(s·N): a firm press deadens the ring more than a light touch (Zheng & James 2011).
TYPE:
|
accel_noise_gain
|
Amplitude of the short high click a tap adds on top of the ring-down: the Hertzian contact transient of
small hard objects, which the modal bank misses.
TYPE:
|
accel_noise_freq
|
Center frequency in Hz of the click. Clamped to what the audio rate can carry, with a warning at build if the click is audible.
TYPE:
|
accel_noise_decay
|
Decay rate in 1/s of the click. Large is short and snappy.
TYPE:
|
ContactAudio
¶
Bases: RigidSensorOptionsMixin['ContactAudioSensor'], SimpleSensorOptions['ContactAudioSensor']
The sound of one link's contacts, keyed by the material of whatever it touches.
read() returns (B, audio_substeps): this step's block of the structure-borne vibration at the link.
The same block is registered with the scene's audio registry, radiating from the link (plus pos_offset),
so any :class:~eden.engine.sensors.spatial_audio.SpatialAudio microphone hears it.
| PARAMETER | DESCRIPTION |
|---|---|
entity_idx
|
Global entity index of the body carrying the sensor. Required; Eden fills it from
TYPE:
|
link_idx_local
|
Local index of the link whose contacts are voiced. Eden fills it from
TYPE:
|
pos_offset
|
Where on the link the sound radiates from, in the link's frame. |
euler_offset
|
Accepted for parity with other link-attached sensors; a point source has no direction. |
properties
|
Material of the things this link may touch, by Eden scene name:
TYPE:
|
default_properties
|
Material of every link
TYPE:
|
audio_substeps
|
Samples emitted per physics step. Must match every other audio participant in the scene; the sample rate
is
TYPE:
|
n_modes
|
Size of the modal bank. Must be the same on every
TYPE:
|
velocity_gate_ref
|
If positive, scale the output by
TYPE:
|
velocity_gate_ang_weight
|
Meters per radian converting the link's angular speed into
TYPE:
|
velocity_gate_smooth
|
One-pole smoothing in
TYPE:
|
Examples:
A fingertip that hears what it touches:
>>> from eden.options import SensorOptions
>>> from eden.engine.sensors.contact_audio import ContactAudio, ContactAudioProperties
>>> steel = ContactAudioProperties(modal_freqs=(1200.0, 2900.0), modal_decays=(6.0, 9.0), modal_gains=(1.0, 0.6))
>>> finger = SensorOptions(
... sensor=ContactAudio(audio_substeps=80, properties={"bowl": steel}),
... attach_entity_name="robot",
... attach_link_name="left_finger",
... )
Attributes¶
properties
class-attribute
instance-attribute
¶
properties: dict[str, ContactAudioProperties] = Field(default_factory=dict)
default_properties
class-attribute
instance-attribute
¶
default_properties: ContactAudioProperties | None = None
velocity_gate_ref
class-attribute
instance-attribute
¶
velocity_gate_ref: float = Field(default=0.0, ge=0.0)
velocity_gate_ang_weight
class-attribute
instance-attribute
¶
velocity_gate_ang_weight: float = Field(default=0.0, ge=0.0)
velocity_gate_smooth
class-attribute
instance-attribute
¶
velocity_gate_smooth: float = Field(default=1.0, gt=0.0, le=1.0)
Methods:¶
materials
¶
materials() -> dict[str, ContactAudioProperties]
Every material this sensor declares, with the default under the key "".
Source code in eden/engine/sensors/contact_audio.py
validate_scene
¶
Require a link to listen on, and that every named material exists in the scene.
Runs in scene.add_sensor, after Eden has injected entity_idx / link_idx_local. Entities are
all added before any sensor, so a name that does not resolve here never will.
| PARAMETER | DESCRIPTION |
|---|---|
scene
|
The scene the sensor is being added to.
TYPE:
|
Source code in eden/engine/sensors/contact_audio.py
ContactVoice
¶
ContactVoice(solver: RigidSolver, metadata: ContactAudioSensorMetadata, n_modes: int, audio_substeps: int)
Bases: AudioSource
Synthesis for every :class:ContactAudioSensor in the scene: one emission point per sensor.
One voice for the whole class rather than one per sensor, so the contact list and link velocities are read
once per step however many sensors exist. Sensors add themselves at build; the material table is rebuilt as
their properties merge in.
Create an empty voice; sensors join through :meth:add_sensor.
| PARAMETER | DESCRIPTION |
|---|---|
solver
|
Solver to read contacts and link velocities from.
TYPE:
|
metadata
|
The sensor class's shared metadata, read for each sensor's link and radiation offset. |
n_modes
|
Size of the modal bank.
TYPE:
|
audio_substeps
|
Samples per simulation step.
TYPE:
|
Source code in eden/engine/sensors/contact_audio.py
Attributes¶
Methods:¶
add_sensor
¶
add_sensor(options: ContactAudio, scene_entities) -> None
Grow every state tensor by one sensor column and merge in the sensor's materials.
| PARAMETER | DESCRIPTION |
|---|---|
options
|
The joining sensor's options.
TYPE:
|
scene_entities
|
The scene's Genesis entities, for resolving material names to links.
TYPE:
|
Source code in eden/engine/sensors/contact_audio.py
reset
¶
Silence the resonators and forget the last contact for envs_idx.
| PARAMETER | DESCRIPTION |
|---|---|
envs_idx
|
Environments to reset.
TYPE:
|
Source code in eden/engine/sensors/contact_audio.py
emit
¶
Synthesize this step's block from the contacts on each sensor link.
Source code in eden/engine/sensors/contact_audio.py
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 | |
ContactAudioSensorMetadata
dataclass
¶
ContactAudioSensorMetadata(voice: ContactVoice | None = None, last_render: int = 0)
ContactAudioSensor
¶
ContactAudioSensor(options: ContactAudio, idx: int, shared_context: AudioContext, shared_metadata: ContactAudioSensorMetadata, manager: SensorManager)
Bases: RigidSensorMixin[ContactAudioSensorMetadata], SimpleSensor[ContactAudio, AudioContext, ContactAudioSensorMetadata]
Joins the scene's :class:ContactVoice and reads back its own column of the block.
The voice does the synthesis, driven once per step by
:meth:~eden.engine.sensors.audio.AudioContext.update. This sensor only copies the result into its read cache.
Source code in eden/engine/sensors/contact_audio.py
Attributes¶
Methods:¶
build
¶
Create the class's voice if this is its first sensor, and add this sensor's column to it.
Source code in eden/engine/sensors/contact_audio.py
Functions:¶
Piano keyboard¶
The sound of a piano keyboard: struck strings, voiced from the motion of the keys.
A keyboard entity (:class:~eden.options.objects.piano.Piano) is only hinges and springs. This sensor turns the
motion of those hinges into notes and publishes them to the scene's audio registry, so any
:class:~eden.engine.sensors.spatial_audio.SpatialAudio microphone hears the instrument, and read() returns
the same signal directly.
The model is a piano action reduced to its three audible events:
- Strike. When a key travels past
strike_fractionof its stroke the hammer is thrown, and how loud the note is depends on how fast the key was moving at that moment, nothing else. A key pressed slowly enough makes no sound, as on the real instrument. - Ring. The string is a bank of partials at
n * f0 * sqrt(1 + inharmonicity * n**2), each an exponentially decaying sinusoid; higher partials and higher notes die faster. While the key stays down the damper is off the string and the note rings on. - Damp. When the key comes back up past
release_fractionthe damper falls and the note is gone in a few tens of milliseconds, unless the sustain pedal (:meth:PianoAudioSensor.set_sustain) is down.
What a key is touched by plays no part, which is the difference from
:class:~eden.engine.sensors.contact_audio.ContactAudio: there the contact force is the excitation and staying
in contact damps the ring, the opposite of both on a piano.
Each partial is held as a rotating phasor rather than as a two-pole recurrence, so a block is linear in the phasors: one matrix product against precomputed tables of decay envelope times sine and cosine, exact to rounding at any pitch. The recurrence would do badly here: at 27.5 Hz and a one-second decay its two state samples differ in the fifth digit, and float32 would turn that into noise. It also makes the damper free: lifting or dropping it only swaps the envelope table.
RoboPianist, where the keyboard's dimensions come from, does not synthesize sound: it sends note-on/off events to
a MIDI synthesizer with every strike at full velocity. Its "key is down" test, within half a degree of the joint
limit, is about 87% of a white key's stroke, which is where strike_fraction defaults.
Limitation: parametric synthesis, not a sampled instrument. It is recognizably a struck string with the right pitch, dynamics and damping, not a grand piano; amplitudes are arbitrary units.
Classes¶
PianoAudio
¶
Bases: RigidEntitySensorOptionsMixin['PianoAudioSensor'], SimpleSensorOptions['PianoAudioSensor']
The sound of a keyboard entity, from the motion of its key joints.
read() returns (B, audio_substeps): this step's block of the whole instrument. The same block is
registered with the scene's audio registry and radiates from the keyboard's base link.
| PARAMETER | DESCRIPTION |
|---|---|
entity_idx
|
Global entity index of the keyboard. Required.
TYPE:
|
link_idx_local
|
Inherited from Genesis' link-attached sensor options.
|
pos_offset
|
Inherited from Genesis' link-attached sensor options.
|
euler_offset
|
Inherited from Genesis' link-attached sensor options.
|
key_notes
|
The keys to voice and what each plays: key joint name to MIDI note number. Its order is the column order of
:attr: |
lowest_midi_note
|
MIDI note number of the entity's first key joint when
TYPE:
|
tuning
|
Frequency of A4 (MIDI note 69), in Hz.
TYPE:
|
strike_fraction
|
Fraction of a key's stroke at which the hammer is thrown.
TYPE:
|
release_fraction
|
Fraction of the stroke below which a returning key drops its damper. Must be below
TYPE:
|
velocity_ref
|
Key speed at the strike, in rad/s, that gives a full-scale note. Loudness is proportional to speed up to here and saturates above. A forte stroke takes a white key through its 10 mm in about 20 ms, 3 rad/s.
TYPE:
|
n_partials
|
Partials per string.
TYPE:
|
inharmonicity
|
String stiffness coefficient
TYPE:
|
partial_rolloff
|
Partial
TYPE:
|
ring_decay
|
Amplitude decay rate, in 1/s, of the fundamental of A4 while the damper is up.
TYPE:
|
decay_pitch_exponent
|
Decay rates scale with
TYPE:
|
partial_decay_growth
|
Partial
TYPE:
|
damped_decay
|
Decay rate, in 1/s, once the damper is down. Partials that already decay faster keep their own rate.
TYPE:
|
audio_substeps
|
Samples emitted per physics step. Must match every other audio participant in the scene. The default of 80
is a 16 kHz rate at Eden's default
TYPE:
|
Examples:
>>> from eden.options import SensorOptions
>>> from eden.engine.sensors.piano_audio import PianoAudio
>>> voice = SensorOptions(sensor=PianoAudio(), attach_entity_name="piano")
Attributes¶
key_notes
class-attribute
instance-attribute
¶
lowest_midi_note
class-attribute
instance-attribute
¶
lowest_midi_note: int = Field(default=21, ge=0, le=127)
inharmonicity
class-attribute
instance-attribute
¶
inharmonicity: float = Field(default=0.0004, ge=0.0)
partial_rolloff
class-attribute
instance-attribute
¶
partial_rolloff: float = Field(default=1.3, ge=0.0)
partial_decay_growth
class-attribute
instance-attribute
¶
partial_decay_growth: float = Field(default=0.6, ge=0.0)
Methods:¶
validate_scene
¶
Require a real rigid entity, and refuse the link attachment this sensor would ignore.
| PARAMETER | DESCRIPTION |
|---|---|
scene
|
The scene the sensor is being added to.
TYPE:
|
Source code in eden/engine/sensors/piano_audio.py
PianoVoice
¶
PianoVoice(options: PianoAudio, solver: RigidSolver, dof_idx: list[int], notes: list[int], emit_link: int)
Bases: AudioSource
Synthesis for one :class:PianoAudioSensor: every string of one keyboard, mixed to one emission point.
Owns the state of each partial, a phasor (c, s) whose signal over the next block is
envelope[k] * (c * sin((k + 1) * w) + s * cos((k + 1) * w)), plus what it needs to tell a strike from a
key that is merely down.
Allocate the strings of the keys resolved by the owning sensor.
| PARAMETER | DESCRIPTION |
|---|---|
options
|
The owning sensor's options.
TYPE:
|
solver
|
Solver to read the key positions from.
TYPE:
|
dof_idx
|
Solver-global DOF index of each key. |
notes
|
MIDI note number each key plays, parallel to |
emit_link
|
Solver-global index of the link the instrument radiates from.
TYPE:
|
Source code in eden/engine/sensors/piano_audio.py
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | |
Attributes¶
keys_down
property
¶
(B, n_keys) bool: keys past their strike point and not yet back up past their release point.
A copy, so it can be kept as the previous step's state: the voice overwrites its own every step.
Methods:¶
set_sustain
¶
Press or lift the sustain pedal.
| PARAMETER | DESCRIPTION |
|---|---|
down
|
Whether the pedal is down: one value, or one per selected environment.
TYPE:
|
envs_idx
|
Environments to set.
TYPE:
|
Source code in eden/engine/sensors/piano_audio.py
start_recording
¶
Start keeping every synthesized block.
Refuses a second start while one is open, as :meth:~eden.entities.camera.Camera.start_recording does:
the only thing it could do instead is throw away what the first one kept, and there is no getting that
back.
Source code in eden/engine/sensors/piano_audio.py
take_recording
¶
Return what was synthesized since the last call (or since recording started), and keep recording.
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
|
Source code in eden/engine/sensors/piano_audio.py
stop_recording
¶
Stop keeping blocks and drop what was kept.
Refuses when no recording is open, as :meth:~eden.entities.camera.Camera.stop_recording does: a stop
with nothing to stop is a mistake in the calling loop far more often than an intent.
Source code in eden/engine/sensors/piano_audio.py
reset
¶
Silence every string, lift the pedal and forget which keys were down, for envs_idx.
| PARAMETER | DESCRIPTION |
|---|---|
envs_idx
|
Environments to reset.
TYPE:
|
Source code in eden/engine/sensors/piano_audio.py
emit
¶
Throw the hammers of the keys that just passed their strike point, then ring every string one block.
Source code in eden/engine/sensors/piano_audio.py
PianoAudioMetadata
dataclass
¶
PianoAudioMetadata(voices: list[PianoVoice] = list(), cache_offsets: list[int] = list(), last_render: int = 0)
PianoAudioSensor
¶
PianoAudioSensor(options: PianoAudio, idx: int, shared_context: AudioContext, shared_metadata: PianoAudioMetadata, manager: SensorManager)
Bases: SimpleSensor[PianoAudio, AudioContext, PianoAudioMetadata]
Registers a :class:PianoVoice with the audio registry and reads back its block.
Source code in eden/engine/sensors/piano_audio.py
Attributes¶
keys_down
property
¶
(B, n_keys) bool: which keys are down, by the same test that throws the hammers.
A copy, safe to keep across steps. Columns follow key_notes, or the entity's joint order without it.
Methods:¶
build
¶
Build this sensor's voice and register it with the scene's audio registry.
Source code in eden/engine/sensors/piano_audio.py
set_sustain
¶
Press or lift the sustain pedal. While it is down, released keys keep ringing.
| PARAMETER | DESCRIPTION |
|---|---|
down
|
Whether the pedal is down: one value, or one per selected environment.
TYPE:
|
envs_idx
|
Environments to set.
TYPE:
|
Source code in eden/engine/sensors/piano_audio.py
start_recording
¶
Start keeping every block the strings synthesize, one per simulation step.
read() returns the block of the latest simulation step only, so a loop that steps the environment once
per control step (decimation simulation steps) reads one block in decimation. A recording has them
all: it is how to listen to a policy that acts at 20 Hz. The signal is the instrument's own, before any
microphone's distance and delay.
Unlike :meth:~eden.entities.camera.Camera.start_recording, which streams to a file, this holds what it
keeps in memory for every environment at once — num_envs * audio_substeps floats a simulation step,
65 MB/s at 1024 environments and the default 80 substeps. :meth:take_recording empties it, so a loop
that drains each step holds one step's worth; one that never drains grows without bound. Starting a
second recording while one is open raises rather than dropping what it holds, and a reset does not cut
the recording: episodes are concatenated, at a different sample in each row.
Source code in eden/engine/sensors/piano_audio.py
take_recording
¶
Return the samples synthesized since the last call, and keep recording.
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
|
Source code in eden/engine/sensors/piano_audio.py
stop_recording
¶
Functions:¶
Audio sources and propagation¶
The audio source registry and delay line every Eden audio sensor shares.
Eden's audio addon runs the usual three-stage pipeline: synthesis (a body turns physics into a block of samples), propagation (the samples travel, attenuating and arriving late), rendering (a microphone sums what reaches it). Sources publish into the registry defined here; microphones render what they find in it, with no knowledge of concrete source types.
Where this lives is the whole reason the addon needs no Genesis fork. Upstream owns the registry as a
scene-level AudioManager, which takes three Genesis edits to exist: Simulator to construct it,
Scene.step to pump it, and Scene.add_audio_source to fill it. Genesis already ships a
mechanism with exactly those lifecycle hooks — SharedSensorContext, a resource shared across
sensor types that SensorManager constructs on first use, refreshes once per step, resets, and
destroys. Hosting the registry there gets the same behaviour as a pure addition.
The ordering that matters comes for free. SensorManager.step calls context.update() before
the per-sensor-type update loop, so every source has synthesized the current step's block by the time
any microphone renders — whatever order the sensors were declared in. A source that synthesized inside
its own sensor's update hook would instead be at the mercy of declaration order, and a microphone
declared first would render a block one step stale.
The delay line lives here for a related but sharper reason: it must advance on every simulation
step, and a microphone does not. Eden skips the per-sensor-type update loop on intermediate decimation
sub-steps (EnvOptions.update_sensors_every_substep) while context.update() still runs, so a
history advanced from a microphone's render hook would drop every block but the last of each control
step — silently shortening every propagation delay. Advancing it here keeps the recorded sound
continuous no matter how often anything renders it.
Classes¶
AudioSource
¶
Bases: ABC
One radiating body in the scene: the synthesis stage of the audio pipeline.
A source generates sound from physics and is never read by the user — a microphone
(:class:~eden.engine.sensors.spatial_audio.SpatialAudioSensor) renders it. Subclasses own their
sample buffers and expose the radiation interface below so receivers stay decoupled from concrete
source types.
An emission point is one place sound leaves the body: a motor source has one per actuated DOF, a contact source one per contact patch. All three tensors are indexed by that axis.
Attributes¶
block
abstractmethod
property
¶
(B, n_emit, audio_substeps) most recently synthesized samples per emission point.
emit_links
abstractmethod
property
¶
(n_emit,) solver-global rigid link index each emission point rides. -1 is static.
Solver-global indices (RigidLink.idx), the links_idx of RigidSolver.get_links_pos() — not
entity-local idx_local. Read once, when the source registers.
emit_offset
abstractmethod
property
¶
(B, n_emit, 3) offset of each emission point in its link's frame, or world position if static.
Read every step, so a source whose emission points move on the body (a contact patch) may update it.
audio_substeps
abstractmethod
property
¶
audio_substeps: int
Samples this source emits per simulation step. Must match every other participant.
Methods:¶
emit
abstractmethod
¶
Synthesize this step's block into the source's own persistent state.
Called once per simulation step by :meth:AudioContext.update, before anything renders it.
AudioContext
¶
Bases: SharedSensorContext
Registry of :class:AudioSource objects plus the delay line carrying their sound.
Held by SensorManager as the shared context of the audio sensor classes, so exactly one
instance exists per scene however many audio sensors are declared. See the module docstring for
why both the registry and the history live here rather than on the scene or on a microphone.
The delay line is a ring of whole blocks: :meth:update overwrites the oldest block in place, and a
reader maps a sample's age to its slot through :attr:hist_newest. Nothing is allocated per step.
Source code in eden/engine/sensors/audio.py
Attributes¶
audio_substeps
property
¶
audio_substeps: int
The scene-wide samples-per-step every audio participant agrees on.
sources
property
¶
sources: tuple[AudioSource, ...]
Every registered source. Empty when microphones exist but nothing radiates.
src_hist
property
¶
(B, n_src, H) ring of samples of every emission point; see :attr:hist_newest for the order.
Empty until the first :meth:update with at least one radiating source.
hist_newest
property
¶
hist_newest: int
Slot of the newest sample in :attr:src_hist. A sample a samples old is at (hist_newest - a) % H.
Methods:¶
activate
¶
register
¶
register(source: AudioSource, owner: str) -> None
Add source to the registry and activate it. Call from a producing sensor's build.
| PARAMETER | DESCRIPTION |
|---|---|
source
|
The source to register. Its
TYPE:
|
owner
|
Name of the options class that declared the source, for the error message.
TYPE:
|
Source code in eden/engine/sensors/audio.py
require_audio_substeps
¶
Claim the scene-wide audio_substeps, or fail if it disagrees with the claim on record.
Sources and microphones exchange fixed-length sample blocks, so one rate has to hold across the scene. Checking it here — at build, naming both values — beats discovering a ragged block inside the render loop.
| PARAMETER | DESCRIPTION |
|---|---|
audio_substeps
|
The caller's samples-per-step.
TYPE:
|
who
|
Name of the calling participant, for the error message.
TYPE:
|
Source code in eden/engine/sensors/audio.py
require_history
¶
require_history(samples: int) -> None
Ensure the delay line is at least samples deep. Call from a consuming sensor's build.
| PARAMETER | DESCRIPTION |
|---|---|
samples
|
Depth this consumer needs: its longest propagation delay plus one block.
TYPE:
|
Source code in eden/engine/sensors/audio.py
update
¶
Synthesize every source's block and write it over the oldest block of the delay line.
Manager-driven, once per simulation step, before anything renders.
Source code in eden/engine/sensors/audio.py
source_positions
¶
(B, n_src, 3) world position of every emission point, parallel to :attr:src_hist.
An emission point rides its link with the offset rotated into the link's frame; a static one sits at its
offset. Poses are the solver-frame ones (relative=False), matching eden.entities.sensor.Sensor.
Source code in eden/engine/sensors/audio.py
blocks_since
¶
Blocks synthesized since a consumer last rendered at n_updates == last_render; warn once if over 1.
A consumer reads the newest block, so one render per :meth:update yields a continuous signal. Eden's
default EnvOptions.update_sensors_every_substep=False skips sensor updates on intermediate decimation
sub-steps, so with decimation=4 a sensor renders every fourth block and its read() is a gapped
stream — fine as an observation, wrong as a recording. That distinction is invisible in the data, and
the decimation is Eden's, which a Genesis sensor cannot see at build, so say so on the first gapped render.
| PARAMETER | DESCRIPTION |
|---|---|
last_render
|
:attr:
TYPE:
|
who
|
Name of the consuming sensor's options class, for the warning.
TYPE:
|
Source code in eden/engine/sensors/audio.py
reset
¶
Clear every source's synthesis state and the delay line for envs_idx.
| PARAMETER | DESCRIPTION |
|---|---|
envs_idx
|
Environments being reset.
TYPE:
|
Source code in eden/engine/sensors/audio.py
destroy
¶
Drop every registered source and the delay line, releasing their buffers.
Source code in eden/engine/sensors/audio.py
Functions:¶
Microphone¶
Airborne point microphone: the rendering stage of the audio pipeline.
Provenance
Ported from neuroagents-lab/tactile-genesis @ 3023bcd
(Genesis/genesis/engine/sensors/spatial_audio.py), where it is a Genesis in-tree sensor reading a scene-level
AudioManager. Here it renders :class:~eden.engine.sensors.audio.AudioContext instead, which needs no
Genesis change; see :mod:eden.engine.sensors.audio.
Deviations from upstream: enable_occlusion is dropped (upstream declares it and never reads it);
attenuation is a Literal rather than a free str whose typos fell back to inverse; the sample
history lives on the context, not the microphone; and the link attachment goes through Genesis'
KinematicSensorMixin, so pos_offset is link-local and set_pos_offset works as on any other sensor.
Limitation: free-field propagation only — no occlusion, no reflections, no room response. A microphone behind a wall hears the source as if the wall were not there.
Classes¶
SpatialAudio
¶
Bases: KinematicSensorOptionsMixin['SpatialAudioSensor'], SimpleSensorOptions['SpatialAudioSensor']
A mono point microphone that hears every audio source registered with the scene.
read() returns (B, audio_substeps): this step's block of samples, one microphone signal per
environment. Sources are summed with ref_distance-referenced distance attenuation and delayed
by distance / speed_of_sound through a fractional delay line.
| PARAMETER | DESCRIPTION |
|---|---|
entity_idx
|
Global entity index the microphone rides.
TYPE:
|
link_idx_local
|
Local link index on that entity.
TYPE:
|
pos_offset
|
Microphone position: in the link's frame when attached, world when static. |
euler_offset
|
Accepted for parity with other link-attached sensors; a mono point microphone has no direction, so it does not change what is heard. |
audio_substeps
|
Samples rendered per physics step. Must match every source in the scene; the effective sample
rate is
TYPE:
|
speed_of_sound
|
Propagation speed in m/s, setting the delay per meter. Default
TYPE:
|
ref_distance
|
Distance in meters at which a source is heard at unit gain. Attenuation is clamped closer than
this, so a source passing through the microphone stays finite. Default
TYPE:
|
attenuation
|
Falloff with distance.
TYPE:
|
enable_doppler
|
Ramp delay and attenuation across each block from last step's value to this step's, which pitch-shifts a source in relative motion. Costs nothing extra; disable for a static rig.
TYPE:
|
max_delay
|
Longest propagation delay to keep history for, in seconds. Sources farther than
TYPE:
|
Examples:
A microphone on a robot's head, hearing its own motors:
>>> from eden.options import SensorOptions
>>> from eden.engine.sensors.spatial_audio import SpatialAudio
>>> mic = SensorOptions(
... sensor=SpatialAudio(audio_substeps=20, ref_distance=0.2),
... attach_entity_name="robot",
... attach_link_name="head_link",
... )
SpatialAudioSensorMetadata
dataclass
¶
SpatialAudioSensorMetadata(speed_of_sound: Tensor = make_tensor_field((0,)), ref_distance: Tensor = make_tensor_field((0,)), inverse_square: Tensor = make_tensor_field((0,), dtype_factory=lambda: bool), doppler: Tensor = make_tensor_field((0,), dtype_factory=lambda: bool), ramp: Tensor = make_tensor_field((0,)), age: Tensor = make_tensor_field((0,)), prev_dist: Tensor = make_tensor_field((0, 0, 0)), last_render: int = 0)
Bases: KinematicSensorMetadataMixin, SimpleSensorMetadata
Per-class state of every :class:SpatialAudioSensor, batched so one pass renders them all.
The listener parameters are grown one column per sensor at build, alongside the link buckets and offsets of
KinematicSensorMetadataMixin. prev_dist also depends on the source count, which is only final once
every source has registered, so it is allocated on the first render instead. The sample history itself is
not here: it belongs to :class:~eden.engine.sensors.audio.AudioContext, which advances it every
simulation step whether or not a microphone renders.
SpatialAudioSensor
¶
SpatialAudioSensor(options: SpatialAudio, idx: int, shared_context: AudioContext, shared_metadata: SpatialAudioSensorMetadata, manager: SensorManager)
Bases: KinematicSensorMixin, SimpleSensor[SpatialAudio, AudioContext, SpatialAudioSensorMetadata]
Renders :class:~eden.engine.sensors.audio.AudioContext into one microphone signal per listener.
For each (listener, source) pair the current distance gives an attenuation and a delay in samples; the delay indexes the per-source sample history with linear interpolation between the two straddling samples, and the results are summed over sources. Interpolating the delay across the block from its value one simulation step ago to its current one is what produces Doppler: a shrinking delay reads the history faster than real time, raising the pitch.
Source code in eden/engine/sensors/spatial_audio.py
Attributes¶
Methods:¶
build
¶
Claim the scene's sample rate and append this listener's column.
Source code in eden/engine/sensors/spatial_audio.py
reset
classmethod
¶
reset(shared_metadata: SpatialAudioSensorMetadata, shared_ground_truth_cache: Tensor, envs_idx)
Clear the Doppler history so a reset environment renders no spurious pitch sweep.
| PARAMETER | DESCRIPTION |
|---|---|
shared_metadata
|
Per-class state to clear. |
shared_ground_truth_cache
|
The class's ground-truth cache slice, handled by the base implementation.
TYPE:
|
envs_idx
|
Environments being reset.
TYPE:
|
Source code in eden/engine/sensors/spatial_audio.py
Functions:¶
Live playback¶
Listen to an audio sensor while the simulation runs.
:class:LiveAudioPlayer streams the blocks an audio sensor's read() returns to the sound card, one block per
simulation step, so a scene can be heard as it is watched. It needs no Python audio package: the samples are piped
as raw PCM into whichever command-line player the machine already has (aplay or ffplay).
Hearing the simulation in sync means running it in real time, and the player is what enforces that:
- A simulation faster than real time is slowed down. :meth:
LiveAudioPlayer.writesleeps until the sound card is about to need the block, keeping onlyleadseconds queued. Without that the pipe would swallow seconds of audio ahead of the picture. - A simulation slower than real time cannot be helped — the sound card runs dry. Each time it does, the player
counts a dropout and rebuilds its cushion of silence, so one slow step costs one short gap instead of leaving
the stream on the edge of crackling from then on. :meth:
LiveAudioPlayer.closereports the count, so "did it keep up" is a number rather than an impression.
The cushion is the whole of the delay this module adds, because the players are told not to add their own. Left
at their defaults they would: a stock aplay holds back 0.5 s of audio before it starts and stays that far
behind (measured: 680 ms), ffplay 2 s. So aplay is given a buffer no deeper than lead and ffplay
its low-delay flags, which leaves lead plus the sound server's own few tens of milliseconds.
:meth:LiveAudioPlayer.close measures that figure — how long the player keeps sounding after the last block — and
reports it. SoX's play is not tried: a play on PATH is not reliably SoX, and its latency was never
measured here.
Eden's interactive viewer already paces env.step() to sim_dt, so a scene light enough to hold that rate
plays cleanly with the viewer open. It also needs one read() per simulation step (decimation=1); see the
engine addons guide on gapped reads.
This module is not imported by :mod:eden.engine.sensors and installs no sensor.
Classes¶
LiveAudioPlayer
¶
LiveAudioPlayer(sample_rate: int, gain: float = 1.0, lead: float = 0.06, command: tuple[str, ...] | None = None)
Stream mono float blocks to the sound card in real time, through a command-line player.
Use as a context manager around the simulation loop::
with LiveAudioPlayer(sample_rate=16000, gain=0.2) as speaker:
for _ in range(steps):
env.step()
speaker.write(env.sensors["mic"].read()[0])
A recording can be peak-normalized after the fact; a live stream cannot, since the peak is not known yet. The
samples are scaled by a fixed gain instead and soft-limited with tanh, so a louder moment than expected
saturates smoothly rather than wrapping around.
| PARAMETER | DESCRIPTION |
|---|---|
sample_rate
|
Samples per second:
TYPE:
|
gain
|
Scale from the sensor's arbitrary units to full scale
TYPE:
|
lead
|
Seconds of audio kept queued ahead of the sound card, which is also how far the sound trails the simulation. Larger rides out slower steps without a dropout; smaller keeps the sound closer to the picture.
TYPE:
|
command
|
Player command reading raw mono S16_LE PCM from stdin, with |
| ATTRIBUTE | DESCRIPTION |
|---|---|
dropouts |
Times a block arrived after the queue had run dry, i.e. audible gaps so far.
TYPE:
|
trailing |
Seconds the player kept sounding after the last block was written, measured by :meth:
TYPE:
|
Source code in eden/engine/sensors/playback.py
Attributes¶
Methods:¶
write
¶
Queue one block, first sleeping until the sound card is lead seconds from needing it.
| PARAMETER | DESCRIPTION |
|---|---|
block
|
Mono float samples of one environment, any shape; flattened in order.
TYPE:
|
Source code in eden/engine/sensors/playback.py
close
¶
Let the queued audio finish, stop the player, and report how the simulation kept up.
Source code in eden/engine/sensors/playback.py
MochiSolver¶
A fully implicit multi-physics solver hosted by Eden on stock Genesis: each substep solves one nonlinear system over
the rigid and articulated bodies and the vertex positions of deformable ones (tetrahedral solids, thin shells,
discrete elastic rods), with a smooth signed-distance penalty contact model. Eden installs it in a scene on the
scene's first Mochi entity. Every entity in a Mochi scene, the ground plane included, takes a Mochi*MaterialOptions
(material options), and the default contact stiffness needs eden.init(precision="64").
The Eden-side entity wrappers are MochiRigidEntity and MochiSoftEntity on the entities page.
Options¶
Options configuring the MochiSolver.
Upstream Genesis declares a solver's options as a field of its SceneOptions; the MochiSolver is hosted by Eden, so
its options class lives here and :func:eden.engine.solvers.mochi.hosting.install_mochi_solver hands it to the solver.
Classes¶
MochiOptions
¶
Bases: GravityMixin, TimeBasedMixin
Options configuring the MochiSolver.
MochiSolver is a fully-implicit solver: at every substep it solves a single nonlinear system in which the inertia of every rigid body and a smooth signed-distance penalty contact model (regularized Coulomb friction, viscous normal damping) are assembled together. There is no separate collision response stage: contact is re-detected at every Newton iterate and enters the same residual and Hessian as the inertia, which is what keeps large time steps stable and is the prerequisite for coupling deformable bodies into the same system without a coupler.
Note
Double precision (gs.init(precision="64")) is recommended. The default contact stiffness of 1e9 Pa/m combined
with a 1 mm activation threshold makes the Newton system ill-conditioned in single precision.
| PARAMETER | DESCRIPTION |
|---|---|
integrator
|
Time integration scheme: "backward_euler" (first order, strongly damped) or "bdf2" (second order, closer to energy-conserving; needs the previous two steps so the first step of a fresh or reset scene falls back to backward Euler). Defaults to "backward_euler".
TYPE:
|
use_newton_euler_inertia
|
Whether the rotational inertia enters as the Newton-Euler residual
TYPE:
|
n_newton_iterations
|
Maximum number of Newton iterations per substep. Defaults to 4.
TYPE:
|
newton_abs_tol
|
Absolute tolerance on the mass-weighted residual norm (unit acceleration under gravity gives a norm of order one). Defaults to 1e-3.
TYPE:
|
newton_rel_tol
|
Relative tolerance on the mass-weighted residual norm with respect to its value at the first iteration. Defaults to 1e-6.
TYPE:
|
explosion_control
|
Whether a substep whose residual grows beyond
TYPE:
|
explosion_abs_tol
|
Absolute residual norm above which the solve is considered diverged. Defaults to 1e9.
TYPE:
|
explosion_rel_tol
|
Residual growth factor above which the solve is considered diverged. Defaults to 1e4.
TYPE:
|
linesearch_type
|
Step acceptance rule: "residual_norm" accepts the first trial whose residual norm does not exceed the current one, "armijo" requires a sufficient decrease of the incremental potential (costs an extra energy assembly per trial), "none" always takes the full Newton step. Defaults to "residual_norm".
TYPE:
|
n_linesearch_iterations
|
Maximum number of step reductions (by
TYPE:
|
linesearch_alpha
|
Step size reduction factor between two line search trials. Defaults to 0.5.
TYPE:
|
linesearch_wolfe1
|
Sufficient decrease parameter of the Armijo rule. Defaults to 1e-4.
TYPE:
|
linear_solver
|
Linear solver for the Newton system: "ldlt" (dense Cholesky of every simulation island, exact, cubic in the
number of degrees of freedom of the island), "pcg" (block-Jacobi preconditioned conjugate gradient, linear per
iteration), or "auto" (dense when the largest island of the environment has at most
TYPE:
|
dense_solver_max_dofs
|
Largest island solved with the dense solver under "auto". Defaults to 50.
TYPE:
|
dense_matrix_max_dofs
|
Largest total number of degrees of freedom for which the dense matrix of the system is allocated (memory quadratic in this number per environment); beyond it every environment is solved with PCG. Defaults to 256.
TYPE:
|
n_pcg_iterations
|
Maximum number of conjugate gradient iterations. If None, the number of degrees of freedom capped at 500. Defaults to None.
TYPE:
|
pcg_rel_tol
|
Relative tolerance of the conjugate gradient solve. Ignored under the "adaptive" tolerance strategy. Defaults to 1e-5.
TYPE:
|
pcg_abs_tol
|
Absolute floor of the conjugate gradient stopping test, on the norm of the preconditioned residual (the residual scaled by the inverse of the Hessian diagonal, i.e. a displacement). A solve stops as soon as that norm drops below the floor, whatever the relative tolerance asks for; mochi's default. Set to 0 to disable. Defaults to 1e-9.
TYPE:
|
linear_tolerance_strategy
|
How tightly each Newton step solves its linear system: "constant" always solves to
TYPE:
|
friction_model
|
Regularization of the Coulomb friction force around zero sliding velocity: "c1" has compact support (exact
Coulomb beyond
TYPE:
|
use_fitted_friction_hessian
|
Whether the friction Hessian uses a quadratic fit that is the same in every tangential direction. The exact Hessian converges faster close to the solution but can stall the Newton iterations at the stick-slip transition. Defaults to True.
TYPE:
|
friction_with_collider_normal
|
Whether the friction plane is defined by the collider's distance gradient (True) or by the colliding surface normal (False). Defaults to True.
TYPE:
|
fade_friction
|
Whether friction fades out as the colliding surface normal and the collider gradient become aligned, i.e. as a sample point passes through the far side of a thin collider. Defaults to True.
TYPE:
|
implicit_normal_force_for_dissipation
|
Whether friction and damping scale with the normal force evaluated at the current iterate instead of the one recovered at the start of the step. The implicit form is required for an accurate coefficient of restitution through normal damping; the explicit form is cheaper and smoother. Defaults to False.
TYPE:
|
boundary_element_type
|
Quadrature rule placing contact sample points on the collision triangles: "P1Q1" (centroid), "P1Q3" (3 points per triangle, degree 2), "P1Q6" (6 points, degree 4). More points resolve contact patches better at a proportional cost. Defaults to "P1Q3".
TYPE:
|
equality_stiffness
|
Stiffness of the penalty enforcing the equality constraints of the articulations (connect, weld and joint
couplings; loop closures): the constraint violation is penalized by
TYPE:
|
equality_damping
|
Damping of the equality constraint penalty,
TYPE:
|
max_contact_pairs_per_env
|
Capacity of the list of (link, collider geom) pairs whose bounding boxes overlap within a substep. If None, the number of possible pairs. Defaults to None.
TYPE:
|
broadphase_margin
|
Absolute padding of the per-step conservative bounding boxes in meters. Defaults to 0.01.
TYPE:
|
spatial_hash_bins_per_item
|
Bins of the spatial hash that locates the collider spheres of shells and rods per inserted sphere, rounded up to a power of two; more bins shorten the chains a query walks at the cost of memory (4 bytes per bin per environment). The tetrahedra of solids use a bounding-box hierarchy instead. Defaults to 2.
TYPE:
|
max_soft_hits_per_sample
|
Capacity of the list of contacts between deformable boundary samples and rigid colliders, per sample. Exceeding it halts the simulation with an error. Defaults to 2.
TYPE:
|
max_deformable_collider_hits_per_query
|
Capacity of the list of contacts between sample points (rigid or deformable) and the tetrahedra of the deformable solid colliders, per sample point. Defaults to 2.
TYPE:
|
max_point_cloud_hits_per_query
|
Capacity of the list of contacts between deformable samples and the collider spheres of shells and rods,
per deformable sample (rigid samples get
TYPE:
|
record_contacts
|
Whether individual contact points can be read back through
TYPE:
|
use_sleeping
|
Whether bodies at rest are put to sleep: a sleeping body keeps its pose exactly and costs neither contact nor element work until an awake body comes near it or a setter changes its state, its controls, the gravity or the contact settings. It pays off for scenes with many settled objects. The cost is physical: slow motion stops for good, such as creep on a slope or the slow settling of a soft body, and the trajectory differs from a run without sleeping. Bodies under an active controller and rods never sleep. Defaults to False.
TYPE:
|
sleep_threshold
|
A step counts as at rest for a body when the solve reduced its residual by less than this factor from the
warm start, which an unforced body at equilibrium does not improve on. Higher values sleep sooner. Only steps
whose solve meets the Newton tolerances count: in single precision, a soft body at rest can stall just above
the default
TYPE:
|
sleep_min_steps
|
Consecutive steps at rest after which every body of an island at rest goes to sleep. Fewer steps sleep sooner but can freeze a body at the turning point of a slow swing. Defaults to 20.
TYPE:
|
sleep_max_speed
|
Speed in m/s above which a body never counts as at rest, whatever its residual: it keeps a body coasting without forces (whose motion the BDF2 warm start predicts exactly) awake. Defaults to 1e-2.
TYPE:
|
step_kernel
|
How a step is executed: "monolith" runs the whole step of every environment in one kernel (one thread per environment, one launch per step, no host round trips), "pipeline" runs each stage as its own kernel with the host driving the loops, "graph" runs the step as one graph-launched kernel whose Newton, line-search and conjugate-gradient loops run on the device with the stages parallel over items and environments (one launch per step). "auto" picks the monolith on the CPU (the pipeline for deformable scenes of at least 3000 vertices when several CPU threads run) and, on the GPU, for rigid scenes of at most 64 degrees of freedom, the pipeline otherwise. The graph kernel is opt-in: on GPUs without device-side graph conditionals (before compute capability 9.0) the runtime replays its loop bodies from the host with one flag readback per round, which runs at the pipeline's speed, and the single module compiles several times slower than the pipeline's kernels (minutes for a cloth with self-contact and an arm). Defaults to "auto".
TYPE:
|
graph_pcg_unroll
|
Conjugate-gradient iterations per round of the graph step kernel's inner loop (each round costs one flag readback on GPUs without device-side graph conditionals; each unrolled iteration is compiled once more). Defaults to 1.
TYPE:
|
joint_limit_stiffness
|
Stiffness in N/m (N*m/rad) of the penalty holding revolute and prismatic joints inside their range. Joint limits are soft: the violation at rest is the limit torque divided by this stiffness. Higher values reduce the violation but stiffen the Newton system. Defaults to 1e4 (the original engine defaults to 100).
TYPE:
|
joint_limit_damping
|
Damping in Ns/m (Nm*s/rad) of the joint limit penalty, resisting the velocity of the violation. Defaults to 0.
TYPE:
|
batch_links_info
|
Whether to batch link info. Defaults to False.
TYPE:
|
batch_joints_info
|
Whether to batch joint info. Defaults to False.
TYPE:
|
batch_dofs_info
|
Whether to batch DOF info. Defaults to False.
TYPE:
|
IK_max_targets
|
Maximum number of simultaneous target links of an inverse-kinematics solve (the scratch buffers are quadratic in it and allocated at the first solve). Defaults to 6.
TYPE:
|
Attributes¶
integrator
class-attribute
instance-attribute
¶
integrator: Literal['backward_euler', 'bdf2'] = 'backward_euler'
use_newton_euler_inertia
class-attribute
instance-attribute
¶
explosion_abs_tol
class-attribute
instance-attribute
¶
linesearch_type
class-attribute
instance-attribute
¶
linesearch_type: Literal['none', 'residual_norm', 'armijo'] = 'residual_norm'
n_linesearch_iterations
class-attribute
instance-attribute
¶
linear_solver
class-attribute
instance-attribute
¶
linear_solver: Literal['auto', 'ldlt', 'pcg'] = 'auto'
linear_tolerance_strategy
class-attribute
instance-attribute
¶
linear_tolerance_strategy: Literal['constant', 'adaptive'] = 'adaptive'
use_fitted_friction_hessian
class-attribute
instance-attribute
¶
friction_with_collider_normal
class-attribute
instance-attribute
¶
implicit_normal_force_for_dissipation
class-attribute
instance-attribute
¶
boundary_element_type
class-attribute
instance-attribute
¶
boundary_element_type: Literal['P1Q1', 'P1Q3', 'P1Q6'] = 'P1Q3'
equality_stiffness
class-attribute
instance-attribute
¶
max_contact_pairs_per_env
class-attribute
instance-attribute
¶
spatial_hash_bins_per_item
class-attribute
instance-attribute
¶
max_soft_hits_per_sample
class-attribute
instance-attribute
¶
max_deformable_collider_hits_per_query
class-attribute
instance-attribute
¶
max_point_cloud_hits_per_query
class-attribute
instance-attribute
¶
step_kernel
class-attribute
instance-attribute
¶
step_kernel: Literal['auto', 'monolith', 'pipeline', 'graph'] = 'auto'
joint_limit_stiffness
class-attribute
instance-attribute
¶
joint_limit_damping
class-attribute
instance-attribute
¶
Materials¶
Classes¶
Base
¶
Bases: Material[EntityT]
Base class of the materials simulated by the MochiSolver, holding the contact parameters they share.
Contact between two bodies is a smooth penalty on the signed distance of sample points placed on one body's collision surface to the other body's signed distance field (SDF). Every pair parameter is combined from the two bodies' values by geometric mean, except the smoothing distance, the activation threshold and the alignment threshold, which are read from the collider body alone.
| PARAMETER | DESCRIPTION |
|---|---|
friction
|
Coulomb friction coefficient. Default is 0.5.
TYPE:
|
penalty_coefficient
|
Contact stiffness in Pa/m: pressure per unit of penetration once the penalty ramp is fully active. Higher values reduce penetration at the cost of a stiffer, more ill-conditioned Newton system. Default is 1e9.
TYPE:
|
penalty_smoothing_half_distance
|
Half-width in meters of the smooth ramp between zero contact pressure and the linear regime. The pressure ramps up over twice this distance below the activation threshold, so a wider ramp gives softer, better conditioned contact and a narrower one sharper contact onset. Default is 0.005.
TYPE:
|
penalty_threshold
|
Signed distance in meters at which contact pressure starts to build up. Positive values start pushing bodies apart before they touch (a contact skin), negative values allow some interpenetration before responding. Default is 0.001.
TYPE:
|
friction_falloff_vel
|
Sliding speed in m/s below which the Coulomb friction force is regularized towards zero. Larger values give smoother, more robust stick-slip transitions with more creep under static load; smaller values approach exact Coulomb friction with a stiffer system. Default is 0.01.
TYPE:
|
viscous_friction
|
Tangential viscous friction coefficient in s/m, multiplying the normal force and the sliding velocity. Default is 0.
TYPE:
|
normal_viscous_damping
|
Normal viscous damping coefficient in s/m, multiplying the normal force and the approach velocity. This is the mechanism controlling the coefficient of restitution: 0 gives fully elastic penalty contact, larger values dissipate more impact energy. Default is 0.
TYPE:
|
max_alignment_normals
|
Cosine in [-1, 1] between the colliding surface normal and the collider gradient above which the contact is disabled, so that a body embedded past the far side of a thin collider can escape rather than being trapped. Default is 0.
TYPE:
|
has_gravity
|
Whether gravity acts on this body. Default is True.
TYPE:
|
contact_layer
|
Name of the contact layer of this body. Contact between two layers can be disabled at the solver level. Default is "default".
TYPE:
|
Note
This class should not be instantiated directly.
Attributes¶
penalty_coefficient
class-attribute
instance-attribute
¶
penalty_smoothing_half_distance
class-attribute
instance-attribute
¶
friction_falloff_vel
class-attribute
instance-attribute
¶
normal_viscous_damping
class-attribute
instance-attribute
¶
Attributes¶
ColliderType
module-attribute
¶
ColliderType = Literal['auto', 'plane', 'sphere', 'box', 'sdf', 'none']
Classes¶
MochiRigid
¶
Bases: Base['MochiEntity'], Rigid
Rigid body material simulated by the MochiSolver.
| PARAMETER | DESCRIPTION |
|---|---|
use_visual_raycasting
|
See Kinematic. Default is False.
TYPE:
|
rho
|
Density in kg/m^3 used to derive the mass and inertia from the collision geometry when the asset specifies none. Overrides per-geometry densities found in the asset. Default is 1000.
TYPE:
|
friction
|
|
penalty_coefficient
|
|
penalty_smoothing_half_distance
|
|
penalty_threshold
|
|
friction_falloff_vel
|
|
viscous_friction
|
Contact parameters, see
TYPE:
|
normal_viscous_damping
|
Contact parameters, see
TYPE:
|
max_alignment_normals
|
Contact parameters, see
TYPE:
|
has_gravity
|
Contact parameters, see
TYPE:
|
contact_layer
|
Contact parameters, see
TYPE:
|
collider_type
|
Representation of this body used when other bodies' sample points collide against it: "plane", "sphere" and "box" are exact analytic distance fields, "sdf" is the precomputed grid of the collision mesh, "none" makes the body collide only through its own sample points (it never acts as a collider), and "auto" selects the analytic field for plane, sphere and box primitives and the grid otherwise. An analytic field requires the matching primitive. Default is "auto".
TYPE:
|
sdf_cell_size
|
Cell size in SDF grid in meters. Contact resolves the penalty ramp against this grid, so the cell should stay well below the ramp width. Default is 0.0025.
TYPE:
|
sdf_min_res
|
Minimum resolution of the SDF grid. Must be at least 16. Default is 32.
TYPE:
|
sdf_max_res
|
Maximum resolution of the SDF grid. Must be >= sdf_min_res. Default is 128.
TYPE:
|
Attributes¶
Attributes¶
Classes¶
Elastic
¶
Bases: Base['MochiSoftEntity']
Deformable (tetrahedral finite element) material simulated by the MochiSolver.
The body is discretized into linear tetrahedra whose vertex positions are unknowns of the same implicit Newton
solve as the rigid bodies. Contact acts on quadrature samples of the boundary triangles against the rigid bodies'
signed distance fields, with the same smooth penalty and friction model (see Base).
| PARAMETER | DESCRIPTION |
|---|---|
E
|
Young's modulus in Pa. Default is 1e5.
TYPE:
|
nu
|
Poisson's ratio. Default is 0.45.
TYPE:
|
rho
|
Density in kg/m^3. Default is 1000.
TYPE:
|
model
|
Constitutive model: "stable_neohookean" (Smith et al. 2018, robust to inversion), "stvk" (Saint Venant-Kirchhoff) or "linear" (small strain). Default is "stable_neohookean".
TYPE:
|
mass_damping
|
Mass-proportional (Rayleigh) damping coefficient in 1/s. Default is 0.
TYPE:
|
stiffness_damping
|
Stiffness-proportional (Kelvin-Voigt) damping coefficient in s, acting through the rest-state elastic tangent on the rate of the Green strain. Default is 0.
TYPE:
|
friction
|
|
penalty_coefficient
|
|
penalty_smoothing_half_distance
|
|
penalty_threshold
|
|
friction_falloff_vel
|
|
viscous_friction
|
Contact parameters, see
TYPE:
|
normal_viscous_damping
|
Contact parameters, see
TYPE:
|
max_alignment_normals
|
Contact parameters, see
TYPE:
|
has_gravity
|
Contact parameters, see
TYPE:
|
contact_layer
|
Contact parameters, see
TYPE:
|
collider_type
|
Whether other bodies' sample points collide against this body: "sdf" (and "auto") builds a signed distance field of the rest shape that is queried through the deformed tetrahedra (contact only registers for points inside the body, the activation threshold is zero), "none" makes the body collide only through its own samples. Default is "auto".
TYPE:
|
Attributes¶
Attributes¶
Classes¶
Shell
¶
Bases: Base['MochiSoftEntity']
Thin shell (cloth-like) material simulated by the MochiSolver.
The surface triangles of the morph are the finite elements: a Saint Venant-Kirchhoff membrane on the metric of the
mid-surface and a Koiter-type bending term on its discrete curvature, both thickness-integrated. The stiffnesses
derive from the 3D isotropic parameters and the thickness (plane stress): membrane shear mu t, membrane
lambda_ps t with lambda_ps = E nu / (1 - nu^2), bending D nu and D (1 - nu) with the plate rigidity
D = E t^3 / (12 (1 - nu^2)), areal density rho t; each can be overridden.
| PARAMETER | DESCRIPTION |
|---|---|
E
|
Young's modulus in Pa. Default is 1e4.
TYPE:
|
nu
|
Poisson's ratio. Default is 0.3.
TYPE:
|
rho
|
Density in kg/m^3. Default is 200.
TYPE:
|
thickness
|
Shell thickness in m. Default is 0.001.
TYPE:
|
membrane_mu
|
Thickness-integrated membrane stiffnesses in N/m, overriding the values derived from E, nu and the thickness.
TYPE:
|
membrane_lambda
|
Thickness-integrated membrane stiffnesses in N/m, overriding the values derived from E, nu and the thickness.
TYPE:
|
bending_alpha
|
Bending stiffnesses in N m, overriding the values derived from E, nu and the thickness.
TYPE:
|
bending_beta
|
Bending stiffnesses in N m, overriding the values derived from E, nu and the thickness.
TYPE:
|
mass_damping
|
Mass-proportional damping coefficient in 1/s. Default is 0.
TYPE:
|
stiffness_damping
|
Stiffness-proportional (Rayleigh) damping coefficient in s. Default is 0.
TYPE:
|
friction
|
|
penalty_coefficient
|
|
penalty_smoothing_half_distance
|
|
penalty_threshold
|
|
friction_falloff_vel
|
|
viscous_friction
|
Contact parameters, see
TYPE:
|
normal_viscous_damping
|
Contact parameters, see
TYPE:
|
max_alignment_normals
|
Contact parameters, see
TYPE:
|
has_gravity
|
Contact parameters, see
TYPE:
|
contact_layer
|
Contact parameters, see
TYPE:
|
collider_type
|
Whether other bodies collide against this shell: "point_cloud" (and "auto") places a sphere of radius
TYPE:
|
collider_radius
|
Radius in m of the collider spheres at the vertices. Default is 0.01.
TYPE:
|
self_contact
|
Whether the samples of this shell collide against its own collider spheres (point-cloud collider only). Default is False.
TYPE:
|
self_contact_exclusion_ratio
|
Samples whose rest position lies within
TYPE:
|
Attributes¶
Classes¶
Rod
¶
Bases: Base['MochiSoftEntity']
Rod (cable, rope, wire) material simulated by the MochiSolver as a discrete elastic rod: stretching of the segments, bending and twisting at the interior nodes.
The stiffnesses derive from the 3D isotropic parameters and the radius of the circular cross-section: axial
E A, flexural E I (both principal directions), torsional G J with G = E / (2 (1 + nu)), linear density
rho A and linear rotational inertia rho J, with A = pi r^2, I = pi r^4 / 4, J = pi r^4 / 2; each can be
overridden.
| PARAMETER | DESCRIPTION |
|---|---|
E
|
Young's modulus in Pa. Default is 1e7.
TYPE:
|
nu
|
Poisson's ratio. Default is 0.3.
TYPE:
|
rho
|
Density in kg/m^3. Default is 1000.
TYPE:
|
axial_stiffness
|
Stretching stiffness
TYPE:
|
flexural_stiffness
|
Bending stiffness
TYPE:
|
torsional_stiffness
|
Twisting stiffness
TYPE:
|
linear_density
|
Mass per unit length in kg/m, overriding the derived value.
TYPE:
|
linear_rotational_inertia
|
Rotational inertia per unit length about the centerline in kg m, overriding the derived value.
TYPE:
|
mass_damping
|
Mass-proportional damping coefficient in 1/s. Default is 0.
TYPE:
|
stiffness_damping
|
Stiffness-proportional (Rayleigh) damping coefficient in s. Default is 0.
TYPE:
|
friction
|
|
penalty_coefficient
|
|
penalty_smoothing_half_distance
|
|
penalty_threshold
|
|
friction_falloff_vel
|
|
viscous_friction
|
Contact parameters, see
TYPE:
|
normal_viscous_damping
|
Contact parameters, see
TYPE:
|
max_alignment_normals
|
Contact parameters, see
TYPE:
|
has_gravity
|
Contact parameters, see
TYPE:
|
contact_layer
|
Contact parameters, see
TYPE:
|
collider_type
|
Whether other bodies collide against this rod: "point_cloud" (and "auto") places a sphere of the rod radius at every node, "none" makes the rod collide only through its own samples. Default is "auto".
TYPE:
|
self_contact
|
Whether the samples of this rod collide against its own collider spheres (point-cloud collider only). Default is False.
TYPE:
|
self_contact_exclusion_ratio
|
Samples whose rest position lies within the rod radius times
TYPE:
|
Attributes¶
flexural_stiffness
class-attribute
instance-attribute
¶
torsional_stiffness
class-attribute
instance-attribute
¶
linear_rotational_inertia
class-attribute
instance-attribute
¶
self_contact_exclusion_ratio
class-attribute
instance-attribute
¶
self_contact_exclusion_ratio: Annotated[ValidFloat, Field(gt=1.0)] = 1.5
Methods:¶
resolve
¶
Stiffnesses and inertia per unit length of a circular cross-section of the given radius; overrides win.
Source code in eden/engine/solvers/mochi/materials/rod.py
Rod morph¶
The rod morph: a polyline the MochiSolver simulates as a discrete elastic rod.
Classes¶
Rod
¶
Bases: Morph
Morph for a rod (cable, rope, wire): a polyline of nodes with a circular cross-section, simulated as a discrete elastic rod by the MochiSolver.
| PARAMETER | DESCRIPTION |
|---|---|
points
|
Positions of the rod nodes in the morph frame, in order along the rod (at least 2).
TYPE:
|
radius
|
Radius of the circular cross-section in meters. Default is 0.01.
TYPE:
|
n_cross_section_segments
|
Number of segments of the tube drawn around the rod. Default is 12.
TYPE:
|
is_closed_loop
|
Whether the last node connects back to the first one. Default is False.
TYPE:
|
pos
|
Position of the morph frame in meters. Default is (0.0, 0.0, 0.0).
TYPE:
|
euler
|
Orientation of the morph frame in degrees (scipy extrinsic x-y-z). Default is (0.0, 0.0, 0.0).
TYPE:
|
quat
|
Orientation of the morph frame as a w-x-y-z quaternion; takes precedence over
TYPE:
|
Hosting on stock Genesis¶
Host the MochiSolver in a scene of stock Genesis.
Upstream Genesis has no seam for a solver it does not ship: Simulator.__init__ builds a fixed list of solvers,
Scene.add_entity decides a surface's defaults from a closed chain of isinstance checks on the material, and the
visualizer names each solver it draws. This module stands in for the edits a Genesis fork would make, one scene at a
time and never on a class, so a scene without a Mochi entity runs exactly the upstream code:
- :func:
install_mochi_solverbuilds the solver and appends it to the simulator's solvers. From then on the scene takes Mochi materials inscene.add_entity, holds the solver asscene.mochi_solver, draws its bodies with the rasterizer, restores them on a reset and frees the solver's arrays with the scene, as a scene of the fork would. It refusesscene.export, which the fork allows:gs.Scene.loadrebuilds a scene on stock Genesis, without the solver; - :func:
create_mochi_sceneisgs.Scene(..., mochi_options=...)of the fork: a new scene, installed; - :func:
add_mochi_entityis whatscene.add_entitydoes for a Mochi material; - :func:
find_mochi_solverreturns the solver of a scene, or None.
What the solver itself needs beyond this (registering its error flags, clearing them when an environment is reset)
lives in :class:~eden.engine.solvers.mochi.mochi_solver.MochiSolver.
Classes¶
Functions:¶
find_mochi_solver
¶
find_mochi_solver(scene: Scene) -> MochiSolver | None
The MochiSolver installed in scene, or None if no Mochi entity was ever added to it.
Source code in eden/engine/solvers/mochi/hosting.py
install_mochi_solver
¶
install_mochi_solver(scene: Scene, options: MochiOptions | None = None) -> MochiSolver
Build a MochiSolver for scene and register it with its simulator, its visualizer and its reset.
Call it once per scene, before the scene is built and before any Mochi entity is added.
| PARAMETER | DESCRIPTION |
|---|---|
scene
|
The unbuilt scene. Its renderer must be the rasterizer, the only one the solver is bound to.
TYPE:
|
options
|
The solver's options. Their unset
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
MochiSolver
|
The installed solver, also reachable as |
Source code in eden/engine/solvers/mochi/hosting.py
create_mochi_scene
¶
create_mochi_scene(mochi_options: MochiOptions | None = None, **scene_kwargs) -> Scene
A new gs.Scene(**scene_kwargs) with a MochiSolver installed (see :func:install_mochi_solver).
| PARAMETER | DESCRIPTION |
|---|---|
mochi_options
|
The solver's options, which a scene of the fork took as
TYPE:
|
**scene_kwargs
|
Everything else
DEFAULT:
|
| RETURNS | DESCRIPTION |
|---|---|
Scene
|
The unbuilt scene, holding the solver as |
Source code in eden/engine/solvers/mochi/hosting.py
add_mochi_entity
¶
add_mochi_entity(scene: Scene, morph: Morph, material: Material, surface: Surface | None = None, visualize_contact: bool = False, vis_mode: str | None = None, name: str | None = None) -> Entity
Scene.add_entity for a Mochi material, on a scene :func:install_mochi_solver was called on.
The upstream Scene.add_entity cannot take one: its surface defaults end in a bare exception for the deformable
materials, and Simulator._add_entity hands a Rigid to the RigidSolver, which declares the most derived
class of the two that claim it. An installed scene's add_entity routes Mochi materials here.
Source code in eden/engine/solvers/mochi/hosting.py
Solver, entities and states¶
MochiSolver
¶
MochiSolver(scene: Scene, sim: Simulator, options: MochiOptions)
Bases: GravityMixin, TimeBasedMixin, KinematicSolver
Fully-implicit multi-physics solver with smooth penalty contact.
Every substep solves one nonlinear system over the generalized coordinates of all rigid and articulated bodies and the vertex positions of all deformable bodies, whose residual is the gradient of an incremental potential (inertia, gravity, damping, elasticity and the contact penalty with its friction and damping terms) and whose Hessian is assembled from per-link, per-contact-pair and per-tetrahedron blocks. The solve is a damped Newton iteration with a line search; contact is re-detected at every iterate.
Source code in eden/engine/solvers/mochi/mochi_solver.py
Attributes¶
n_attachments
property
¶
Number of rigid-deformable vertex attachments registered on the deformable entities.
is_links_info_batched
property
¶
is_links_info_batched: bool
Whether the inertial properties of a link are stored per environment rather than shared by the batch.
n_dofs_total
property
¶
Degrees of freedom of the Newton system: rigid, then 3 per deformable vertex, then one twist per rod segment.
Methods:¶
add_entity
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
build
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | |
substep_pre_coupling
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 | |
substep_post_coupling
¶
get_soft_state_render
¶
Environment-offset render vertex positions of the deformable surfaces, shape (n_vverts, B), as (positions, None, None) (UVs and faces are read from the visual geoms).
Source code in eden/engine/solvers/mochi/mochi_solver.py
get_state
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_state
¶
set_state(f, state, envs_idx=None, *, partial: bool = False) -> None
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_qpos
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_dofs_position
¶
destroy_arrays
¶
Free the ndarrays of the solver right away, as Simulator.destroy frees those of the other solvers.
Simulator.destroy gathers the arrays to free from data, which this solver refuses, so the destroy of its
scene calls this instead, after it (see eden.engine.solvers.mochi.hosting). They are those its attributes
hold, alone or in the structs (dataclasses) and the containers they hold. One already freed — by an earlier
call, or by Simulator.destroy as the array of another solver — is left alone; a field is freed by qd.reset.
Source code in eden/engine/solvers/mochi/mochi_solver.py
get_contacts
¶
Contact points of the current state, as a dict of arrays laid out (n_envs, n_contacts, ...) (see
MochiEntity.get_contacts and MochiSoftEntity.get_contacts). The contact axis holds the largest
per-environment count; shorter environments carry -1 indices and zeros in their unused slots.
Source code in eden/engine/solvers/mochi/mochi_solver.py
2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 | |
get_collider_distances
¶
Signed distance and distance gradient of world points to a collider geom, in the world frame, as seen by the contact model (a grid collider only answers inside its grid; other points are flagged invalid).
| RETURNS | DESCRIPTION |
|---|---|
(distances, gradients, is_valid) : tuple of arrays of shape (n_points,), (n_points, 3), (n_points,)
|
|
Source code in eden/engine/solvers/mochi/mochi_solver.py
get_convergence_info
¶
Per-environment outcome of the last solve: number of Newton iterations, number of conjugate-gradient iterations summed over the Newton iterations, status (0 running, 1 converged, 2 stopped at the iteration budget, 3 diverged), and the plain residual norm before and after the solve.
Source code in eden/engine/solvers/mochi/mochi_solver.py
get_contact_capacity_usage
¶
Per bounded contact list, the largest per-environment count of the last full assembly (the one the last
linear solve used) and the capacity of the list (what the max_* options size), to tune the capacities of a
scene.
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_dofs_kp
¶
set_dofs_kv
¶
set_dofs_stiffness
¶
set_dofs_armature
¶
set_dofs_damping
¶
set_dofs_limit
¶
get_links_pos
¶
Link positions in the requested reference frame, as RigidSolver.get_links_pos.
KinematicSolver's own getter knows only the link origin; the inertial frames the other references name
are maintained by this solver as well, so the whole ref vocabulary is answered here.
Source code in eden/engine/solvers/mochi/mochi_solver.py
get_links_vel
¶
Link velocities in the requested reference frame, as RigidSolver.get_links_vel.
Source code in eden/engine/solvers/mochi/mochi_solver.py
get_dofs_kp
¶
get_dofs_kv
¶
get_dofs_act_gain
¶
get_dofs_act_bias
¶
get_dofs_force_range
¶
get_dofs_stiffness
¶
get_dofs_invweight
¶
get_dofs_armature
¶
get_dofs_damping
¶
get_dofs_frictionloss
¶
set_dofs_force_range
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_dofs_frictionloss
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_dofs_act_gain
¶
set_dofs_act_bias
¶
control_dofs_force
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
control_dofs_velocity
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
control_dofs_position
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
control_dofs_position_velocity
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
get_dofs_control_force
¶
The control force the last step applied, as the rigid solver reports it (see substep_post_coupling).
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_links_contact_params
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
get_links_mass
¶
The mass of each link, as the solver currently uses it.
Source code in eden/engine/solvers/mochi/mochi_solver.py
get_links_inertia
¶
The inertia matrix of each link, in its inertial frame, as the solver currently uses it.
Source code in eden/engine/solvers/mochi/mochi_solver.py
get_links_COM
¶
The center of mass (COM) of each link, as an offset from the origin of its local frame.
Source code in eden/engine/solvers/mochi/mochi_solver.py
get_links_invweight
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_links_mass
¶
set_links_inertia
¶
set_links_COM
¶
set_links_has_gravity
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_gravity
¶
Set the gravity acting on the bodies of this solver, in m/s^2, and wake the environments it reaches.
Source code in eden/engine/solvers/mochi/mochi_solver.py
enable_layer_contact
¶
Enable or disable contact between two contact layers (symmetric).
Source code in eden/engine/solvers/mochi/mochi_solver.py
enable_entity_contact
¶
enable_entity_contact(entity_a, entity_b, is_enabled: bool = True)
Enable or disable contact between two entities (symmetric).
Source code in eden/engine/solvers/mochi/mochi_solver.py
get_soft_entity_state
¶
get_soft_vertices_position
¶
get_soft_vertices_velocity
¶
get_soft_vertices_contact_force
¶
set_soft_vertices_position
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_soft_vertices_velocity
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_soft_vertices_fixed
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_soft_vertices_target
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
set_soft_entity_contact_params
¶
Source code in eden/engine/solvers/mochi/mochi_solver.py
MochiEntity
¶
Bases: RigidEntity
Rigid entity simulated by the MochiSolver.
Loading, kinematics, pose/velocity access and joint control are shared with RigidEntity. Contact is the solver's smooth penalty model, so the contact readback exposes the sample-based contact points; the mass-matrix and constraint-solver specific accessors of the rigid solver are unavailable.
Methods:¶
get_contacts
¶
Returns the contact points computed at the end of the most recent scene.step(), filtered to those involving
this entity (and with_entity if given).
A contact point is a sample of the collision surface of side A pressed into the distance field of side B
(a rigid geom, or a deformable body). The returned dict has the keys 'entity_a', 'entity_b' (scene entity
indices), 'link_a', 'link_b', 'geom_a', 'geom_b' (-1 for a deformable side), 'verts_a', 'bary_a', 'verts_b',
'bary_b' (entity-local vertices and weights a deformable side is spread over, -1 padded), 'position', 'normal'
(unit, pointing away from B), 'distance' (signed, negative when penetrating), 'force_a' (force on A),
'force_b' (= -force_a), 'weight' (surface measure the sample stands for) and, for a parallelized scene or
with is_padded, 'valid_mask'. Shapes are (n_envs, n_contacts, ...) for a parallelized scene and
(n_contacts, ...) otherwise; an unparallelized scene without is_padded keeps only the matching contacts.
Source code in eden/engine/solvers/mochi/mochi_entity.py
get_links_net_contact_force
¶
set_contact_params
¶
set_contact_params(*, penalty_coefficient=None, friction=None, penalty_smoothing_half_distance=None, penalty_threshold=None, friction_falloff_vel=None, viscous_friction=None, normal_viscous_damping=None, links_idx_local=None)
Update the contact parameters of the collision geoms of the given links (all links by default). See
eden.engine.solvers.mochi.materials.Rigid for the meaning of each parameter.
Source code in eden/engine/solvers/mochi/mochi_entity.py
set_friction
¶
set_has_gravity
¶
Enable or disable gravity on every link of the entity.
Source code in eden/engine/solvers/mochi/mochi_entity.py
get_dofs_force
¶
get_mass_mat
¶
set_friction_ratio
¶
set_friction_torsional
¶
set_friction_rolling
¶
set_mass_shift
¶
set_COM_shift
¶
apply_links_external_wrench
¶
detect_collision
¶
get_kinetic_energy
¶
get_potential_energy
¶
get_total_energy
¶
get_links_acc
¶
get_links_acc_ang
¶
get_verts
¶
set_geoms_friction_ratio
¶
MochiSoftEntity
¶
MochiSoftEntity(scene, solver, material, morph, surface, idx, idx_in_solver, v_start, vvert_start, vface_start, name=None)
Bases: Entity
Deformable body simulated by the MochiSolver: a tetrahedral mesh (materials.Elastic), a thin shell made of the
surface triangles of the morph (materials.Shell) or a discrete elastic rod along the polyline of a Rod morph
(materials.Rod), whose vertex positions are unknowns of the implicit solve.
The solid simulation mesh is tetrahedralized from the morph surface (tetgen options on the morph), or read directly
from a tetgen '.node' / '.ele' file pair or a legacy ASCII '.vtk' file given as a gs.morphs.Mesh. The boundary
triangles (all triangles of a shell) carry the contact samples and the render mesh; a rod carries its samples on
the centerline and renders as a tube.
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
Attributes¶
contact_skin
property
¶
Rest vertices and triangles of the contact skin, or None when the samples lie on the boundary triangles.
Methods:¶
sample
¶
Build the render geoms and the simulation mesh (tetrahedra, shell triangles or rod segments) of the morph.
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
instantiate
¶
Set the rest vertex positions (with the morph orientation applied about their centroid and the morph pose offset composed on) and the elements (tetrahedra, shell triangles or rod segments).
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
get_state
¶
attach_to_link
¶
Rigidly attach vertices of this deformable body to a rigid link.
Each attached vertex is tied by a stiff penalty (inside the implicit solve) to the point of the link it coincides with when the scene is built, so place the body at its mounted pose. Forces act on both sides and the coupled bodies form one simulation island. Must be called before the scene is built.
| PARAMETER | DESCRIPTION |
|---|---|
link
|
A link of a rigid or articulated entity simulated by the MochiSolver.
TYPE:
|
verts_idx
|
Indices of the vertices of this entity to attach.
TYPE:
|
stiffness
|
Penalty stiffness k of the energy 1/2 k |c|^2 per vertex, in N/m. Defaults to 1e6 (mochi's constraint default).
TYPE:
|
damping
|
Damping d on the violation rate, adding 1/2 (d / dt) |c - c_stage_start|^2 per stage. Defaults to 0.
TYPE:
|
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
set_visual_mesh
¶
Replace the render mesh of this solid by a detailed visual mesh skinned by the simulation tetrahedra.
Every vertex of the visual mesh is embedded in the tetrahedron whose barycentric coordinates it takes (points
outside the coarse simulation mesh extrapolate linearly from the nearest one), so the visual surface follows
the deformation of the simulation mesh - the usual pairing of a fine render mesh with a coarse collision
mesh. pos, euler (degrees) and scale place the visual mesh in the frame of the simulation mesh file;
the entity's morph transform then applies to both. Must be called before the scene is built.
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
set_contact_skin
¶
Carry the contact samples of this solid on a triangle surface embedded in its simulation tetrahedra.
The samples otherwise lie on the boundary triangles of the tetrahedral mesh, so a coarse mesh contacts through
a coarse surface. A detailed skin (fingertip pads, the authored collision surface) places them where the
contact actually happens, at the cost of more samples and of contact forces spread over the four vertices of
a tetrahedron instead of three. The skin vertices follow the tetrahedra as the visual mesh does (see
set_visual_mesh, which takes the same placement arguments). The skin carries the samples only: other bodies
still collide against the tetrahedra. Must be called before the scene is built.
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
morph_frame_to_world
¶
Rest (world) positions of points given in the morph frame, placed as instantiate placed the mesh.
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
world_to_morph_frame
¶
Inverse of morph_frame_to_world: morph-frame positions of rest (world) points.
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
get_vertices_position
¶
World positions of the vertices, shape (n_vertices, 3) or (n_envs, n_vertices, 3).
get_vertices_velocity
¶
Velocities of the vertices, shape (n_vertices, 3) or (n_envs, n_vertices, 3).
get_vertices_contact_force
¶
Net contact force applied to each vertex, shape (n_vertices, 3) or (n_envs, n_vertices, 3).
get_contacts
¶
Contact points of the most recent scene.step() involving this body (and with_entity if given), with the
keys documented in MochiEntity.get_contacts. On the side of this body a contact point carries the vertices of
the surface element (verts_a / verts_b, local to this body, -1 padded) and the weights spreading the force
over them (bary_a / bary_b).
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
set_position
¶
Move the body: a (3,) or (n_envs, 3) offset of the rest positions, or full vertex positions of shape (n_vertices, 3) or (n_envs, n_vertices, 3).
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
set_velocity
¶
Set the vertex velocities: one (3,) or (n_envs, 3) velocity for all vertices, or per vertex of shape (n_vertices, 3) or (n_envs, n_vertices, 3).
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
set_vertices_fixed
¶
Fix the given vertices at their current position (Dirichlet condition), or release them.
verts_idx_local is one set for every environment, shape (n,), or one set per environment, shape (n_envs, n),
whose -1 entries are skipped.
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
set_vertices_target
¶
Prescribe the positions the given vertices must reach at the end of the next step (moving Dirichlet
condition); the vertices become fixed until released with set_vertices_fixed(..., is_fixed=False).
verts_idx_local is one set for every environment, shape (n,), with pos of shape (n, 3) or (n_envs, n, 3),
or one set per environment, shape (n_envs, n) whose -1 entries are skipped, with pos of shape (n_envs, n, 3).
This is how a gripper carries whichever vertices it closed on in each environment.
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
set_contact_params
¶
set_contact_params(*, penalty_coefficient=None, friction=None, penalty_smoothing_half_distance=None, penalty_threshold=None, friction_falloff_vel=None, viscous_friction=None, normal_viscous_damping=None)
Update the contact parameters of this body (see eden.engine.solvers.mochi.materials.Elastic).
Source code in eden/engine/solvers/mochi/mochi_soft_entity.py
Dynamic states queried from the MochiSolver and its deformable entities.
Classes¶
MochiSoftEntityState
¶
Dynamic state queried from a MochiSoftEntity: vertex positions and velocities, shape (n_envs, n_vertices, 3).
Source code in eden/engine/solvers/mochi/states.py
MochiSolverState
¶
Dynamic state queried from a MochiSolver: generalized coordinates and velocities, link poses, and the multistep history the time integration extrapolates from.
Source code in eden/engine/solvers/mochi/states.py
Error codes¶
The flags the solver's kernels set in the per-environment errno field.
Error flags the MochiSolver's kernels raise in the per-environment errno field of its kinematic tree.