Improvements to time synchronisation in simulate.
- 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
This commit is contained in:
committed by
Copybara-Service
parent
7839c1c42a
commit
834e8dd506
+42
-24
@@ -22,7 +22,6 @@
|
||||
#include <thread>
|
||||
|
||||
#include <mujoco/mujoco.h>
|
||||
#include <mujoco/mjxmacro.h>
|
||||
#include "glfw_dispatch.h"
|
||||
#include "simulate.h"
|
||||
#include "array_safety.h"
|
||||
@@ -34,9 +33,9 @@ namespace mju = ::mujoco::sample_util;
|
||||
using ::mujoco::Glfw;
|
||||
|
||||
// constants
|
||||
const double syncmisalign = 0.1; // maximum time mis-alignment before re-sync
|
||||
const double refreshfactor = 0.7; // fraction of refresh available for simulation
|
||||
const int kErrorLength = 1024;
|
||||
const double syncMisalign = 0.1; // maximum mis-alignment before re-sync (simulation seconds)
|
||||
const double simRefreshFraction = 0.7; // fraction of refresh available for simulation
|
||||
const int kErrorLength = 1024; // load error string length
|
||||
|
||||
// model and data
|
||||
mjModel* m = nullptr;
|
||||
@@ -101,8 +100,8 @@ mjModel* LoadModel(const char* file, mj::Simulate& sim) {
|
||||
// simulate in background thread (while rendering in main thread)
|
||||
void PhysicsLoop(mj::Simulate& sim) {
|
||||
// cpu-sim syncronization point
|
||||
double cpusync = 0;
|
||||
mjtNum simsync = 0;
|
||||
double syncCPU = 0;
|
||||
mjtNum syncSim = 0;
|
||||
|
||||
// run until asked to exit
|
||||
while (!sim.exitrequest.load()) {
|
||||
@@ -154,6 +153,7 @@ void PhysicsLoop(mj::Simulate& sim) {
|
||||
}
|
||||
|
||||
{
|
||||
// lock the sim mutex
|
||||
const std::lock_guard<std::mutex> lock(sim.mtx);
|
||||
|
||||
// run only if model is present
|
||||
@@ -161,30 +161,39 @@ void PhysicsLoop(mj::Simulate& sim) {
|
||||
// running
|
||||
if (sim.run) {
|
||||
// record cpu time at start of iteration
|
||||
double tmstart = Glfw().glfwGetTime();
|
||||
double startCPU = Glfw().glfwGetTime();
|
||||
|
||||
// elapsed CPU and simulation time since last sync
|
||||
double elapsedCPU = startCPU - syncCPU;
|
||||
double elapsedSim = d->time - syncSim;
|
||||
|
||||
// inject noise
|
||||
if (sim.ctrlnoisestd) {
|
||||
// convert rate and scale to discrete time given current timestep
|
||||
// convert rate and scale to discrete time (Ornstein–Uhlenbeck)
|
||||
mjtNum rate = mju_exp(-m->opt.timestep / sim.ctrlnoiserate);
|
||||
mjtNum scale = sim.ctrlnoisestd * mju_sqrt(1-rate*rate);
|
||||
|
||||
for (int i=0; i<m->nu; i++) {
|
||||
// update noise
|
||||
ctrlnoise[i] = rate * ctrlnoise[i] + scale * mju_standardNormal(nullptr);
|
||||
|
||||
// apply noise
|
||||
d->ctrl[i] = ctrlnoise[i];
|
||||
}
|
||||
}
|
||||
|
||||
// out-of-sync (for any reason)
|
||||
mjtNum offset = mju_abs((d->time*sim.slow_down-simsync)-(tmstart-cpusync));
|
||||
if (d->time*sim.slow_down<simsync || tmstart<cpusync || cpusync==0 ||
|
||||
offset > syncmisalign*sim.slow_down || sim.speed_changed) {
|
||||
// requested slow-down factor
|
||||
double slowdown = 100 / sim.percentRealTime[sim.realTimeIndex];
|
||||
|
||||
// misalignment condition: distance from target sim time is bigger than syncmisalign
|
||||
bool misaligned = mju_abs(elapsedCPU/slowdown - elapsedSim) > syncMisalign;
|
||||
|
||||
// out-of-sync (for any reason): reset sync times, step
|
||||
if (elapsedSim < 0 || elapsedCPU < 0 || syncCPU == 0 || misaligned || sim.speedChanged) {
|
||||
// re-sync
|
||||
cpusync = tmstart;
|
||||
simsync = d->time*sim.slow_down;
|
||||
sim.speed_changed = false;
|
||||
syncCPU = startCPU;
|
||||
syncSim = d->time;
|
||||
sim.speedChanged = false;
|
||||
|
||||
// clear old perturbations, apply new
|
||||
mju_zero(d->xfrc_applied, 6*m->nbody);
|
||||
@@ -195,22 +204,31 @@ void PhysicsLoop(mj::Simulate& sim) {
|
||||
mj_step(m, d);
|
||||
}
|
||||
|
||||
// in-sync
|
||||
// in-sync: step until ahead of cpu
|
||||
else {
|
||||
// step while simtime lags behind cputime, and within safefactor
|
||||
while ((d->time*sim.slow_down-simsync) < (Glfw().glfwGetTime()-cpusync) &&
|
||||
(Glfw().glfwGetTime()-tmstart) < refreshfactor/sim.vmode.refreshRate) {
|
||||
bool measured = false;
|
||||
mjtNum prevSim = d->time;
|
||||
double refreshTime = simRefreshFraction/sim.refreshRate;
|
||||
|
||||
// step while sim lags behind cpu and within refreshTime
|
||||
while ((d->time - syncSim)*slowdown < (Glfw().glfwGetTime()-syncCPU) &&
|
||||
(Glfw().glfwGetTime()-startCPU) < refreshTime) {
|
||||
// measure slowdown before first step
|
||||
if (!measured && elapsedSim) {
|
||||
sim.measuredSlowdown = elapsedCPU / elapsedSim;
|
||||
measured = true;
|
||||
}
|
||||
|
||||
// clear old perturbations, apply new
|
||||
mju_zero(d->xfrc_applied, 6*m->nbody);
|
||||
sim.applyposepertubations(0); // move mocap bodies only
|
||||
sim.applyforceperturbations();
|
||||
|
||||
// run mj_step
|
||||
mjtNum prevtm = d->time*sim.slow_down;
|
||||
// call mj_step
|
||||
mj_step(m, d);
|
||||
|
||||
// break on reset
|
||||
if (d->time*sim.slow_down<prevtm) {
|
||||
// break if reset
|
||||
if (d->time < prevSim) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -226,7 +244,7 @@ void PhysicsLoop(mj::Simulate& sim) {
|
||||
mj_forward(m, d);
|
||||
}
|
||||
}
|
||||
} // std::lock_guard<std::mutex>
|
||||
} // release std::lock_guard<std::mutex>
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user