834e8dd506
- Improved time-sync logic readability in `PhysicsLoop`. - Fixed unattainable condition for breaking from stepping loop if data was reset. - Added measurement of actual real-time tracking. - Moved slowdown overlay to top left. - Report slowdown as % of real-time, rather than fraction. - Report actual slowdown if different than requested (very small timestep, PhysicsLoop cannot keep up) by more than 10%. - Replaced slowdown increments of 2 with decimal-rounded increments of 10^(1/10). This is a finer-grained scale (approximately 3x as fine) and well suited for decimal percentage representation. - Maximum slowdown increased to 1000. - Added `Simulate.refreshrate` with a default of 60Hz, in case videomode refreshrate is 0 (can occur e.g. when forwarding over X11). - Remove realtime reporting from regular info overlay. - Various cleanups. PiperOrigin-RevId: 471547481 Change-Id: Idd2514a4defb1dc431617f3a515cc747c4fd8971
234 lines
7.1 KiB
C++
234 lines
7.1 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 <condition_variable>
|
|
#include <mutex>
|
|
#include <thread>
|
|
|
|
#include <GLFW/glfw3.h>
|
|
#include <mujoco/mujoco.h>
|
|
|
|
#ifdef MJSIMULATE_STATIC
|
|
// static library
|
|
#define MJSIMULATEAPI
|
|
#define MJSIMULATELOCAL
|
|
#else
|
|
#ifdef MJSIMULATE_DLL_EXPORTS
|
|
#define MJSIMULATEAPI MUJOCO_HELPER_DLL_EXPORT
|
|
#else
|
|
#define MJSIMULATEAPI MUJOCO_HELPER_DLL_IMPORT
|
|
#endif
|
|
#define MJSIMULATELOCAL MUJOCO_HELPER_DLL_LOCAL
|
|
#endif
|
|
|
|
namespace mujoco {
|
|
|
|
//-------------------------------- global -----------------------------------------------
|
|
|
|
// Simulate states not contained in MuJoCo structures
|
|
class MJSIMULATEAPI Simulate {
|
|
public:
|
|
// create object and initialize the simulate ui
|
|
Simulate() = default;
|
|
|
|
// Apply UI pose perturbations to model and data
|
|
void applyposepertubations(int flg_paused);
|
|
|
|
// Apply UI force perturbations to model and data
|
|
void applyforceperturbations();
|
|
|
|
// Request that the Simulate UI thread render a new model
|
|
// optionally delete the old model and data when done
|
|
void load(const char* file, mjModel* m, mjData* d, bool delete_old_m_d);
|
|
|
|
// functions below are used by the renderthread
|
|
// load mjb or xml model that has been requested by load()
|
|
void loadmodel();
|
|
|
|
// prepare to render
|
|
void prepare();
|
|
|
|
// render the ui to the window
|
|
void render();
|
|
|
|
// clear callbacks registered in external structures
|
|
void clearcallback();
|
|
|
|
// loop to render the UI (must be called from main thread because of MacOS)
|
|
// https://discourse.glfw.org/t/multithreading-glfw/573/5
|
|
void renderloop();
|
|
|
|
// constants
|
|
static constexpr int kMaxFilenameLength = 1000;
|
|
|
|
// model and data to be visualized
|
|
mjModel* mnew = nullptr;
|
|
mjData* dnew = nullptr;
|
|
bool delete_old_m_d = false;
|
|
|
|
mjModel* m = nullptr;
|
|
mjData* d = nullptr;
|
|
std::mutex mtx;
|
|
std::condition_variable cond_loadrequest;
|
|
|
|
// 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 fullscreen = 0;
|
|
int vsync = 1;
|
|
int busywait = 0;
|
|
|
|
// keyframe index
|
|
int key = 0;
|
|
|
|
// simulation
|
|
int run = 1;
|
|
|
|
// atomics for cross-thread messages
|
|
std::atomic_bool exitrequest = false;
|
|
std::atomic_bool droploadrequest = false;
|
|
std::atomic_bool screenshotrequest = false;
|
|
std::atomic_int uiloadrequest = 0;
|
|
|
|
// loadrequest
|
|
// 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 loadError[kMaxFilenameLength] = "";
|
|
char dropfilename[kMaxFilenameLength] = "";
|
|
char filename[kMaxFilenameLength] = "";
|
|
char previous_filename[kMaxFilenameLength] = "";
|
|
|
|
// time synchronization
|
|
int realTimeIndex = 0;
|
|
bool speedChanged = true;
|
|
float measuredSlowdown = 1.0;
|
|
// logarithmically spaced realtime 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 ctrlnoisestd = 0.0;
|
|
double ctrlnoiserate = 0.0;
|
|
|
|
// watch
|
|
char field[mjMAXUITEXT] = "qpos";
|
|
int index = 0;
|
|
|
|
// physics: need sync
|
|
int disable[mjNDISABLE] = {0};
|
|
int enable[mjNENABLE] = {0};
|
|
|
|
// rendering: need sync
|
|
int camera = 0;
|
|
|
|
// abstract visualization
|
|
mjvScene scn = {};
|
|
mjvCamera cam = {};
|
|
mjvOption vopt = {};
|
|
mjvPerturb pert = {};
|
|
mjvFigure figconstraint = {};
|
|
mjvFigure figcost = {};
|
|
mjvFigure figtimer = {};
|
|
mjvFigure figsize = {};
|
|
mjvFigure figsensor = {};
|
|
|
|
// OpenGL rendering and UI
|
|
GLFWvidmode vmode = {};
|
|
int refreshRate = 60;
|
|
int windowpos[2] = {0};
|
|
int windowsize[2] = {0};
|
|
mjrContext con = {};
|
|
GLFWwindow* window = nullptr;
|
|
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 defOption[14] = {
|
|
{mjITEM_SECTION, "Option", 1, nullptr, "AO"},
|
|
{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_CHECKINT, "Left UI (Tab)", 1, &this->ui0_enable, " #258"},
|
|
{mjITEM_CHECKINT, "Right UI", 1, &this->ui1_enable, "S#258"},
|
|
{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"},
|
|
#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_END}
|
|
};
|
|
|
|
|
|
// simulation section of UI
|
|
const mjuiDef defSimulation[12] = {
|
|
{mjITEM_SECTION, "Simulation", 1, nullptr, "AS"},
|
|
{mjITEM_RADIO, "", 2, &this->run, "Pause\nRun"},
|
|
{mjITEM_BUTTON, "Reset", 2, nullptr, " #259"},
|
|
{mjITEM_BUTTON, "Reload", 2, nullptr, "CL"},
|
|
{mjITEM_BUTTON, "Align", 2, nullptr, "CA"},
|
|
{mjITEM_BUTTON, "Copy pose", 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", 2, &this->ctrlnoisestd, "0 2"},
|
|
{mjITEM_SLIDERNUM, "Noise rate", 2, &this->ctrlnoiserate, "0 2"},
|
|
{mjITEM_END}
|
|
};
|
|
|
|
|
|
// watch section of UI
|
|
const mjuiDef defWatch[5] = {
|
|
{mjITEM_SECTION, "Watch", 0, 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};
|
|
};
|
|
|
|
} // namespace mujoco
|
|
|
|
#endif
|