Files
Mujoco_WASM/simulate/simulate.h
T
Kevin Zakka e272e46770 Rename simulate "copy pose" button to "copy state", also save "qvel', ctrl, mpos and mquat if present. SHIFT-click will save values at full precision.
Example formatting:

```
# default precision
<key
  time="2.99"
  qpos="9.4287e-07 3.02972e-07 1.19963 0.999999 -9.3978e-06 0.00102591 8.06948e-06 -0.000337469 -0.000182994 -0.00282448 -0.000358084 -0.00370829 0.0631084 0.0996347 -0.00161963 0.0260658 -2.09992 -0.00459387 0.0474729 -0.0171906 0.0018216 0.72549 0.00133026 0.724951 0.000816735 0.724389 0.000454671 0.724 -0.00146646 -1.12233e-05 -0.0995942 0.00159877 0.0261695 -2.09993 0.00458912 0.0473279 0.0171317 0.00177837 0.725447 0.00131597 0.724941 0.00081406 0.724393 0.000465918 0.72402 -0.00140271 -1.42355e-05 0.0949171 0.0790632 -0.461397 1.02664 -9.42133e-05 -0.55539 -0.0952333 -0.0790705 -0.460978 1.02899 8.38339e-05 -0.671707 0.401772 0.197427 1.00101 0.999979 0.00125793 -0.00100982 -0.00631542"
  qvel="0.000132784 3.49155e-05 0.000536692 -0.000560519 -0.0242142 0.00693928 0.00119816 -0.000234844 0.00350923 -0.00036047 0.00137217 0.026605 0.000647559 -0.000813737 0.00151335 0.00158296 -0.000628786 0.0184574 -0.000751955 8.75972e-05 0.00017462 9.47749e-05 0.00018949 7.40435e-05 0.000170318 -2.65743e-05 5.67808e-05 0.000256585 -8.77605e-05 -0.00070146 5.90881e-05 0.00186213 0.00150788 0.000645196 0.0183261 -0.00189709 0.000234144 0.000180699 0.00020344 0.000161977 0.000154203 0.000127231 2.28526e-05 -2.85628e-06 0.000232213 2.32483e-05 -0.000415728 0.000285987 0.00873081 0.00361353 5.70155e-05 0.000316513 -0.00778618 0.00404914 -0.0239508 -0.0375704 -0.000528571 4.92965 3.51479e-15 -4.54231e-16 -9.39907e-15 -7.5733e-14 -1.54433e-14 8.71183e-14"
  ctrl="0 0 0 0 0 0 0.106158 0 0 -2.1308 0 0 0 0 0 0 0 0 0 -0.106158 0 0 -2.1308 0 0 0 0 0 0 0 0 0 0.1 0.08 -0.477 1.033 0 -0.556 -0.1 -0.08 -0.477 1.033 0 -0.556"
  mpos="0.5 0 0.5 0.5 0 0.5 0.5 0 0.5"
  mquat="1 0 0 0 1 0 0 0 1 0 0 0"
/>

# full precision
<key
  time="19.78000000000035     "
  qpos="-5.456931939182209e-13 4.210058291674657e-14  1.199596868198444      0.9999997705632744     -1.123107929038176e-05 -0.000677308838908712  -4.574736052563342e-13 4.81467762006399e-06   -0.0001470530836508491 -0.01285126600561344   -0.0003314670141921113 -0.003753266058087929  0.06165064101255623    0.01573179146576803    -3.013291189105153e-05 0.00898818165332332    -0.0005543865919957537 0.001960768693378906   0.003171337599202657   0.05998529721754536    -0.006869809693640725  0.7108455550986151     -2.768022862919269e-05 0.7234287700347743     -2.65358465809282e-05  0.7234305602907817     -2.427451970418236e-05 0.7234340023869628     0.0001444026830625057  0.004779116304496144   -0.01556918200171751   2.367030531882565e-05  0.00887857821391661    -0.0005393749458389281 -0.002002170464405725  0.004065365245773832   -0.059404381452836     -0.006700853036263364  0.7112073591870905     -2.793780628594305e-05 0.7234283783336887     -2.67570932686378e-05  0.7234302250735107     -2.446320244250625e-05 0.7234337167312712     0.0001441442525519209  0.004776981844081063   0.001964241516442131   -0.001279289930183057  0.0009901921906584617  0.005102724602622095   -0.0001969944416873799 0.0006055390861509305  -0.001981889349334628  0.001289775046291938   0.0009924227912350586  0.005084179458414934   0.0001970337430004949  0.0006055492125132441  0.4000059982468001     0.2000006330421453     1.001005166968155      0.9999986986616857     0.001264259603338192   -0.00100188514175821   -2.342548568520312e-05"
  qvel="8.028421913635349e-14  -7.933623204015651e-15 -1.766955416585763e-09 -1.242788325114248e-10 1.380680257623891e-08  4.072776622758517e-13  -2.152263214542579e-08 -3.878177040643757e-09 -7.698904695669224e-07 1.032750347898222e-09  -1.203279562630678e-09 -7.551458300921444e-08 2.469496246057255e-06  8.917880101195273e-08  -6.756803792056489e-06 -2.872076666906819e-06 1.785650772865195e-06  -5.01959632083366e-06  -5.599940213931672e-06 8.890181416091634e-07  1.64456042549838e-06   -1.371426840474208e-09 -2.120678515220117e-09 -2.024654983099582e-09 -3.146661587304676e-09 -2.584650818327909e-09 -4.007447393764789e-09 3.46515937628953e-09   1.271775029987075e-08  -2.488928508740539e-06 -8.746797042033161e-08 -6.8499144406484e-06   -2.901446048768352e-06 -1.790798673741401e-06 -5.013863339758384e-06 5.931327072492727e-06  9.379458926032098e-07  1.728266244061688e-06  -1.546731198107214e-09 -2.391411625987869e-09 -2.205072839286403e-09 -3.425520861505272e-09 -2.768770278332858e-09 -4.29204657928239e-09  3.77970711367913e-09   1.210836106515995e-08  2.48779855243758e-07   1.794262457563144e-08  2.425338558609319e-07  -4.490996387632652e-09 1.5470867213074e-10    -1.056275279772894e-10 -2.528203830071622e-07 -1.984360361016969e-08 2.432485190492325e-07  -4.490126811319184e-09 -1.553085076375719e-10 -1.050844929338509e-10 -5.03087850573677e-15  -3.069973955284326e-15 1.263049899085103e-14  -2.132657422360499e-13 1.240631051776082e-14  7.346260313161907e-14 "
  ctrl="0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                      0                     "
  mpos="0.5                    0                      0.5                    0.5                    0                      0.5                    0.5                    0                      0.5                   "
  mquat="1                      0                      0                      0                      1                      0                      0                      0                      1                      0                      0                      0                     "
/>
```

