Speed improvements in mj_solveLD (14% on Intel Skylake Xeon 36 cores).
PiperOrigin-RevId: 486960670 Change-Id: I8fce96427158ec36069dea893500059e93bd40db
This commit is contained in:
committed by
Copybara-Service
parent
9ffde761e8
commit
9fc9823a8c
@@ -38,6 +38,7 @@ General
|
|||||||
joints. See the two new examples:
|
joints. See the two new examples:
|
||||||
`particle_free.xml <https://github.com/deepmind/mujoco/tree/main/model/composite/particle_free.xml>`_ and
|
`particle_free.xml <https://github.com/deepmind/mujoco/tree/main/model/composite/particle_free.xml>`_ and
|
||||||
`particle_free2d.xml <https://github.com/deepmind/mujoco/tree/main/model/composite/particle_free2d.xml>`_
|
`particle_free2d.xml <https://github.com/deepmind/mujoco/tree/main/model/composite/particle_free2d.xml>`_
|
||||||
|
- Better mj_solveLD performance. This gives an improvement of 14% on a Intel Skylake Xeon with 36 cores.
|
||||||
|
|
||||||
Version 2.3.0 (October 18, 2022)
|
Version 2.3.0 (October 18, 2022)
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|||||||
@@ -1088,32 +1088,26 @@ void mj_factorM(const mjModel* m, mjData* d) {
|
|||||||
// sparse backsubstitution: x = inv(L'*D*L)*y
|
// sparse backsubstitution: x = inv(L'*D*L)*y
|
||||||
// L is in lower triangle of qLD; D is on diagonal of qLD
|
// L is in lower triangle of qLD; D is on diagonal of qLD
|
||||||
// handle n vectors at once
|
// handle n vectors at once
|
||||||
void mj_solveLD(const mjModel* m, mjtNum* x, const mjtNum* y, int n,
|
void mj_solveLD(const mjModel* m, mjtNum* restrict x, int n,
|
||||||
const mjtNum* qLD, const mjtNum* qLDiagInv) {
|
const mjtNum* qLD, const mjtNum* qLDiagInv) {
|
||||||
mjtNum tmp;
|
|
||||||
|
|
||||||
// local copies of key variables
|
// local copies of key variables
|
||||||
int* dof_Madr = m->dof_Madr;
|
int* dof_Madr = m->dof_Madr;
|
||||||
int* dof_parentid = m->dof_parentid;
|
int* dof_parentid = m->dof_parentid;
|
||||||
int nv = m->nv;
|
int nv = m->nv;
|
||||||
|
|
||||||
// x = y
|
|
||||||
if (x != y) {
|
|
||||||
mju_copy(x, y, n*nv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// single vector
|
// single vector
|
||||||
if (n==1) {
|
if (n==1) {
|
||||||
// x <- inv(L') * x; skip simple, exploit sparsity of input vector
|
// x <- inv(L') * x; skip simple, exploit sparsity of input vector
|
||||||
for (int i=nv-1; i>=0; i--) {
|
for (int i=nv-1; i>=0; i--) {
|
||||||
if (!m->dof_simplenum[i] && (tmp = x[i])) {
|
if (!m->dof_simplenum[i] && x[i]) {
|
||||||
// init
|
// init
|
||||||
int Madr_ij = dof_Madr[i]+1;
|
int Madr_ij = dof_Madr[i]+1;
|
||||||
int j = dof_parentid[i];
|
int j = dof_parentid[i];
|
||||||
|
|
||||||
// traverse ancestors backwards
|
// traverse ancestors backwards
|
||||||
|
// read directly from x[i] since i cannot be a parent of itself
|
||||||
while (j>=0) {
|
while (j>=0) {
|
||||||
x[j] -= qLD[Madr_ij++]*tmp; // x(j) -= L(i,j) * x(i)
|
x[j] -= qLD[Madr_ij++]*x[i]; // x(j) -= L(i,j) * x(i)
|
||||||
|
|
||||||
// advance to parent
|
// advance to parent
|
||||||
j = dof_parentid[j];
|
j = dof_parentid[j];
|
||||||
@@ -1134,14 +1128,13 @@ void mj_solveLD(const mjModel* m, mjtNum* x, const mjtNum* y, int n,
|
|||||||
int j = dof_parentid[i];
|
int j = dof_parentid[i];
|
||||||
|
|
||||||
// traverse ancestors backwards
|
// traverse ancestors backwards
|
||||||
tmp = x[i];
|
// write directly in x[i] since i cannot be a parent of itself
|
||||||
while (j>=0) {
|
while (j>=0) {
|
||||||
tmp -= qLD[Madr_ij++]*x[j]; // x(i) -= L(i,j) * x(j)
|
x[i] -= qLD[Madr_ij++]*x[j]; // x(i) -= L(i,j) * x(j)
|
||||||
|
|
||||||
// advance to parent
|
// advance to parent
|
||||||
j = dof_parentid[j];
|
j = dof_parentid[j];
|
||||||
}
|
}
|
||||||
x[i] = tmp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1149,6 +1142,7 @@ void mj_solveLD(const mjModel* m, mjtNum* x, const mjtNum* y, int n,
|
|||||||
// multiple vectors
|
// multiple vectors
|
||||||
else {
|
else {
|
||||||
int offset;
|
int offset;
|
||||||
|
mjtNum tmp;
|
||||||
|
|
||||||
// x <- inv(L') * x; skip simple
|
// x <- inv(L') * x; skip simple
|
||||||
for (int i=nv-1; i>=0; i--) {
|
for (int i=nv-1; i>=0; i--) {
|
||||||
@@ -1204,11 +1198,13 @@ void mj_solveLD(const mjModel* m, mjtNum* x, const mjtNum* y, int n,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// sparse backsubstitution: x = inv(L'*D*L)*y
|
// sparse backsubstitution: x = inv(L'*D*L)*y
|
||||||
// use factorization in d
|
// use factorization in d
|
||||||
void mj_solveM(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n) {
|
void mj_solveM(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n) {
|
||||||
mj_solveLD(m, x, y, n, d->qLD, d->qLDiagInv);
|
if (x != y) {
|
||||||
|
mju_copy(x, y, n*m->nv);
|
||||||
|
}
|
||||||
|
mj_solveLD(m, x, n, d->qLD, d->qLDiagInv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ MJAPI void mj_factorI(const mjModel* m, mjData* d, const mjtNum* M, mjtNum* qLD,
|
|||||||
MJAPI void mj_factorM(const mjModel* m, mjData* d);
|
MJAPI void mj_factorM(const mjModel* m, mjData* d);
|
||||||
|
|
||||||
// sparse backsubstitution: x = inv(L'*D*L)*y
|
// sparse backsubstitution: x = inv(L'*D*L)*y
|
||||||
MJAPI void mj_solveLD(const mjModel* m, mjtNum* x, const mjtNum* y, int n,
|
MJAPI void mj_solveLD(const mjModel* m, mjtNum* x, int n,
|
||||||
const mjtNum* qLD, const mjtNum* qLDiagInv);
|
const mjtNum* qLD, const mjtNum* qLDiagInv);
|
||||||
|
|
||||||
// sparse backsubstitution: x = inv(L'*D*L)*y, use factorization in d
|
// sparse backsubstitution: x = inv(L'*D*L)*y, use factorization in d
|
||||||
|
|||||||
@@ -576,7 +576,8 @@ void mj_EulerSkip(const mjModel* m, mjData* d, int skipfactor) {
|
|||||||
|
|
||||||
// solve
|
// solve
|
||||||
mju_add(qfrc, d->qfrc_smooth, d->qfrc_constraint, nv);
|
mju_add(qfrc, d->qfrc_smooth, d->qfrc_constraint, nv);
|
||||||
mj_solveLD(m, qacc, qfrc, 1, d->qH, d->qHDiagInv);
|
mju_copy(qacc, qfrc, m->nv);
|
||||||
|
mj_solveLD(m, qacc, 1, d->qH, d->qHDiagInv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance state and time
|
// advance state and time
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// A benchmark for comparing different implementations of mj_solveLD.
|
||||||
|
|
||||||
|
#include <benchmark/benchmark.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <absl/base/attributes.h>
|
||||||
|
#include <mujoco/mjdata.h>
|
||||||
|
#include <mujoco/mujoco.h>
|
||||||
|
#include "test/fixture.h"
|
||||||
|
|
||||||
|
namespace mujoco {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// number of steps to roll out before benhmarking
|
||||||
|
static const int kNumWarmupSteps = 500;
|
||||||
|
|
||||||
|
// number of steps to benchmark
|
||||||
|
static const int kNumBenchmarkSteps = 50;
|
||||||
|
|
||||||
|
// copy array into vector
|
||||||
|
std::vector<mjtNum> AsVector(const mjtNum* array, int n) {
|
||||||
|
return std::vector<mjtNum>(array, array + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------- old functions --------------------------------
|
||||||
|
|
||||||
|
void ABSL_ATTRIBUTE_NOINLINE solveLD_baseline(const mjModel* m, mjtNum* x,
|
||||||
|
const mjtNum* y,
|
||||||
|
const mjtNum* qLD,
|
||||||
|
const mjtNum* qLDiagInv) {
|
||||||
|
mjtNum tmp;
|
||||||
|
|
||||||
|
// local copies of key variables
|
||||||
|
int* dof_Madr = m->dof_Madr;
|
||||||
|
int* dof_parentid = m->dof_parentid;
|
||||||
|
int nv = m->nv;
|
||||||
|
|
||||||
|
// x = y
|
||||||
|
if (x != y) {
|
||||||
|
mju_copy(x, y, nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// x <- inv(L') * x; skip simple, exploit sparsity of input vector
|
||||||
|
for (int i=nv-1; i >= 0; i--) {
|
||||||
|
if (!m->dof_simplenum[i] && (tmp = x[i])) {
|
||||||
|
// init
|
||||||
|
int Madr_ij = dof_Madr[i]+1;
|
||||||
|
int j = dof_parentid[i];
|
||||||
|
|
||||||
|
// traverse ancestors backwards
|
||||||
|
while (j >= 0) {
|
||||||
|
x[j] -= qLD[Madr_ij++]*tmp; // x(j) -= L(i,j) * x(i)
|
||||||
|
|
||||||
|
// advance to parent
|
||||||
|
j = dof_parentid[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// x <- inv(D) * x
|
||||||
|
for (int i=0; i < nv; i++) {
|
||||||
|
x[i] *= qLDiagInv[i]; // x(i) /= L(i,i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// x <- inv(L) * x; skip simple
|
||||||
|
for (int i=0; i < nv; i++) {
|
||||||
|
if (!m->dof_simplenum[i]) {
|
||||||
|
// init
|
||||||
|
int Madr_ij = dof_Madr[i]+1;
|
||||||
|
int j = dof_parentid[i];
|
||||||
|
|
||||||
|
// traverse ancestors backwards
|
||||||
|
tmp = x[i];
|
||||||
|
while (j>= 0) {
|
||||||
|
tmp -= qLD[Madr_ij++]*x[j]; // x(i) -= L(i,j) * x(j)
|
||||||
|
|
||||||
|
// advance to parent
|
||||||
|
j = dof_parentid[j];
|
||||||
|
}
|
||||||
|
x[i] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void solveM_baseline(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y) {
|
||||||
|
solveLD_baseline(m, x, y, d->qLD, d->qLDiagInv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------- benchmark ------------------------------------
|
||||||
|
|
||||||
|
static void BM_solveLD(benchmark::State& state, bool new_function) {
|
||||||
|
static mjModel* m = LoadModelFromPath("composite/cloth.xml");
|
||||||
|
mjData* d = mj_makeData(m);
|
||||||
|
|
||||||
|
// warm-up rollout to get a typcal state
|
||||||
|
for (int i=0; i < kNumWarmupSteps; i++) {
|
||||||
|
AddCtrlNoise(m, d, i);
|
||||||
|
mj_step(m, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate gadient
|
||||||
|
mjMARKSTACK;
|
||||||
|
mjtNum *grad = mj_stackAlloc(d, m->nv);
|
||||||
|
mjtNum *Ma = mj_stackAlloc(d, m->nv);
|
||||||
|
mjtNum *res = mj_stackAlloc(d, m->nv);
|
||||||
|
|
||||||
|
// compute gradient
|
||||||
|
mj_mulM(m, d, Ma, d->qacc);
|
||||||
|
for (int i=0; i < m->nv; i++) {
|
||||||
|
grad[i] = Ma[i] - d->qfrc_smooth[i] - d->qfrc_constraint[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// save state
|
||||||
|
std::vector<mjtNum> qpos = AsVector(d->qpos, m->nq);
|
||||||
|
std::vector<mjtNum> qvel = AsVector(d->qvel, m->nv);
|
||||||
|
std::vector<mjtNum> act = AsVector(d->act, m->na);
|
||||||
|
std::vector<mjtNum> warmstart = AsVector(d->qacc_warmstart, m->nv);
|
||||||
|
|
||||||
|
// reset state, benchmark subsequent kNumBenchmarkSteps steps
|
||||||
|
while (state.KeepRunningBatch(kNumBenchmarkSteps)) {
|
||||||
|
mju_copy(d->qpos, qpos.data(), m->nq);
|
||||||
|
mju_copy(d->qvel, qvel.data(), m->nv);
|
||||||
|
mju_copy(d->act, act.data(), m->na);
|
||||||
|
mju_copy(d->qacc_warmstart, warmstart.data(), m->nv);
|
||||||
|
|
||||||
|
for (int i=0; i < kNumBenchmarkSteps; i++) {
|
||||||
|
if (new_function) {
|
||||||
|
mj_solveM(m, d, res, grad, 1);
|
||||||
|
} else {
|
||||||
|
solveM_baseline(m, d, res, grad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finalize
|
||||||
|
mjFREESTACK;
|
||||||
|
mj_deleteData(d);
|
||||||
|
state.SetItemsProcessed(state.iterations());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_solveLD_new(benchmark::State& state) {
|
||||||
|
MujocoErrorTestGuard guard;
|
||||||
|
BM_solveLD(state, true);
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_solveLD_new);
|
||||||
|
|
||||||
|
void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_solveLD_old(benchmark::State& state) {
|
||||||
|
MujocoErrorTestGuard guard;
|
||||||
|
BM_solveLD(state, false);
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_solveLD_old);
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace mujoco
|
||||||
@@ -28,53 +28,23 @@
|
|||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using ::testing::NotNull;
|
|
||||||
|
|
||||||
// number of steps to roll out before benhmarking
|
// number of steps to roll out before benhmarking
|
||||||
static const int kNumWarmupSteps = 500;
|
static const int kNumWarmupSteps = 500;
|
||||||
|
|
||||||
// number of steps to benchmark
|
// number of steps to benchmark
|
||||||
static const int kNumBenchmarkSteps = 50;
|
static const int kNumBenchmarkSteps = 50;
|
||||||
|
|
||||||
|
|
||||||
// copy array into vector
|
// copy array into vector
|
||||||
std::vector<mjtNum> AsVector(const mjtNum* array, int n) {
|
std::vector<mjtNum> AsVector(const mjtNum* array, int n) {
|
||||||
return std::vector<mjtNum>(array, array + n);
|
return std::vector<mjtNum>(array, array + n);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_ctrl_noise(const mjModel* m, mjData* d, int step) {
|
|
||||||
for (int i = 0; i < m->nu; i++) {
|
|
||||||
mjtNum center = 0.0;
|
|
||||||
mjtNum radius = 1.0;
|
|
||||||
mjtNum* range = m->actuator_ctrlrange + 2 * i;
|
|
||||||
if (m->actuator_ctrllimited[i]) {
|
|
||||||
center = (range[1] + range[0]) / 2;
|
|
||||||
radius = (range[1] - range[0]) / 2;
|
|
||||||
}
|
|
||||||
radius *= 0.01;
|
|
||||||
d->ctrl[i] = center + radius * (2 * mju_Halton(step, i + 2) - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void assert_model_not_null(mjModel* model,
|
|
||||||
const std::array<char, 1024>& error) {
|
|
||||||
ASSERT_THAT(model, NotNull()) << "Failed to load model: " << error.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
static mjModel* load_model(const char* model_path) {
|
|
||||||
const std::string xml_path = GetModelPath(model_path);
|
|
||||||
std::array<char, 1024> error;
|
|
||||||
mjModel* model = mj_loadXML(xml_path.c_str(), nullptr, error.data(), error.size());
|
|
||||||
assert_model_not_null(model, error);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void run_step_benchmark(const mjModel* model, benchmark::State& state) {
|
static void run_step_benchmark(const mjModel* model, benchmark::State& state) {
|
||||||
mjData* data = mj_makeData(model);
|
mjData* data = mj_makeData(model);
|
||||||
|
|
||||||
// warm-up rollout to get a typcal state
|
// warm-up rollout to get a typcal state
|
||||||
for (int i=0; i < kNumWarmupSteps; i++) {
|
for (int i=0; i < kNumWarmupSteps; i++) {
|
||||||
add_ctrl_noise(model, data, i);
|
AddCtrlNoise(model, data, i);
|
||||||
mj_step(model, data);
|
mj_step(model, data);
|
||||||
}
|
}
|
||||||
// save state
|
// save state
|
||||||
@@ -91,7 +61,7 @@ static void run_step_benchmark(const mjModel* model, benchmark::State& state) {
|
|||||||
mju_copy(data->qacc_warmstart, warmstart.data(), model->nv);
|
mju_copy(data->qacc_warmstart, warmstart.data(), model->nv);
|
||||||
|
|
||||||
for (int i=0; i < kNumBenchmarkSteps; i++) {
|
for (int i=0; i < kNumBenchmarkSteps; i++) {
|
||||||
add_ctrl_noise(model, data, i+kNumWarmupSteps);
|
AddCtrlNoise(model, data, i+kNumWarmupSteps);
|
||||||
mj_step(model, data);
|
mj_step(model, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,21 +77,21 @@ static void run_step_benchmark(const mjModel* model, benchmark::State& state) {
|
|||||||
|
|
||||||
void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_StepCloth(benchmark::State& state) {
|
void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_StepCloth(benchmark::State& state) {
|
||||||
MujocoErrorTestGuard guard;
|
MujocoErrorTestGuard guard;
|
||||||
static mjModel* model = load_model("composite/cloth.xml");
|
static mjModel* model = LoadModelFromPath("composite/cloth.xml");
|
||||||
run_step_benchmark(model, state);
|
run_step_benchmark(model, state);
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_StepCloth);
|
BENCHMARK(BM_StepCloth);
|
||||||
|
|
||||||
void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_StepHumanoid(benchmark::State& state) {
|
void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_StepHumanoid(benchmark::State& state) {
|
||||||
MujocoErrorTestGuard guard;
|
MujocoErrorTestGuard guard;
|
||||||
static mjModel* model = load_model("humanoid/humanoid.xml");
|
static mjModel* model = LoadModelFromPath("humanoid/humanoid.xml");
|
||||||
run_step_benchmark(model, state);
|
run_step_benchmark(model, state);
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_StepHumanoid);
|
BENCHMARK(BM_StepHumanoid);
|
||||||
|
|
||||||
void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_StepHumanoid100(benchmark::State& state) {
|
void ABSL_ATTRIBUTE_NO_TAIL_CALL BM_StepHumanoid100(benchmark::State& state) {
|
||||||
MujocoErrorTestGuard guard;
|
MujocoErrorTestGuard guard;
|
||||||
static mjModel* model = load_model("humanoid100/humanoid100.xml");
|
static mjModel* model = LoadModelFromPath("humanoid100/humanoid100.xml");
|
||||||
run_step_benchmark(model, state);
|
run_step_benchmark(model, state);
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_StepHumanoid100);
|
BENCHMARK(BM_StepHumanoid100);
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ static void LinearSystem(const mjModel* m, mjData* d, mjtNum* A, mjtNum* B) {
|
|||||||
Ac[i*nv + i] = -m->jnt_stiffness[i];
|
Ac[i*nv + i] = -m->jnt_stiffness[i];
|
||||||
Ac[nv*nv + i*nv + i] = -m->dof_damping[i];
|
Ac[nv*nv + i*nv + i] = -m->dof_damping[i];
|
||||||
}
|
}
|
||||||
mj_solveLD(m, Ac, Ac, 2*nv, d->qH, d->qHDiagInv);
|
mj_solveLD(m, Ac, 2*nv, d->qH, d->qHDiagInv);
|
||||||
|
|
||||||
// A = [dt*Ac; Ac]
|
// A = [dt*Ac; Ac]
|
||||||
mju_transpose(A, Ac, 2*nv, nv);
|
mju_transpose(A, Ac, 2*nv, nv);
|
||||||
@@ -314,7 +314,8 @@ static void LinearSystem(const mjModel* m, mjData* d, mjtNum* A, mjtNum* B) {
|
|||||||
if (B) {
|
if (B) {
|
||||||
mjtNum *Bc = mj_stackAlloc(d, nu*nv);
|
mjtNum *Bc = mj_stackAlloc(d, nu*nv);
|
||||||
mjtNum *BcT = mj_stackAlloc(d, nv*nu);
|
mjtNum *BcT = mj_stackAlloc(d, nv*nu);
|
||||||
mj_solveLD(m, Bc, d->actuator_moment, nu, d->qH, d->qHDiagInv);
|
mju_copy(Bc, d->actuator_moment, nv*nu);
|
||||||
|
mj_solveLD(m, Bc, nu, d->qH, d->qHDiagInv);
|
||||||
mju_transpose(BcT, Bc, nu, nv);
|
mju_transpose(BcT, Bc, nu, nv);
|
||||||
mju_scl(B, BcT, dt*dt, nu*nv);
|
mju_scl(B, BcT, dt*dt, nu*nv);
|
||||||
mju_scl(B+nu*nv, BcT, dt, nu*nv);
|
mju_scl(B+nu*nv, BcT, dt, nu*nv);
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using ::testing::NotNull;
|
||||||
|
|
||||||
ABSL_CONST_INIT static absl::Mutex handlers_mutex(absl::kConstInit);
|
ABSL_CONST_INIT static absl::Mutex handlers_mutex(absl::kConstInit);
|
||||||
static int guard_count ABSL_GUARDED_BY(handlers_mutex) = 0;
|
static int guard_count ABSL_GUARDED_BY(handlers_mutex) = 0;
|
||||||
|
|
||||||
@@ -80,6 +82,20 @@ mjModel* LoadModelFromString(std::string_view xml, char* error,
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void AssertModelNotNull(mjModel* model,
|
||||||
|
const std::array<char, 1024>& error) {
|
||||||
|
ASSERT_THAT(model, NotNull()) << "Failed to load model: " << error.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
mjModel* LoadModelFromPath(const char* model_path) {
|
||||||
|
const std::string xml_path = GetModelPath(model_path);
|
||||||
|
std::array<char, 1024> error;
|
||||||
|
mjModel* model = mj_loadXML(
|
||||||
|
xml_path.c_str(), nullptr, error.data(), error.size());
|
||||||
|
AssertModelNotNull(model, error);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string GetFileContents(const char* path) {
|
const std::string GetFileContents(const char* path) {
|
||||||
std::ifstream ifs;
|
std::ifstream ifs;
|
||||||
ifs.open(path, std::ifstream::in);
|
ifs.open(path, std::ifstream::in);
|
||||||
@@ -118,4 +134,18 @@ const std::string SaveAndReadXml(const mjModel* model) {
|
|||||||
return contents;
|
return contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddCtrlNoise(const mjModel* m, mjData* d, int step) {
|
||||||
|
for (int i = 0; i < m->nu; i++) {
|
||||||
|
mjtNum center = 0.0;
|
||||||
|
mjtNum radius = 1.0;
|
||||||
|
mjtNum* range = m->actuator_ctrlrange + 2 * i;
|
||||||
|
if (m->actuator_ctrllimited[i]) {
|
||||||
|
center = (range[1] + range[0]) / 2;
|
||||||
|
radius = (range[1] - range[0]) / 2;
|
||||||
|
}
|
||||||
|
radius *= 0.01;
|
||||||
|
d->ctrl[i] = center + radius * (2 * mju_Halton(step, i + 2) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <absl/strings/string_view.h>
|
#include <absl/strings/string_view.h>
|
||||||
|
#include <mujoco/mjdata.h>
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
|
|
||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
@@ -53,8 +54,15 @@ const std::string GetModelPath(absl::string_view path);
|
|||||||
mjModel* LoadModelFromString(absl::string_view xml, char* error = nullptr,
|
mjModel* LoadModelFromString(absl::string_view xml, char* error = nullptr,
|
||||||
int error_size = 0);
|
int error_size = 0);
|
||||||
|
|
||||||
|
// Returns a newly-allocated mjModel, loaded from the contents in model_path.
|
||||||
|
// On failure it asserts that model is null.
|
||||||
|
mjModel* LoadModelFromPath(const char* model_path);
|
||||||
|
|
||||||
// Returns a string loaded from first saving the model given an input.
|
// Returns a string loaded from first saving the model given an input.
|
||||||
const std::string SaveAndReadXml(const mjModel* model);
|
const std::string SaveAndReadXml(const mjModel* model);
|
||||||
|
|
||||||
|
// Adds control noise.
|
||||||
|
void AddCtrlNoise(const mjModel* m, mjData* d, int step);
|
||||||
|
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
#endif // MUJOCO_TEST_FIXTURE_H_
|
#endif // MUJOCO_TEST_FIXTURE_H_
|
||||||
|
|||||||
Reference in New Issue
Block a user