🔄 Data Flow¶
Understanding how simulation data flows through OrcaGym is key to using the environment correctly.
Core Data Flow¶
RL Policy
│
▼ action (nu,)
┌──────────────────────┐
│ OrcaGymEulerEnv │
│ ┌────────────────┐ │
│ │ do_simulation() │ │ ← Core stepping method
│ │ ├─ Set control │ │
│ │ ├─ Physics step│ │
│ │ └─ Sync state │ │
│ └───────┬────────┘ │
│ ▼ │
│ Simulation state │
│ qpos/qvel/time/... │
│ │ │
│ ┌───────▼────────┐ │
│ │ _get_obs() │──┼──▶ obs
│ │ subclass step() │──┼──▶ reward / terminated / truncated
│ └────────────────┘ │
└──────────────────────┘
│
▼ obs
RL Policy
What Happens Inside step()¶
# When you call env.step(action):
env.step(action)
│
├─▶ do_simulation(ctrl, n_frames)
│ ├─▶ Set control input ctrl
│ └─▶ Execute n_frames physics simulation steps
│
├─▶ _get_obs() # Build the observation
├─▶ Compute reward / terminated / truncated # Implemented by the subclass within its own step() logic
│
└─▶ Return (obs, reward, terminated, truncated, info)
Note:
compute_reward()is not a public method ofOrcaGymEulerEnv. The computation of reward and termination conditions is done by subclasses within their ownstep()implementation (OrcaGymEulerEnv.stepis an abstract method that subclasses must override).
State Synchronization Rules¶
After modifying the simulation state, certain operations must be performed to ensure data consistency:
| Modification Action | Required Operation |
|---|---|
set_joint_qpos() |
mj_forward() |
set_joint_qvel() |
mj_forward() |
set_mocap_pos_and_quat() |
mj_forward() |
Important: After
do_simulation()returns, the data is already automatically synchronized. You only need to manually callmj_forward()when you manually modify the state.
Why mj_forward() Is Needed After Modifying qpos¶
MuJoCo has many "derived quantities" (body poses, sensor values, contact forces, etc.) that need mj_forward to be refreshed. Only modifying qpos without calling forward leads to:
- Body poses being NaN or stale values
- Incorrect sensor data
- Inconsistent contact state
# ✅ Correct
env.set_joint_qpos(qpos)
env.mj_forward() # Refresh all derived quantities
# Now it is safe to read
body_pos = env.data.body_xpos("end_effector")
# ❌ Wrong — forgot forward
env.set_joint_qpos(...)
body_pos = env.data.body_xpos("end_effector") # May be NaN!
Common Data Sync Issues¶
| Symptom | Possible Cause | Solution |
|---|---|---|
| Reading stale state | No sync after modifying state | do_simulation() auto-syncs; manual operations need mj_forward() |
| Pose NaN | mj_forward() not called after modifying qpos |
Call mj_forward() after modifying the state |
| Sensor values unchanged | sensordata depends on forward | Read sensors after mj_forward() |
| Abnormal contact forces | Contact refreshes after step | Read contact immediately after step |