PiperOrigin-RevId: 741427042
Change-Id: Ib33f3e6215b7933d9267fc4c222d7a0abc75dbb4
2025-03-28 00:38:19 -07:00

331 lines
10 KiB
C++

// Copyright 2021 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MUJOCO_SIMULATE_SIMULATE_H_
#define MUJOCO_SIMULATE_SIMULATE_H_
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <optional>
#include <ratio>
#include <string>
#include <utility>
#include <vector>
#include <mujoco/mjui.h>
#include <mujoco/mujoco.h>
#include "platform_ui_adapter.h"
namespace mujoco {
// The viewer itself doesn't require a reentrant mutex, however we use it in
// order to provide a Python sync API that doesn't require separate locking
// (since sync is by far the most common operation), but that also won't
// deadlock if called when a lock is already held by the user script on the
// same thread.
class SimulateMutex : public std::recursive_mutex {};
using MutexLock = std::unique_lock<std::recursive_mutex>;
// Simulate states not contained in MuJoCo structures
class Simulate {
public:
using Clock = std::chrono::steady_clock;
static_assert(std::ratio_less_equal_v<Clock::period, std::milli>);
static constexpr int kMaxGeom = 20000;
// create object and initialize the simulate ui
Simulate(
std::unique_ptr<PlatformUIAdapter> platform_ui_adapter,
mjvCamera* cam, mjvOption* opt, mjvPerturb* pert, bool is_passive);
// Synchronize mjModel and mjData state with UI inputs, and update
// visualization.
void Sync();
void UpdateHField(int hfieldid);
void UpdateMesh(int meshid);
void UpdateTexture(int texid);
// Request that the Simulate UI display a "loading" message
// Called prior to Load or LoadMessageClear
void LoadMessage(const char* displayed_filename);
// Request that the Simulate UI thread render a new model
void Load(mjModel* m, mjData* d, const char* displayed_filename);
// Clear the loading message
// Can be called instead of Load to clear the message without
// requesting the UI load a model
void LoadMessageClear(void);
// functions below are used by the renderthread
// load mjb or xml model that has been requested by load()
void LoadOnRenderThread();
// render the ui to the window
void Render();
// loop to render the UI (must be called from main thread because of MacOS)
void RenderLoop();
// add state to history buffer
void AddToHistory();
// inject control noise
void InjectNoise();
// constants
static constexpr int kMaxFilenameLength = 1000;
// whether the viewer is operating in passive mode, where it cannot assume
// that it has exclusive access to mjModel, mjData, and various mjv objects
bool is_passive_ = false;
// model and data to be visualized
mjModel* mnew_ = nullptr;
mjData* dnew_ = nullptr;
mjModel* m_ = nullptr;
mjData* d_ = nullptr;
int ncam_ = 0;
int nkey_ = 0;
int state_size_ = 0; // number of mjtNums in a history buffer state
int nhistory_ = 0; // number of states saved in history buffer
int history_cursor_ = 0; // cursor pointing at last saved state
std::vector<int> body_parentid_;
std::vector<int> jnt_type_;
std::vector<int> jnt_group_;
std::vector<int> jnt_qposadr_;
std::vector<std::optional<std::pair<mjtNum, mjtNum>>> jnt_range_;
std::vector<std::string> jnt_names_;
std::vector<int> actuator_group_;
std::vector<std::optional<std::pair<mjtNum, mjtNum>>> actuator_ctrlrange_;
std::vector<std::string> actuator_names_;
std::vector<mjtNum> history_; // history buffer (nhistory x state_size)
// mjModel and mjData fields that can be modified by the user through the GUI
std::vector<mjtNum> qpos_;
std::vector<mjtNum> qpos_prev_;
std::vector<mjtNum> ctrl_;
std::vector<mjtNum> ctrl_prev_;
mjvSceneState scnstate_;
mjOption mjopt_prev_;
mjvOption opt_prev_;
mjvCamera cam_prev_;
int warn_vgeomfull_prev_;
// pending GUI-driven actions, to be applied at the next call to Sync
struct {
std::optional<std::string> save_xml;
std::optional<std::string> save_mjb;
std::optional<std::string> print_model;
std::optional<std::string> print_data;
bool reset;
bool align;
bool copy_key;
bool copy_key_full_precision;
bool load_from_history;
bool load_key;
bool save_key;
bool zero_ctrl;
int newperturb;
bool select;
mjuiState select_state;
bool ui_update_simulation;
bool ui_update_physics;
bool ui_update_rendering;
bool ui_update_joint;
bool ui_update_ctrl;
bool ui_remake_ctrl;
} pending_ = {};
SimulateMutex mtx;
std::condition_variable_any cond_loadrequest;
int frames_ = 0;
std::chrono::time_point<Clock> last_fps_update_;
double fps_ = 0;
// options
int spacing = 0;
int color = 0;
int font = 0;
int ui0_enable = 1;
int ui1_enable = 1;
int help = 0;
int info = 0;
int profiler = 0;
int sensor = 0;
int pause_update = 1;
int fullscreen = 0;
int vsync = 1;
int busywait = 0;
// keyframe index
int key = 0;
// index of history-scrubber slider
int scrub_index = 0;
// simulation
int run = 1;
// atomics for cross-thread messages
std::atomic_int exitrequest = 0;
std::atomic_int droploadrequest = 0;
std::atomic_int screenshotrequest = 0;
std::atomic_int uiloadrequest = 0;
// loadrequest
// 3: display a loading message
// 2: render thread asked to update its model
// 1: showing "loading" label, about to load
// 0: model loaded or no load requested.
int loadrequest = 0;
// strings
char load_error[kMaxFilenameLength] = "";
char dropfilename[kMaxFilenameLength] = "";
char filename[kMaxFilenameLength] = "";
char previous_filename[kMaxFilenameLength] = "";
// time synchronization
int real_time_index = 0;
bool speed_changed = true;
float measured_slowdown = 1.0;
// logarithmically spaced real-time slow-down coefficients (percent)
static constexpr float percentRealTime[] = {
100, 80, 66, 50, 40, 33, 25, 20, 16, 13,
10, 8, 6.6, 5.0, 4, 3.3, 2.5, 2, 1.6, 1.3,
1, .8, .66, .5, .4, .33, .25, .2, .16, .13,
.1
};
// control noise
double ctrl_noise_std = 0.0;
double ctrl_noise_rate = 0.0;
// watch
char field[mjMAXUITEXT] = "qpos";
int index = 0;
// physics: need sync
int disable[mjNDISABLE] = {0};
int enable[mjNENABLE] = {0};
int enableactuator[mjNGROUP] = {0};
// rendering: need sync
int camera = 0;
// abstract visualization
mjvScene scn;
mjvCamera& cam;
mjvOption& opt;
mjvPerturb& pert;
mjvFigure figconstraint = {};
mjvFigure figcost = {};
mjvFigure figtimer = {};
mjvFigure figsize = {};
mjvFigure figsensor = {};
// additional user-defined visualization geoms (used in passive mode)
mjvScene* user_scn = nullptr;
mjtByte user_scn_flags_prev_[mjNRNDFLAG];
std::vector<std::pair<mjrRect, mjvFigure>> user_figures_;
// OpenGL rendering and UI
int refresh_rate = 60;
int window_pos[2] = {0};
int window_size[2] = {0};
std::unique_ptr<PlatformUIAdapter> platform_ui;
mjuiState& uistate;
mjUI ui0 = {};
mjUI ui1 = {};
// Constant arrays needed for the option section of UI and the UI interface
// TODO setting the size here is not ideal
const mjuiDef def_option[13] = {
{mjITEM_SECTION, "Option", mjPRESERVE, nullptr, "AO"},
{mjITEM_CHECKINT, "Help", 2, &this->help, " #290"},
{mjITEM_CHECKINT, "Info", 2, &this->info, " #291"},
{mjITEM_CHECKINT, "Profiler", 2, &this->profiler, " #292"},
{mjITEM_CHECKINT, "Sensor", 2, &this->sensor, " #293"},
{mjITEM_CHECKINT, "Pause update", 2, &this->pause_update, ""},
#ifdef __APPLE__
{mjITEM_CHECKINT, "Fullscreen", 0, &this->fullscreen, " #294"},
#else
{mjITEM_CHECKINT, "Fullscreen", 1, &this->fullscreen, " #294"},
#endif
{mjITEM_CHECKINT, "Vertical Sync", 1, &this->vsync, ""},
{mjITEM_CHECKINT, "Busy Wait", 1, &this->busywait, ""},
{mjITEM_SELECT, "Spacing", 1, &this->spacing, "Tight\nWide"},
{mjITEM_SELECT, "Color", 1, &this->color, "Default\nOrange\nWhite\nBlack"},
{mjITEM_SELECT, "Font", 1, &this->font, "50 %\n100 %\n150 %\n200 %\n250 %\n300 %"},
{mjITEM_END}
};
// simulation section of UI
const mjuiDef def_simulation[14] = {
{mjITEM_SECTION, "Simulation", mjPRESERVE, nullptr, "AS"},
{mjITEM_RADIO, "", 5, &this->run, "Pause\nRun"},
{mjITEM_BUTTON, "Reset", 2, nullptr, " #259"},
{mjITEM_BUTTON, "Reload", 5, nullptr, "CL"},
{mjITEM_BUTTON, "Align", 2, nullptr, "CA"},
{mjITEM_BUTTON, "Copy state", 2, nullptr, "CC"},
{mjITEM_SLIDERINT, "Key", 3, &this->key, "0 0"},
{mjITEM_BUTTON, "Load key", 3},
{mjITEM_BUTTON, "Save key", 3},
{mjITEM_SLIDERNUM, "Noise scale", 5, &this->ctrl_noise_std, "0 1"},
{mjITEM_SLIDERNUM, "Noise rate", 5, &this->ctrl_noise_rate, "0 4"},
{mjITEM_SEPARATOR, "History", 1},
{mjITEM_SLIDERINT, "", 5, &this->scrub_index, "0 0"},
{mjITEM_END}
};
// watch section of UI
const mjuiDef def_watch[5] = {
{mjITEM_SECTION, "Watch", mjPRESERVE, nullptr, "AW"},
{mjITEM_EDITTXT, "Field", 2, this->field, "qpos"},
{mjITEM_EDITINT, "Index", 2, &this->index, "1"},
{mjITEM_STATIC, "Value", 2, nullptr, " "},
{mjITEM_END}
};
// info strings
char info_title[Simulate::kMaxFilenameLength] = {0};
char info_content[Simulate::kMaxFilenameLength] = {0};
// pending uploads
std::condition_variable_any cond_upload_;
int texture_upload_ = -1;
int mesh_upload_ = -1;
int hfield_upload_ = -1;
};
} // namespace mujoco
#endif