Introduce new logging API, fixes #858

PiperOrigin-RevId: 930744288
Change-Id: I6ec1203b55c031390f3eef23192e2337508ce886
This commit is contained in:
Yuval Tassa
2026-06-11 14:36:17 -07:00
committed by Copybara-Service
parent a2abaf7aef
commit 58f6d52491
45 changed files with 2586 additions and 422 deletions
+96 -27
View File
@@ -107,6 +107,7 @@ enum {
SECT_RENDERING,
SECT_VISUALIZATION,
SECT_GROUP,
SECT_LOGGING,
NSECT0,
// right ui
@@ -367,22 +368,27 @@ void UpdateProfiler(mj::Simulate* sim, const mjModel* m, const mjData* d) {
}
// get timers: total, collision, prepare, solve, other
mjtNum total = d->timer[mjTIMER_STEP].duration;
int number = d->timer[mjTIMER_STEP].number;
mjtNum total = d->timer[mjTIMER_STEP].duration - sim->timer_prev_[mjTIMER_STEP].duration;
int number = d->timer[mjTIMER_STEP].number - sim->timer_prev_[mjTIMER_STEP].number;
int prev_forward_number = sim->timer_prev_[mjTIMER_FORWARD].number;
mjtNum prev_forward_duration = sim->timer_prev_[mjTIMER_FORWARD].duration;
if (!number) {
total = d->timer[mjTIMER_FORWARD].duration;
number = d->timer[mjTIMER_FORWARD].number;
total = d->timer[mjTIMER_FORWARD].duration - prev_forward_duration;
number = d->timer[mjTIMER_FORWARD].number - prev_forward_number;
}
if (number) { // skip update if no measurements
if (number > 0) { // skip update if no measurements
float tdata[5] = {
static_cast<float>(total/number),
static_cast<float>(d->timer[mjTIMER_POS_COLLISION].duration/number),
static_cast<float>(d->timer[mjTIMER_POS_MAKE].duration/number) +
static_cast<float>(d->timer[mjTIMER_POS_PROJECT].duration/number),
static_cast<float>(d->timer[mjTIMER_CONSTRAINT].duration/number),
0
};
static_cast<float>(total / number),
static_cast<float>((d->timer[mjTIMER_POS_COLLISION].duration -
sim->timer_prev_[mjTIMER_POS_COLLISION].duration) / number),
static_cast<float>((d->timer[mjTIMER_POS_MAKE].duration -
sim->timer_prev_[mjTIMER_POS_MAKE].duration +
d->timer[mjTIMER_POS_PROJECT].duration -
sim->timer_prev_[mjTIMER_POS_PROJECT].duration) / number),
static_cast<float>((d->timer[mjTIMER_CONSTRAINT].duration -
sim->timer_prev_[mjTIMER_CONSTRAINT].duration) / number),
0};
tdata[4] = tdata[0] - tdata[1] - tdata[2] - tdata[3];
// update figtimer
@@ -399,6 +405,10 @@ void UpdateProfiler(mj::Simulate* sim, const mjModel* m, const mjData* d) {
}
}
for (int i = 0; i < mjNTIMER; i++) {
sim->timer_prev_[i] = d->timer[i];
}
// get total number of iterations and nonzeros
mjtNum sqrt_nnz = 0;
int solver_niter = 0;
@@ -1154,6 +1164,35 @@ void MakeGroupSection(mj::Simulate* sim) {
mjui_add(&sim->ui0, defGroup);
}
// make logging section of UI
void MakeLoggingSection(mj::Simulate* sim) {
mjLogConfig cfg = mju_getLogConfig();
sim->log_console = cfg.logto_console;
sim->log_file = cfg.logto_file;
for (int i = 0; i < mjNTOPIC; i++) {
sim->log_topics[i] = ((cfg.topics & (1 << i)) != 0);
}
mjuiDef defLogging[] = {
{mjITEM_SECTION, "Logging", mjPRESERVE, nullptr, "AL"},
{mjITEM_CHECKBYTE, "Console", 2, &sim->log_console, ""},
{mjITEM_CHECKBYTE, "File", 2, &sim->log_file, ""},
{mjITEM_SEPARATOR, "Info topics", 1},
{mjITEM_END}
};
mjui_add(&sim->ui0, defLogging);
mjuiDef defTopic[] = {
{mjITEM_CHECKBYTE, "", 2, nullptr, ""},
{mjITEM_END}
};
for (int i = 0; i < mjNTOPIC; i++) {
mju::strcpy_arr(defTopic[0].name, mjTOPICSTRING[i]);
defTopic[0].pdata = sim->log_topics + i;
mjui_add(&sim->ui0, defTopic);
}
}
// make joint section of UI
void MakeJointSection(mj::Simulate* sim) {
mjuiDef defJoint[] = {
@@ -1304,6 +1343,7 @@ void MakeUiSections(mj::Simulate* sim, const mjModel* m, const mjData* d) {
MakeRenderingSection(sim, m);
MakeVisualizationSection(sim, m);
MakeGroupSection(sim);
MakeLoggingSection(sim);
MakeJointSection(sim);
MakeControlSection(sim);
MakeEqualitySection(sim);
@@ -1406,14 +1446,6 @@ mjtNum Timer() {
return elapsed.count();
}
// clear all times
void ClearTimers(mjData* d) {
for (int i=0; i<mjNTIMER; i++) {
d->timer[i].duration = 0;
d->timer[i].number = 0;
}
}
// copy current camera to clipboard as MJCF specification
void CopyCamera(mj::Simulate* sim) {
mjvGLCamera* camera = sim->scn.camera;
@@ -1479,6 +1511,28 @@ void UpdateSettings(mj::Simulate* sim, const mjModel* m) {
if (old_camera != sim->camera) {
sim->pending_.ui_update_rendering = true;
}
// logging flags
mjLogConfig cfg = mju_getLogConfig();
bool logging_changed = false;
if (sim->log_console != cfg.logto_console) {
sim->log_console = cfg.logto_console;
logging_changed = true;
}
if (sim->log_file != cfg.logto_file) {
sim->log_file = cfg.logto_file;
logging_changed = true;
}
for (int i = 0; i < mjNTOPIC; i++) {
int enabled = ((cfg.topics & (1 << i)) != 0);
if (sim->log_topics[i] != enabled) {
sim->log_topics[i] = enabled;
logging_changed = true;
}
}
if (logging_changed) {
sim->pending_.ui_update_logging = true;
}
}
// Compute suitable font scale.
@@ -1722,7 +1776,6 @@ void UiEvent(mjuiState* state) {
// rendering section
else if (it && it->sectionid==SECT_RENDERING) {
// only update the camera when the camera itself changed
if (it->pdata == &sim->camera) {
if (sim->camera==0) {
@@ -1772,6 +1825,20 @@ void UiEvent(mjuiState* state) {
}
}
// logging section
else if (it && it->sectionid==SECT_LOGGING) {
mjLogConfig cfg = mju_getLogConfig();
cfg.logto_console = sim->log_console;
cfg.logto_file = sim->log_file;
cfg.topics = 0;
for (int i = 0; i < mjNTOPIC; i++) {
if (sim->log_topics[i]) {
cfg.topics |= (1 << i);
}
}
mju_setLogConfig(cfg);
}
// stop if UI processed event
if (it!=nullptr || (state->type==mjEVENT_KEY && state->key==0)) {
return;
@@ -1815,8 +1882,6 @@ void UiEvent(mjuiState* state) {
case mjKEY_RIGHT: // step forward
if (!sim->is_passive_ && sim->m_ && !sim->run) {
ClearTimers(sim->d_);
// currently in scrubber: increment scrub, load state, update slider UI
if (sim->scrub_index < 0) {
sim->scrub_index++;
@@ -1839,7 +1904,6 @@ void UiEvent(mjuiState* state) {
case mjKEY_LEFT: // step backward
if (!sim->is_passive_ && sim->m_) {
sim->run = 0;
ClearTimers(sim->d_);
// decrement scrub, load state
sim->scrub_index = mjMAX(sim->scrub_index - 1, 1 - sim->nhistory_);
@@ -2184,6 +2248,7 @@ void Simulate::Sync(bool state_only) {
if (pending_.reset) {
mj_resetData(m_, d_);
memset(timer_prev_, 0, sizeof(timer_prev_));
mj_forward(m_, d_);
load_error[0] = '\0';
update_profiler = true;
@@ -2366,9 +2431,6 @@ void Simulate::Sync(bool state_only) {
UpdateSensorImage(this, m_, d_);
}
// clear timers once profiler info has been copied
ClearTimers(d_);
if (this->run || this->is_passive_) {
// clear old perturbations, apply new
mju_zero(d_->xfrc_applied, 6*m_->nbody);
@@ -2689,6 +2751,13 @@ void Simulate::Render() {
pending_.ui_update_visualization = false;
}
if (pending_.ui_update_logging) {
if (this->ui0_enable && this->ui0.sect[SECT_LOGGING].state) {
mjui0_update_section(this, SECT_LOGGING);
}
pending_.ui_update_logging = false;
}
if (is_passive_) {
if (this->ui0_enable && this->ui0.sect[SECT_RENDERING].state &&
(cam_prev_.type != cam.type ||
+7
View File
@@ -172,6 +172,7 @@ class Simulate {
bool ui_update_joint;
bool ui_update_ctrl;
bool ui_update_equality;
bool ui_update_logging;
bool ui_remake_ctrl;
} pending_ = {};
@@ -257,6 +258,12 @@ class Simulate {
int enable[mjNENABLE] = {0};
int enableactuator[mjNGROUP] = {0};
// logging: need sync
mjtByte log_console = 0;
mjtByte log_file = 0;
mjtByte log_topics[mjNTOPIC] = {0};
mjTimerStat timer_prev_[mjNTIMER] = {};
// rendering: need sync
int camera = 0;