Frames and units#
The chief/reference orbit and the robot simulation use different length units. Keep the conversion at the interface explicit.
Quantity |
Frame |
Units |
|---|---|---|
|
Absolute Earth-centered inertial (ECI) |
km |
|
Absolute ECI |
km/s |
Free-joint |
Chief-centered local inertial, axes parallel to ECI |
m |
Free-joint |
Local inertial velocity relative to chief |
m/s |
Free-joint |
Body angular velocity |
rad/s |
|
Absolute ECI |
m |
|
Absolute ECI |
m/s |
|
World force / torque |
N / N·m |
|
Simulation time |
s |
|
Environment clock, referenced to J2000.0 |
s |
qpos[:3] applies to a model whose first joint is free. For models with more
joints, use their addresses rather than assuming every body has this layout.
Chief-centered world#
MuJoCo world has its origin at the propagated chief. Its axes stay parallel
to ECI; they do not rotate with the orbit. XML free-body positions are offsets
from that origin. Do not put the chief’s absolute position or velocity in the
MuJoCo initial state.
For a local position \(r\) in meters, the absolute position is
\(R_{\mathrm{ECI,m}} = 1000 R_{\mathrm{chief,km}} + r\).
The Python ECI conversion helpers return SI values even though data.orbit
stores km and km/s.
LVLH#
LVLH is the derived rotating frame: x is radial outward, y is along-track, and z is the orbit normal. Use the conversion helpers, including the position argument when converting velocity; it accounts for frame rotation.
Continuing the quick start:
position_lvlh_m = np.array([10.0, 0.0, 0.0])
velocity_lvlh_m_s = np.zeros(3)
data.qpos[:3] = data.world_position_from_lvlh(position_lvlh_m)
data.qvel[:3] = data.world_velocity_from_lvlh(position_lvlh_m, velocity_lvlh_m_s)
mjo_forward(model, data)
The inverse helpers are lvlh_position_from_world and
lvlh_velocity_from_world. data.frame exposes the current derived frame.
Body and inertia axes#
xmat maps body vectors to world vectors. ximat maps principal-inertia
vectors to world vectors; these matrices differ when body_iquat is nonidentity.
For a body-frame torque, write:
body_id = model.body_id("spacecraft")
tau_body_nm = np.array([0.0, 0.0, 0.01])
data.xfrc_applied[body_id, 3:] = data.xmat[body_id].reshape(3, 3) @ tau_body_nm
Use XML fullinertia for a nondiagonal inertia tensor. MuJoCo decomposes it
into principal moments and an inertia orientation during compilation; editing
only body_inertia or body_iquat afterwards does not rebuild the dynamics.
Actuator and environment units#
Spacecraft actuator commands use SI: reaction-wheel torque in N·m,
magnetorquer dipole in A·m², thruster force in N, and CMG gimbal rate in rad/s.
Their *Spec limits use the same units. Do not scale these commands to km.
See actuators and sensors.
Central-body orbital parameters use km: gm is km³/s² and radius is km.
Surface positions and areas use m and m²; mass and inertia use kg and kg·m².
SurfaceSpec.center_of_pressure_body is measured from the body COM in body
axes; ThrusterSpec.position_body is measured from the body origin. See
forces and disturbances for the force laws and
application-point conventions.
Epoch-aware orbit inputs#
The default OrbitInit(..., frame="ECI", epoch=None) needs no optional
frame-conversion dependencies. Install the frames environment for
epoch-aware GCRF, TEME, or ITRF inputs. Epoch-dependent input frames require
an epoch and are normalized to the canonical inertial state. Supplying an
epoch sets the environment clock to its J2000.0 offset. These conversions do
not change MuJoCo’s local world convention. See OrbitInit in the
API reference and the frame conversion tests for supported inputs.