Initial open sourcing of MuJoCo.

PiperOrigin-RevId: 450374687
Change-Id: Ie3225a46ce095fc28ae8e63c326a640261f562bb
This commit is contained in:
Saran Tunyasuvunakool
2022-05-23 01:08:10 -07:00
committed by Copybara-Service
parent 0e5d062302
commit 1913a02b40
275 changed files with 99607 additions and 935 deletions
+74
View File
@@ -0,0 +1,74 @@
# 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
#
# https://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.
set(MUJOCO_ENGINE_SRCS
engine_array_safety.h
engine_callback.c
engine_callback.h
engine_collision_box.c
engine_collision_convex.c
engine_collision_convex.h
engine_collision_driver.c
engine_collision_driver.h
engine_collision_primitive.c
engine_collision_primitive.h
engine_core_constraint.c
engine_core_constraint.h
engine_core_smooth.c
engine_core_smooth.h
engine_crossplatform.h
engine_file.c
engine_file.h
engine_forward.c
engine_forward.h
engine_inverse.c
engine_inverse.h
engine_io.c
engine_io.h
engine_macro.h
engine_print.c
engine_print.h
engine_ray.c
engine_ray.h
engine_sensor.c
engine_sensor.h
engine_setconst.c
engine_setconst.h
engine_solver.c
engine_solver.h
engine_support.c
engine_support.h
engine_util_blas.c
engine_util_blas.h
engine_util_errmem.c
engine_util_errmem.h
engine_util_misc.c
engine_util_misc.h
engine_util_solve.c
engine_util_solve.h
engine_util_sparse.c
engine_util_sparse.h
engine_util_spatial.c
engine_util_spatial.h
engine_vfs.c
engine_vfs.h
engine_vis_init.c
engine_vis_init.h
engine_vis_interact.c
engine_vis_interact.h
engine_vis_visualize.c
engine_vis_visualize.h
)
target_sources(mujoco PRIVATE ${MUJOCO_ENGINE_SRCS})
+31
View File
@@ -0,0 +1,31 @@
// 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_SRC_ENGINE_ENGINE_ARRAY_SAFETY_H_
#define MUJOCO_SRC_ENGINE_ENGINE_ARRAY_SAFETY_H_
#include <stdio.h>
#include <string.h>
// Evaluates to sizeof(arr) if arr is a char array, and emits a compiler error
// otherwise. In particular, emits a compiler error if arr is a char*.
#define mjSIZEOFARRAY(arr) _Generic(&(arr), char(*)[sizeof(arr)]: sizeof(arr))
#define mjSNPRINTF(dest, ...) snprintf(dest, mjSIZEOFARRAY(dest), __VA_ARGS__)
#define mjSTRNCAT(dest, src) strncat(dest, src, mjSIZEOFARRAY(dest) - strlen(dest) - 1)
#define mjSTRNCPY(dest, src) mju_strncpy(dest, src, mjSIZEOFARRAY(dest))
#endif // MUJOCO_SRC_ENGINE_ENGINE_ARRAY_SAFETY_H_
+42
View File
@@ -0,0 +1,42 @@
// 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.
#include "engine/engine_callback.h"
#include <mujoco/mjdata.h>
//------------------------- global callback pointers -----------------------------------------------
mjfGeneric mjcb_passive = 0;
mjfGeneric mjcb_control = 0;
mjfConFilt mjcb_contactfilter = 0;
mjfSensor mjcb_sensor = 0;
mjfTime mjcb_time = 0;
mjfAct mjcb_act_bias = 0;
mjfAct mjcb_act_gain = 0;
mjfAct mjcb_act_dyn = 0;
// reset callbacks to defauls
void mj_resetCallbacks(void) {
mjcb_passive = 0;
mjcb_control = 0;
mjcb_contactfilter = 0;
mjcb_sensor = 0;
mjcb_time = 0;
mjcb_act_bias = 0;
mjcb_act_gain = 0;
mjcb_act_dyn = 0;
}
+42
View File
@@ -0,0 +1,42 @@
// 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_SRC_ENGINE_ENGINE_CALLBACK_H_
#define MUJOCO_SRC_ENGINE_ENGINE_CALLBACK_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#ifdef __cplusplus
extern "C" {
#endif
// global callback function pointers
MJAPI extern mjfGeneric mjcb_passive;
MJAPI extern mjfGeneric mjcb_control;
MJAPI extern mjfConFilt mjcb_contactfilter;
MJAPI extern mjfSensor mjcb_sensor;
MJAPI extern mjfTime mjcb_time;
MJAPI extern mjfAct mjcb_act_bias;
MJAPI extern mjfAct mjcb_act_gain;
MJAPI extern mjfAct mjcb_act_dyn;
// reset callbacks to defaults
MJAPI void mj_resetCallbacks(void);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_CALLBACK_H_
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+64
View File
@@ -0,0 +1,64 @@
// 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_SRC_ENGINE_ENGINE_COLLISION_CONVEX_H_
#define MUJOCO_SRC_ENGINE_ENGINE_COLLISION_CONVEX_H_
// libCCD has an unconditional `#define _CRT_SECURE_NO_WARNINGS` on Windows.
// TODO(stunya): Remove once https://github.com/danfis/libccd/pull/77 is merged
#ifdef _CRT_SECURE_NO_WARNINGS
#undef _CRT_SECURE_NO_WARNINGS
#endif
#include <ccd/vec3.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
// ccd general object type
struct _mjtCCD {
const mjModel* model;
const mjData* data;
int geom;
int meshindex;
mjtNum margin;
mjtNum rotate[4];
};
typedef struct _mjtCCD mjtCCD;
// ccd support function
void mjccd_support(const void *obj, const ccd_vec3_t *dir, ccd_vec3_t *vec);
// pairwise collision functions using ccd
int mjc_PlaneConvex (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
int mjc_ConvexHField (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
int mjc_Convex (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
// fix contact frame normal
void mjc_fixNormal(const mjModel* m, const mjData* d, mjContact* con, int g1, int g2);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_COLLISION_CONVEX_H_
+784
View File
@@ -0,0 +1,784 @@
// 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.
#include "engine/engine_collision_driver.h"
#include <stddef.h>
#include <string.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
#include "engine/engine_callback.h"
#include "engine/engine_collision_convex.h"
#include "engine/engine_collision_primitive.h"
#include "engine/engine_core_constraint.h"
#include "engine/engine_crossplatform.h"
#include "engine/engine_io.h"
#include "engine/engine_macro.h"
#include "engine/engine_util_blas.h"
#include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h"
#include "engine/engine_util_solve.h"
#include "engine/engine_util_spatial.h"
// table of pair-wise collision functions
mjfCollision mjCOLLISIONFUNC[mjNGEOMTYPES][mjNGEOMTYPES] = {
/* PLANE HFIELD SPHERE CAPSULE ELLIPSOID CYLINDER BOX MESH */
/*PLANE */ {0, 0, mjc_PlaneSphere, mjc_PlaneCapsule, mjc_PlaneConvex, mjc_PlaneCylinder, mjc_PlaneBox, mjc_PlaneConvex},
/*HFIELD */ {0, 0, mjc_ConvexHField, mjc_ConvexHField, mjc_ConvexHField, mjc_ConvexHField, mjc_ConvexHField, mjc_ConvexHField},
/*SHPERE */ {0, 0, mjc_SphereSphere, mjc_SphereCapsule, mjc_Convex, mjc_Convex, mjc_SphereBox, mjc_Convex},
/*CAPSULE */ {0, 0, 0, mjc_CapsuleCapsule, mjc_Convex, mjc_Convex, mjc_CapsuleBox, mjc_Convex},
/*ELLIPSOID */ {0, 0, 0, 0, mjc_Convex, mjc_Convex, mjc_Convex, mjc_Convex},
/*CYLINDER */ {0, 0, 0, 0, 0, mjc_Convex, mjc_Convex, mjc_Convex},
/*BOX */ {0, 0, 0, 0, 0, 0, mjc_BoxBox, mjc_Convex},
/*MESH */ {0, 0, 0, 0, 0, 0, 0, mjc_Convex}
};
//----------------------------- collision detection entry point ------------------------------------
void mj_collision(const mjModel* m, mjData* d) {
int g1, g2, signature, merged, b1 = 0, b2 = 0, exadr = 0, pairadr = 0, startadr;
int nexclude = m->nexclude, npair = m->npair, nbodypair = ((m->nbody-1)*m->nbody)/2;
int *broadphasepair = 0;
mjMARKSTACK;
// clear size
d->ncon = 0;
// return if disabled
if (mjDISABLED(mjDSBL_CONSTRAINT) || mjDISABLED(mjDSBL_CONTACT)
|| m->nconmax==0 || m->nbody < 2) {
return;
}
// predefined only; ignore exclude
if (m->opt.collision==mjCOL_PAIR) {
for (pairadr=0; pairadr<npair; pairadr++) {
mj_collideGeoms(m, d, pairadr, -1, 0, 0);
}
}
// dynamic only or merge; apply exclude
else {
// call broadphase collision detector
broadphasepair = (int*)mj_stackAlloc(d, (m->nbody*(m->nbody-1))/2);
nbodypair = mj_broadphase(m, d, broadphasepair, (m->nbody*(m->nbody-1))/2);
// loop over body pairs (broadphase or all)
for (int i=0; i<nbodypair; i++) {
// reconstruct body pair ids
b1 = (broadphasepair[i]>>16) & 0xFFFF;
b2 = broadphasepair[i] & 0xFFFF;
// compute signature for this body pair
signature = ((b1+1)<<16) + (b2+1);
// merge predefined pairs
merged = 0;
startadr = pairadr;
if (npair && m->opt.collision==mjCOL_ALL) {
// test all predefined pairs for which pair_signature<=signature
while (pairadr<npair && m->pair_signature[pairadr]<=signature) {
if (m->pair_signature[pairadr]==signature) {
merged = 1;
}
mj_collideGeoms(m, d, pairadr++, -1, 0, 0);
}
}
// handle exclusion
if (nexclude) {
// advance exadr while exclude_signature < signature
while (m->exclude_signature[exadr]<signature && exadr<nexclude) {
exadr++;
}
// skip this body pair if its signature is found in exclude array
if (exadr<nexclude && m->exclude_signature[exadr]==signature) {
continue;
}
}
// test all geom pairs within this body pair
if (m->body_geomnum[b1] && m->body_geomnum[b2]) {
for (g1=m->body_geomadr[b1]; g1<m->body_geomadr[b1]+m->body_geomnum[b1]; g1++) {
for (g2=m->body_geomadr[b2]; g2<m->body_geomadr[b2]+m->body_geomnum[b2]; g2++) {
// merged: make sure geom pair is not repeated
if (merged) {
// find matching pair
int found = 0;
for (int k=startadr; k<pairadr; k++) {
if ((m->pair_geom1[k]==g1 && m->pair_geom2[k]==g2) ||
(m->pair_geom1[k]==g2 && m->pair_geom2[k]==g1)) {
found = 1;
break;
}
}
// not found: test
if (!found) {
mj_collideGeoms(m, d, g1, g2, 0, 0);
}
}
// not merged: always test
else {
mj_collideGeoms(m, d, g1, g2, 0, 0);
}
}
}
}
}
// finish merging predefined pairs
if (npair && m->opt.collision==mjCOL_ALL)
while (pairadr<npair) {
mj_collideGeoms(m, d, pairadr++, -1, 0, 0);
}
}
mjFREESTACK;
}
//----------------------------- broad-phase collision detection ------------------------------------
// helper structure for SAP sorting
struct _mjtBroadphase {
float value;
int body_ismax;
};
typedef struct _mjtBroadphase mjtBroadphase;
// make AABB for one body
static void makeAABB(const mjModel* m, mjData* d, mjtNum* aabb, int body, const mjtNum* frame) {
int geom;
mjtNum _aabb[6], cen;
// no geoms attached to body: set to 0
if (m->body_geomnum[body]==0) {
mju_zero(aabb, 6);
return;
}
// process all body geoms
for (int i=0; i<m->body_geomnum[body]; i++) {
// get geom id
geom = m->body_geomadr[body]+i;
// set _aabb for this geom
for (int j=0; j<3; j++) {
cen = mju_dot3(d->geom_xpos+3*geom, frame+3*j);
_aabb[2*j] = cen - m->geom_rbound[geom] - m->geom_margin[geom];
_aabb[2*j+1] = cen + m->geom_rbound[geom] + m->geom_margin[geom];
}
// update body aabb
if (i==0) {
mju_copy(aabb, _aabb, 6);
} else {
for (int j=0; j<3; j++) {
aabb[2*j] = mju_min(aabb[2*j], _aabb[2*j]);
aabb[2*j+1] = mju_max(aabb[2*j+1], _aabb[2*j+1]);
}
}
}
}
// return 1 if body has plane or hfield geom, 0 otherwise
static int has_plane_or_hfield(const mjModel* m, int body) {
int start = m->body_geomadr[body];
int end = m->body_geomadr[body] + m->body_geomnum[body];
// scan geoms belonging to body
int g;
for (g=start; g<end; g++) {
if (m->geom_type[g]==mjGEOM_PLANE || m->geom_type[g]==mjGEOM_HFIELD) {
return 1;
}
}
return 0;
}
// add body pair in buffer
static void add_pair(const mjModel* m, int b1, int b2, int* npair, int* pair, int maxpair) {
// add pair if there is room in buffer
if ((*npair)<maxpair) {
// exlude based on contype and conaffinity
if (m && m->body_geomnum[b1]==1 && m->body_geomnum[b2]==1) {
// get contypes and conaffinities
int contype1 = m->geom_contype[m->body_geomadr[b1]];
int conaffinity1 = m->geom_conaffinity[m->body_geomadr[b1]];
int contype2 = m->geom_contype[m->body_geomadr[b2]];
int conaffinity2 = m->geom_conaffinity[m->body_geomadr[b2]];
// compatibility check
if (!(contype1 & conaffinity2) && !(contype2 & conaffinity1)) {
return;
}
}
// add pair
if (b1<b2) {
pair[*npair] = (b1<<16) + b2;
} else {
pair[*npair] = (b2<<16) + b1;
}
(*npair)++;
} else {
mju_error("Broadphase buffer full");
}
}
// comparison function for broadphase
quicksortfunc(broadcompare, context, el1, el2) {
mjtBroadphase* b1 = (mjtBroadphase*)el1;
mjtBroadphase* b2 = (mjtBroadphase*)el2;
if (b1->value<b2->value) {
return -1;
} else if (b1->value==b2->value) {
return 0;
} else {
return 1;
}
}
// comparison function for pair sorting
quicksortfunc(paircompare, context, el1, el2) {
int signature1 = *(int*)el1;
int signature2 = *(int*)el2;
if (signature1<signature2) {
return -1;
} else if (signature1==signature2) {
return 0;
} else {
return 1;
}
}
// does body have collidable geoms
static int can_collide(const mjModel* m, int b) {
int g;
// scan geoms; return if collidable
for (g=0; g<m->body_geomnum[b]; g++) {
int ind = m->body_geomadr[b] + g;
if (m->geom_contype[ind] || m->geom_conaffinity[ind]) {
return 1;
}
}
// none found
return 0;
}
// broadphase collision detector
int mj_broadphase(const mjModel* m, mjData* d, int* pair, int maxpair) {
int i, j, b1, b2, toremove, cnt, npair = 0, nbody = m->nbody, ngeom = m->ngeom;
mjtNum cov[9], cen[3], dif[3], eigval[3], frame[9], quat[4];
mjtBroadphase *sortbuf, *activebuf;
mjtNum *aabb;
mjMARKSTACK;
// world with geoms, and body with plane or hfield, can collide all bodies
for (b1=0; b1<nbody; b1++) {
// cannot colide
if (!can_collide(m, b1)) {
continue;
}
// world with geoms, or welded body with plane or hfield
if ((b1==0 && m->body_geomnum[b1]>0) || (m->body_weldid[b1]==0 && has_plane_or_hfield(m, b1))) {
for (b2=0; b2<nbody; b2++) {
if (b1!=b2) {
add_pair(NULL, b1, b2, &npair, pair, maxpair);
}
}
}
}
// find center of non-world geoms; return if none
cnt = 0;
mju_zero3(cen);
for (i=0; i<ngeom; i++) {
if (m->geom_bodyid[i]) {
mju_addTo3(cen, d->geom_xpos+3*i);
cnt++;
}
}
if (cnt==0) {
return npair;
} else {
for (i=0; i<3; i++) {
cen[i] /= cnt;
}
}
// compute covariance
mju_zero(cov, 9);
for (i=0; i<ngeom; i++) {
if (m->geom_bodyid[i]) {
mju_sub3(dif, d->geom_xpos+3*i, cen);
mjtNum D00 = dif[0]*dif[0];
mjtNum D01 = dif[0]*dif[1];
mjtNum D02 = dif[0]*dif[2];
mjtNum D11 = dif[1]*dif[1];
mjtNum D12 = dif[1]*dif[2];
mjtNum D22 = dif[2]*dif[2];
cov[0] += D00;
cov[1] += D01;
cov[2] += D02;
cov[3] += D01;
cov[4] += D11;
cov[5] += D12;
cov[6] += D02;
cov[7] += D12;
cov[8] += D22;
}
}
for (i=0; i<9; i++) {
cov[i] /= cnt;
}
// construct covariance-aligned 3D frame
mju_eig3(eigval, frame, quat, cov);
// allocate AABB; clear world entry (not used)
aabb = mj_stackAlloc(d, 6*nbody);
mju_zero(aabb, 6);
// construct body AABB for the aligned frame, count collidable
int bufcnt = 0;
for (i=1; i<nbody; i++) {
makeAABB(m, d, aabb+6*i, i, frame);
if (can_collide(m, i)) {
bufcnt++;
}
}
// nothing collidable
if (!bufcnt) {
goto endbroad;
}
// allocate sort buffer
i = sizeof(mjtBroadphase)/sizeof(mjtNum);
j = sizeof(mjtBroadphase)%sizeof(mjtNum);
sortbuf = (mjtBroadphase*)mj_stackAlloc(d, 2*bufcnt*(i + (j ? 1 : 0)));
activebuf = (mjtBroadphase*)mj_stackAlloc(d, 2*bufcnt*(i + (j ? 1 : 0)));
// init sortbuf with axis0
j = 0;
for (i=1; i<nbody; i++) {
// cannot colide
if (!can_collide(m, i)) {
continue;
}
// init
sortbuf[2*j].body_ismax = i;
sortbuf[2*j].value = (float)aabb[6*i];
sortbuf[2*j+1].body_ismax = i + 0x10000;
sortbuf[2*j+1].value = (float)aabb[6*i+1];
j++;
}
// sanity check; SHOULD NOT OCCUR
if (j!=bufcnt) {
mju_error("Internal error in broadphase: unexpected bufcnt");
}
// sort along axis0
mjQUICKSORT(sortbuf, 2*bufcnt, sizeof(mjtBroadphase), broadcompare, 0);
// sweep and prune
cnt = 0; // size of active list
for (i=0; i<2*bufcnt; i++) {
// min value: collide with all in list, add
if (!(sortbuf[i].body_ismax & 0x10000)) {
for (j=0; j<cnt; j++) {
// get body ids
b1 = activebuf[j].body_ismax;
b2 = sortbuf[i].body_ismax;
// use the other two axes to prune if possible
if (aabb[6*b1+2] > aabb[6*b2+3] ||
aabb[6*b1+3] < aabb[6*b2+2] ||
aabb[6*b1+4] > aabb[6*b2+5] ||
aabb[6*b1+5] < aabb[6*b2+4]) {
continue;
}
// add body pair if there is room in buffer
add_pair(m, b1, b2, &npair, pair, maxpair);
}
// add to list
activebuf[cnt] = sortbuf[i];
cnt++;
}
// max value: remove corresponding min value from list
else {
toremove = sortbuf[i].body_ismax & 0xFFFF;
for (j=0; j<cnt; j++) {
if (activebuf[j].body_ismax==toremove) {
if (j<cnt-1) {
memmove(activebuf+j, activebuf+j+1, sizeof(mjtBroadphase)*(cnt-1-j));
}
cnt--;
break;
}
}
}
}
endbroad:
// sort pairs by signature
if (npair) {
mjQUICKSORT(pair, npair, sizeof(int), paircompare, 0);
}
mjFREESTACK;
return npair;
}
//----------------------------- narrow-phase collision detection -----------------------------------
// plane : geom_center distance, assuming g1 is plane
static mjtNum plane_geom(const mjModel* m, mjData* d, int g1, int g2) {
mjtNum* mat1 = d->geom_xmat + 9*g1;
mjtNum norm[3] = {mat1[2], mat1[5], mat1[8]};
mjtNum dif[3];
mju_sub3(dif, d->geom_xpos + 3*g2, d->geom_xpos + 3*g1);
return mju_dot3(dif, norm);
}
// test two geoms for collision, apply filters, add to contact list
// flg_user disables filters and uses usermargin
void mj_collideGeoms(const mjModel* m, mjData* d, int g1, int g2, int flg_user, mjtNum usermargin) {
int i, num, type1, type2, b1, b2, weld1, weld2, condim;
mjtNum margin, gap, mix, friction[5], solref[mjNREF], solimp[mjNIMP];
mjContact con[mjMAXCONPAIR];
int ipair = (g2<0 ? g1 : -1);
// get explicit geom ids from pair
if (ipair>=0) {
g1 = m->pair_geom1[ipair];
g2 = m->pair_geom2[ipair];
}
// order geoms by type
if (m->geom_type[g1] > m->geom_type[g2]) {
i = g1;
g1 = g2;
g2 = i;
}
// copy types and bodies
type1 = m->geom_type[g1];
type2 = m->geom_type[g2];
b1 = m->geom_bodyid[g1];
b2 = m->geom_bodyid[g2];
weld1 = m->body_weldid[b1];
weld2 = m->body_weldid[b2];
// return if no collision function
if (!mjCOLLISIONFUNC[type1][type2]) {
return;
}
// apply filters if not predefined pair and not flg_user
if (ipair<0 && !flg_user) {
// user filter if defined
if (mjcb_contactfilter) {
if (mjcb_contactfilter(m, d, g1, g2)) {
return;
}
}
// otherwise built-in filter
else if (mj_contactFilter(
type1, m->geom_contype[g1], m->geom_conaffinity[g1],
weld1, m->body_weldid[m->body_parentid[weld1]],
type2, m->geom_contype[g2], m->geom_conaffinity[g2],
weld2, m->body_weldid[m->body_parentid[weld2]],
!mjDISABLED(mjDSBL_FILTERPARENT) && weld1 && weld2)) {
return;
}
}
// set margin, gap, condim: dynamic
if (ipair<0) {
// margin and gap: max
margin = mju_max(m->geom_margin[g1], m->geom_margin[g2]);
gap = mju_max(m->geom_gap[g1], m->geom_gap[g2]);
// condim: priority or max
if (m->geom_priority[g1]!=m->geom_priority[g2]) {
int gp = (m->geom_priority[g1]>m->geom_priority[g2] ? g1 : g2);
condim = m->geom_condim[gp];
} else {
condim = mjMAX(m->geom_condim[g1], m->geom_condim[g2]);
}
}
// set margin, gap, condim: pair
else {
margin = m->pair_margin[ipair];
gap = m->pair_gap[ipair];
condim = m->pair_dim[ipair];
}
// adjust margin
if (flg_user) {
margin = usermargin;
} else {
margin = mj_assignMargin(m, margin);
}
// bounding sphere filter
if (m->geom_rbound[g1]>0 && m->geom_rbound[g2]>0 &&
(mju_dist3(d->geom_xpos+3*g1, d->geom_xpos+3*g2) >
m->geom_rbound[g1] + m->geom_rbound[g2] + margin)) {
return;
}
// plane : bounding sphere filter
if (m->geom_type[g1]==mjGEOM_PLANE && m->geom_rbound[g2]>0
&& plane_geom(m, d, g1, g2) > margin+m->geom_rbound[g2]) {
return;
}
if (m->geom_type[g2]==mjGEOM_PLANE && m->geom_rbound[g1]>0
&& plane_geom(m, d, g2, g1) > margin+m->geom_rbound[g1]) {
return;
}
// call collision detector to generate contacts
num = mjCOLLISIONFUNC[type1][type2](m, d, con, g1, g2, margin);
// no contacts from near-phase
if (!num) {
return;
}
// check number of contacts, SHOULD NOT OCCUR
if (num>mjMAXCONPAIR) {
mju_error("Too many contacts returned by collision function");
}
// remove repeated contacts in box-box
if (type1==mjGEOM_BOX && type2==mjGEOM_BOX) {
// use dim field to mark: -1: bad, 0: good
for (i=0; i<num; i++) {
con[i].dim = 0;
}
// find bad
int j;
for (i=0; i<num-1; i++) {
for (j=i+1; j<num; j++) {
if (con[i].pos[0]==con[j].pos[0] &&
con[i].pos[1]==con[j].pos[1] &&
con[i].pos[2]==con[j].pos[2]) {
con[i].dim = -1;
break;
}
}
}
// consolidate good
i = 0;
for (j=0; j<num; j++) {
if (con[j].dim==0) {
// different: copy
if (i<j) {
con[i] = con[j];
}
// advance either way
i++;
}
}
// adjust size
num = i;
}
// set friction, solref, solimp: dynamic
if (ipair<0) {
// different priority
if (m->geom_priority[g1]!=m->geom_priority[g2]) {
int gp = (m->geom_priority[g1]>m->geom_priority[g2] ? g1 : g2);
// friction
for (i=0; i<3; i++) {
friction[2*i] = m->geom_friction[3*gp+i];
}
// reference
mju_copy(solref, m->geom_solref+mjNREF*gp, mjNREF);
// impedance
mju_copy(solimp, m->geom_solimp+mjNIMP*gp, mjNIMP);
}
// same priority
else {
// friction: max
for (i=0; i<3; i++) {
friction[2*i] = mju_max(m->geom_friction[3*g1+i], m->geom_friction[3*g2+i]);
}
// solver mix factor
if (m->geom_solmix[g1]>=mjMINVAL && m->geom_solmix[g2]>=mjMINVAL) {
mix = m->geom_solmix[g1] / (m->geom_solmix[g1] + m->geom_solmix[g2]);
} else if (m->geom_solmix[g1]<mjMINVAL && m->geom_solmix[g2]<mjMINVAL) {
mix = 0.5;
} else if (m->geom_solmix[g1]<mjMINVAL) {
mix = 0.0;
} else {
mix = 1.0;
}
// reference standard: mix
if (m->geom_solref[mjNREF*g1]>0 && m->geom_solref[mjNREF*g2]>0) {
for (i=0; i<mjNREF; i++) {
solref[i] = mix*m->geom_solref[mjNREF*g1+i] + (1-mix)*m->geom_solref[mjNREF*g2+i];
}
}
// reference direct: min
else {
for (i=0; i<mjNREF; i++) {
solref[i] = mju_min(m->geom_solref[mjNREF*g1+i], m->geom_solref[mjNREF*g2+i]);
}
}
// impedance: mix
mju_scl(solimp, m->geom_solimp+mjNIMP*g1, mix, mjNIMP);
mju_addToScl(solimp, m->geom_solimp+mjNIMP*g2, 1-mix, mjNIMP);
}
// unpack 5D friction
friction[1] = friction[0];
friction[3] = friction[4];
}
// set friction, solref, solimp: pair
else {
// friction
for (i=0; i<5; i++) {
friction[i] = m->pair_friction[5*ipair+i];
}
// reference
mju_copy(solref, m->pair_solref+mjNREF*ipair, mjNREF);
// impedance
mju_copy(solimp, m->pair_solimp+mjNIMP*ipair, mjNIMP);
}
// clamp friction to mjMINMU
for (i=0; i<5; i++) {
friction[i] = mju_max(mjMINMU, friction[i]);
}
// add contact returned by collision detector
for (i=0; i<num; i++) {
// set contact data
if (condim > 6 || condim < 0) { // SHOULD NOT OCCUR
mju_error_i("Invalid condim value: %d", i);
}
con[i].dim = condim;
con[i].geom1 = g1;
con[i].geom2 = g2;
con[i].includemargin = margin-gap;
mju_copy(con[i].friction, friction, 5);
mj_assignRef(m, con[i].solref, solref);
mj_assignImp(m, con[i].solimp, solimp);
// exclude in gap
if (con[i].dist<con[i].includemargin) {
con[i].exclude = 0;
} else {
con[i].exclude = 1;
}
// complete frame
mju_makeFrame(con[i].frame);
// clear fields that are computed later
con[i].efc_address = -1;
con[i].mu = 0;
mju_zero(con[i].H, 36);
// add to mjData, abort if too many contacts
if (mj_addContact(m, d, con + i)) {
return;
}
}
}
// filter contacts: 1- discard, 0- proceed
int mj_contactFilter(int type1, int contype1, int conaffinity1, int weldbody1, int weldparent1,
int type2, int contype2, int conaffinity2, int weldbody2, int weldparent2,
int filterparent) {
// compatibility check
if (!(contype1 & conaffinity2) && !(contype2 & conaffinity1)) {
return 1;
}
// same weldbody check
if (weldbody1==weldbody2) {
return 1;
}
// weldparent check
if (filterparent && (weldbody1==weldparent2 || weldbody2==weldparent1)) {
return 1;
}
// all tests passed
return 0;
}
+49
View File
@@ -0,0 +1,49 @@
// 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_SRC_ENGINE_ENGINE_COLLISION_DRIVER_H_
#define MUJOCO_SRC_ENGINE_ENGINE_COLLISION_DRIVER_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
// collision function pointers and max contact pairs
MJAPI extern mjfCollision mjCOLLISIONFUNC[mjNGEOMTYPES][mjNGEOMTYPES];
// collision detection entry point
MJAPI void mj_collision(const mjModel* m, mjData* d);
// broad phase collistion detection; return list of body pairs for narrow phase
int mj_broadphase(const mjModel* m, mjData* d, int* bodypair, int maxpair);
// test two geoms for collision, apply filters, add to contact list
// flg_user disables filters and uses usermargin
void mj_collideGeoms(const mjModel* m, mjData* d,
int g1, int g2, int flg_user, mjtNum usermargin);
// number of possible collisions based on fitlers and geom types
int mj_contactFilter(int type1, int contype1, int conaffinity1, int weldbody1, int weldparent1,
int type2, int contype2, int conaffinity2, int weldbody2, int weldparent2,
int filterparent);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_COLLISION_DRIVER_H_
+460
View File
@@ -0,0 +1,460 @@
// 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.
#include "engine/engine_collision_primitive.h"
#include <math.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
#include "engine/engine_util_blas.h"
#include "engine/engine_util_spatial.h"
//--------------------------- plane collisions -----------------------------------------------------
// plane : sphere (actual implementation, can be called with modified parameters)
static int _PlaneSphere(mjContact* con, mjtNum margin,
mjtNum* pos1, mjtNum* mat1, mjtNum* size1,
mjtNum* pos2, mjtNum* mat2, mjtNum* size2) {
mjtNum tmp[3];
mjtNum cdist;
// set normal
con[0].frame[0] = mat1[2];
con[0].frame[1] = mat1[5];
con[0].frame[2] = mat1[8];
// compute distance, return if too large
mju_sub3(tmp, pos2, pos1);
cdist = mju_dot3(tmp, con[0].frame);
if (cdist > margin + size2[0]) {
return 0;
}
// depth and position
con[0].dist = cdist - size2[0];
mju_scl3(tmp, con[0].frame, -con[0].dist/2 - size2[0]);
mju_add3(con[0].pos, pos2, tmp);
mju_zero3(con[0].frame+3);
return 1;
}
// plane : sphere
int mjc_PlaneSphere(const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin) {
mjGETINFO
return _PlaneSphere(con, margin, pos1, mat1, size1, pos2, mat2, size2);
}
// plane : capsule
int mjc_PlaneCapsule(const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin) {
mjGETINFO
mjtNum pos[3], axis[3], segment[3];
int n1, n2;
// get capsule axis, segment = scaled axis
axis[0] = mat2[2];
axis[1] = mat2[5];
axis[2] = mat2[8];
mju_scl3(segment, axis, size2[1]);
// get point 1, do sphere-plane test
mju_add3(pos, pos2, segment);
n1 = _PlaneSphere(con, margin, pos1, mat1, size1, pos, mat2, size2);
// get point 2, do sphere-plane test
mju_sub3(pos, pos2, segment);
n2 = _PlaneSphere(con+n1, margin, pos1, mat1, size1, pos, mat2, size2);
// align contact frames with capsule axis
if (n1) {
mju_copy3(con->frame+3, axis);
}
if (n2) {
mju_copy3((con+n1)->frame+3, axis);
}
return n1+n2;
}
// plane : cylinder
int mjc_PlaneCylinder(const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin) {
mjGETINFO
mjtNum normal[3] = {mat1[2], mat1[5], mat1[8]};
mjtNum axis[3] = {mat2[2], mat2[5], mat2[8]};
mjtNum vec[3], vec1[3];
mjtNum len, scl, dist0, prjaxis, prjvec, prjvec1;
int cnt = 0;
// project, make sure axis points towards plane
prjaxis = mju_dot3(normal, axis);
if (prjaxis > 0) {
mju_scl3(axis, axis, -1);
prjaxis = -prjaxis;
}
// compute normal distance to cylinder center
mju_sub3(vec, pos2, pos1);
dist0 = mju_dot3(vec, normal);
// remove component of -normal along axis, compute length
mju_scl3(vec, axis, prjaxis);
mju_subFrom3(vec, normal);
len = mju_norm3(vec);
// general configuration: normalize vector, scale by radius
if (len >= mjMINVAL) {
scl = size2[0]/len;
vec[0] *= scl;
vec[1] *= scl;
vec[2] *= scl;
}
// disk parallel to plane: pick x-axis of cylinder, scale by radius
else {
vec[0] = mat2[0]*size2[0];
vec[1] = mat2[3]*size2[0];
vec[2] = mat2[6]*size2[0];
}
// project vector on normal
prjvec = mju_dot3(vec, normal);
// scale axis by half-length
mju_scl3(axis, axis, size2[1]);
prjaxis *= size2[1];
// check first point, construct contact
if (dist0 + prjaxis + prjvec <= margin) {
con[cnt].dist = dist0 + prjaxis + prjvec;
mju_add3(con[cnt].pos, pos2, vec);
mju_addTo3(con[cnt].pos, axis);
mju_addToScl3(con[cnt].pos, normal, -con[cnt].dist*0.5);
mju_copy3(con[cnt].frame, normal);
mju_zero3(con[cnt].frame+3);
cnt++;
} else {
return 0; // nearest point is above margin: no contacts
}
// check second point, construct contact
if (dist0 - prjaxis + prjvec <= margin) {
con[cnt].dist = dist0 - prjaxis + prjvec;
mju_add3(con[cnt].pos, pos2, vec);
mju_subFrom3(con[cnt].pos, axis);
mju_addToScl3(con[cnt].pos, normal, -con[cnt].dist*0.5);
mju_copy3(con[cnt].frame, normal);
mju_zero3(con[cnt].frame+3);
cnt++;
}
// try to add triangle points on side closer to plane
prjvec1 = -prjvec*0.5;
if (dist0 + prjaxis + prjvec1 <= margin) {
// compute sideways vector: vec1
mju_cross(vec1, vec, axis);
mju_normalize3(vec1);
mju_scl3(vec1, vec1, size2[0]*mju_sqrt(3.0)/2);
// add point A
con[cnt].dist = dist0 + prjaxis + prjvec1;
mju_add3(con[cnt].pos, pos2, vec1);
mju_addTo3(con[cnt].pos, axis);
mju_addToScl3(con[cnt].pos, vec, -0.5);
mju_addToScl3(con[cnt].pos, normal, -con[cnt].dist*0.5);
mju_copy3(con[cnt].frame, normal);
mju_zero3(con[cnt].frame+3);
cnt++;
// add point B
con[cnt].dist = dist0 + prjaxis + prjvec1;
mju_sub3(con[cnt].pos, pos2, vec1);
mju_addTo3(con[cnt].pos, axis);
mju_addToScl3(con[cnt].pos, vec, -0.5);
mju_addToScl3(con[cnt].pos, normal, -con[cnt].dist*0.5);
mju_copy3(con[cnt].frame, normal);
mju_zero3(con[cnt].frame+3);
cnt++;
}
return cnt;
}
// plane : box
int mjc_PlaneBox(const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin) {
mjGETINFO
int cnt = 0;
// get normal, difference between centers, normal distance
mjtNum norm[3] = {mat1[2], mat1[5], mat1[8]};
mjtNum dif[3], vec[3], corner[3], dist, ldist;
mju_sub3(dif, pos2, pos1);
dist = mju_dot3(dif, norm);
// test all corners, pick bottom 4
for (int i=0; i<8; i++) {
// get corner in local coordinates
vec[0] = (i&1 ? size2[0] : -size2[0]);
vec[1] = (i&2 ? size2[1] : -size2[1]);
vec[2] = (i&4 ? size2[2] : -size2[2]);
// get corner in global coordinates relative to box center
mju_rotVecMat(corner, vec, mat2);
// compute distance to plane, skip if too far or pointing up
ldist = mju_dot3(norm, corner);
if (dist + ldist > margin || ldist > 0) {
continue;
}
// construct contact
con[cnt].dist = dist + ldist;
mju_copy3(con[cnt].frame, norm);
mju_zero3(con[cnt].frame+3);
mju_addTo3(corner, pos2);
mju_scl3(vec, norm, -con[cnt].dist/2);
mju_add3(con[cnt].pos, corner, vec);
// count; max is 4
if (++cnt >= 4) {
return 4;
}
}
return cnt;
}
//--------------------------- sphere and capsule collisions ----------------------------------------
// sphere : sphere (actual implementation, can be called with modified parameters)
static int _SphereSphere(mjContact* con, mjtNum margin,
mjtNum* pos1, mjtNum* mat1, mjtNum* size1,
mjtNum* pos2, mjtNum* mat2, mjtNum* size2) {
mjtNum len, cdist;
mjtNum axis1[3], axis2[3];
// check bounding spheres (this is called from other functions)
cdist = mju_dist3(pos1, pos2);
if (cdist > margin + size1[0] + size2[0]) {
return 0;
}
// depth and normal
con[0].dist = cdist - size1[0] - size2[0];
mju_sub3(con[0].frame, pos2, pos1);
len = mju_normalize3(con[0].frame);
// if centers are the same, norm = cross-product of z axes
// if z axes are parallel, norm = [1;0;0]
if (len < mjMINVAL) {
axis1[0] = mat1[2];
axis1[1] = mat1[5];
axis1[2] = mat1[8];
axis2[0] = mat2[2];
axis2[1] = mat2[5];
axis2[2] = mat2[8];
mju_cross(con[0].frame, axis1, axis2);
mju_normalize3(con[0].frame);
}
// position
mju_scl3(con[0].pos, con[0].frame, size1[0] + con[0].dist/2);
mju_addTo3(con[0].pos, pos1);
mju_zero3(con[0].frame+3);
return 1;
}
// sphere : sphere
int mjc_SphereSphere(const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin) {
mjGETINFO
return _SphereSphere(con, margin, pos1, mat1, size1, pos2, mat2, size2);
}
// sphere : capsule
int mjc_SphereCapsule(const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin) {
mjGETINFO
mjtNum x, axis[3], vec[3];
// get capsule axis (scaled)
axis[0] = mat2[2] * size2[1];
axis[1] = mat2[5] * size2[1];
axis[2] = mat2[8] * size2[1];
// find projection, clip to segment
mju_sub3(vec, pos1, pos2);
x = mju_dot3(axis, vec) / mju_dot3(axis, axis);
if (x > 1) {
x = 1;
} else if (x < -1) {
x = -1;
}
// find nearest point on segment, do sphere-sphere test
mju_scl3(vec, axis, x);
mju_addTo3(vec, pos2);
return _SphereSphere(con, margin, pos1, mat1, size1, vec, mat2, size2);
}
// capsule : capsule
int mjc_CapsuleCapsule(const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin) {
mjGETINFO
mjtNum axis1[3], axis2[3], dif[3], vec1[3], vec2[3];
mjtNum ma, mb, mc, u, v, det, x1, x2;
int n1, n2, n3, n4;
// get capsule axes (scaled) and center difference
axis1[0] = mat1[2] * size1[1];
axis1[1] = mat1[5] * size1[1];
axis1[2] = mat1[8] * size1[1];
axis2[0] = mat2[2] * size2[1];
axis2[1] = mat2[5] * size2[1];
axis2[2] = mat2[8] * size2[1];
mju_sub3(dif, pos1, pos2);
// compute matrix coefficients and determinant
ma = mju_dot3(axis1, axis1);
mb = -mju_dot3(axis1, axis2);
mc = mju_dot3(axis2, axis2);
u = -mju_dot3(axis1, dif);
v = mju_dot3(axis2, dif);
det = ma*mc - mb*mb;
// general configuration (non-parallel axes)
if (fabs(det) >= mjMINVAL) {
// find projections, clip to segments
x1 = (mc*u - mb*v) / det;
x2 = (ma*v - mb*u) / det;
if (x1 > 1) {
x1 = 1;
x2 = (v-mb)/mc;
} else if (x1 < -1) {
x1 = -1;
x2 = (v+mb)/mc;
}
if (x2 > 1) {
x2 = 1;
x1 = (u-mb)/ma;
if (x1 > 1) {
x1 = 1;
} else if (x1 < -1) {
x1 = -1;
}
} else if (x2 < -1) {
x2 = -1;
x1 = (u+mb)/ma;
if (x1 > 1) {
x1 = 1;
} else if (x1 < -1) {
x1 = -1;
}
}
// find nearest points, do sphere-sphere test
mju_scl3(vec1, axis1, x1);
mju_addTo3(vec1, pos1);
mju_scl3(vec2, axis2, x2);
mju_addTo3(vec2, pos2);
return _SphereSphere(con, margin, vec1, mat1, size1, vec2, mat2, size2);
}
// parallel axes
else {
// x1 = 1
mju_add3(vec1, pos1, axis1);
x2 = (v - mb) / mc;
if (x2 > 1) {
x2 = 1;
} else if (x2 < -1) {
x2 = -1;
}
mju_scl3(vec2, axis2, x2);
mju_addTo3(vec2, pos2);
n1 = _SphereSphere(con, margin, vec1, mat1, size1, vec2, mat2, size2);
// x1 = -1
mju_sub3(vec1, pos1, axis1);
x2 = (v + mb) / mc;
if (x2 > 1) {
x2 = 1;
} else if (x2 < -1) {
x2 = -1;
}
mju_scl3(vec2, axis2, x2);
mju_addTo3(vec2, pos2);
n2 = _SphereSphere(con+n1, margin, vec1, mat1, size1, vec2, mat2, size2);
// return if two contacts already found
if (n1+n2>=2) {
return n1+n2;
}
// x2 = 1
mju_add3(vec2, pos2, axis2);
x1 = (u - mb) / ma;
if (x1 > 1) {
x1 = 1;
} else if (x1 < -1) {
x1 = -1;
}
mju_scl3(vec1, axis1, x1);
mju_addTo3(vec1, pos1);
n3 = _SphereSphere(con+n1+n2, margin, vec1, mat1, size1, vec2, mat2, size2);
// return if two contacts already found
if (n1+n2+n3>=2) {
return n1+n2+n3;
}
// x2 = -1
mju_sub3(vec2, pos2, axis2);
x1 = (u + mb) / ma;
if (x1 > 1) {
x1 = 1;
} else if (x1 < -1) {
x1 = -1;
}
mju_scl3(vec1, axis1, x1);
mju_addTo3(vec1, pos1);
n4 = _SphereSphere(con+n1+n2+n3, margin, vec1, mat1, size1, vec2, mat2, size2);
return n1+n2+n3+n4;
}
}
+64
View File
@@ -0,0 +1,64 @@
// 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_SRC_ENGINE_ENGINE_COLLISION_PRIMITIVE_H_
#define MUJOCO_SRC_ENGINE_ENGINE_COLLISION_PRIMITIVE_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
// define and extract geom info
#define mjGETINFO \
mjtNum* pos1 = d->geom_xpos + 3*g1; \
mjtNum* mat1 = d->geom_xmat + 9*g1; \
mjtNum* size1= m->geom_size + 3*g1; \
mjtNum* pos2 = d->geom_xpos + 3*g2; \
mjtNum* mat2 = d->geom_xmat + 9*g2; \
mjtNum* size2= m->geom_size + 3*g2; \
(void) size1; (void) size2;
#ifdef __cplusplus
extern "C" {
#endif
// plane collisions
int mjc_PlaneSphere (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
int mjc_PlaneCapsule (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
int mjc_PlaneCylinder (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
int mjc_PlaneBox (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
// sphere and capsule collisions
int mjc_SphereSphere (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
int mjc_SphereCapsule (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
int mjc_CapsuleCapsule (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
// box collisions: from boxcollisions.c
int mjc_CapsuleBox (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
int mjc_SphereBox (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
int mjc_BoxBox (const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_COLLISION_PRIMITIVE_H_
File diff suppressed because it is too large Load Diff
+115
View File
@@ -0,0 +1,115 @@
// 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_SRC_ENGINE_ENGINE_CORE_CONSTRAINT_H_
#define MUJOCO_SRC_ENGINE_ENGINE_CORE_CONSTRAINT_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
//-------------------------- Jacobian-related ------------------------------------------------------
// determine type of friction cone
MJAPI int mj_isPyramidal(const mjModel* m);
// determine type of constraint Jacobian
MJAPI int mj_isSparse(const mjModel* m);
// determine type of solver
MJAPI int mj_isDual(const mjModel* m);
// multiply Jacobian by vector
MJAPI void mj_mulJacVec(const mjModel* m, mjData* d, mjtNum* res, const mjtNum* vec);
// multiply JacobianT by vector
MJAPI void mj_mulJacTVec(const mjModel* m, mjData* d, mjtNum* res, const mjtNum* vec);
//-------------------------- utility functions -----------------------------------------------------
// assign/override solver reference parameters
void mj_assignRef(const mjModel* m, mjtNum* target, const mjtNum* source);
// assign/override solver impedance parameters
void mj_assignImp(const mjModel* m, mjtNum* target, const mjtNum* source);
// assign/override geom/limit/tendon margin
mjtNum mj_assignMargin(const mjModel* m, mjtNum source);
// add contact to d->contact list; return 0 if success; 1 if buffer full
MJAPI int mj_addContact(const mjModel* m, mjData* d, const mjContact* con);
// add #size rows to constraint Jacobian; set pos, margin, frictionloss, type, id
// result: 0=success; 1=buffer full
int mj_addConstraint(const mjModel* m, mjData* d,
const mjtNum* jac, const mjtNum* pos,
const mjtNum* margin, mjtNum frictionloss,
int size, int type, int id, int NV, const int* chain);
// merge dof chains for two bodies
int mj_mergeChain(const mjModel* m, int* dofid, int b1, int b2);
// merge dof chains for two simple bodies
int mj_mergeChainSimple(const mjModel* m, int* dofid, int b1, int b2);
//-------------------------- constraint instantiation ----------------------------------------------
// equality constraints
void mj_instantiateEquality(const mjModel* m, mjData* d);
// frictional dofs and tendons
void mj_instantiateFriction(const mjModel* m, mjData* d);
// joint and tendon limits
void mj_instantiateLimit(const mjModel* m, mjData* d);
// frictionelss and frictional contacts
void mj_instantiateContact(const mjModel* m, mjData* d);
//------------------------ parameter computation/extraction ----------------------------------------
// compute efc_diagApprox
void mj_diagApprox(const mjModel* m, mjData* d);
// compute efc_R, efc_D, efc_KDIP, adjust diagApprox
void mj_makeImpedance(const mjModel* m, mjData* d);
//---------------------------- top-level API for constraint construction ---------------------------
// main driver: call all functions above
MJAPI void mj_makeConstraint(const mjModel* m, mjData* d);
// compute efc_AR
MJAPI void mj_projectConstraint(const mjModel* m, mjData* d);
// compute efc_vel, efc_aref
MJAPI void mj_referenceConstraint(const mjModel* m, mjData* d);
// compute efc_state, efc_force, qfrc_constraint
// optional: cost(qacc) = shat(jar) where jar = Jac*qacc-aref; cone Hessians
MJAPI void mj_constraintUpdate(const mjModel* m, mjData* d, const mjtNum* jar,
mjtNum cost[1], int flg_coneHessian);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_CORE_CONSTRAINT_H_
File diff suppressed because it is too large Load Diff
+127
View File
@@ -0,0 +1,127 @@
// 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_SRC_ENGINE_ENGINE_CORE_SMOOTH_H_
#define MUJOCO_SRC_ENGINE_ENGINE_CORE_SMOOTH_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
//-------------------------- position --------------------------------------------------------------
// forward kinematics
MJAPI void mj_kinematics(const mjModel* m, mjData* d);
// map inertias and motion dofs to global frame centered at CoM
MJAPI void mj_comPos(const mjModel* m, mjData* d);
// compute camera and light positions and orientations
MJAPI void mj_camlight(const mjModel* m, mjData* d);
// compute tendon lengths, velocities and moment arms
MJAPI void mj_tendon(const mjModel* m, mjData* d);
// compute actuator transmission lengths and moments
MJAPI void mj_transmission(const mjModel* m, mjData* d);
//-------------------------- inertia ---------------------------------------------------------------
// composite rigid body inertia algorithm, with skip
void mj_crbSkip(const mjModel* m, mjData* d, int skipsimple);
// composite rigid body inertia algorithm
MJAPI void mj_crb(const mjModel* m, mjData* d);
// sparse L'*D*L factorizaton of the inertia matrix M, assumed spd
MJAPI void mj_factorM(const mjModel* m, mjData* d);
// sparse backsubstitution: x = inv(L'*D*L)*y
MJAPI void mj_solveM(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n);
// half of sparse backsubstitution: x = sqrt(inv(D))*inv(L')*y
MJAPI void mj_solveM2(const mjModel* m, mjData* d, mjtNum* x, const mjtNum* y, int n);
//-------------------------- velocity --------------------------------------------------------------
// compute cvel, cdof_dot
MJAPI void mj_comVel(const mjModel* m, mjData* d);
// passive forces
MJAPI void mj_passive(const mjModel* m, mjData* d);
// subtree linear velocity and angular momentum
MJAPI void mj_subtreeVel(const mjModel* m, mjData* d);
//------------------------- fluid model ------------------------------------------------------------
void mj_inertiaBoxFluidModel(const mjModel* m, mjData* d, int i);
void mj_ellipsoidFluidModel(const mjModel* m, mjData* d, int bodyid);
// compute forces due to added mass (potential flow)
void mj_addedMassForces(
const mjtNum local_vels[6], const mjtNum local_accels[6],
const mjtNum fluid_density, const mjtNum virtual_mass[3],
const mjtNum virtual_inertia[3], mjtNum local_force[6]);
// compute forces due to viscous effects
void mj_viscousForces(
const mjtNum local_vels[6], const mjtNum fluid_density,
const mjtNum fluid_viscosity, const mjtNum size[3],
const mjtNum magnus_lift_coef, const mjtNum kutta_lift_coef,
const mjtNum blunt_drag_coef, const mjtNum slender_drag_coef,
const mjtNum ang_drag_coef, mjtNum local_force[6]);
void readFluidGeomInteraction(const mjtNum * geom_fluid_coefs,
mjtNum * geom_fluid_coef,
mjtNum * blunt_drag_coef,
mjtNum * slender_drag_coef,
mjtNum * ang_drag_coef,
mjtNum * kutta_lift_coef,
mjtNum * magnus_lift_coef,
mjtNum virtual_mass[3],
mjtNum virtual_inertia[3]);
void writeFluidGeomInteraction (mjtNum * geom_fluid_coefs,
const mjtNum * geom_fluid_coef,
const mjtNum * blunt_drag_coef,
const mjtNum * slender_drag_coef,
const mjtNum * ang_drag_coef,
const mjtNum * kutta_lift_coef,
const mjtNum * magnus_lift_coef,
const mjtNum virtual_mass[3],
const mjtNum virtual_inertia[3]);
//-------------------------- RNE -------------------------------------------------------------------
// RNE: compute M(qpos)*qacc + C(qpos,qvel); flg_acc=0 removes inertial term
MJAPI void mj_rne(const mjModel* m, mjData* d, int flg_acc, mjtNum* result);
// RNE with complete data: compute cacc, cfrc_ext, cfrc_int
MJAPI void mj_rnePostConstraint(const mjModel* m, mjData* d);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_CORE_SMOOTH_H_
+49
View File
@@ -0,0 +1,49 @@
// 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_SRC_ENGINE_ENGINE_CROSSPLATFORM_H_
#define MUJOCO_SRC_ENGINE_ENGINE_CROSSPLATFORM_H_
#include <stdlib.h>
// Windows
#ifdef _WIN32
// #define isnan _isnan
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#define mjQUICKSORT(buf, elnum, elsz, func, context) \
qsort_s(buf, elnum, elsz, func, context)
#define quicksortfunc(name, context, el1, el2) \
static int name(void* context, const void* el1, const void* el2)
// Unix-common
#else
// Apple
#ifdef __APPLE__
#define mjQUICKSORT(buf, elnum, elsz, func, context) \
qsort_r(buf, elnum, elsz, context, func)
#define quicksortfunc(name, context, el1, el2) \
static int name(void* context, const void* el1, const void* el2)
// non-Apple
#else
#define mjQUICKSORT(buf, elnum, elsz, func, context) \
qsort_r(buf, elnum, elsz, func, context)
#define quicksortfunc(name, context, el1, el2) \
static int name(const void* el1, const void* el2, void* context)
#endif
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_CROSSPLATFORM_H_
+80
View File
@@ -0,0 +1,80 @@
// Copyright 2022 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.
#include "engine/engine_file.h"
#include <stdio.h>
#include <limits.h>
#include "engine/engine_util_errmem.h"
void* mju_fileToMemory(const char* filename, int* filesize) {
// open file
*filesize = 0;
FILE* fp = fopen(filename, "rb");
if (!fp) {
return NULL;
}
// find size
if (fseek(fp, 0, SEEK_END) != 0) {
fclose(fp);
mju_warning_s("Failed to calculate size for '%s'", filename);
return NULL;
}
// ensure file size fits in int
long long_filesize = ftell(fp);
if (long_filesize > INT_MAX) {
fclose(fp);
mju_warning_s("File size over 2GB is not supported. File: '%s'", filename);
return NULL;
} else if (long_filesize < 0) {
fclose(fp);
mju_warning_s("Failed to calculate size for '%s'", filename);
return NULL;
}
*filesize = long_filesize;
// go back to start of file
if (fseek(fp, 0, SEEK_SET) != 0) {
fclose(fp);
mju_warning_s("Read error while reading '%s'", filename);
return NULL;
}
// allocate and read
void* buffer = mju_malloc(*filesize);
if (!buffer) {
mju_error("mjFileToMemory: could not allocate memory");
}
size_t bytes_read = fread(buffer, 1, *filesize, fp);
// check that read data matches file size
if (bytes_read != *filesize) { // SHOULD NOT OCCUR
if (ferror(fp)) {
fclose(fp);
mju_free(buffer);
*filesize = 0;
mju_warning_s("Read error while reading '%s'", filename);
return NULL;
} else if (feof(fp)) {
*filesize = bytes_read;
}
}
// close file, return contents
fclose(fp);
return buffer;
}
+29
View File
@@ -0,0 +1,29 @@
// Copyright 2022 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_SRC_ENGINE_ENGINE_FILE_H_
#define MUJOCO_SRC_ENGINE_ENGINE_FILE_H_
#ifdef __cplusplus
extern "C" {
#endif
// read file into memory buffer (allocated here with mju_malloc)
void* mju_fileToMemory(const char* filename, int* filesize);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_FILE_H_
+768
View File
@@ -0,0 +1,768 @@
// 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.
#include "engine/engine_forward.h"
#include <stddef.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
#include "engine/engine_callback.h"
#include "engine/engine_collision_driver.h"
#include "engine/engine_core_constraint.h"
#include "engine/engine_core_smooth.h"
#include "engine/engine_inverse.h"
#include "engine/engine_io.h"
#include "engine/engine_macro.h"
#include "engine/engine_sensor.h"
#include "engine/engine_solver.h"
#include "engine/engine_support.h"
#include "engine/engine_util_blas.h"
#include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h"
#include "engine/engine_util_sparse.h"
//--------------------------- check values ---------------------------------------------------------
// check positions, reset if bad
void mj_checkPos(const mjModel* m, mjData* d) {
for (int i=0; i<m->nq; i++) {
if (mju_isBad(d->qpos[i])) {
mj_warning(d, mjWARN_BADQPOS, i);
mj_resetData(m, d);
d->warning[mjWARN_BADQPOS].number++;
d->warning[mjWARN_BADQPOS].lastinfo = i;
return;
}
}
}
// check velocities, reset if bad
void mj_checkVel(const mjModel* m, mjData* d) {
for (int i=0; i<m->nv; i++) {
if (mju_isBad(d->qvel[i])) {
mj_warning(d, mjWARN_BADQVEL, i);
mj_resetData(m, d);
d->warning[mjWARN_BADQVEL].number++;
d->warning[mjWARN_BADQVEL].lastinfo = i;
return;
}
}
}
// check accelerations, reset if bad
void mj_checkAcc(const mjModel* m, mjData* d) {
for (int i=0; i<m->nv; i++) {
if (mju_isBad(d->qacc[i])) {
mj_warning(d, mjWARN_BADQACC, i);
mj_resetData(m, d);
d->warning[mjWARN_BADQACC].number++;
d->warning[mjWARN_BADQACC].lastinfo = i;
mj_forward(m, d);
return;
}
}
}
//-------------------------- solver components -----------------------------------------------------
// position-dependent computations
void mj_fwdPosition(const mjModel* m, mjData* d) {
TM_START1;
TM_START;
mj_kinematics(m, d);
mj_comPos(m, d);
mj_camlight(m, d);
mj_tendon(m, d);
mj_transmission(m, d);
TM_END(mjTIMER_POS_KINEMATICS);
TM_RESTART;
mj_crb(m, d);
mj_factorM(m, d);
TM_END(mjTIMER_POS_INERTIA);
TM_RESTART;
mj_collision(m, d);
TM_END(mjTIMER_POS_COLLISION);
TM_RESTART;
mj_makeConstraint(m, d);
TM_END(mjTIMER_POS_MAKE);
TM_RESTART;
mj_projectConstraint(m, d);
TM_END(mjTIMER_POS_PROJECT);
TM_END1(mjTIMER_POSITION);
}
// velocity-dependent computations
void mj_fwdVelocity(const mjModel* m, mjData* d) {
TM_START;
// tendon velocity: dense or sparse
if (mj_isSparse(m)) {
mju_mulMatVecSparse(d->ten_velocity, d->ten_J, d->qvel, m->ntendon,
d->ten_J_rownnz, d->ten_J_rowadr, d->ten_J_colind, NULL);
} else {
mju_mulMatVec(d->ten_velocity, d->ten_J, d->qvel, m->ntendon, m->nv);
}
// actuator velocity
mju_mulMatVec(d->actuator_velocity, d->actuator_moment, d->qvel, m->nu, m->nv);
// standard velocity computations
mj_comVel(m, d);
mj_passive(m, d);
mj_referenceConstraint(m, d);
// compute qfrc_bias with abbreviated RNE (without acceleration)
mj_rne(m, d, 0, d->qfrc_bias);
TM_END(mjTIMER_VELOCITY);
}
// (qpos, qvel, crtl, act) => (qfrc_actuator, actuator_force, act_dot)
void mj_fwdActuation(const mjModel* m, mjData* d) {
TM_START;
int nv = m->nv, nu = m->nu, na = m->na;
mjtNum gain, bias, tau;
mjtNum *prm, *moment = d->actuator_moment, *force = d->actuator_force;
// clear results
mju_zero(d->qfrc_actuator, nv);
if (nu) {
mju_zero(d->actuator_force, nu);
}
// check controls, set to 0 if any are bad
for (int i=0; i<nu; i++) {
if (mju_isBad(d->ctrl[i])) {
mj_warning(d, mjWARN_BADCTRL, i);
mju_zero(d->ctrl, nu);
break;
}
}
// disabled or no actuation: return
if (nu==0 || mjDISABLED(mjDSBL_ACTUATION)) {
return;
}
// force = gain .* [ctrl/act] + bias
for (int i=0; i<nu; i++) {
// clamp ctrl
if (m->actuator_ctrllimited[i] && !mjDISABLED(mjDSBL_CLAMPCTRL)) {
if (d->ctrl[i] < m->actuator_ctrlrange[2*i]) {
d->ctrl[i] = m->actuator_ctrlrange[2*i];
} else if (d->ctrl[i] > m->actuator_ctrlrange[2*i+1]) {
d->ctrl[i] = m->actuator_ctrlrange[2*i+1];
}
}
// extract gain info
prm = m->actuator_gainprm + mjNGAIN*i;
// handle according to gain type
switch (m->actuator_gaintype[i]) {
case mjGAIN_FIXED: // fixed gain: prm = gain
gain = prm[0];
break;
case mjGAIN_MUSCLE: // muscle gain
gain = mju_muscleGain(d->actuator_length[i],
d->actuator_velocity[i],
m->actuator_lengthrange+2*i,
m->actuator_acc0[i],
prm);
break;
default: // user gain
if (mjcb_act_gain) {
gain = mjcb_act_gain(m, d, i);
} else {
gain = 1;
}
}
// set force = gain .* [ctrl/act]
if (m->actuator_dyntype[i]==mjDYN_NONE) {
force[i] = gain * d->ctrl[i];
} else {
force[i] = gain * d->act[i-(nu-na)];
}
// extract bias info
prm = m->actuator_biasprm + mjNBIAS*i;
// handle according to bias type
switch (m->actuator_biastype[i]) {
case mjBIAS_NONE: // none
bias = 0.0;
break;
case mjBIAS_AFFINE: // affine: prm = [const, kp, kv]
bias = prm[0] + prm[1]*d->actuator_length[i] + prm[2]*d->actuator_velocity[i];
break;
case mjBIAS_MUSCLE: // muscle passive force
bias = mju_muscleBias(d->actuator_length[i],
m->actuator_lengthrange+2*i,
m->actuator_acc0[i],
prm);
break;
default: // user bias
if (mjcb_act_bias) {
bias = mjcb_act_bias(m, d, i);
} else {
bias = 0;
}
}
// add bias
force[i] += bias;
}
// clamp actuator_force
for (int i=0; i<nu; i++) {
if (m->actuator_forcelimited[i]) {
if (force[i]<m->actuator_forcerange[2*i]) {
force[i] = m->actuator_forcerange[2*i];
} else if (force[i]>m->actuator_forcerange[2*i+1]) {
force[i] = m->actuator_forcerange[2*i+1];
}
}
}
// qfrc_actuator = moment' * force
mju_mulMatTVec(d->qfrc_actuator, moment, force, nu, nv);
// act_dot for stateful actuators
for (int i=nu-na; i<nu; i++) {
// extract info
prm = m->actuator_dynprm + i*mjNDYN;
int j = i-(nu-na);
// compute act_dot according to dynamics type
switch (m->actuator_dyntype[i]) {
case mjDYN_INTEGRATOR: // simple integrator
d->act_dot[j] = d->ctrl[i];
break;
case mjDYN_FILTER: // linear filter: prm = tau
tau = mju_max(mjMINVAL, prm[0]);
d->act_dot[j] = (d->ctrl[i] - d->act[j]) / tau;
break;
case mjDYN_MUSCLE: // muscle model: prm = (tau_act, tau_deact)
d->act_dot[j] = mju_muscleDynamics(d->ctrl[i], d->act[j], prm);
break;
default: // user dynamics
if (mjcb_act_dyn) {
d->act_dot[j] = mjcb_act_dyn(m, d, i);
} else {
d->act_dot[j] = 0;
}
}
}
TM_END(mjTIMER_ACTUATION);
}
// add up all non-constraint forces, compute qacc_smooth
void mj_fwdAcceleration(const mjModel* m, mjData* d) {
TM_START;
mjMARKSTACK;
int nv = m->nv;
// qforce = sum of all non-constraint forces
mju_sub(d->qfrc_smooth, d->qfrc_passive, d->qfrc_bias, nv); // qfrc_bias is negative
mju_addTo(d->qfrc_smooth, d->qfrc_applied, nv);
mju_addTo(d->qfrc_smooth, d->qfrc_actuator, nv);
mj_xfrcAccumulate(m, d, d->qfrc_smooth);
// qacc_smooth = M \ qfr_smooth
mj_solveM(m, d, d->qacc_smooth, d->qfrc_smooth, 1);
mjFREESTACK;
TM_END(mjTIMER_ACCELERATION);
}
// warmstart/init solver
static void warmstart(const mjModel* m, mjData* d) {
int nv = m->nv, nefc = d->nefc;
// warmstart with best of (qacc_warmstart, qacc_smooth)
if (!mjDISABLED(mjDSBL_WARMSTART)) {
mjMARKSTACK;
mjtNum* jar = mj_stackAlloc(d, nefc);
// start with qacc = qacc_warmstart
mju_copy(d->qacc, d->qacc_warmstart, nv);
// compute jar(qacc_warmstart)
mj_mulJacVec(m, d, jar, d->qacc_warmstart);
mju_subFrom(jar, d->efc_aref, nefc);
// update constraints, save cost(qacc_warmstart)
mjtNum cost_warmstart;
mj_constraintUpdate(m, d, jar, &cost_warmstart, 0);
// PGS
if (m->opt.solver==mjSOL_PGS) {
// cost(force_warmstart)
mjtNum PGS_warmstart = mju_dot(d->efc_force, d->efc_b, nefc);
mjtNum* ARf = mj_stackAlloc(d, nefc);
if (mj_isSparse(m))
mju_mulMatVecSparse(ARf, d->efc_AR, d->efc_force, nefc,
d->efc_AR_rownnz, d->efc_AR_rowadr,
d->efc_AR_colind, NULL);
else {
mju_mulMatVec(ARf, d->efc_AR, d->efc_force, nefc, nefc);
}
PGS_warmstart += 0.5*mju_dot(d->efc_force, ARf, nefc);
// use zero if better
if (PGS_warmstart>0) {
mju_zero(d->efc_force, nefc);
mju_zero(d->qfrc_constraint, nv);
}
}
// non-PGS
else {
// add Gauss to cost(qacc_warmstart)
mjtNum* Ma = mj_stackAlloc(d, nv);
mj_mulM(m, d, Ma, d->qacc_warmstart);
for (int i=0; i<nv; i++) {
cost_warmstart += 0.5*(Ma[i]-d->qfrc_smooth[i])*(d->qacc_warmstart[i]-d->qacc_smooth[i]);
}
// cost(qacc_smooth)
mjtNum cost_smooth;
mj_constraintUpdate(m, d, d->efc_b, &cost_smooth, 0);
// use qacc_smooth if better
if (cost_warmstart>cost_smooth) {
mju_copy(d->qacc, d->qacc_smooth, nv);
}
}
mjFREESTACK;
}
// coldstart with qacc = qacc_smooth, efc_force = 0
else {
mju_copy(d->qacc, d->qacc_smooth, nv);
mju_zero(d->efc_force, nefc);
}
}
// compute efc_b, efc_force, qfrc_constraint; update qacc
void mj_fwdConstraint(const mjModel* m, mjData* d) {
TM_START;
int nv = m->nv, nefc = d->nefc;
// no constraints: copy unconstrained acc, clear forces, return
if (!nefc) {
mju_copy(d->qacc, d->qacc_smooth, nv);
mju_copy(d->qacc_warmstart, d->qacc_smooth, nv);
mju_zero(d->qfrc_constraint, nv);
d->solver_iter = 0;
return;
}
// compute efc_b = J*qacc_smooth - aref
mj_mulJacVec(m, d, d->efc_b, d->qacc_smooth);
mju_subFrom(d->efc_b, d->efc_aref, nefc);
// warmstart solver
warmstart(m, d);
d->solver_iter = 0;
// run main solver
switch (m->opt.solver) {
case mjSOL_PGS: // PGS
mj_solPGS(m, d, m->opt.iterations);
break;
case mjSOL_CG: // CG
mj_solCG(m, d, m->opt.iterations);
break;
case mjSOL_NEWTON: // Newton
mj_solNewton(m, d, m->opt.iterations);
break;
default:
mju_error_i("Unknown solver type %d", m->opt.solver);
}
// save result for next step warmstart
mju_copy(d->qacc_warmstart, d->qacc, nv);
// run noslip solver if enabled
if (m->opt.noslip_iterations>0) {
mj_solNoSlip(m, d, m->opt.noslip_iterations);
}
TM_END(mjTIMER_CONSTRAINT);
}
//-------------------------- integrators ----------------------------------------------------------
// Euler integrator, semi-implicit in velocity
void mj_Euler(const mjModel* m, mjData* d) {
int i, nv = m->nv, nM = m->nM;
mjMARKSTACK;
mjtNum* saveM = mj_stackAlloc(d, nM);
mjtNum* saveLD = mj_stackAlloc(d, nM);
mjtNum* saveLDiagInv = mj_stackAlloc(d, nv);
mjtNum* saveLDiagSqrtInv = mj_stackAlloc(d, nv);
mjtNum* qfrc = mj_stackAlloc(d, nv);
mjtNum* qacc = mj_stackAlloc(d, nv);
// check for dof damping
for (i=0; i<nv; i++) {
if (m->dof_damping[i]>0) {
break;
}
}
// no damping: explicit velocity integration
if (i>=nv) {
mju_addToScl(d->qvel, d->qacc, m->opt.timestep, nv);
}
// damping: integrate implicitly
else {
// save M and factorization
mju_copy(saveM, d->qM, nM);
mju_copy(saveLD, d->qLD, nM);
mju_copy(saveLDiagInv, d->qLDiagInv, nv);
mju_copy(saveLDiagSqrtInv, d->qLDiagSqrtInv, nv);
// add hB to diagonal of M
for (i=0; i<nv; i++) {
d->qM[m->dof_Madr[i]] += m->opt.timestep * m->dof_damping[i];
}
// factor
mj_factorM(m, d);
// solve
mju_add(qfrc, d->qfrc_smooth, d->qfrc_constraint, nv);
mj_solveM(m, d, qacc, qfrc, 1);
// integrate velocity
mju_addToScl(d->qvel, qacc, m->opt.timestep, nv);
// restore M and factorization
mju_copy(d->qM, saveM, nM);
mju_copy(d->qLD, saveLD, nM);
mju_copy(d->qLDiagInv, saveLDiagInv, nv);
mju_copy(d->qLDiagSqrtInv, saveLDiagSqrtInv, nv);
}
// update act
if (m->na) {
mju_addToScl(d->act, d->act_dot, m->opt.timestep, m->na);
// clamp activations
for (i=0; i<m->na; i++) {
int iu = i + m->nu - m->na;
if (m->actuator_actlimited[iu]) {
mjtNum min = m->actuator_actrange[2*iu];
mjtNum max = m->actuator_actrange[2*iu+1];
if (d->act[i]<min) {
d->act[i] = min;
} else if (d->act[i]>max) {
d->act[i] = max;
}
}
}
}
// update qpos using new qvel
mj_integratePos(m, d->qpos, d->qvel, m->opt.timestep);
// advance time
d->time += m->opt.timestep;
mjFREESTACK;
}
// RK4 tableau
const mjtNum RK4_A[9] = {
0.5, 0, 0,
0, 0.5, 0,
0, 0, 1
};
const mjtNum RK4_B[4] = {
1.0/6.0, 1.0/3.0, 1.0/3.0, 1.0/6.0
};
// Runge Kutta explicit order-N integrator
// (A,B) is the tableau, C is set to row_sum(A)
void mj_RungeKutta(const mjModel* m, mjData* d, int N) {
int nv = m->nv, nq = m->nq, na = m->na;
mjtNum h = m->opt.timestep, time = d->time;
mjtNum C[9], T[9], *X[10], *F[10], *dX;
const mjtNum* A = (N==4 ? RK4_A : 0);
const mjtNum* B = (N==4 ? RK4_B : 0);
mjMARKSTACK;
// check order
if (!A) {
mju_error("Supported RK orders: N=4");
}
// allocate space for intermediate solutions
dX = mj_stackAlloc(d, 2*nv+na);
for (int i=0; i<N; i++) {
X[i] = mj_stackAlloc(d, nq+nv+na);
F[i] = mj_stackAlloc(d, nv+na);
}
// precompute C and T; C,T,A have size (N-1)
for (int i=1; i<N; i++) {
// C(i) = sum_j A(i,j)
C[i-1] = 0;
for (int j=0; j<i; j++) {
C[i-1] += A[(i-1)*(N-1)+j];
}
// compute T
T[i-1] = d->time + C[i-1]*h;
}
// init X[0], F[0]; mj_forward() was already called
mju_copy(X[0], d->qpos, nq);
mju_copy(X[0]+nq, d->qvel, nv);
mju_copy(F[0], d->qacc, nv);
if (na) {
mju_copy(X[0]+nq+nv, d->act, na);
mju_copy(F[0]+nv, d->act_dot, na);
}
// compute the remaining X[i], F[i]
for (int i=1; i<N; i++) {
// compute dX
mju_zero(dX, 2*nv+na);
for (int j=0; j<i; j++) {
mju_addToScl(dX, X[j]+nq, A[(i-1)*(N-1)+j], nv);
mju_addToScl(dX+nv, F[j], A[(i-1)*(N-1)+j], nv+na);
}
// compute X[i] = X[0] '+' dX
mju_copy(X[i], X[0], nq+nv+na);
mj_integratePos(m, X[i], dX, h);
mju_addToScl(X[i]+nq, dX+nv, h, nv+na);
// set X[i], T[i-1] in mjData
mju_copy(d->qpos, X[i], nq);
mju_copy(d->qvel, X[i]+nq, nv);
if (na) {
mju_copy(d->act, X[i]+nq+nv, na);
}
d->time = T[i-1];
// evaluate F[i]
mj_forwardSkip(m, d, mjSTAGE_NONE, 1); // 1: do not recompute sensors and energy
mju_copy(F[i], d->qacc, nv);
if (na) {
mju_copy(F[i]+nv, d->act_dot, na);
}
}
// compute dX for final update (using B instead of A)
mju_zero(dX, 2*nv+na);
for (int j=0; j<N; j++) {
mju_addToScl(dX, X[j]+nq, B[j], nv);
mju_addToScl(dX+nv, F[j], B[j], nv+na);
}
// compute Xfinal
d->time = time + h;
mju_copy(d->qpos, X[0], nq+nv+na);
mj_integratePos(m, d->qpos, dX, h);
mju_addToScl(d->qvel, dX+nv, h, nv);
if (na) {
mju_addToScl(d->act, dX+2*nv, h, na);
// clamp activations
for (int i=0; i<m->na; i++) {
int iu = i + m->nu - m->na;
if (m->actuator_actlimited[iu]) {
mjtNum min = m->actuator_actrange[2*iu];
mjtNum max = m->actuator_actrange[2*iu+1];
if (d->act[i]<min) {
d->act[i] = min;
} else if (d->act[i]>max) {
d->act[i] = max;
}
}
}
}
mjFREESTACK;
}
//-------------------------- top-level API ---------------------------------------------------------
// forward dynamics with skip; skipstage is mjtStage
void mj_forwardSkip(const mjModel* m, mjData* d, int skipstage, int skipsensor) {
TM_START;
// position-dependent
if (skipstage<mjSTAGE_POS) {
mj_fwdPosition(m, d);
if (!skipsensor) {
mj_sensorPos(m, d);
}
if (mjENABLED(mjENBL_ENERGY)) {
mj_energyPos(m, d);
}
}
// velocity-dependent
if (skipstage<mjSTAGE_VEL) {
mj_fwdVelocity(m, d);
if (!skipsensor) {
mj_sensorVel(m, d);
}
if (mjENABLED(mjENBL_ENERGY)) {
mj_energyVel(m, d);
}
}
// acceleration-dependent
if (mjcb_control) {
mjcb_control(m, d);
}
mj_fwdActuation(m, d);
mj_fwdAcceleration(m, d);
mj_fwdConstraint(m, d);
if (!skipsensor) {
mj_sensorAcc(m, d);
}
TM_END(mjTIMER_FORWARD);
}
// forward dynamics
void mj_forward(const mjModel* m, mjData* d) {
mj_forwardSkip(m, d, mjSTAGE_NONE, 0);
}
// advance simulation using control callback
void mj_step(const mjModel* m, mjData* d) {
TM_START;
// common to all integrators
mj_checkPos(m, d);
mj_checkVel(m, d);
mj_forward(m, d);
mj_checkAcc(m, d);
// compare forward and inverse solutions if enabled
if (mjENABLED(mjENBL_FWDINV)) {
mj_compareFwdInv(m, d);
}
// use selected integrator
if (m->opt.integrator==mjINT_RK4) {
mj_RungeKutta(m, d, 4);
} else {
mj_Euler(m, d);
}
TM_END(mjTIMER_STEP);
}
// advance simulation in two phases: before input is set by user
void mj_step1(const mjModel* m, mjData* d) {
TM_START;
mj_checkPos(m, d);
mj_checkVel(m, d);
mj_fwdPosition(m, d);
mj_sensorPos(m, d);
mj_energyPos(m, d);
mj_fwdVelocity(m, d);
mj_sensorVel(m, d);
mj_energyVel(m, d);
if (mjcb_control) {
mjcb_control(m, d);
}
TM_END(mjTIMER_STEP);
}
// >>>> user can modify ctrl and q/xfrc_applied between step1 and step2 <<<<
// advance simulation in two phases: after input is set by user
void mj_step2(const mjModel* m, mjData* d) {
TM_START;
mj_fwdActuation(m, d);
mj_fwdAcceleration(m, d);
mj_fwdConstraint(m, d);
mj_sensorAcc(m, d);
mj_checkAcc(m, d);
// compare forward and inverse solutions if enabled
if (mjENABLED(mjENBL_FWDINV)) {
mj_compareFwdInv(m, d);
}
// integrate with Euler; ignore integrator option
mj_Euler(m, d);
d->timer[mjTIMER_STEP].number--;
TM_END(mjTIMER_STEP);
}
+80
View File
@@ -0,0 +1,80 @@
// 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_SRC_ENGINE_ENGINE_FORWARD_H_
#define MUJOCO_SRC_ENGINE_ENGINE_FORWARD_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
// check positions, velocities, accelerations; reset if bad
MJAPI void mj_checkPos(const mjModel* m, mjData* d);
MJAPI void mj_checkVel(const mjModel* m, mjData* d);
MJAPI void mj_checkAcc(const mjModel* m, mjData* d);
//-------------------------------- top-level API ---------------------------------------------------
// advance simulation: use control callback, no external force, RK4 available
MJAPI void mj_step(const mjModel* m, mjData* d);
// advance simulation in two steps: before external force/control is set by user
MJAPI void mj_step1(const mjModel* m, mjData* d);
// advance simulation in two steps: after external force/control is set by user
MJAPI void mj_step2(const mjModel* m, mjData* d);
// forward dynamics
MJAPI void mj_forward(const mjModel* m, mjData* d);
// forward dynamics with skip; skipstage is mjtStage
MJAPI void mj_forwardSkip(const mjModel* m, mjData* d,
int skipstage, int skipsensor);
//-------------------------------- integrators -----------------------------------------------------
// Euler integrator, semi-implicit in velocity
MJAPI void mj_Euler(const mjModel* m, mjData* d);
// Runge Kutta explicit order-N integrator
MJAPI void mj_RungeKutta(const mjModel* m, mjData* d, int N);
//-------------------------------- solver components -----------------------------------------------
// computations that depend only on qpos
MJAPI void mj_fwdPosition(const mjModel* m, mjData* d);
// computations that depend only on qpos and qvel
MJAPI void mj_fwdVelocity(const mjModel* m, mjData* d);
// compute actuator force
MJAPI void mj_fwdActuation(const mjModel* m, mjData* d);
// add up all non-constraint forces, compute qacc_unc
MJAPI void mj_fwdAcceleration(const mjModel* m, mjData* d);
// forward constraint
MJAPI void mj_fwdConstraint(const mjModel* m, mjData* d);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_FORWARD_H_
+213
View File
@@ -0,0 +1,213 @@
// 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.
#include "engine/engine_inverse.h"
#include <stddef.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
#include "engine/engine_collision_driver.h"
#include "engine/engine_core_constraint.h"
#include "engine/engine_core_smooth.h"
#include "engine/engine_io.h"
#include "engine/engine_macro.h"
#include "engine/engine_sensor.h"
#include "engine/engine_support.h"
#include "engine/engine_util_blas.h"
#include "engine/engine_util_sparse.h"
// position-dependent computations
void mj_invPosition(const mjModel* m, mjData* d) {
TM_START1;
TM_START;
mj_kinematics(m, d);
mj_comPos(m, d);
mj_camlight(m, d);
mj_tendon(m, d);
mj_transmission(m, d);
TM_END(mjTIMER_POS_KINEMATICS);
TM_RESTART;
mj_crb(m, d);
mj_factorM(m, d);
TM_END(mjTIMER_POS_INERTIA);
TM_RESTART;
mj_collision(m, d);
TM_END(mjTIMER_POS_COLLISION);
TM_RESTART;
mj_makeConstraint(m, d);
TM_END(mjTIMER_POS_MAKE);
TM_END1(mjTIMER_POSITION);
}
// velocity-dependent computations
void mj_invVelocity(const mjModel* m, mjData* d) {
TM_START;
// tendon velocity: dense or sparse
if (mj_isSparse(m)) {
mju_mulMatVecSparse(d->ten_velocity, d->ten_J, d->qvel, m->ntendon,
d->ten_J_rownnz, d->ten_J_rowadr, d->ten_J_colind, NULL);
} else {
mju_mulMatVec(d->ten_velocity, d->ten_J, d->qvel, m->ntendon, m->nv);
}
// actuator velocity
mju_mulMatVec(d->actuator_velocity, d->actuator_moment, d->qvel, m->nu, m->nv);
// standard velocity computations
mj_comVel(m, d);
mj_passive(m, d);
mj_referenceConstraint(m, d);
// compute qfrc_bias with abbreviated RNE (without acceleration)
mj_rne(m, d, 0, d->qfrc_bias);
TM_END(mjTIMER_VELOCITY);
}
// inverse constraint solver
void mj_invConstraint(const mjModel* m, mjData* d) {
TM_START;
int nefc = d->nefc;
// no constraints: clear, return
if (!nefc) {
mju_zero(d->qfrc_constraint, m->nv);
TM_END(mjTIMER_CONSTRAINT);
return;
}
mjMARKSTACK;
mjtNum* jar = mj_stackAlloc(d, nefc);
// compute jar = Jac*qacc - aref
mj_mulJacVec(m, d, jar, d->qacc);
mju_subFrom(jar, d->efc_aref, nefc);
// call update function
mj_constraintUpdate(m, d, jar, NULL, 0);
mjFREESTACK;
TM_END(mjTIMER_CONSTRAINT);
}
// inverse dynamics with skip; skipstage is mjtStage
void mj_inverseSkip(const mjModel* m, mjData* d,
int skipstage, int skipsensor) {
TM_START;
int nv = m->nv;
// position-dependent
if (skipstage<mjSTAGE_POS) {
mj_invPosition(m, d);
if (!skipsensor) {
mj_sensorPos(m, d);
}
if (mjENABLED(mjENBL_ENERGY)) {
mj_energyPos(m, d);
}
}
// velocity-dependent
if (skipstage<mjSTAGE_VEL) {
mj_invVelocity(m, d);
if (!skipsensor) {
mj_sensorVel(m, d);
}
if (mjENABLED(mjENBL_ENERGY)) {
mj_energyVel(m, d);
}
}
// acceleration-dependent
mj_invConstraint(m, d);
mj_rne(m, d, 1, d->qfrc_inverse);
if (!skipsensor) {
mj_sensorAcc(m, d);
}
// qfrc_inverse += artmature*qacc - qfrc_passive - qfrc_constraint
for (int i=0; i<nv; i++) {
d->qfrc_inverse[i] += m->dof_armature[i]*d->qacc[i]
- d->qfrc_passive[i] - d->qfrc_constraint[i];
}
TM_END(mjTIMER_INVERSE);
}
// inverse dynamics
void mj_inverse(const mjModel* m, mjData* d) {
mj_inverseSkip(m, d, mjSTAGE_NONE, 0);
}
// compare forward and inverse dynamics, without changing results of forward
// fwdinv[0] = norm(qfrc_constraint(forward) - qfrc_constraint(inverse))
// fwdinv[1] = norm(qfrc_applied(forward) - qfrc_inverse)
void mj_compareFwdInv(const mjModel* m, mjData* d) {
int nv = m->nv, nefc = d->nefc;
mjtNum *qforce, *dif, *save_qfrc_constraint, *save_efc_force;
mjMARKSTACK;
// clear result, return if no constraints
d->solver_fwdinv[0] = d->solver_fwdinv[1] = 0;
if (!nefc) {
return;
}
// allocate
qforce = mj_stackAlloc(d, nv);
dif = mj_stackAlloc(d, nv);
save_qfrc_constraint = mj_stackAlloc(d, nv);
save_efc_force = mj_stackAlloc(d, nefc);
// qforce = qfrc_applied + J'*xfrc_applied + qfrc_actuator
// should equal result of inverse dynamics
mju_add(qforce, d->qfrc_applied, d->qfrc_actuator, nv);
mj_xfrcAccumulate(m, d, qforce);
// save forward dynamics results that are about to be modified
mju_copy(save_qfrc_constraint, d->qfrc_constraint, nv);
mju_copy(save_efc_force, d->efc_force, nefc);
// run inverse dynamics, do not update position and velocity,
mj_inverseSkip(m, d, mjSTAGE_VEL, 1); // 1: do not recompute sensors and energy
// compute statistics
mju_sub(dif, save_qfrc_constraint, d->qfrc_constraint, nv);
d->solver_fwdinv[0] = mju_norm(dif, nv);
mju_sub(dif, qforce, d->qfrc_inverse, nv);
d->solver_fwdinv[1] = mju_norm(dif, nv);
// restore forward dynamics results
mju_copy(d->qfrc_constraint, save_qfrc_constraint, nv);
mju_copy(d->efc_force, save_efc_force, nefc);
mjFREESTACK;
}
+49
View File
@@ -0,0 +1,49 @@
// 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_SRC_ENGINE_ENGINE_INVERSE_H_
#define MUJOCO_SRC_ENGINE_ENGINE_INVERSE_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
// inverse dynamics
MJAPI void mj_inverse(const mjModel* m, mjData* d);
// Inverse dynamics with skip; skipstage is mjtStage.
MJAPI void mj_inverseSkip(const mjModel* m, mjData* d,
int skipstage, int skipsensor);
// position-dependent computations
MJAPI void mj_invPosition(const mjModel* m, mjData* d);
// velocity-dependent computations
MJAPI void mj_invVelocity(const mjModel* m, mjData* d);
// inverse constraint solver
MJAPI void mj_invConstraint(const mjModel* m, mjData* d);
// compare forward and inverse dynamics, without changing results of forward dynamics
MJAPI void mj_compareFwdInv(const mjModel* m, mjData* d);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_INVERSE_H_
File diff suppressed because it is too large Load Diff
+108
View File
@@ -0,0 +1,108 @@
// 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_SRC_ENGINE_ENGINE_IO_H_
#define MUJOCO_SRC_ENGINE_ENGINE_IO_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
//------------------------------- initialization ---------------------------------------------------
// Set default options for length range computation.
MJAPI void mj_defaultLROpt(mjLROpt* opt);
// set default solver paramters
MJAPI void mj_defaultSolRefImp(mjtNum* solref, mjtNum* solimp);
// set options to default values
MJAPI void mj_defaultOption(mjOption* opt);
// set visual options to default values
MJAPI void mj_defaultVisual(mjVisual* vis);
// set statistics to default values; compute later in compiler
void mj_defaultStatistic(mjStatistic* stat);
//------------------------------- mjModel ----------------------------------------------------------
// allocate mjModel
mjModel* mj_makeModel(int nq, int nv, int nu, int na, int nbody, int njnt,
int ngeom, int nsite, int ncam, int nlight,
int nmesh, int nmeshvert, int nmeshtexvert, int nmeshface, int nmeshgraph,
int nskin, int nskinvert, int nskintexvert, int nskinface,
int nskinbone, int nskinbonevert, int nhfield, int nhfielddata,
int ntex, int ntexdata, int nmat, int npair, int nexclude,
int neq, int ntendon, int nwrap, int nsensor,
int nnumeric, int nnumericdata, int ntext, int ntextdata,
int ntuple, int ntupledata, int nkey, int nmocap,
int nuser_body, int nuser_jnt, int nuser_geom, int nuser_site, int nuser_cam,
int nuser_tendon, int nuser_actuator, int nuser_sensor, int nnames);
// copy mjModel; allocate new if dest is NULL
MJAPI mjModel* mj_copyModel(mjModel* dest, const mjModel* src);
// save model to binary file
MJAPI void mj_saveModel(const mjModel* m, const char* filename, void* buffer, int buffer_sz);
// load model from binary MJB file
// if vfs is not NULL, look up file in vfs before reading from disk
MJAPI mjModel* mj_loadModel(const char* filename, const mjVFS* vfs);
// de-allocate model
MJAPI void mj_deleteModel(mjModel* m);
// size of buffer needed to hold model
MJAPI int mj_sizeModel(const mjModel* m);
// validate reference fields in a model; return null if valid, error message otherwise
MJAPI const char* mj_validateReferences(const mjModel* m);
//------------------------------- mjData -----------------------------------------------------------
// Allocate mjData correponding to given model.
// If the model buffer is unallocated the initial configuration will not be set.
MJAPI mjData* mj_makeData(const mjModel* m);
// Copy mjData.
// m is only required to contain the size fields from MJMODEL_INTS.
MJAPI mjData* mj_copyData(mjData* dest, const mjModel* m, const mjData* src);
// set data to defaults
MJAPI void mj_resetData(const mjModel* m, mjData* d);
// set data to defaults, fill everything else with debug_value
MJAPI void mj_resetDataDebug(const mjModel* m, mjData* d, unsigned char debug_value);
// reset data, set fields from specified keyframe
MJAPI void mj_resetDataKeyframe(const mjModel* m, mjData* d, int key);
// mjData stack allocate
MJAPI mjtNum* mj_stackAlloc(mjData* d, int size);
// de-allocate data
MJAPI void mj_deleteData(mjData* d);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_IO_H_
+43
View File
@@ -0,0 +1,43 @@
// 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_SRC_ENGINE_ENGINE_MACRO_H_
#define MUJOCO_SRC_ENGINE_ENGINE_MACRO_H_
#include "engine/engine_callback.h"
//-------------------------------- utility macros --------------------------------------------------
// mark and free stack
#define mjMARKSTACK int _mark = d->pstack;
#define mjFREESTACK d->pstack = _mark;
// check bitflag
#define mjDISABLED(x) (m->opt.disableflags & (x))
#define mjENABLED(x) (m->opt.enableflags & (x))
// max and min macros
#define mjMAX(a,b) (((a) > (b)) ? (a) : (b))
#define mjMIN(a,b) (((a) < (b)) ? (a) : (b))
//-------------------------- timer macros ----------------------------------------------------------
#define TM_START mjtNum _tm = (mjcb_time ? mjcb_time() : 0);
#define TM_RESTART _tm = (mjcb_time ? mjcb_time() : 0);
#define TM_END(i) {d->timer[i].duration += ((mjcb_time ? mjcb_time() : 0) - _tm); d->timer[i].number++;}
#define TM_START1 mjtNum _tm1 = (mjcb_time ? mjcb_time() : 0);
#define TM_END1(i) {d->timer[i].duration += ((mjcb_time ? mjcb_time() : 0) - _tm1); d->timer[i].number++;}
#endif // MUJOCO_SRC_ENGINE_ENGINE_MACRO_H_
+944
View File
@@ -0,0 +1,944 @@
// 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.
#include "engine/engine_print.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mjxmacro.h>
#include "engine/engine_core_constraint.h"
#include "engine/engine_io.h"
#include "engine/engine_macro.h"
#include "engine/engine_support.h"
#include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h"
#define FLOAT_FORMAT "% -9.2g"
#define FLOAT_FORMAT_MAX_LEN 20
#define INT_FORMAT " %d"
#define NAME_FORMAT "%-21s"
//----------------------------------- static utility functions -------------------------------------
// print 2D array of mjtNum into file
static void printArray(const char* str, int nr, int nc, const mjtNum* data, FILE* fp,
const char* float_format) {
if (nr && nc) {
fprintf(fp, "%s\n ", str);
for (int r=0; r<nr; r++) {
for (int c=0; c<nc; c++) {
fprintf(fp, float_format, data[c + r*nc]);
fprintf(fp, " ");
}
fprintf(fp, "\n ");
}
fprintf(fp, "\n");
}
}
// print 2D array of int into file
static void printArrayInt(const char* str, int nr, int nc, const int* data, FILE* fp) {
if (nr && nc) {
fprintf(fp, "%s\n ", str);
for (int r=0; r<nr; r++) {
for (int c=0; c<nc; c++) {
fprintf(fp, "%d ", data[c + r*nc]);
}
fprintf(fp, "\n ");
}
fprintf(fp, "\n");
}
}
// print sparse matrix
static void printSparse(const char* str, const mjtNum* mat, int nr,
const int* rownnz, const int* rowadr,
const int* colind, FILE* fp, const char* float_format) {
fprintf(fp, "%s\n ", str);
for (int r=0; r<nr; r++) {
for (int adr=rowadr[r]; adr<rowadr[r]+rownnz[r]; adr++) {
fprintf(fp, "%d: ", colind[adr]);
fprintf(fp, float_format, mat[adr]);
fprintf(fp, " ");
}
fprintf(fp, "\n ");
}
fprintf(fp, "\n");
}
// print vector
static void printVector(const char* str, const mjtNum* data, int n, FILE* fp,
const char* float_format) {
// print str
fprintf(fp, "%s", str);
// print data
for (int i=0; i<n; i++) {
fprintf(fp, float_format, data[i]);
fprintf(fp, " ");
}
fprintf(fp, "\n");
}
//------------------------------ printing functions ------------------------------------------------
// return whether float_format is a valid format string for a single float
static bool validateFloatFormat(const char* float_format) {
// check for nullptr;
if (!float_format) {
return false;
}
// example valid format string: "% -9.2g"
if (strnlen(float_format, FLOAT_FORMAT_MAX_LEN + 1) > FLOAT_FORMAT_MAX_LEN) {
mju_warning_i("Format string longer than limit of %d.", FLOAT_FORMAT_MAX_LEN);
return false;
}
int cur_idx = 0;
if (float_format[cur_idx] != '%') {
mju_warning("Format string must start with '%'.");
return false;
}
cur_idx++;
// flag characters. allow at most one of each flag
const char flag_characters[] = "-+ #0";
int flag_character_counts[sizeof(flag_characters)] = { 0 };
char* c;
while (c = strchr(flag_characters, float_format[cur_idx]), c != NULL) {
int flag_idx = (c - flag_characters)/sizeof(char);
flag_character_counts[flag_idx]++;
if (flag_character_counts[flag_idx] > 1) {
mju_warning("Format string contains repeated flag.");
return false;
}
cur_idx++;
}
// width. disallow *, which requires additional argument
while (strchr("0123456789", float_format[cur_idx]) != NULL) {
cur_idx++;
}
// precision. disallow *, which requires additional argument
if (float_format[cur_idx] == '.') {
cur_idx++;
while (strchr("0123456789", float_format[cur_idx]) != NULL) {
cur_idx++;
}
}
// length
if (float_format[cur_idx] == 'L') {
cur_idx++;
}
// specifier must be a valid float format
if (strchr("fgGeE", float_format[cur_idx]) == NULL) {
mju_warning("Format string specifier must be one of \"fgGeE\".");
return false;
}
cur_idx++;
if (float_format[cur_idx] == '\0') {
return true;
} else {
mju_warning_s("Unable to match format string %s with expected pattern for a single float.",
float_format);
return false;
}
}
// Clang sometimes goes OOM when the -Wuninitialized warning is enabled for this function
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wuninitialized"
#endif
// print mjModel to text file, specifying format. float_format must be a
// valid printf-style format string for a single float value
void mj_printFormattedModel(const mjModel* m, const char* filename, const char* float_format) {
// get file
FILE* fp;
if (filename) {
fp = fopen(filename, "wt");
} else {
fp = stdout;
}
// check for nullptr
if (!fp) {
mju_warning_s("Could not open file '%s' for writing mjModel", filename);
return;
}
// validate format string
if (!validateFloatFormat(float_format)) {
mju_warning("WARNING: Received invalid float_format. Using default instead.");
float_format = FLOAT_FORMAT;
}
// compute total body mass
mjtNum totalmass = 0;
for (int i=0; i<m->nbody; i++) {
totalmass += m->body_mass[i];
}
// software version and model name
fprintf(fp, "MuJoCo version %s\n", mj_versionString());
fprintf(fp, "model name %s\n\n", m->names);
// sizes
#define X( name ) \
if(m->name) { \
fprintf(fp, NAME_FORMAT, #name); \
fprintf(fp, INT_FORMAT "\n", m->name); \
}
MJMODEL_INTS
#undef X
fprintf(fp, "\n");
// scalar options
#define X( type, name ) \
fprintf(fp, NAME_FORMAT, #name); \
fprintf(fp, float_format, m->opt.name); \
fprintf(fp, "\n");
MJOPTION_FLOATS
#undef X
#define X( type, name ) \
fprintf(fp, NAME_FORMAT, #name); \
fprintf(fp, INT_FORMAT "\n", m->opt.name);
MJOPTION_INTS
#undef X
// vector options
#define X( name, sz ) \
fprintf(fp, NAME_FORMAT, #name); \
for (int i=0; i < sz; i++) { \
fprintf(fp, float_format, m->opt.name[i]); \
fprintf(fp, " "); \
} \
fprintf(fp, "\n");
MJOPTION_VECTORS
#undef X
fprintf(fp, "\n");
// total mass
fprintf(fp, NAME_FORMAT, "totalmass");
fprintf(fp, float_format, totalmass);
fprintf(fp, "\n\n");
// statistics
fprintf(fp, NAME_FORMAT, "meaninertia");
fprintf(fp, float_format, m->stat.meaninertia);
fprintf(fp, "\n");
fprintf(fp, NAME_FORMAT, "meanmass");
fprintf(fp, float_format, m->stat.meanmass);
fprintf(fp, "\n");
fprintf(fp, NAME_FORMAT, "meansize");
fprintf(fp, float_format, m->stat.meansize);
fprintf(fp, "\n");
fprintf(fp, NAME_FORMAT, "extent");
fprintf(fp, float_format, m->stat.extent);
fprintf(fp, "\n");
fprintf(fp, NAME_FORMAT, "center");
fprintf(fp, float_format, m->stat.center[0]);
fprintf(fp, float_format, m->stat.center[1]);
fprintf(fp, float_format, m->stat.center[2]);
fprintf(fp, "\n\n");
// qpos0
fprintf(fp, NAME_FORMAT, "qpos0");
for (int i=0; i<m->nq; i++) {
fprintf(fp, float_format, m->qpos0[i]);
fprintf(fp, " ");
}
fprintf(fp, "\n\n");
// qpos_spring
fprintf(fp, NAME_FORMAT, "qpos_spring");
for (int i=0; i<m->nq; i++) {
fprintf(fp, float_format, m->qpos_spring[i]);
fprintf(fp, " ");
}
fprintf(fp, "\n\n");
// values used by MJMODEL_POINTERS macro
MJMODEL_POINTERS_PREAMBLE(m)
// object_class points to the integer size identifying the class of arrays currently being printed
// used to organise the printout into category groups
// note that comparison is based on the integer address, not its value
const int* object_class;
#define X( type, name, num, sz ) \
if (&m->num == object_class && (strncmp(#name, "name_", 5)!=0) && sz) { \
fprintf(fp, " "); \
fprintf(fp, NAME_FORMAT, #name); \
for (int j=0; j < sz; j++) { \
((strcmp(#type, "mjtNum") == 0) || (strcmp(#type, "float") == 0)) ? \
(fprintf(fp, float_format, (mjtNum)m->name[sz*i+j]), \
fprintf(fp, " ")) : \
fprintf(fp, INT_FORMAT " ", (int)m->name[sz*i+j]); \
} \
fprintf(fp, "\n"); \
}
// bodies
for (int i=0; i<m->nbody; i++) {
fprintf(fp, "\nBODY %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_bodyadr[i]);
object_class = &m->nbody;
MJMODEL_POINTERS
}
if (m->nbody) fprintf(fp, "\n");
// joints
for (int i=0; i<m->njnt; i++) {
fprintf(fp, "\nJOINT %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_jntadr[i]);
object_class = &m->njnt;
MJMODEL_POINTERS
}
if (m->njnt) fprintf(fp, "\n");
// dofs
for (int i=0; i<m->nv; i++) {
fprintf(fp, "\nDOF %d:\n", i);
object_class = &m->nv;
MJMODEL_POINTERS
}
if (m->nv) fprintf(fp, "\n");
// geoms
for (int i=0; i<m->ngeom; i++) {
fprintf(fp, "\nGEOM %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_geomadr[i]);
object_class = &m->ngeom;
MJMODEL_POINTERS
}
if (m->ngeom) fprintf(fp, "\n");
// sites
for (int i=0; i<m->nsite; i++) {
fprintf(fp, "\nSITE %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_siteadr[i]);
object_class = &m->nsite;
MJMODEL_POINTERS
}
if (m->nsite) fprintf(fp, "\n");
// cameras
for (int i=0; i<m->ncam; i++) {
fprintf(fp, "\nCAMERA %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_camadr[i]);
object_class = &m->ncam;
MJMODEL_POINTERS
}
if (m->ncam) fprintf(fp, "\n");
// lights
for (int i=0; i<m->nlight; i++) {
fprintf(fp, "\nLIGHT %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_lightadr[i]);
object_class = &m->nlight;
MJMODEL_POINTERS
}
if (m->nlight) fprintf(fp, "\n");
// meshes
for (int i=0; i<m->nmesh; i++) {
fprintf(fp, "\nMESH %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_meshadr[i]);
object_class = &m->nmesh;
MJMODEL_POINTERS
if (m->mesh_graphadr[i]>=0) {
fprintf(fp, " " NAME_FORMAT, "qhull face");
fprintf(fp, " %d\n", m->mesh_graph[m->mesh_graphadr[i]+1]);
fprintf(fp, " " NAME_FORMAT, "qhull vert");
fprintf(fp, " %d\n", m->mesh_graph[m->mesh_graphadr[i]]);
}
}
if (m->nmesh) fprintf(fp, "\n");
// skins
for (int i=0; i<m->nskin; i++) {
fprintf(fp, "\nSKIN %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_skinadr[i]);
object_class = &m->nskin;
MJMODEL_POINTERS
}
if (m->nskin) fprintf(fp, "\n");
// hfields
for (int i=0; i<m->nhfield; i++) {
fprintf(fp, "\nHEIGHTFIELD %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_hfieldadr[i]);
object_class = &m->nhfield;
MJMODEL_POINTERS
}
if (m->nhfield) fprintf(fp, "\n");
// textures
for (int i=0; i<m->ntex; i++) {
fprintf(fp, "\nTEXTURE %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_texadr[i]);
object_class = &m->ntex;
MJMODEL_POINTERS
}
if (m->ntex) fprintf(fp, "\n");
// materials
for (int i=0; i<m->nmat; i++) {
fprintf(fp, "\nMATERIAL %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_matadr[i]);
object_class = &m->nmat;
MJMODEL_POINTERS
}
if (m->nmat) fprintf(fp, "\n");
// pairs
for (int i=0; i<m->npair; i++) {
fprintf(fp, "\nPAIR %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_pairadr[i]);
object_class = &m->npair;
MJMODEL_POINTERS
}
if (m->npair) fprintf(fp, "\n");
// excludes
for (int i=0; i<m->nexclude; i++) {
fprintf(fp, "\nEXCLUDE %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_excludeadr[i]);
object_class = &m->nexclude;
MJMODEL_POINTERS
}
if (m->nexclude) fprintf(fp, "\n");
// equality constraints
for (int i=0; i<m->neq; i++) {
fprintf(fp, "\nEQUALITY %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_eqadr[i]);
object_class = &m->neq;
MJMODEL_POINTERS
}
if (m->neq) fprintf(fp, "\n");
// tendons
for (int i=0; i<m->ntendon; i++) {
fprintf(fp, "\nTENDON %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_tendonadr[i]);
object_class = &m->ntendon;
MJMODEL_POINTERS
fprintf(fp, " path \n");
for (int j=0; j<m->tendon_num[i]; j++) {
int k = m->tendon_adr[i]+j;
fprintf(fp, " %d %d ", m->wrap_type[k], m->wrap_objid[k]);
fprintf(fp, float_format, m->wrap_prm[k]);
fprintf(fp, "\n");
}
fprintf(fp, "\n");
}
if (m->ntendon) fprintf(fp, "\n");
// actuators
for (int i=0; i<m->nu; i++) {
fprintf(fp, "\nACTUATOR %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_actuatoradr[i]);
object_class = &m->nu;
MJMODEL_POINTERS
}
if (m->nu) fprintf(fp, "\n");
// sensors
for (int i=0; i<m->nsensor; i++) {
fprintf(fp, "\nSENSOR %d:\n", i);
fprintf(fp, " " NAME_FORMAT, "name");
fprintf(fp, " %s\n", m->names + m->name_sensoradr[i]);
object_class = &m->nsensor;
MJMODEL_POINTERS
}
if (m->nsensor) fprintf(fp, "\n");
// custom numeric parameters
for (int i=0; i<m->nnumeric; i++) {
fprintf(fp, "\nNUMERIC %d:\n", i);
fprintf(fp, " name %s\n", m->names + m->name_numericadr[i]);
fprintf(fp, " size %d\n", m->numeric_size[i]);
fprintf(fp, " value ");
for (int j=0; j<m->numeric_size[i]; j++) {
fprintf(fp, float_format, m->numeric_data[m->numeric_adr[i]+j]);
}
fprintf(fp, "\n");
}
if (m->nnumeric) fprintf(fp, "\n");
// custom text parameters
for (int i=0; i<m->ntext; i++) {
fprintf(fp, "\nTEXT %d:\n", i);
fprintf(fp, " name %s\n", m->names + m->name_textadr[i]);
fprintf(fp, " size %d\n", m->text_size[i]);
fprintf(fp, " value %s\n", m->text_data + m->text_adr[i]);
}
if (m->ntext) fprintf(fp, "\n");
// custom tuple parameters
for (int i=0; i<m->ntuple; i++) {
fprintf(fp, "\nTUPLE %d:\n", i);
fprintf(fp, " name %s\n", m->names + m->name_tupleadr[i]);
fprintf(fp, " size %d\n", m->tuple_size[i]);
fprintf(fp, " elements\n");
for (int j=m->tuple_adr[i]; j<m->tuple_adr[i]+m->tuple_size[i]; j++) {
fprintf(fp, " %s %d, prm = ",
mju_type2Str(m->tuple_objtype[j]), m->tuple_objid[j]);
fprintf(fp, float_format, m->tuple_objprm[j]);
fprintf(fp, "\n");
}
}
if (m->ntuple) fprintf(fp, "\n");
// keyframes (only if different from default)
for (int i=0; i<m->nkey; i++) {
// print name
if (m->names[m->name_keyadr[i]]) {
fprintf(fp, "key_name%d %s\n", i, m->names + m->name_keyadr[i]);
}
// print time if non-0
if (m->key_time[i]!=0) {
fprintf(fp, "key_time%d %.4f\n", i, m->key_time[i]);
}
// check qpos for difference
int k = 0;
for (int j=0; j<m->nq; j++)
if (m->qpos0[j] != m->key_qpos[i*m->nq + j]) {
k = 1;
}
// print if different
if (k==1) {
fprintf(fp, "key_qpos%d ", i);
for (int j=0; j<m->nq; j++) {
fprintf(fp, float_format, m->key_qpos[i*m->nq + j]);
}
fprintf(fp, "\n");
}
// check qvel for nonzero
for (int j=0; j<m->nv; j++)
if (m->key_qvel[i*m->nv + j]) {
k = 2;
}
// print if nozero
if (k==2) {
fprintf(fp, "key_qvel%d ", i);
for (int j=0; j<m->nv; j++) {
fprintf(fp, float_format, m->key_qvel[i*m->nv + j]);
}
fprintf(fp, "\n");
}
// check act for nonzero
for (int j=0; j<m->na; j++)
if (m->key_act[i*m->na + j]) {
k = 3;
}
// print if nonzero
if (k==3) {
fprintf(fp, "key_act%d ", i);
for (int j=0; j<m->na; j++) {
fprintf(fp, float_format, m->key_act[i*m->na + j]);
}
fprintf(fp, "\n");
}
// check mpos for difference
if (m->nmocap) {
for (int j=0; j<m->nbody; j++) {
if (m->body_mocapid[j]>=0) {
int id = m->body_mocapid[j];
if (m->body_pos[3*j] != m->key_mpos[i*3*m->nmocap + 3*id] ||
m->body_pos[3*j+1] != m->key_mpos[i*3*m->nmocap + 3*id+1] ||
m->body_pos[3*j+2] != m->key_mpos[i*3*m->nmocap + 3*id+2]) {
k = 4;
break;
}
}
}
}
// print if nonzero
if (k==4) {
fprintf(fp, "key_mpos%d ", i);
for (int j=0; j<3*m->nmocap; j++) {
fprintf(fp, float_format, m->key_mpos[i*3*m->nmocap + j]);
}
fprintf(fp, "\n");
}
// check mquat for difference
if (m->nmocap) {
for (int j=0; j<m->nbody; j++) {
if (m->body_mocapid[j]>=0) {
int id = m->body_mocapid[j];
if (m->body_quat[4*j] != m->key_mquat[i*4*m->nmocap + 4*id] ||
m->body_quat[4*j+1] != m->key_mquat[i*4*m->nmocap + 4*id+1] ||
m->body_quat[4*j+2] != m->key_mquat[i*4*m->nmocap + 4*id+2] ||
m->body_quat[4*j+3] != m->key_mquat[i*4*m->nmocap + 4*id+3]) {
k = 5;
break;
}
}
}
}
// print if nonzero
if (k==5) {
fprintf(fp, "key_mquat%d ", i);
for (int j=0; j<4*m->nmocap; j++) {
fprintf(fp, float_format, m->key_mquat[i*4*m->nmocap + j]);
}
fprintf(fp, "\n");
}
// new line if any data was written
if (k) {
fprintf(fp, "\n");
}
}
#undef X
if (filename) {
fclose(fp);
}
}
// print mjModel to text file
void mj_printModel(const mjModel* m, const char* filename) {
mj_printFormattedModel(m, filename, FLOAT_FORMAT);
}
// print mjModel to text file, specifying format. float_format must be a
// valid printf-style format string for a single float value
void mj_printFormattedData(const mjModel* m, mjData* d, const char* filename,
const char* float_format) {
mjtNum *M;
mjMARKSTACK;
// check format string
if (!validateFloatFormat(float_format)) {
mju_warning("WARNING: Received invalid float_format. Using default instead.");
float_format = FLOAT_FORMAT;
}
// stack in use, SHOULD NOT OCCUR
if (d->pstack) {
mju_error("Attempting to print mjData when stack is in use");
}
// get file
FILE* fp;
if (filename) {
fp = fopen(filename, "wt");
} else {
fp = stdout;
}
// check for nullptr
if (!fp) {
mju_warning_s("Could not open file '%s' for writing mjModel", filename);
mjFREESTACK;
return;
}
// allocate full inertia
M = mj_stackAlloc(d, m->nv*m->nv);
// ---------------------------------- print mjData fields
fprintf(fp, "SIZES\n");
#define X( type, name ) \
if(strcmp(#type, "int")==0) { \
fprintf(fp, " "); \
fprintf(fp, NAME_FORMAT, #name); \
fprintf(fp, INT_FORMAT "\n", (int)d->name); \
}
MJDATA_SCALAR
#undef X
fprintf(fp, "\n");
// WARNING
int active_warnings = 0;
for (int i=0; i<mjNWARNING; i++) {
active_warnings += d->warning[i].number;
}
if (active_warnings) {
fprintf(fp, "WARNING\n");
for (int i=0; i<mjNWARNING; i++)
if (d->warning[i].number)
fprintf(fp, " %d: lastinfo = %d number = %d\n",
i, d->warning[i].lastinfo, d->warning[i].number);
fprintf(fp, "\n");
}
// TIMER
mjtNum active_timers = 0;
for (int i=0; i<mjNTIMER; i++) {
active_timers += d->timer[i].duration;
}
if (active_timers) {
fprintf(fp, "TIMER\n");
for (int i=0; i<mjNTIMER; i++) {
fprintf(fp, " %d: duration = ",i);
fprintf(fp, float_format, d->timer[i].duration);
fprintf(fp, " number = %d\n", d->timer[i].number);
}
fprintf(fp, "\n");
}
// SOLVER STAT
if (d->solver_iter) {
fprintf(fp, "SOLVER STAT\n");
fprintf(fp, " solver_iter = %d\n", d->solver_iter);
fprintf(fp, " solver_nnz = %d\n", d->solver_nnz);
for (int i=0; i<mjMIN(mjNSOLVER, d->solver_iter); i++) {
fprintf(fp, " %d: improvement = ", i);
fprintf(fp, float_format, d->solver[i].improvement);
fprintf(fp, " gradient = ");
fprintf(fp, float_format, d->solver[i].gradient);
fprintf(fp, " lineslope = ");
fprintf(fp, float_format, d->solver[i].lineslope);
fprintf(fp, "\n");
fprintf(fp, " nactive = %d nchange = %d neval = %d nupdate = %d\n",
d->solver[i].nactive, d->solver[i].nchange,
d->solver[i].neval, d->solver[i].nupdate);
}
printVector("solver_fwdinv = ", d->solver_fwdinv, 2, fp, float_format);
fprintf(fp, "\n");
}
printVector("ENERGY = ", d->energy, 2, fp, float_format);
fprintf(fp, "\n");
fprintf(fp, "TIME = ");
fprintf(fp, float_format, d->time);
fprintf(fp, "\n\n");
printArray("QPOS", m->nq, 1, d->qpos, fp, float_format);
printArray("QVEL", m->nv, 1, d->qvel, fp, float_format);
printArray("ACT", m->na, 1, d->act, fp, float_format);
printArray("QACC_WARMSTART", m->nv, 1, d->qacc_warmstart, fp, float_format);
printArray("CTRL", m->nu, 1, d->ctrl, fp, float_format);
printArray("QFRC_APPLIED", m->nq, 1, d->qfrc_applied, fp, float_format);
printArray("XFRC_APPLIED", m->nbody, 6, d->xfrc_applied, fp, float_format);
printArray("MOCAP_POS", m->nmocap, 3, d->mocap_pos, fp, float_format);
printArray("MOCAP_QUAT", m->nmocap, 4, d->mocap_quat, fp, float_format);
printArray("QACC", m->nv, 1, d->qacc, fp, float_format);
printArray("ACT_DOT", m->na, 1, d->act_dot, fp, float_format);
printArray("USERDATA", m->nuserdata, 1, d->userdata, fp, float_format);
printArray("SENSOR", m->nsensordata, 1, d->sensordata, fp, float_format);
printArray("XPOS", m->nbody, 3, d->xpos, fp, float_format);
printArray("XQUAT", m->nbody, 4, d->xquat, fp, float_format);
printArray("XMAT", m->nbody, 9, d->xmat, fp, float_format);
printArray("XIPOS", m->nbody, 3, d->xipos, fp, float_format);
printArray("XIMAT", m->nbody, 9, d->ximat, fp, float_format);
printArray("XANCHOR", m->njnt, 3, d->xanchor, fp, float_format);
printArray("XAXIS", m->njnt, 3, d->xaxis, fp, float_format);
printArray("GEOM_XPOS", m->ngeom, 3, d->geom_xpos, fp, float_format);
printArray("GEOM_XMAT", m->ngeom, 9, d->geom_xmat, fp, float_format);
printArray("SITE_XPOS", m->nsite, 3, d->site_xpos, fp, float_format);
printArray("SITE_XMAT", m->nsite, 9, d->site_xmat, fp, float_format);
printArray("CAM_XPOS", m->ncam, 3, d->cam_xpos, fp, float_format);
printArray("CAM_XMAT", m->ncam, 9, d->cam_xmat, fp, float_format);
printArray("LIGHT_XPOS", m->nlight, 3, d->light_xpos, fp, float_format);
printArray("LIGHT_XDIR", m->nlight, 3, d->light_xdir, fp, float_format);
printArray("SUBTREE_COM", m->nbody, 3, d->subtree_com, fp, float_format);
printArray("CDOF", m->nv, 6, d->cdof, fp, float_format);
printArray("CINERT", m->nbody, 10, d->cinert, fp, float_format);
printArray("TEN_LENGTH", m->ntendon, 1, d->ten_length, fp, float_format);
if (!mj_isSparse(m)) {
printArray("TEN_MOMENT", m->ntendon, m->nv, d->ten_J, fp, float_format);
} else {
printArrayInt("TEN_J_ROWNNZ", m->ntendon, 1, d->ten_J_rownnz, fp);
printArrayInt("TEN_J_ROWADR", m->ntendon, 1, d->ten_J_rowadr, fp);
printSparse("TEN_J", d->ten_J, m->ntendon, d->ten_J_rownnz,
d->ten_J_rowadr, d->ten_J_colind, fp, float_format);
}
for (int i=0; i<m->ntendon; i++) {
fprintf(fp, "TENDON %d: %d wrap points\n", i, d->ten_wrapnum[i]);
for (int j=0; j<d->ten_wrapnum[i]; j++) {
fprintf(fp, " %d: ", d->wrap_obj[d->ten_wrapadr[i]+j]);
printVector("", d->wrap_xpos+3*(d->ten_wrapadr[i]+j), 3, fp, float_format);
}
fprintf(fp, "\n");
}
printArray("ACTUATOR_LENGTH", m->nu, 1, d->actuator_length, fp, float_format);
printArray("ACTUATOR_MOMENT", m->nu, m->nv, d->actuator_moment, fp, float_format);
printArray("CRB", m->nbody, 10, d->crb, fp, float_format);
// construct and print full M matrix
mj_fullM(m, M, d->qM);
printArray("QM", m->nv, m->nv, M, fp, float_format);
// construct and print full LD matrix
mj_fullM(m, M, d->qLD);
printArray("QLD", m->nv, m->nv, M, fp, float_format);
printArray("QLDIAGINV", m->nv, 1, d->qLDiagInv, fp, float_format);
printArray("QLDIAGSQRTINV", m->nv, 1, d->qLDiagSqrtInv, fp, float_format);
// contact
fprintf(fp, "CONTACT\n");
for (int i=0; i<d->ncon; i++) {
fprintf(fp, " %d:\n dim %d\n geom %d %d\n",
i, d->contact[i].dim, d->contact[i].geom1, d->contact[i].geom2);
fprintf(fp, " exclude %d\n efc_address %d\n",
d->contact[i].exclude, d->contact[i].efc_address);
printVector(" solref ", d->contact[i].solref, mjNREF, fp, float_format);
printVector(" solimp ", d->contact[i].solimp, mjNIMP, fp, float_format);
printVector(" dist ", &d->contact[i].dist, 1, fp, float_format);
printVector(" includemargin", &d->contact[i].includemargin, 1, fp, float_format);
printVector(" pos ", d->contact[i].pos, 3, fp, float_format);
printVector(" frame ", d->contact[i].frame, 9, fp, float_format);
printVector(" friction ", d->contact[i].friction, 5, fp, float_format);
printVector(" mu ", &d->contact[i].mu, 1, fp, float_format);
}
if (d->ncon) fprintf(fp, "\n");
printArrayInt("EFC_TYPE", d->nefc, 1, d->efc_type, fp);
printArrayInt("EFC_ID", d->nefc, 1, d->efc_id, fp);
if (!mj_isSparse(m)) {
printArray("EFC_J", d->nefc, m->nv, d->efc_J, fp, float_format);
printArray("EFC_AR", d->nefc, d->nefc, d->efc_AR, fp, float_format);
} else {
printArrayInt("EFC_J_ROWNNZ", d->nefc, 1, d->efc_J_rownnz, fp);
printArrayInt("EFC_J_ROWADR", d->nefc, 1, d->efc_J_rowadr, fp);
printSparse("EFC_J", d->efc_J, d->nefc, d->efc_J_rownnz,
d->efc_J_rowadr, d->efc_J_colind, fp, float_format);
printArrayInt("EFC_AR_ROWNNZ", d->nefc, 1, d->efc_AR_rownnz, fp);
printArrayInt("EFC_AR_ROWADR", d->nefc, 1, d->efc_AR_rowadr, fp);
printSparse("EFC_AR", d->efc_AR, d->nefc, d->efc_AR_rownnz,
d->efc_AR_rowadr, d->efc_AR_colind, fp, float_format);
}
printArray("EFC_POS", d->nefc, 1, d->efc_pos, fp, float_format);
printArray("EFC_MARGIN", d->nefc, 1, d->efc_margin, fp, float_format);
printArray("EFC_FRICTIONLOSS", d->nefc, 1, d->efc_frictionloss, fp, float_format);
printArray("EFC_DIAGAPPROX", d->nefc, 1, d->efc_diagApprox, fp, float_format);
printArray("EFC_KBIP", d->nefc, 4, d->efc_KBIP, fp, float_format);
printArray("EFC_D", d->nefc, 1, d->efc_D, fp, float_format);
printArray("EFC_R", d->nefc, 1, d->efc_R, fp, float_format);
printArray("TEN_VELOCITY", m->ntendon, 1, d->ten_velocity, fp, float_format);
printArray("ACTUATOR_VELOCITY", m->nu, 1, d->actuator_velocity, fp, float_format);
printArray("CVEL", m->nbody, 6, d->cvel, fp, float_format);
printArray("CDOF_DOT", m->nv, 6, d->cdof_dot, fp, float_format);
printArray("QFRC_BIAS", m->nv, 1, d->qfrc_bias, fp, float_format);
printArray("QFRC_PASSIVE", m->nv, 1, d->qfrc_passive, fp, float_format);
printArray("EFC_VEL", d->nefc, 1, d->efc_vel, fp, float_format);
printArray("EFC_AREF", d->nefc, 1, d->efc_aref, fp, float_format);
printArray("SUBTREE_LINVEL", m->nbody, 3, d->subtree_linvel, fp, float_format);
printArray("SUBTREE_ANGMOM", m->nbody, 3, d->subtree_angmom, fp, float_format);
printArray("ACTUATOR_FORCE", m->nu, 1, d->actuator_force, fp, float_format);
printArray("QFRC_ACTUATOR", m->nv, 1, d->qfrc_actuator, fp, float_format);
printArray("QFRC_SMOOTH", m->nv, 1, d->qfrc_smooth, fp, float_format);
printArray("QACC_SMOOTH", m->nv, 1, d->qacc_smooth, fp, float_format);
printArray("EFC_B", d->nefc, 1, d->efc_b, fp, float_format);
printArray("EFC_FORCE", d->nefc, 1, d->efc_force, fp, float_format);
printArrayInt("EFC_STATE", d->nefc, 1, d->efc_state, fp);
printArray("QFRC_CONSTRAINT", m->nv, 1, d->qfrc_constraint, fp, float_format);
printArray("QFRC_INVERSE", m->nv, 1, d->qfrc_inverse, fp, float_format);
printArray("CACC", m->nbody, 6, d->cacc, fp, float_format);
printArray("CFRC_INT", m->nbody, 6, d->cfrc_int, fp, float_format);
printArray("CFRC_EXT", m->nbody, 6, d->cfrc_ext, fp, float_format);
if (filename) {
fclose(fp);
}
mjFREESTACK;
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
// print mjData to text file
void mj_printData(const mjModel* m, mjData* d, const char* filename) {
mj_printFormattedData(m, d, filename, FLOAT_FORMAT);
}
+47
View File
@@ -0,0 +1,47 @@
// 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_SRC_ENGINE_ENGINE_PRINT_H_
#define MUJOCO_SRC_ENGINE_ENGINE_PRINT_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
// print mjModel to text file, specifying format
// float_format must be a valid printf-style format string for a single float value
MJAPI void mj_printFormattedModel(const mjModel* m, const char* filename,
const char* float_format);
// print model and option to text file
MJAPI void mj_printModel(const mjModel* m, const char* filename);
// print mjData to text file, specifying format
// float_format must be a valid printf-style format string for a single float value
MJAPI void mj_printFormattedData(const mjModel* m, mjData* d, const char* filename,
const char* float_format);
// print data to text file
MJAPI void mj_printData(const mjModel* m, mjData* d, const char* filename);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_PRINT_H_
+821
View File
@@ -0,0 +1,821 @@
// 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.
//---------------------------------//
#include "engine/engine_ray.h"
#include <stddef.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mjvisualize.h>
#include "engine/engine_macro.h"
#include "engine/engine_util_blas.h"
#include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h"
#include "engine/engine_util_spatial.h"
//---------------------------- utility functions ---------------------------------------------------
// map ray to local geom frame
static void ray_map(const mjtNum* pos, const mjtNum* mat, const mjtNum* pnt, const mjtNum* vec,
mjtNum* lpnt, mjtNum* lvec) {
const mjtNum dif[3] = {pnt[0]-pos[0], pnt[1]-pos[1], pnt[2]-pos[2]};
// lpnt = mat' * dif
lpnt[0] = mat[0]*dif[0] + mat[3]*dif[1] + mat[6]*dif[2];
lpnt[1] = mat[1]*dif[0] + mat[4]*dif[1] + mat[7]*dif[2];
lpnt[2] = mat[2]*dif[0] + mat[5]*dif[1] + mat[8]*dif[2];
// lvec = mat' * vec
lvec[0] = mat[0]*vec[0] + mat[3]*vec[1] + mat[6]*vec[2];
lvec[1] = mat[1]*vec[0] + mat[4]*vec[1] + mat[7]*vec[2];
lvec[2] = mat[2]*vec[0] + mat[5]*vec[1] + mat[8]*vec[2];
}
// eliminate geom
static int ray_eliminate(const mjModel* m, const mjData* d, int geomid,
const mjtByte* geomgroup, mjtByte flg_static, int bodyexclude) {
// body exclusion
if (m->geom_bodyid[geomid]==bodyexclude) {
return 1;
}
// invisible geom exclusion
if (m->geom_matid[geomid]<0 && m->geom_rgba[4*geomid+3]==0) {
return 1;
}
// invisible material exclusion
if (m->geom_matid[geomid]>=0 && m->mat_rgba[4*m->geom_matid[geomid]+3]==0) {
return 1;
}
// static exclusion
if (!flg_static && m->geom_bodyid[geomid]==0) {
return 1;
}
// plane and hfield inclusion
if (m->geom_type[geomid]==mjGEOM_PLANE || m->geom_type[geomid]==mjGEOM_HFIELD) {
return 0;
}
// no geomgroup inclusion
if (!geomgroup) {
return 0;
}
// group inclusion/exclusion
int groupid = mjMIN(mjNGROUP-1, mjMAX(0, m->geom_group[geomid]));
return (geomgroup[groupid]==0);
}
// compute solution from quadratic: a*x^2 + 2*b*x + c = 0
static mjtNum ray_quad(mjtNum a, mjtNum b, mjtNum c, mjtNum* x) {
// compute determinant and check
mjtNum det = b*b - a*c;
if (det<mjMINVAL) {
x[0] = -1;
x[1] = -1;
return -1;
}
det = mju_sqrt(det);
// compute the two solutions
x[0] = (-b-det)/a;
x[1] = (-b+det)/a;
// finalize result
if (x[0]>=0) {
return x[0];
} else if (x[1]>=0) {
return x[1];
} else {
return -1;
}
}
// intersect ray with triangle
static mjtNum ray_triangle(mjtNum v[][3], const mjtNum* lpnt, const mjtNum* lvec,
const mjtNum* b0, const mjtNum* b1) {
// dif = v[i] - lpnt
mjtNum dif[3][3];
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++) {
dif[i][j] = v[i][j] - lpnt[j];
}
}
// project difference vectors in normal plane
mjtNum planar[3][2];
for (int i=0; i<3; i++) {
planar[i][0] = mju_dot3(b0, dif[i]);
planar[i][1] = mju_dot3(b1, dif[i]);
}
// reject if on the same side of any coordinate axis
if ((planar[0][0]>0 && planar[1][0]>0 && planar[2][0]>0) ||
(planar[0][0]<0 && planar[1][0]<0 && planar[2][0]<0) ||
(planar[0][1]>0 && planar[1][1]>0 && planar[2][1]>0) ||
(planar[0][1]<0 && planar[1][1]<0 && planar[2][1]<0)) {
return -1;
}
// determine if origin is inside planar projection of triangle
// A = (p0-p2, p1-p2), b = -p2, solve A*t = b
mjtNum A[4] = {planar[0][0]-planar[2][0], planar[1][0]-planar[2][0],
planar[0][1]-planar[2][1], planar[1][1]-planar[2][1]};
mjtNum b[2] = {-planar[2][0], -planar[2][1]};
mjtNum det = A[0]*A[3] - A[1]*A[2];
if (mju_abs(det)<mjMINVAL) {
return -1;
}
mjtNum t0 = (A[3]*b[0] - A[1]*b[1]) / det;
mjtNum t1 = (-A[2]*b[0] + A[0]*b[1]) / det;
// check if outside
if (t0<0 || t1<0|| t0+t1>1) {
return -1;
}
// intersect ray with plane of triangle
mju_sub3(dif[0], v[0], v[2]); // v0-v2
mju_sub3(dif[1], v[1], v[2]); // v1-v2
mju_sub3(dif[2], lpnt, v[2]); // lp-v2
mjtNum nrm[3];
mju_cross(nrm, dif[0], dif[1]); // normal to triangle plane
mjtNum denom = mju_dot3(lvec, nrm);
if (mju_abs(denom)<mjMINVAL) {
return -1;
}
return (-mju_dot3(dif[2], nrm) / denom);
}
//---------------------------- geom-specific intersection functions --------------------------------
// plane
static mjtNum ray_plane(const mjtNum* pos, const mjtNum* mat, const mjtNum* size,
const mjtNum* pnt, const mjtNum* vec) {
// map to local frame
mjtNum lpnt[3], lvec[3];
ray_map(pos, mat, pnt, vec, lpnt, lvec);
// z-vec not pointing towards front face: reject
if (lvec[2]>-mjMINVAL) {
return -1;
}
// intersection with plane
const mjtNum x = -lpnt[2]/lvec[2];
if (x<0) {
return -1;
}
mjtNum p0 = lpnt[0] + x*lvec[0];
mjtNum p1 = lpnt[1] + x*lvec[1];
// accept only within rendered rectangle
if ((size[0]<=0 || mju_abs(p0)<=size[0]) &&
(size[1]<=0 || mju_abs(p1)<=size[1])) {
return x;
} else {
return -1;
}
}
// sphere
static mjtNum ray_sphere(const mjtNum* pos, const mjtNum* mat, const mjtNum* size,
const mjtNum* pnt, const mjtNum* vec) {
// (x*vec+pnt-pos)'*(x*vec+pnt-pos) = size[0]*size[0]
mjtNum dif[3] = {pnt[0]-pos[0], pnt[1]-pos[1], pnt[2]-pos[2]};
mjtNum a = vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2];
mjtNum b = vec[0]*dif[0] + vec[1]*dif[1] + vec[2]*dif[2];
mjtNum c = dif[0]*dif[0] + dif[1]*dif[1] + dif[2]*dif[2] - size[0]*size[0];
// solve a*x^2 + 2*b*x + c = 0
mjtNum xx[2];
return ray_quad(a, b, c, xx);
}
// capsule
static mjtNum ray_capsule(const mjtNum* pos, const mjtNum* mat, const mjtNum* size,
const mjtNum* pnt, const mjtNum* vec) {
// bounding sphere test
mjtNum ssz = size[0] + size[1];
if (ray_sphere(pos, NULL, &ssz, pnt, vec)<0) {
return -1;
}
// map to local frame
mjtNum lpnt[3], lvec[3];
ray_map(pos, mat, pnt, vec, lpnt, lvec);
// init solution
mjtNum x = -1, sol, xx[2];
// cylinder round side: (x*lvec+lpnt)'*(x*lvec+lpnt) = size[0]*size[0]
mjtNum a = lvec[0]*lvec[0] + lvec[1]*lvec[1];
mjtNum b = lvec[0]*lpnt[0] + lvec[1]*lpnt[1];
mjtNum c = lpnt[0]*lpnt[0] + lpnt[1]*lpnt[1] - size[0]*size[0];
// solve a*x^2 + 2*b*x + c = 0
sol = ray_quad(a, b, c, xx);
// make sure round solution is between flat sides
if (sol>=0 && mju_abs(lpnt[2]+sol*lvec[2])<=size[1]) {
if (x<0 || sol<x) {
x = sol;
}
}
// top cap
mjtNum ldif[3] = {lpnt[0], lpnt[1], lpnt[2]-size[1]};
a = lvec[0]*lvec[0] + lvec[1]*lvec[1] + lvec[2]*lvec[2];
b = lvec[0]*ldif[0] + lvec[1]*ldif[1] + lvec[2]*ldif[2];
c = ldif[0]*ldif[0] + ldif[1]*ldif[1] + ldif[2]*ldif[2] - size[0]*size[0];
ray_quad(a, b, c, xx);
// accept only top half of sphere
for (int i=0; i<2; i++) {
if (xx[i]>=0 && lpnt[2]+xx[i]*lvec[2]>=size[1]) {
if (x<0 || xx[i]<x) {
x = xx[i];
}
}
}
// bottom cap
ldif[2] = lpnt[2]+size[1];
b = lvec[0]*ldif[0] + lvec[1]*ldif[1] + lvec[2]*ldif[2];
c = ldif[0]*ldif[0] + ldif[1]*ldif[1] + ldif[2]*ldif[2] - size[0]*size[0];
ray_quad(a, b, c, xx);
// accept only bottom half of sphere
for (int i=0; i<2; i++) {
if (xx[i]>=0 && lpnt[2]+xx[i]*lvec[2]<=-size[1]) {
if (x<0 || xx[i]<x) {
x = xx[i];
}
}
}
return x;
}
// ellipsoid
static mjtNum ray_ellipsoid(const mjtNum* pos, const mjtNum* mat, const mjtNum* size,
const mjtNum* pnt, const mjtNum* vec) {
// map to local frame
mjtNum lpnt[3], lvec[3];
ray_map(pos, mat, pnt, vec, lpnt, lvec);
// invert size^2
mjtNum s[3] = {1/(size[0]*size[0]), 1/(size[1]*size[1]), 1/(size[2]*size[2])};
// (x*lvec+lpnt)' * diag(1./size^2) * (x*lvec+lpnt) = 1
mjtNum a = s[0]*lvec[0]*lvec[0] + s[1]*lvec[1]*lvec[1] + s[2]*lvec[2]*lvec[2];
mjtNum b = s[0]*lvec[0]*lpnt[0] + s[1]*lvec[1]*lpnt[1] + s[2]*lvec[2]*lpnt[2];
mjtNum c = s[0]*lpnt[0]*lpnt[0] + s[1]*lpnt[1]*lpnt[1] + s[2]*lpnt[2]*lpnt[2] - 1;
// solve a*x^2 + 2*b*x + c = 0
mjtNum xx[2];
return ray_quad(a, b, c, xx);
}
// cylinder
static mjtNum ray_cylinder(const mjtNum* pos, const mjtNum* mat, const mjtNum* size,
const mjtNum* pnt, const mjtNum* vec) {
// bounding sphere test
mjtNum ssz = mju_sqrt(size[0]*size[0] + size[1]*size[1]);
if (ray_sphere(pos, NULL, &ssz, pnt, vec)<0) {
return -1;
}
// map to local frame
mjtNum lpnt[3], lvec[3];
ray_map(pos, mat, pnt, vec, lpnt, lvec);
// init solution
mjtNum x = -1, sol;
// flat sides
int side;
if (mju_abs(lvec[2])>mjMINVAL) {
for (side=-1; side<=1; side+=2) {
// soludion of: lpnt[2] + x*lvec[2] = side*height_size
sol = (side*size[1]-lpnt[2])/lvec[2];
// process if non-negative
if (sol>=0) {
// intersection with horizontal face
mjtNum p0 = lpnt[0] + sol*lvec[0];
mjtNum p1 = lpnt[1] + sol*lvec[1];
// accept within radius
if (p0*p0 + p1*p1 <= size[0]*size[0]) {
if (x<0 || sol<x) {
x = sol;
}
}
}
}
}
// (x*lvec+lpnt)'*(x*lvec+lpnt) = size[0]*size[0]
mjtNum a = lvec[0]*lvec[0] + lvec[1]*lvec[1];
mjtNum b = lvec[0]*lpnt[0] + lvec[1]*lpnt[1];
mjtNum c = lpnt[0]*lpnt[0] + lpnt[1]*lpnt[1] - size[0]*size[0];
// solve a*x^2 + 2*b*x + c = 0
mjtNum xx[2];
sol = ray_quad(a, b, c, xx);
// make sure round solution is between flat sides
if (sol>=0 && mju_abs(lpnt[2]+sol*lvec[2])<=size[1]) {
if (x<0 || sol<x) {
x = sol;
}
}
return x;
}
// box
static mjtNum ray_box(const mjtNum* pos, const mjtNum* mat, const mjtNum* size,
const mjtNum* pnt, const mjtNum* vec, mjtNum* all) {
// clear all
if (all) {
for (int i=0; i<6; i++) {
all[i] = -1;
}
}
// bounding sphere test
mjtNum ssz = mju_sqrt(size[0]*size[0] + size[1]*size[1] + size[2]*size[2]);
if (ray_sphere(pos, NULL, &ssz, pnt, vec)<0) {
return -1;
}
// faces
const int iface[3][2] = {
{1, 2},
{0, 2},
{0, 1}
};
// map to local frame
mjtNum lpnt[3], lvec[3];
ray_map(pos, mat, pnt, vec, lpnt, lvec);
// init solution
mjtNum x = -1, sol;
// loop over axes with non-zero vec
for (int i=0; i<3; i++) {
if (mju_abs(lvec[i])>mjMINVAL) {
for (int side=-1; side<=1; side+=2) {
// soludion of: lpnt[i] + x*lvec[i] = side*size[i]
sol = (side*size[i]-lpnt[i])/lvec[i];
// process if non-negative
if (sol>=0) {
// intersection with face
mjtNum p0 = lpnt[iface[i][0]] + sol*lvec[iface[i][0]];
mjtNum p1 = lpnt[iface[i][1]] + sol*lvec[iface[i][1]];
// accept within rectangle
if (mju_abs(p0)<=size[iface[i][0]] &&
mju_abs(p1)<=size[iface[i][1]]) {
// update
if (x<0 || sol<x) {
x = sol;
}
// save in all
if (all) {
all[2*i+(side+1)/2] = sol;
}
}
}
}
}
}
return x;
}
// interect ray with hfield
mjtNum mj_rayHfield(const mjModel* m, const mjData* d, int id,
const mjtNum* pnt, const mjtNum* vec) {
// check geom type
if (m->geom_type[id]!=mjGEOM_HFIELD) {
mju_error("mj_rayHfield: geom with hfield type expected");
}
// hfield id and dimensions
int hid = m->geom_dataid[id];
int nrow = m->hfield_nrow[hid];
int ncol = m->hfield_ncol[hid];
const mjtNum* size = m->hfield_size + 4*hid;
const float* data = m->hfield_data + m->hfield_adr[hid];
// compute size and pos of base box
mjtNum base_size[3] = {size[0], size[1], size[3]*0.5};
mjtNum base_pos[3] = {
d->geom_xpos[3*id] - d->geom_xmat[9*id+2]*size[3]*0.5,
d->geom_xpos[3*id+1] - d->geom_xmat[9*id+5]*size[3]*0.5,
d->geom_xpos[3*id+2] - d->geom_xmat[9*id+8]*size[3]*0.5
};
// compute size and pos of top box
mjtNum top_size[3] = {size[0], size[1], size[2]*0.5};
mjtNum top_pos[3] = {
d->geom_xpos[3*id] + d->geom_xmat[9*id+2]*size[2]*0.5,
d->geom_xpos[3*id+1] + d->geom_xmat[9*id+5]*size[2]*0.5,
d->geom_xpos[3*id+2] + d->geom_xmat[9*id+8]*size[2]*0.5
};
// init: intersection with base box
mjtNum x = ray_box(base_pos, d->geom_xmat+9*id, base_size, pnt, vec, NULL);
// check top box: done if no intersection
mjtNum all[6];
mjtNum top_intersect = ray_box(top_pos, d->geom_xmat+9*id, top_size, pnt, vec, all);
if (top_intersect<0) {
return x;
}
// map to local frame
mjtNum lpnt[3], lvec[3];
ray_map(d->geom_xpos+3*id, d->geom_xmat+9*id, pnt, vec, lpnt, lvec);
// construct basis vectors of normal plane
mjtNum b0[3] = {1, 1, 1}, b1[3];
if (mju_abs(lvec[0])>=mju_abs(lvec[1]) && mju_abs(lvec[0])>=mju_abs(lvec[2])) {
b0[0] = 0;
} else if (mju_abs(lvec[1])>=mju_abs(lvec[2])) {
b0[1] = 0;
} else {
b0[2] = 0;
}
mju_addScl3(b1, b0, lvec, -mju_dot3(lvec, b0)/mju_dot3(lvec, lvec));
mju_normalize3(b1);
mju_cross(b0, b1, lvec);
mju_normalize3(b0);
// find ray segment intersecting top box
mjtNum seg[2] = {0, top_intersect};
for (int i=0; i<6; i++) {
if (all[i]>seg[1]) {
seg[0] = top_intersect;
seg[1] = all[i];
}
}
// project segment endpoints in horizontal plane, discretize
mjtNum dx = (2.0*size[0]) / (ncol-1);
mjtNum dy = (2.0*size[1]) / (nrow-1);
mjtNum SX[2], SY[2];
for (int i=0; i<2; i++) {
SX[i] = (lpnt[0] + seg[i]*lvec[0] + size[0]) / dx;
SY[i] = (lpnt[1] + seg[i]*lvec[1] + size[1]) / dy;
}
// compute ranges, with +1 padding
int cmin = mjMAX(0, (int)mju_floor(mjMIN(SX[0], SX[1]))-1);
int cmax = mjMIN(ncol-1, (int)mju_ceil(mjMAX(SX[0], SX[1]))+1);
int rmin = mjMAX(0, (int)mju_floor(mjMIN(SY[0], SY[1]))-1);
int rmax = mjMIN(nrow-1, (int)mju_ceil(mjMAX(SY[0], SY[1]))+1);
// check triangles within bounds
for (int r=rmin; r<rmax; r++) {
for (int c=cmin; c<cmax; c++) {
// first triangle
mjtNum va[3][3] = {
{dx*c-size[0], dy*r-size[1], data[r*ncol+c]*size[2]},
{dx*(c+1)-size[0], dy*(r+1)-size[1], data[(r+1)*ncol+(c+1)]*size[2]},
{dx*(c+1)-size[0], dy*r-size[1], data[r*ncol+(c+1)]*size[2]}
};
mjtNum sol = ray_triangle(va, lpnt, lvec, b0, b1);
if (sol>=0 && (x<0 || sol<x)) {
x = sol;
}
// second triangle
mjtNum vb[3][3] = {
{dx*c-size[0], dy*r-size[1], data[r*ncol+c]*size[2]},
{dx*(c+1)-size[0], dy*(r+1)-size[1], data[(r+1)*ncol+(c+1)]*size[2]},
{dx*c-size[0], dy*(r+1)-size[1], data[(r+1)*ncol+c]*size[2]}
};
sol = ray_triangle(vb, lpnt, lvec, b0, b1);
if (sol>=0 && (x<0 || sol<x)) {
x = sol;
}
}
}
// check viable sides of top box
for (int i=0; i<4; i++) {
if (all[i]>=0 && (all[i]<x || x<0)) {
// normalized height of intersection point
mjtNum z = (lpnt[2] + all[i]*lvec[2]) / size[2];
// rectangle points
mjtNum y, y0, z0, z1;
// side normal to x-axis
if (i<2) {
y = (lpnt[1] + all[i]*lvec[1] + size[1]) / dy;
y0 = mjMAX(0, mjMIN(nrow-2, mju_floor(y)));
z0 = (mjtNum)data[mju_round(y0)*nrow + (i==1 ? ncol-1 : 0)];
z1 = (mjtNum)data[mju_round(y0+1)*nrow + (i==1 ? ncol-1 : 0)];
}
// side normal to y-axis
else {
y = (lpnt[0] + all[i]*lvec[0] + size[0]) / dx;
y0 = mjMAX(0, mjMIN(ncol-2, mju_floor(y)));
z0 = (mjtNum)data[mju_round(y0) + (i==3 ? (nrow-1)*ncol : 0)];
z1 = (mjtNum)data[mju_round(y0+1) + (i==3 ? (nrow-1)*ncol : 0)];
}
// check if point is below line segment
if (z < z0*(y0+1-y) + z1*(y-y0)) {
x = all[i];
}
}
}
return x;
}
// interect ray with mesh
mjtNum mj_rayMesh(const mjModel* m, const mjData* d, int id,
const mjtNum* pnt, const mjtNum* vec) {
// check geom type
if (m->geom_type[id]!=mjGEOM_MESH) {
mju_error("mj_rayMesh: geom with mesh type expected");
}
// bounding box test
if (ray_box(d->geom_xpos+3*id, d->geom_xmat+9*id, m->geom_size+3*id, pnt, vec, NULL)<0) {
return -1;
}
// map to local frame
mjtNum lpnt[3], lvec[3];
ray_map(d->geom_xpos+3*id, d->geom_xmat+9*id, pnt, vec, lpnt, lvec);
// construct basis vectors of normal plane
mjtNum b0[3] = {1, 1, 1}, b1[3];
if (mju_abs(lvec[0])>=mju_abs(lvec[1]) && mju_abs(lvec[0])>=mju_abs(lvec[2])) {
b0[0] = 0;
} else if (mju_abs(lvec[1])>=mju_abs(lvec[2])) {
b0[1] = 0;
} else {
b0[2] = 0;
}
mju_addScl3(b1, b0, lvec, -mju_dot3(lvec, b0)/mju_dot3(lvec, lvec));
mju_normalize3(b1);
mju_cross(b0, b1, lvec);
mju_normalize3(b0);
// init solution
mjtNum x = -1, sol;
// process all triangles
int face, meshid = m->geom_dataid[id];
for (face = m->mesh_faceadr[meshid];
face < m->mesh_faceadr[meshid] + m->mesh_facenum[meshid];
face++) {
// get float vertices
float* vf[3];
vf[0] = m->mesh_vert + 3*(m->mesh_face[3*face] + m->mesh_vertadr[meshid]);
vf[1] = m->mesh_vert + 3*(m->mesh_face[3*face+1] + m->mesh_vertadr[meshid]);
vf[2] = m->mesh_vert + 3*(m->mesh_face[3*face+2] + m->mesh_vertadr[meshid]);
// convert to mjtNum
mjtNum v[3][3];
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++) {
v[i][j] = (mjtNum)vf[i][j];
}
}
// solve
sol = ray_triangle(v, lpnt, lvec, b0, b1);
// update
if (sol>=0 && (x<0 || sol<x)) {
x = sol;
}
}
return x;
}
// interect ray with pure geom, no meshes or hfields
mjtNum mju_rayGeom(const mjtNum* pos, const mjtNum* mat, const mjtNum* size,
const mjtNum* pnt, const mjtNum* vec, int geomtype) {
switch (geomtype) {
case mjGEOM_PLANE:
return ray_plane(pos, mat, size, pnt, vec);
case mjGEOM_SPHERE:
return ray_sphere(pos, mat, size, pnt, vec);
case mjGEOM_CAPSULE:
return ray_capsule(pos, mat, size, pnt, vec);
case mjGEOM_ELLIPSOID:
return ray_ellipsoid(pos, mat, size, pnt, vec);
case mjGEOM_CYLINDER:
return ray_cylinder(pos, mat, size, pnt, vec);
case mjGEOM_BOX:
return ray_box(pos, mat, size, pnt, vec, NULL);
default:
mju_error_i("mju_rayGeom: unexpected geom type %d", geomtype);
return -1;
}
}
// interect ray with skin, return nearest vertex id
mjtNum mju_raySkin(int nface, int nvert, const int* face, const float* vert,
const mjtNum* pnt, const mjtNum* vec, int vertid[1]) {
// compute bounding box
mjtNum box[3][2] = {{0, 0}, {0, 0}, {0, 0}};
for (int i=0; i<nvert; i++) {
for (int j=0; j<3; j++) {
// update minimum along side j
if (box[j][0]>vert[3*i+j] || i==0) {
box[j][0] = vert[3*i+j];
}
// update maximum along side j
if (box[j][1]<vert[3*i+j] || i==0) {
box[j][1] = vert[3*i+j];
}
}
}
// construct box geom
mjtNum pos[3], size[3], mat[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1};
for (int j=0; j<3; j++) {
pos[j] = 0.5*(box[j][0]+box[j][1]);
size[j] = 0.5*(box[j][1]-box[j][0]);
}
// apply bounding-box filter
if (ray_box(pos, mat, size, pnt, vec, NULL)<0) {
return -1;
}
// construct basis vectors of normal plane
mjtNum b0[3] = {1, 1, 1}, b1[3];
if (mju_abs(vec[0])>=mju_abs(vec[1]) && mju_abs(vec[0])>=mju_abs(vec[2])) {
b0[0] = 0;
} else if (mju_abs(vec[1])>=mju_abs(vec[2])) {
b0[1] = 0;
} else {
b0[2] = 0;
}
mju_addScl3(b1, b0, vec, -mju_dot3(vec, b0)/mju_dot3(vec, vec));
mju_normalize3(b1);
mju_cross(b0, b1, vec);
mju_normalize3(b0);
// init solution
mjtNum x = -1, sol;
// process all faces
for (int i=0; i<nface; i++) {
// get float vertices
const float* vf[3];
vf[0] = vert + 3*(face[3*i]);
vf[1] = vert + 3*(face[3*i+1]);
vf[2] = vert + 3*(face[3*i+2]);
// convert to mjtNum
mjtNum v[3][3];
for (int j=0; j<3; j++) {
for (int k=0; k<3; k++) {
v[j][k] = (mjtNum)vf[j][k];
}
}
// solve
sol = ray_triangle(v, pnt, vec, b0, b1);
// update
if (sol>=0 && (x<0 || sol<x)) {
x = sol;
// construct intersection point
mjtNum intersect[3];
mju_addScl3(intersect, pnt, vec, sol);
// find nearest vertex
mjtNum dist = mju_dist3(intersect, v[0]);
*vertid = face[3*i];
for (int j=1; j<3; j++) {
mjtNum newdist = mju_dist3(intersect, v[j]);
if (newdist<dist) {
dist = newdist;
*vertid = face[3*i+j];
}
}
}
}
return x;
}
//---------------------------- main entry point ---------------------------------------------------
// intersect ray (pnt+x*vec, x>=0) with visible geoms, except geoms on bodyexclude
// return geomid and distance (x) to nearest surface, or -1 if no intersection
// geomgroup, flg_static are as in mjvOption; geomgroup==NULL skips group exclusion
mjtNum mj_ray(const mjModel* m, const mjData* d, const mjtNum* pnt, const mjtNum* vec,
const mjtByte* geomgroup, mjtByte flg_static, int bodyexclude,
int geomid[1]) {
mjtNum dist, newdist;
// check vector length
if (mju_norm3(vec)<mjMINVAL) {
mju_error("mj_ray: vector length is too small");
}
// clear result
dist = -1;
*geomid = -1;
// loop over geoms not eliminated by mask and bodyexclude
for (int i=0; i<m->ngeom; i++) {
if (!ray_eliminate(m, d, i, geomgroup, flg_static, bodyexclude)) {
// handle mesh and hfield separately
if (m->geom_type[i]==mjGEOM_MESH) {
newdist = mj_rayMesh(m, d, i, pnt, vec);
} else if (m->geom_type[i]==mjGEOM_HFIELD) {
newdist = mj_rayHfield(m, d, i, pnt, vec);
}
// otherwise general dispatch
else {
newdist = mju_rayGeom(d->geom_xpos+3*i, d->geom_xmat+9*i,
m->geom_size+3*i, pnt, vec, m->geom_type[i]);
}
// update if closer intersection found
if (newdist>=0 && (newdist<dist || dist<0)) {
dist = newdist;
*geomid = i;
}
}
}
return dist;
}
+53
View File
@@ -0,0 +1,53 @@
// 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_SRC_ENGINE_ENGINE_RAY_H_
#define MUJOCO_SRC_ENGINE_ENGINE_RAY_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
// intersect ray (pnt+x*vec, x>=0) with visible geoms, except geoms on bodyexclude
// return geomid and distance (x) to nearest surface, or -1 if no intersection
// geomgroup, flg_static are as in mjvOption; geomgroup==NULL skips group exclusion
MJAPI mjtNum mj_ray(const mjModel* m, const mjData* d, const mjtNum* pnt, const mjtNum* vec,
const mjtByte* geomgroup, mjtByte flg_static, int bodyexclude,
int geomid[1]);
// interect ray with hfield
MJAPI mjtNum mj_rayHfield(const mjModel* m, const mjData* d, int geomid,
const mjtNum* pnt, const mjtNum* vec);
// interect ray with mesh
MJAPI mjtNum mj_rayMesh(const mjModel* m, const mjData* d, int geomid,
const mjtNum* pnt, const mjtNum* vec);
// interect ray with pure geom, no meshes or hfields
MJAPI mjtNum mju_rayGeom(const mjtNum* pos, const mjtNum* mat, const mjtNum* size,
const mjtNum* pnt, const mjtNum* vec, int geomtype);
// interect ray with skin, return nearest vertex id
MJAPI mjtNum mju_raySkin(int nface, int nvert, const int* face, const float* vert,
const mjtNum* pnt, const mjtNum* vec, int vertid[1]);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_RAY_H_
+747
View File
@@ -0,0 +1,747 @@
// 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.
#include "engine/engine_sensor.h"
#include <stddef.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
#include "engine/engine_callback.h"
#include "engine/engine_core_smooth.h"
#include "engine/engine_io.h"
#include "engine/engine_macro.h"
#include "engine/engine_ray.h"
#include "engine/engine_support.h"
#include "engine/engine_util_blas.h"
#include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h"
#include "engine/engine_util_spatial.h"
//-------------------------------- utility ---------------------------------------------------------
// add sensor noise after each stage
static void add_noise(const mjModel* m, mjData* d, mjtStage stage) {
int adr, dim;
mjtNum rnd[4], noise, quat[4], res[4];
// process sensors matching stage and having positive noise
for (int i=0; i<m->nsensor; i++) {
if (m->sensor_needstage[i]==stage && m->sensor_noise[i]>0) {
// get sensor info
adr = m->sensor_adr[i];
dim = m->sensor_dim[i];
noise = m->sensor_noise[i];
// real or positive: add noise directly, with clamp for positive
if (m->sensor_datatype[i]==mjDATATYPE_REAL ||
m->sensor_datatype[i]==mjDATATYPE_POSITIVE)
for (int j=0; j<dim; j++) {
// get random numbers; use only the first one
rnd[0] = mju_standardNormal(rnd+1);
// positive
if (m->sensor_datatype[i]==mjDATATYPE_POSITIVE) {
// add noise only if positive, keep it positive
if (d->sensordata[adr+j]>0) {
d->sensordata[adr+j] = mjMAX(0, d->sensordata[adr+j]+rnd[0]*noise);
}
}
// real
else {
d->sensordata[adr+j] += rnd[0]*noise;
}
}
// axis or quat: rotate around random axis by random angle
else {
// get four random numbers
rnd[0] = mju_standardNormal(rnd+1);
rnd[2] = mju_standardNormal(rnd+3);
// scale angle, normalize axis, make quaterion
rnd[0] *= noise;
mju_normalize3(rnd+1);
mju_axisAngle2Quat(quat, rnd+1, rnd[0]);
// axis
if (m->sensor_datatype[i]==mjDATATYPE_AXIS) {
// apply quaternion rotation to axis, assign
mju_rotVecQuat(res, d->sensordata+adr, quat);
mju_copy3(d->sensordata+adr, res);
}
// quaternion
else if (m->sensor_datatype[i]==mjDATATYPE_QUATERNION) {
// apply quaternion rotation to quaternion, assign
mju_mulQuat(res, d->sensordata+adr, quat);
mju_copy4(d->sensordata+adr, res);
}
// unknown datatype
else {
mju_error_i("Unknown datatype in sensor %d", i);
}
}
}
}
}
// apply cutoff after each stage
static void apply_cutoff(const mjModel* m, mjData* d, mjtStage stage) {
// process sensors matching stage and having positive cutoff
for (int i=0; i<m->nsensor; i++) {
if (m->sensor_needstage[i]==stage && m->sensor_cutoff[i]>0) {
// get sensor info
int adr = m->sensor_adr[i];
int dim = m->sensor_dim[i];
mjtNum cutoff = m->sensor_cutoff[i];
// process all dimensions
for (int j=0; j<dim; j++)
// real: apply on both sides
if (m->sensor_datatype[i]==mjDATATYPE_REAL)
d->sensordata[adr+j] =
mju_min(cutoff, mju_max(-cutoff, d->sensordata[adr+j]));
// positive: apply on positive side only
else if (m->sensor_datatype[i]==mjDATATYPE_POSITIVE)
d->sensordata[adr+j] =
mju_min(cutoff, d->sensordata[adr+j]);
}
}
}
// get xpos and xmat pointers to an object in mjData
static void get_xpos_xmat(const mjData* d, int type, int id, int sensor_id,
mjtNum **xpos, mjtNum **xmat) {
switch (type) {
case mjOBJ_XBODY:
*xpos = d->xpos + 3*id;
*xmat = d->xmat + 9*id;
break;
case mjOBJ_BODY:
*xpos = d->xipos + 3*id;
*xmat = d->ximat + 9*id;
break;
case mjOBJ_GEOM:
*xpos = d->geom_xpos + 3*id;
*xmat = d->geom_xmat + 9*id;
break;
case mjOBJ_SITE:
*xpos = d->site_xpos + 3*id;
*xmat = d->site_xmat + 9*id;
break;
case mjOBJ_CAMERA:
*xpos = d->cam_xpos + 3*id;
*xmat = d->cam_xmat + 9*id;
break;
default:
mju_error_i("Invalid object type in sensor %d", sensor_id);
}
}
// get global quaternion of an object in mjData
static void get_xquat(const mjModel* m, const mjData* d, int type, int id, int sensor_id,
mjtNum *quat) {
switch (type) {
case mjOBJ_XBODY:
mju_copy4(quat, d->xquat+4*id);
break;
case mjOBJ_BODY:
mju_mulQuat(quat, d->xquat+4*id, m->body_iquat+4*id);
break;
case mjOBJ_GEOM:
mju_mulQuat(quat, d->xquat+4*m->geom_bodyid[id], m->geom_quat+4*id);
break;
case mjOBJ_SITE:
mju_mulQuat(quat, d->xquat+4*m->site_bodyid[id], m->site_quat+4*id);
break;
case mjOBJ_CAMERA:
mju_mulQuat(quat, d->xquat+4*m->cam_bodyid[id], m->cam_quat+4*id);
break;
default:
mju_error_i("Invalid object type in sensor %d", sensor_id);
}
}
//-------------------------------- sensor ----------------------------------------------------------
// position-dependent sensors
void mj_sensorPos(const mjModel* m, mjData* d) {
int rgeomid, objtype, objid, reftype, refid, adr, offset, nusersensor = 0;
int ne = d->ne, nf = d->nf, nefc = d->nefc;
mjtNum rvec[3], *xpos, *xmat, *xpos_ref, *xmat_ref;
// process sensors matching stage
for (int i=0; i<m->nsensor; i++) {
if (m->sensor_needstage[i]==mjSTAGE_POS) {
// get sensor info
objtype = m->sensor_objtype[i];
objid = m->sensor_objid[i];
refid = m->sensor_refid[i];
reftype = m->sensor_reftype[i];
adr = m->sensor_adr[i];
// process according to type
switch (m->sensor_type[i]) {
case mjSENS_MAGNETOMETER: // magnetometer
mju_mulMatTVec(d->sensordata+adr, d->site_xmat+9*objid, m->opt.magnetic, 3, 3);
break;
case mjSENS_RANGEFINDER: // rangefinder
rvec[0] = d->site_xmat[9*objid+2];
rvec[1] = d->site_xmat[9*objid+5];
rvec[2] = d->site_xmat[9*objid+8];
d->sensordata[adr] = mj_ray(m, d, d->site_xpos+3*objid, rvec, NULL, 1,
m->site_bodyid[objid], &rgeomid);
break;
case mjSENS_JOINTPOS: // jointpos
d->sensordata[adr] = d->qpos[m->jnt_qposadr[objid]];
break;
case mjSENS_TENDONPOS: // tendonpos
d->sensordata[adr] = d->ten_length[objid];
break;
case mjSENS_ACTUATORPOS: // actuatorpos
d->sensordata[adr] = d->actuator_length[objid];
break;
case mjSENS_BALLQUAT: // ballquat
mju_copy4(d->sensordata+adr, d->qpos+m->jnt_qposadr[objid]);
break;
case mjSENS_JOINTLIMITPOS: // jointlimitpos
d->sensordata[adr] = 0;
for (int j=ne+nf; j<nefc; j++) {
if (d->efc_type[j]==mjCNSTR_LIMIT_JOINT && d->efc_id[j]==objid) {
d->sensordata[adr] = d->efc_pos[j] - d->efc_margin[j];
break;
}
}
break;
case mjSENS_TENDONLIMITPOS: // tendonlimitpos
d->sensordata[adr] = 0;
for (int j=ne+nf; j<nefc; j++) {
if (d->efc_type[j]==mjCNSTR_LIMIT_TENDON && d->efc_id[j]==objid) {
d->sensordata[adr] = d->efc_pos[j] - d->efc_margin[j];
break;
}
}
break;
case mjSENS_FRAMEPOS: // framepos
case mjSENS_FRAMEXAXIS: // framexaxis
case mjSENS_FRAMEYAXIS: // frameyaxis
case mjSENS_FRAMEZAXIS: // framezaxis
// get xpos and xmat pointers for object frame
get_xpos_xmat(d, objtype, objid, i, &xpos, &xmat);
// reference frame unspecified: global frame
if (refid == -1) {
if (m->sensor_type[i]==mjSENS_FRAMEPOS) {
mju_copy3(d->sensordata+adr, xpos);
} else {
// offset = (0 or 1 or 2) for (x or y or z)-axis sensors, respectively
offset = m->sensor_type[i] - mjSENS_FRAMEXAXIS;
d->sensordata[adr] = xmat[offset];
d->sensordata[adr+1] = xmat[offset+3];
d->sensordata[adr+2] = xmat[offset+6];
}
}
// reference frame specified
else {
get_xpos_xmat(d, reftype, refid, i, &xpos_ref, &xmat_ref);
if (m->sensor_type[i]==mjSENS_FRAMEPOS) {
mju_sub3(rvec, xpos, xpos_ref);
mju_rotVecMatT(d->sensordata+adr, rvec, xmat_ref);
} else {
// offset = (0 or 1 or 2) for (x or y or z)-axis sensors, respectively
offset = m->sensor_type[i] - mjSENS_FRAMEXAXIS;
mjtNum axis[3] = {xmat[offset], xmat[offset+3], xmat[offset+6]};
mju_rotVecMatT(d->sensordata+adr, axis, xmat_ref);
}
}
break;
case mjSENS_FRAMEQUAT: // framequat
{
// get global object quaternion
mjtNum objquat[4];
get_xquat(m, d, objtype, objid, i, objquat);
// reference frame unspecified: copy object quaternion
if (refid == -1) {
mju_copy4(d->sensordata+adr, objquat);
} else {
// reference frame specified, get global reference quaternion
mjtNum refquat[4];
get_xquat(m, d, reftype, refid, i, refquat);
// relative quaternion
mju_negQuat(refquat, refquat);
mju_mulQuat(d->sensordata+adr, refquat, objquat);
}
}
break;
case mjSENS_SUBTREECOM: // subtreecom
mju_copy3(d->sensordata+adr, d->subtree_com+3*objid);
break;
case mjSENS_USER: // user
nusersensor++;
break;
default:
mju_error_i("Invalid sensor type in POS stage, sensor %d", i);
}
}
}
// fill in user sensors if detected
if (nusersensor && mjcb_sensor) {
mjcb_sensor(m, d, mjSTAGE_POS);
}
// add noise if enabled
if (mjENABLED(mjENBL_SENSORNOISE)) {
add_noise(m, d, mjSTAGE_POS);
}
// cutoff
apply_cutoff(m, d, mjSTAGE_POS);
}
// velocity-dependent sensors
void mj_sensorVel(const mjModel* m, mjData* d) {
int type, objtype, objid, reftype, refid, adr, nusersensor = 0;
int ne = d->ne, nf = d->nf, nefc = d->nefc;
mjtNum xvel[6];
// process sensors matching stage
int subtreeVel = 0;
for (int i=0; i<m->nsensor; i++) {
if (m->sensor_needstage[i]==mjSTAGE_VEL) {
// get sensor info
type = m->sensor_type[i];
objtype = m->sensor_objtype[i];
objid = m->sensor_objid[i];
refid = m->sensor_refid[i];
reftype = m->sensor_reftype[i];
adr = m->sensor_adr[i];
// call mj_subtreeVel when first relevant sensor is encountered
if (subtreeVel==0 &&
(type==mjSENS_SUBTREELINVEL ||
type==mjSENS_SUBTREEANGMOM ||
type==mjSENS_USER)) {
// compute subtree_linvel, subtree_angmom
mj_subtreeVel(m, d);
// mark computed
subtreeVel = 1;
}
// process according to type
switch (type) {
case mjSENS_VELOCIMETER: // velocimeter
// xvel = site velocity, in site frame
mj_objectVelocity(m, d, mjOBJ_SITE, objid, xvel, 1);
// assign linear velocity
mju_copy3(d->sensordata+adr, xvel+3);
break;
case mjSENS_GYRO: // gyro
// xvel = site velocity, in site frame
mj_objectVelocity(m, d, mjOBJ_SITE, objid, xvel, 1);
// assign angular velocity
mju_copy3(d->sensordata+adr, xvel);
break;
case mjSENS_JOINTVEL: // jointvel
d->sensordata[adr] = d->qvel[m->jnt_dofadr[objid]];
break;
case mjSENS_TENDONVEL: // tendonvel
d->sensordata[adr] = d->ten_velocity[objid];
break;
case mjSENS_ACTUATORVEL: // actuatorvel
d->sensordata[adr] = d->actuator_velocity[objid];
break;
case mjSENS_BALLANGVEL: // ballangvel
mju_copy3(d->sensordata+adr, d->qvel+m->jnt_dofadr[objid]);
break;
case mjSENS_JOINTLIMITVEL: // jointlimitvel
d->sensordata[adr] = 0;
for (int j=ne+nf; j<nefc; j++) {
if (d->efc_type[j]==mjCNSTR_LIMIT_JOINT && d->efc_id[j]==objid) {
d->sensordata[adr] = d->efc_vel[j];
break;
}
}
break;
case mjSENS_TENDONLIMITVEL: // tendonlimitvel
d->sensordata[adr] = 0;
for (int j=ne+nf; j<nefc; j++) {
if (d->efc_type[j]==mjCNSTR_LIMIT_TENDON && d->efc_id[j]==objid) {
d->sensordata[adr] = d->efc_vel[j];
break;
}
}
break;
case mjSENS_FRAMELINVEL: // framelinvel
case mjSENS_FRAMEANGVEL: // frameangvel
// xvel = 6D object velocity, in global frame
mj_objectVelocity(m, d, objtype, objid, xvel, 0);
if (refid > -1) { // reference frame specified
mjtNum *xpos, *xmat, *xpos_ref, *xmat_ref, xvel_ref[6], rel_vel[6], cross[3], rvec[3];
// in global frame: object and reference position, reference orientation and velocity
get_xpos_xmat(d, objtype, objid, i, &xpos, &xmat);
get_xpos_xmat(d, reftype, refid, i, &xpos_ref, &xmat_ref);
mj_objectVelocity(m, d, reftype, refid, xvel_ref, 0);
// subtract velocities
mju_sub(rel_vel, xvel, xvel_ref, 6);
// linear velocity: add correction due to rotating reference frame
mju_sub3(rvec, xpos, xpos_ref);
mju_cross(cross, rvec, xvel_ref);
mju_addTo3(rel_vel+3, cross);
// project into reference frame
mju_rotVecMatT(xvel, rel_vel, xmat_ref);
mju_rotVecMatT(xvel+3, rel_vel+3, xmat_ref);
}
// copy linear or angular component
if (m->sensor_type[i]==mjSENS_FRAMELINVEL) {
mju_copy3(d->sensordata+adr, xvel+3);
} else {
mju_copy3(d->sensordata+adr, xvel);
}
break;
case mjSENS_SUBTREELINVEL: // subtreelinvel
mju_copy3(d->sensordata+adr, d->subtree_linvel+3*objid);
break;
case mjSENS_SUBTREEANGMOM: // subtreeangmom
mju_copy3(d->sensordata+adr, d->subtree_angmom+3*objid);
break;
case mjSENS_USER: // user
nusersensor++;
break;
default:
mju_error_i("Invalid type in VEL stage, sensor %d", i);
}
}
}
// fill in user sensors if detected
if (nusersensor && mjcb_sensor) {
mjcb_sensor(m, d, mjSTAGE_VEL);
}
// add noise if enabled
if (mjENABLED(mjENBL_SENSORNOISE)) {
add_noise(m, d, mjSTAGE_VEL);
}
// cutoff
apply_cutoff(m, d, mjSTAGE_VEL);
}
// acceleration/force-dependent sensors
void mj_sensorAcc(const mjModel* m, mjData* d) {
int rootid, bodyid, type, objtype, objid, body1, body2, adr, nusersensor = 0;
int ne = d->ne, nf = d->nf, nefc = d->nefc;
mjtNum tmp[6], conforce[6], conray[3];
mjContact* con;
// process sensors matching stage
int rnePost = 0;
for (int i=0; i<m->nsensor; i++) {
if (m->sensor_needstage[i]==mjSTAGE_ACC) {
// get sensor info
type = m->sensor_type[i];
objtype = m->sensor_objtype[i];
objid = m->sensor_objid[i];
adr = m->sensor_adr[i];
// call mj_rnePostConstraint when first relevant sensor is encountered
if (rnePost==0 &&
type!=mjSENS_TOUCH &&
type!=mjSENS_ACTUATORFRC &&
type!=mjSENS_JOINTLIMITFRC &&
type!=mjSENS_TENDONLIMITFRC) {
// compute cacc, cfrc_int, cfrc_ext
mj_rnePostConstraint(m, d);
// mark computed
rnePost = 1;
}
// process according to type
switch (type) {
case mjSENS_TOUCH: // touch
// extract body data
bodyid = m->site_bodyid[objid];
rootid = m->body_rootid[bodyid];
// clear result
d->sensordata[adr] = 0;
// find contacts in sensor zone, add normal forces
for (int j=0; j<d->ncon; j++) {
// contact pointer, contacting bodies
con = d->contact + j;
body1 = m->geom_bodyid[con->geom1];
body2 = m->geom_bodyid[con->geom2];
// select contacts involving sensorized body
if (con->efc_address>=0 && (bodyid==body1 || bodyid==body2)) {
// get contact force:torque in contact frame
mj_contactForce(m, d, j, conforce);
// nothing to do if normal is zero
if (conforce[0]<=0) {
continue;
}
// convert contact normal force to global frame, normalize
mju_scl3(conray, con->frame, conforce[0]);
mju_normalize3(conray);
// flip ray direction if sensor is on body2
if (bodyid==body2) {
mju_scl3(conray, conray, -1);
}
// add if ray-zone intersection (always true when con->pos inside zone)
if (mju_rayGeom(d->site_xpos+3*objid, d->site_xmat+9*objid,
m->site_size+3*objid, con->pos, conray,
m->site_type[objid]) >= 0) {
d->sensordata[adr] += conforce[0];
}
}
}
break;
case mjSENS_ACCELEROMETER: // accelerometer
// tmp = site acceleration, in site frame
mj_objectAcceleration(m, d, mjOBJ_SITE, objid, tmp, 1);
// assign linear acceleration
mju_copy3(d->sensordata+adr, tmp+3);
break;
case mjSENS_FORCE: // force
// extract body data
bodyid = m->site_bodyid[objid];
rootid = m->body_rootid[bodyid];
// tmp = interaction force between body and parent, in site frame
mju_transformSpatial(tmp, d->cfrc_int+6*bodyid, 1,
d->site_xpos+3*objid, d->subtree_com+3*rootid, d->site_xmat+9*objid);
// assign force
mju_copy3(d->sensordata+adr, tmp+3);
break;
case mjSENS_TORQUE: // torque
// extract body data
bodyid = m->site_bodyid[objid];
rootid = m->body_rootid[bodyid];
// tmp = interaction force between body and parent, in site frame
mju_transformSpatial(tmp, d->cfrc_int+6*bodyid, 1,
d->site_xpos+3*objid, d->subtree_com+3*rootid, d->site_xmat+9*objid);
// assign torque
mju_copy3(d->sensordata+adr, tmp);
break;
case mjSENS_ACTUATORFRC: // actuatorfrc
d->sensordata[adr] = d->actuator_force[objid];
break;
case mjSENS_JOINTLIMITFRC: // jointlimitfrc
d->sensordata[adr] = 0;
for (int j=ne+nf; j<nefc; j++) {
if (d->efc_type[j]==mjCNSTR_LIMIT_JOINT && d->efc_id[j]==objid) {
d->sensordata[adr] = d->efc_force[j];
break;
}
}
break;
case mjSENS_TENDONLIMITFRC: // tendonlimitfrc
d->sensordata[adr] = 0;
for (int j=ne+nf; j<nefc; j++) {
if (d->efc_type[j]==mjCNSTR_LIMIT_TENDON && d->efc_id[j]==objid) {
d->sensordata[adr] = d->efc_force[j];
break;
}
}
break;
case mjSENS_FRAMELINACC: // framelinacc
case mjSENS_FRAMEANGACC: // frameangacc
// get 6D object acceleration, in global frame
mj_objectAcceleration(m, d, objtype, objid, tmp, 0);
// copy linear or angular component
if (m->sensor_type[i]==mjSENS_FRAMELINACC) {
mju_copy3(d->sensordata+adr, tmp+3);
} else {
mju_copy3(d->sensordata+adr, tmp);
}
break;
case mjSENS_USER: // user
nusersensor++;
break;
default:
mju_error_i("Invalid type in ACC stage, sensor %d", i);
}
}
}
// fill in user sensors if detected
if (nusersensor && mjcb_sensor) {
mjcb_sensor(m, d, mjSTAGE_ACC);
}
// add noise if enabled
if (mjENABLED(mjENBL_SENSORNOISE)) {
add_noise(m, d, mjSTAGE_ACC);
}
// cutoff
apply_cutoff(m, d, mjSTAGE_ACC);
}
//-------------------------------- energy ----------------------------------------------------------
// position-dependent energy (potential)
void mj_energyPos(const mjModel* m, mjData* d) {
int padr;
mjtNum dif[3], stiffness;
// disabled: clear and return
if (!mjENABLED(mjENBL_ENERGY)) {
d->energy[0] = d->energy[1] = 0;
return;
}
// init potential energy: -sum_i body(i).mass * mju_dot(body(i).pos, gravity)
d->energy[0] = 0;
if (!mjDISABLED(mjDSBL_GRAVITY)) {
for (int i=1; i<m->nbody; i++) {
d->energy[0] -= m->body_mass[i] * mju_dot3(m->opt.gravity, d->xipos+3*i);
}
}
// add joint-level springs
if (!mjDISABLED(mjDSBL_PASSIVE)) {
for (int i=0; i<m->njnt; i++) {
stiffness = m->jnt_stiffness[i];
padr = m->jnt_qposadr[i];
switch (m->jnt_type[i]) {
case mjJNT_FREE:
mju_sub3(dif, d->qpos+padr, m->qpos_spring+padr);
d->energy[0] += 0.5*stiffness*mju_dot3(dif, dif);
// continue with rotations
padr += 3;
case mjJNT_BALL:
// covert quatertion difference into angular "velocity"
mju_subQuat(dif, d->qpos + padr, m->qpos_spring + padr);
d->energy[0] += 0.5*stiffness*mju_dot3(dif, dif);
break;
case mjJNT_SLIDE:
case mjJNT_HINGE:
d->energy[0] += 0.5*stiffness*
(d->qpos[padr] - m->qpos_spring[padr])*
(d->qpos[padr] - m->qpos_spring[padr]);
break;
}
}
}
// add tendon-level springs
if (!mjDISABLED(mjDSBL_PASSIVE)) {
for (int i=0; i<m->ntendon; i++) {
stiffness = m->tendon_stiffness[i];
d->energy[0] += 0.5*stiffness*(d->ten_length[i] - m->tendon_lengthspring[i])*
(d->ten_length[i] - m->tendon_lengthspring[i]);
}
}
}
// velocity-dependent energy (kinetic)
void mj_energyVel(const mjModel* m, mjData* d) {
mjtNum *vec;
mjMARKSTACK;
// return if disabled (already cleared in potential)
if (!mjENABLED(mjENBL_ENERGY)) {
return;
}
vec = mj_stackAlloc(d, m->nv);
// kinetic energy: 0.5 * qvel' * M * qvel
mj_mulM(m, d, vec, d->qvel);
d->energy[1] = 0.5*mju_dot(vec, d->qvel, m->nv);
mjFREESTACK;
}
+50
View File
@@ -0,0 +1,50 @@
// 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_SRC_ENGINE_ENGINE_SENSOR_H_
#define MUJOCO_SRC_ENGINE_ENGINE_SENSOR_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
//-------------------------------- sensors ---------------------------------------------------------
// position-dependent sensors
MJAPI void mj_sensorPos(const mjModel* m, mjData* d);
// velocity-dependent sensors
MJAPI void mj_sensorVel(const mjModel* m, mjData* d);
// acceleration/force-dependent sensors
MJAPI void mj_sensorAcc(const mjModel* m, mjData* d);
//-------------------------------- energy ----------------------------------------------------------
// position-dependent energy (potential)
MJAPI void mj_energyPos(const mjModel* m, mjData* d);
// velocity-dependent energy (kinetic)
MJAPI void mj_energyVel(const mjModel* m, mjData* d);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_SENSOR_H_
+556
View File
@@ -0,0 +1,556 @@
// 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.
#include "engine/engine_setconst.h"
#include <stdio.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
#include "engine/engine_core_constraint.h"
#include "engine/engine_core_smooth.h"
#include "engine/engine_forward.h"
#include "engine/engine_io.h"
#include "engine/engine_macro.h"
#include "engine/engine_support.h"
#include "engine/engine_util_blas.h"
#include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h"
#include "engine/engine_util_spatial.h"
// set quantities that depend on qpos0
static void set0(mjModel* m, mjData* d) {
int id, id1, id2, dnum, nv = m->nv;
mjtNum A[36] = {0}, pos[3], quat[4];
mjMARKSTACK;
mjtNum* jac = mj_stackAlloc(d, 6*nv);
mjtNum* tmp = mj_stackAlloc(d, 6*nv);
int* cammode = 0;
int* lightmode = 0;
// save camera and light mode, set to fixed
if (m->ncam) {
cammode = (int*) mj_stackAlloc(d, m->ncam);
for (int i=0; i<m->ncam; i++) {
cammode[i] = m->cam_mode[i];
m->cam_mode[i] = mjCAMLIGHT_FIXED;
}
}
if (m->nlight) {
lightmode = (int*) mj_stackAlloc(d, m->nlight);
for (int i=0; i<m->nlight; i++) {
lightmode[i] = m->light_mode[i];
m->light_mode[i] = mjCAMLIGHT_FIXED;
}
}
// run computations in qpos0
mju_copy(d->qpos, m->qpos0, m->nq);
mj_kinematics(m, d);
mj_comPos(m, d);
mj_camlight(m, d);
mj_crbSkip(m, d, 0);
// save dof_M0
for (int i=0; i<nv; i++) {
m->dof_M0[i] = d->qM[m->dof_Madr[i]];
}
// run remaining computations (factorM needs dof_M0)
mj_factorM(m, d);
mj_tendon(m, d);
mj_transmission(m, d);
// restore camera and light mode
for (int i=0; i<m->ncam; i++) {
m->cam_mode[i] = cammode[i];
}
for (int i=0; i<m->nlight; i++) {
m->light_mode[i] = lightmode[i];
}
// set tendon_length0, actuator_length0
mju_copy(m->tendon_length0, d->ten_length, m->ntendon);
mju_copy(m->actuator_length0, d->actuator_length, m->nu);
// compute body_invweight0
m->body_invweight0[0] = m->body_invweight0[1] = 0.0;
for (int i=1; i<m->nbody; i++) {
if (nv) {
// inverse spatial inertia: A = J*inv(M)*J'
mj_jacBodyCom(m, d, jac, jac+3*nv, i);
mj_solveM(m, d, tmp, jac, 6);
mju_mulMatMatT(A, jac, tmp, 6, nv, 6);
}
// average diagonal and assign
m->body_invweight0[2*i] = (A[0] + A[7] + A[14])/3;
m->body_invweight0[2*i+1] = (A[21] + A[28] + A[35])/3;
}
// compute dof_invweight0
for (int i=0; i<m->njnt; i++) {
id = m->jnt_dofadr[i];
// get number of components
if (m->jnt_type[i]==mjJNT_FREE) {
dnum = 6;
} else if (m->jnt_type[i]==mjJNT_BALL) {
dnum = 3;
} else {
dnum = 1;
}
// inverse joint inertia: A = J*inv(M)*J'
if (nv) {
mju_zero(jac, dnum*nv);
for (int j=0; j<dnum; j++) {
jac[j*(nv+1) + id] = 1;
}
mj_solveM(m, d, tmp, jac, dnum);
mju_mulMatMatT(A, jac, tmp, dnum, nv, dnum);
}
// average diagonal and assign
if (dnum==6) {
m->dof_invweight0[id] = m->dof_invweight0[id+1] = m->dof_invweight0[id+2] =
(A[0] + A[7] + A[14])/3;
m->dof_invweight0[id+3] = m->dof_invweight0[id+4] = m->dof_invweight0[id+5] =
(A[21] + A[28] + A[35])/3;
} else if (dnum==3)
m->dof_invweight0[id] = m->dof_invweight0[id+1] = m->dof_invweight0[id+2] =
(A[0] + A[4] + A[8])/3;
else {
m->dof_invweight0[id] = A[0];
}
}
// compute tendon_invweight0
if (nv) {
for (int i=0; i<m->ntendon; i++) {
// make dense vector into tmp
if (mj_isSparse(m)) {
mju_zero(tmp, nv);
int end = d->ten_J_rowadr[i] + d->ten_J_rownnz[i];
for (int j=d->ten_J_rowadr[i]; j<end; j++) {
tmp[d->ten_J_colind[j]] = d->ten_J[j];
}
} else {
mju_copy(tmp, d->ten_J+i*nv, nv);
}
// solve into tmp+nv
mj_solveM(m, d, tmp+nv, tmp, 1);
m->tendon_invweight0[i] = mju_dot(tmp, tmp+nv, nv);
}
// compute actuator_acc0
for (int i=0; i<m->nu; i++) {
mj_solveM(m, d, tmp, d->actuator_moment+i*nv, 1);
m->actuator_acc0[i] = mju_norm(tmp, nv);
}
} else {
for (int i=0; i<m->nu; i++) {
m->actuator_acc0[i] = 0;
}
}
// compute missing eq_data for body constraints
for (int i=0; i<m->neq; i++) {
// get ids
id1 = m->eq_obj1id[i];
id2 = m->eq_obj2id[i];
// connect constraint
if (m->eq_type[i]==mjEQ_CONNECT) {
// pos = anchor position in global frame
mj_local2Global(d, pos, 0, m->eq_data+mjNEQDATA*i, 0, id1, 0);
// data[3-5] = anchor position in body2 local frame
mju_subFrom3(pos, d->xpos+3*id2);
mju_rotVecMatT(m->eq_data+mjNEQDATA*i+3, pos, d->xmat+9*id2);
}
// weld constraint
else if (m->eq_type[i]==mjEQ_WELD) {
// skip if user has set any quaternion data
if (m->eq_data[mjNEQDATA*i+3] ||
m->eq_data[mjNEQDATA*i+4] ||
m->eq_data[mjNEQDATA*i+5] ||
m->eq_data[mjNEQDATA*i+6]) {
// normalize quaternion just in case
mju_normalize4(m->eq_data+mjNEQDATA*i+3);
continue;
}
// data[0-2] = xpos2-xpos1 in body1 local frame
mju_sub3(pos, d->xpos+3*id2, d->xpos+3*id1);
mju_rotVecMatT(m->eq_data+mjNEQDATA*i, pos, d->xmat+9*id1);
// data[3-6] = neg(xquat1)*xquat2 = "xquat2-xquat1" in body1 local frame
mju_negQuat(quat, d->xquat+4*id1);
mju_mulQuat(m->eq_data+mjNEQDATA*i+3, quat, d->xquat+4*id2);
}
}
// camera compos0, pos0, mat0
for (int i=0; i<m->ncam; i++) {
// get body ids
id = m->cam_bodyid[i]; // camera body
id1 = m->cam_targetbodyid[i]; // target body
// compute positional offsets
mju_sub3(m->cam_pos0+3*i, d->cam_xpos+3*i, d->xpos+3*id);
mju_sub3(m->cam_poscom0+3*i, d->cam_xpos+3*i, d->subtree_com+ (id1>=0 ? 3*id1 : 3*id));
// copy mat
mju_copy(m->cam_mat0+9*i, d->cam_xmat+9*i, 9);
}
// light compos0, pos0, dir0
for (int i=0; i<m->nlight; i++) {
// get body ids
id = m->light_bodyid[i]; // light body
id1 = m->light_targetbodyid[i]; // target body
// compute positional offsets
mju_sub3(m->light_pos0+3*i, d->light_xpos+3*i, d->xpos+3*id);
mju_sub3(m->light_poscom0+3*i, d->light_xpos+3*i, d->subtree_com+ (id1>=0 ? 3*id1 : 3*id));
// copy dir
mju_copy3(m->light_dir0+3*i, d->light_xdir+3*i);
}
mjFREESTACK;
}
// accumulate bounding box
static void updateBox(mjtNum* xmin, mjtNum* xmax, mjtNum* pos, mjtNum radius) {
for (int i=0; i<3; i++) {
xmin[i] = mjMIN(xmin[i], pos[i] - radius);
xmax[i] = mjMAX(xmax[i], pos[i] + radius);
}
}
// compute stat; assume computations already executed in qpos0
static void setStat(mjModel* m, mjData* d) {
mjtNum xmin[3] = {1E+10, 1E+10, 1E+10};
mjtNum xmax[3] = {-1E+10, -1E+10, -1E+10};
mjtNum rbound;
mjMARKSTACK;
mjtNum* body = mj_stackAlloc(d, m->nbody);
// compute bounding box of bodies, joint centers, geoms and sites
for (int i=1; i<m->nbody; i++) {
updateBox(xmin, xmax, d->xpos+3*i, 0);
updateBox(xmin, xmax, d->xipos+3*i, 0);
}
for (int i=0; i<m->njnt; i++) {
updateBox(xmin, xmax, d->xanchor+3*i, 0);
}
for (int i=0; i<m->nsite; i++) {
updateBox(xmin, xmax, d->site_xpos+3*i, 0);
}
for (int i=0; i<m->ngeom; i++) {
// set rbound: regular geom rbound, or 0.1 of plane or hfield max size
rbound = 0;
if (m->geom_rbound[i] > 0) {
rbound = m->geom_rbound[i];
} else if (m->geom_type[i]==mjGEOM_PLANE) {
// finite in at least one direction
if (m->geom_size[3*i] || m->geom_size[3*i+1]) {
rbound = mjMAX(m->geom_size[3*i], m->geom_size[3*i+1]) * 0.1;
}
// infinite in both directions
else {
rbound = 1;
}
} else if (m->geom_type[i]==mjGEOM_HFIELD) {
int j = m->geom_dataid[i];
rbound = mjMAX(m->hfield_size[4*j],
mjMAX(m->hfield_size[4*j+1],
mjMAX(m->hfield_size[4*j+2], m->hfield_size[4*j+3]))) * 0.1;
}
updateBox(xmin, xmax, d->geom_xpos+3*i, rbound);
}
// compute center
mju_add3(m->stat.center, xmin, xmax);
mju_scl3(m->stat.center, m->stat.center, 0.5);
// compute bounding box size
if (xmax[0]>xmin[0])
m->stat.extent = mju_max(1E-5,
mju_max(xmax[0]-xmin[0], mju_max(xmax[1]-xmin[1], xmax[2]-xmin[2])));
// set body size to max com-joint distance
mju_zero(body, m->nbody);
for (int i=0; i<m->njnt; i++) {
// handle this body
int id = m->jnt_bodyid[i];
body[id] = mju_max(body[id], mju_dist3(d->xipos+3*id, d->xanchor+3*i));
// handle parent body
id = m->body_parentid[id];
body[id] = mju_max(body[id], mju_dist3(d->xipos+3*id, d->xanchor+3*i));
}
body[0] = 0;
// set body size to max of old value, and geom rbound + com-geom dist
for (int i=1; i<m->nbody; i++) {
for (int id=m->body_geomadr[i]; id<m->body_geomadr[i]+m->body_geomnum[i]; id++) {
if (m->geom_rbound[id]>0) {
body[i] = mju_max(body[i], m->geom_rbound[id] + mju_dist3(d->xipos+3*i, d->geom_xpos+3*id));
}
}
}
// compute meansize, make sure all sizes are above min
if (m->nbody>1) {
m->stat.meansize = 0;
for (int i=1; i<m->nbody; i++) {
body[i] = mju_max(body[i], 1E-5);
m->stat.meansize += body[i]/(m->nbody-1);
}
}
// fix extent if too small compared to meanbody
m->stat.extent = mju_max(m->stat.extent, 2 * m->stat.meansize);
// compute meanmass
if (m->nbody>1) {
m->stat.meanmass = 0;
for (int i=1; i<m->nbody; i++) {
m->stat.meanmass += m->body_mass[i];
}
m->stat.meanmass /= (m->nbody-1);
}
// compute meaninertia
if (m->nv) {
m->stat.meaninertia = 0;
for (int i=0; i<m->nv; i++) {
m->stat.meaninertia += d->qM[m->dof_Madr[i]];
}
m->stat.meaninertia /= m->nv;
}
mjFREESTACK;
}
// set quantities that depend on qpos_spring
static void setSpring(mjModel* m, mjData* d) {
// run computations in qpos_spring
mju_copy(d->qpos, m->qpos_spring, m->nq);
mj_kinematics(m, d);
mj_comPos(m, d);
mj_tendon(m, d);
mj_transmission(m, d);
// copy if model spring length is negative
for (int i=0; i<m->ntendon; i++) {
if (m->tendon_lengthspring[i]<0) {
m->tendon_lengthspring[i] = d->ten_length[i];
}
}
}
// entry point: set all constant fields of mjModel, except for lengthrange
void mj_setConst(mjModel* m, mjData* d) {
// compute subtreemass
for (int i=0; i<m->nbody; i++) {
m->body_subtreemass[i] = m->body_mass[i];
}
for (int i=m->nbody-1; i>0; i--) {
m->body_subtreemass[m->body_parentid[i]] += m->body_subtreemass[i];
}
// call functions
set0(m, d);
setStat(m, d);
setSpring(m, d);
}
//----------------------------- actuator length range computation ----------------------------------
// evaluate actuator length, advance special dynamics
static mjtNum evalAct(const mjModel* m, mjData* d, int index, int side,
const mjLROpt* opt) {
int nv = m->nv;
// reduce velocity
mju_scl(d->qvel, d->qvel, mju_exp(-m->opt.timestep/mjMAX(0.01, opt->timeconst)), nv);
// step1: compute inertia and actuator moments
mj_step1(m, d);
// set force to generate desired acceleration
mj_solveM(m, d, d->qfrc_applied, d->actuator_moment+index*nv, 1);
mjtNum nrm = mju_norm(d->qfrc_applied, nv);
mju_scl(d->qfrc_applied, d->actuator_moment+index*nv,
(2*side-1)*opt->accel/mjMAX(mjMINVAL, nrm), nv);
// impose maxforce
nrm = mju_norm(d->qfrc_applied, nv);
if (opt->maxforce>0 && nrm>opt->maxforce) {
mju_scl(d->qfrc_applied, d->qfrc_applied, opt->maxforce/mjMAX(mjMINVAL, nrm), nv);
}
// step2: apply force
mj_step2(m, d);
// return actuator length
return d->actuator_length[index];
}
// Set length range for specified actuator, return 1 if ok, 0 if error.
int mj_setLengthRange(mjModel* m, mjData* d, int index,
const mjLROpt* opt, char* error, int error_sz) {
// check index
if (index<0 || index>=m->nu) {
mju_error("Invalid actuator index in mj_setLengthRange");
}
// skip depending on mode and type
int ismuscle = (m->actuator_gaintype[index]==mjGAIN_MUSCLE ||
m->actuator_biastype[index]==mjBIAS_MUSCLE);
int isuser = (m->actuator_gaintype[index]==mjGAIN_USER ||
m->actuator_biastype[index]==mjBIAS_USER);
if ((opt->mode==mjLRMODE_NONE) ||
(opt->mode==mjLRMODE_MUSCLE && !ismuscle) ||
(opt->mode==mjLRMODE_MUSCLEUSER && !ismuscle && !isuser)) {
return 1;
}
// use existing length range if available
if (opt->useexisting && (m->actuator_lengthrange[2*index] < m->actuator_lengthrange[2*index+1])) {
return 1;
}
// get transmission id
int threadid = m->actuator_trnid[index];
// use joint and tendon limits if available
if (opt->uselimit) {
// joint or jointinparent
if (m->actuator_trntype[index]==mjTRN_JOINT ||
m->actuator_trntype[index]==mjTRN_JOINTINPARENT) {
// make sure joint is limited
if (m->jnt_limited[threadid]) {
// copy range
m->actuator_lengthrange[2*index] = m->jnt_range[2*threadid];
m->actuator_lengthrange[2*index+1] = m->jnt_range[2*threadid+1];
// skip optimization
return 1;
}
}
// tendon
if (m->actuator_trntype[index]==mjTRN_TENDON) {
// make sure tendon is limited
if (m->tendon_limited[threadid]) {
// copy range
m->actuator_lengthrange[2*index] = m->tendon_range[2*threadid];
m->actuator_lengthrange[2*index+1] = m->tendon_range[2*threadid+1];
// skip optimization
return 1;
}
}
}
// optimize in both directions
mjtNum lmin[2] = {0, 0}, lmax[2] = {0, 0};
int side;
for (side=0; side<2; side++) {
// init at qpos0
mj_resetData(m, d);
// simulate
int updated = 0;
while (d->time < opt->inttotal) {
// advance and get length
mjtNum len = evalAct(m, d, index, side, opt);
// reset: cannot proceed
if (d->time==0) {
snprintf(error, error_sz, "Unstable lengthrange simulation in actuator %d", index);
return 0;
}
// update limits
if (d->time > opt->inttotal-opt->inteval) {
if (len<lmin[side] || !updated) {
lmin[side] = len;
}
if (len>lmax[side] || !updated) {
lmax[side] = len;
}
updated = 1;
}
}
// assign
m->actuator_lengthrange[2*index+side] = (side==0 ? lmin[side] : lmax[side]);
}
// check range
mjtNum dif = m->actuator_lengthrange[2*index+1] - m->actuator_lengthrange[2*index];
if (dif<=0) {
snprintf(error, error_sz,
"Invalid lengthrange (%g, %g) in actuator %d",
m->actuator_lengthrange[2*index],
m->actuator_lengthrange[2*index+1], index);
return 0;
}
// check convergence, side 0
if (lmax[0]-lmin[0]>opt->tolrange*dif) {
snprintf(error, error_sz,
"Lengthrange computation did not converge in actuator %d:\n"
" eval (%g, %g)\n range (%g, %g)",
index, lmin[0], lmax[0],
m->actuator_lengthrange[2*index],
m->actuator_lengthrange[2*index+1]);
return 0;
}
// check convergence, side 1
if (lmax[1]-lmin[1]>opt->tolrange*dif) {
snprintf(error, error_sz,
"Lengthrange computation did not converge in actuator %d:\n"
" eval (%g, %g)\n range (%g, %g)",
index, lmin[1], lmax[1],
m->actuator_lengthrange[2*index],
m->actuator_lengthrange[2*index+1]);
return 0;
}
return 1;
}
+37
View File
@@ -0,0 +1,37 @@
// 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_SRC_ENGINE_ENGINE_SETCONST_H_
#define MUJOCO_SRC_ENGINE_ENGINE_SETCONST_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
// Set constant fields of mjModel, corresponding to qpos0 configuration.
MJAPI void mj_setConst(mjModel* m, mjData* d);
// Set actuator_lengthrange for specified actuator; return 1 if ok, 0 if error.
MJAPI int mj_setLengthRange(mjModel* m, mjData* d, int index,
const mjLROpt* opt, char* error, int error_sz);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_SETCONST_H_
File diff suppressed because it is too large Load Diff
+34
View File
@@ -0,0 +1,34 @@
// 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_SRC_ENGINE_ENGINE_SOLVER_H_
#define MUJOCO_SRC_ENGINE_ENGINE_SOLVER_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
// PGS solver
void mj_solPGS(const mjModel* m, mjData* d, int maxiter);
// No Slip solver (modified PGS)
void mj_solNoSlip(const mjModel* m, mjData* d, int maxiter);
// CG solver
void mj_solCG(const mjModel* m, mjData* d, int maxiter);
// Newton solver
void mj_solNewton(const mjModel* m, mjData* d, int maxiter);
#endif // MUJOCO_SRC_ENGINE_ENGINE_SOLVER_H_
File diff suppressed because it is too large Load Diff
+162
View File
@@ -0,0 +1,162 @@
// 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_SRC_ENGINE_ENGINE_SUPPORT_H_
#define MUJOCO_SRC_ENGINE_ENGINE_SUPPORT_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
// strings
MJAPI extern const char* mjDISABLESTRING[mjNDISABLE];
MJAPI extern const char* mjENABLESTRING[mjNENABLE];
MJAPI extern const char* mjTIMERSTRING[mjNTIMER];
//-------------------------- Jacobians -------------------------------------------------------------
// compute 3/6-by-nv Jacobian of global point attached to given body
MJAPI void mj_jac(const mjModel* m, const mjData* d,
mjtNum* jacp, mjtNum* jacr, const mjtNum point[3], int body);
// compute body frame Jacobian
MJAPI void mj_jacBody(const mjModel* m, const mjData* d,
mjtNum* jacp, mjtNum* jacr, int body);
// compute body center-of-mass Jacobian
MJAPI void mj_jacBodyCom(const mjModel* m, const mjData* d,
mjtNum* jacp, mjtNum* jacr, int body);
// compute geom Jacobian
MJAPI void mj_jacGeom(const mjModel* m, const mjData* d,
mjtNum* jacp, mjtNum* jacr, int geom);
// compute site Jacobian
MJAPI void mj_jacSite(const mjModel* m, const mjData* d,
mjtNum* jacp, mjtNum* jacr, int site);
// compute translation Jacobian of point, and rotation Jacobian of axis
MJAPI void mj_jacPointAxis(const mjModel* m, mjData* d,
mjtNum* jacPoint, mjtNum* jacAxis,
const mjtNum point[3], const mjtNum axis[3], int body);
// compute 3/6-by-nv sparse Jacobian of global point attached to given body
void mj_jacSparse(const mjModel* m, const mjData* d,
mjtNum* jacp, mjtNum* jacr, const mjtNum* point, int body,
int NV, int* chain);
// sparse Jacobian difference for simple body contacts
void mj_jacSparseSimple(const mjModel* m, const mjData* d,
mjtNum* jacdifp, mjtNum* jacdifr, const mjtNum* point,
int body, int flg_second, int NV, int start);
// dense or sparse Jacobian difference for two body points: pos2 - pos1, global
int mj_jacDifPair(const mjModel* m, const mjData* d, int* chain,
int b1, int b2, const mjtNum pos1[3], const mjtNum pos2[3],
mjtNum* jac1p, mjtNum* jac2p, mjtNum* jacdifp,
mjtNum* jac1r, mjtNum* jac2r, mjtNum* jacdifr);
//-------------------------- name functions --------------------------------------------------------
// get id of object with specified name; -1: not found; type is mjtObj
MJAPI int mj_name2id(const mjModel* m, int type, const char* name);
// get name of object with specified id; 0: invalid type or id; type is mjtObj
MJAPI const char* mj_id2name(const mjModel* m, int type, int id);
//-------------------------- inertia functions -----------------------------------------------------
// convert sparse inertia matrix M into full matrix
MJAPI void mj_fullM(const mjModel* m, mjtNum* dst, const mjtNum* M);
// multiply vector by inertia matrix
MJAPI void mj_mulM(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec);
// multiply vector by (inertia matrix)^(1/2)
MJAPI void mj_mulM2(const mjModel* m, const mjData* d, mjtNum* res, const mjtNum* vec);
// add inertia matrix to destination matrix
// destination can be sparse uncompressed, or dense when all int* are NULL
MJAPI void mj_addM(const mjModel* m, mjData* d, mjtNum* dst,
int* rownnz, int* rowadr, int* colind);
//-------------------------- perturbations ---------------------------------------------------------
// apply cartesian force and torque
MJAPI void mj_applyFT(const mjModel* m, mjData* d,
const mjtNum force[3], const mjtNum torque[3],
const mjtNum point[3], int body, mjtNum* qfrc_target);
// accumulate xfrc_applied in qfrc
void mj_xfrcAccumulate(const mjModel* m, mjData* d, mjtNum* qfrc);
//-------------------------- coordinate transformation ---------------------------------------------
// compute object 6D velocity in object-centered frame, world/local orientation
MJAPI void mj_objectVelocity(const mjModel* m, const mjData* d,
int objtype, int objid, mjtNum res[6], int flg_local);
// compute object 6D acceleration in object-centered frame, world/local orientation
MJAPI void mj_objectAcceleration(const mjModel* m, const mjData* d,
int objtype, int objid, mjtNum res[6], int flg_local);
//-------------------------- miscellaneous ---------------------------------------------------------
// extract 6D force:torque for one contact, in contact frame
MJAPI void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]);
// compute velocity by finite-differencing two positions
MJAPI void mj_differentiatePos(const mjModel* m, mjtNum* qvel, mjtNum dt,
const mjtNum* qpos1, const mjtNum* qpos2);
// integrate position with given velocity
MJAPI void mj_integratePos(const mjModel* m, mjtNum* qpos, const mjtNum* qvel, mjtNum dt);
// normalize all quaterions in qpos-type vector
MJAPI void mj_normalizeQuat(const mjModel* m, mjtNum* qpos);
// map from body local to global Cartesian coordinates
MJAPI void mj_local2Global(mjData* d, mjtNum xpos[3], mjtNum xmat[9],
const mjtNum pos[3], const mjtNum quat[4],
int body, mjtByte sameframe);
// sum all body masses
MJAPI mjtNum mj_getTotalmass(const mjModel* m);
// scale body masses and inertias to achieve specified total mass
MJAPI void mj_setTotalmass(mjModel* m, mjtNum newmass);
// high-level warning function: count warnings in mjData, print only the first time
MJAPI void mj_warning(mjData* d, int warning, int info);
// version number
MJAPI int mj_version(void);
// current version of MuJoCo as a null-terminated string
MJAPI const char* mj_versionString();
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_SUPPORT_H_
+795
View File
@@ -0,0 +1,795 @@
// 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.
#include "engine/engine_util_blas.h"
#include <string.h>
#include <mujoco/mjmodel.h>
#ifdef mjUSEPLATFORMSIMD
#if defined(__AVX__) && defined(mjUSEDOUBLE)
#define mjUSEAVX
#include "immintrin.h"
#endif
#endif
//------------------------------ 3D vector and matrix-vector operations ----------------------------
// res = 0
void mju_zero3(mjtNum res[3]) {
res[0] = 0;
res[1] = 0;
res[2] = 0;
}
// res = vec
void mju_copy3(mjtNum res[3], const mjtNum data[3]) {
res[0] = data[0];
res[1] = data[1];
res[2] = data[2];
}
// res = vec*scl
void mju_scl3(mjtNum res[3], const mjtNum vec[3], mjtNum scl) {
res[0] = vec[0] * scl;
res[1] = vec[1] * scl;
res[2] = vec[2] * scl;
}
// res = vec1 + vec2
void mju_add3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3]) {
res[0] = vec1[0] + vec2[0];
res[1] = vec1[1] + vec2[1];
res[2] = vec1[2] + vec2[2];
}
// res = vec1 - vec2
void mju_sub3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3]) {
res[0] = vec1[0] - vec2[0];
res[1] = vec1[1] - vec2[1];
res[2] = vec1[2] - vec2[2];
}
// res += vec
void mju_addTo3(mjtNum res[3], const mjtNum vec[3]) {
res[0] += vec[0];
res[1] += vec[1];
res[2] += vec[2];
}
// res -= vec
void mju_subFrom3(mjtNum res[3], const mjtNum vec[3]) {
res[0] -= vec[0];
res[1] -= vec[1];
res[2] -= vec[2];
}
// res += vec*scl
void mju_addToScl3(mjtNum res[3], const mjtNum vec[3], mjtNum scl) {
res[0] += vec[0] * scl;
res[1] += vec[1] * scl;
res[2] += vec[2] * scl;
}
// res = vec1 + vec2*scl
void mju_addScl3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3], mjtNum scl) {
res[0] = vec1[0] + scl*vec2[0];
res[1] = vec1[1] + scl*vec2[1];
res[2] = vec1[2] + scl*vec2[2];
}
// normalize vector, return length before normalization
mjtNum mju_normalize3(mjtNum vec[3]) {
mjtNum norm = mju_sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
if (norm<mjMINVAL) {
vec[0] = 1;
vec[1] = 0;
vec[2] = 0;
} else {
mjtNum normInv = 1/norm;
vec[0] *= normInv;
vec[1] *= normInv;
vec[2] *= normInv;
}
return norm;
}
// compute vector length (without normalizing)
mjtNum mju_norm3(const mjtNum vec[3]) {
return mju_sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
}
// vector dot-product
mjtNum mju_dot3(const mjtNum vec1[3], const mjtNum vec2[3]) {
return vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2];
}
// Cartesian distance between 3D vectors
mjtNum mju_dist3(const mjtNum pos1[3], const mjtNum pos2[3]) {
mjtNum dif[3] = {pos1[0]-pos2[0], pos1[1]-pos2[1], pos1[2]-pos2[2]};
return mju_sqrt(dif[0]*dif[0] + dif[1]*dif[1] + dif[2]*dif[2]);
}
// multiply vector by 3D rotation matrix
void mju_rotVecMat(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]) {
res[0] = mat[0]*vec[0] + mat[1]*vec[1] + mat[2]*vec[2];
res[1] = mat[3]*vec[0] + mat[4]*vec[1] + mat[5]*vec[2];
res[2] = mat[6]*vec[0] + mat[7]*vec[1] + mat[8]*vec[2];
}
// multiply vector by transposed 3D rotation matrix
void mju_rotVecMatT(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]) {
res[0] = mat[0]*vec[0] + mat[3]*vec[1] + mat[6]*vec[2];
res[1] = mat[1]*vec[0] + mat[4]*vec[1] + mat[7]*vec[2];
res[2] = mat[2]*vec[0] + mat[5]*vec[1] + mat[8]*vec[2];
}
//------------------------------ 4D vector and matrix-vector operations ----------------------------
// res = 0
void mju_zero4(mjtNum res[4]) {
res[0] = 0;
res[1] = 0;
res[2] = 0;
res[3] = 0;
}
// res = (1,0,0,0)
void mju_unit4(mjtNum res[4]) {
res[0] = 1;
res[1] = 0;
res[2] = 0;
res[3] = 0;
}
// res = vec
void mju_copy4(mjtNum res[4], const mjtNum data[4]) {
res[0] = data[0];
res[1] = data[1];
res[2] = data[2];
res[3] = data[3];
}
// normalize vector, return length before normalization
mjtNum mju_normalize4(mjtNum vec[4]) {
mjtNum norm = mju_sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2] + vec[3]*vec[3]);
if (norm<mjMINVAL) {
vec[0] = 1;
vec[1] = 0;
vec[2] = 0;
vec[3] = 0;
} else {
mjtNum normInv = 1/norm;
vec[0] *= normInv;
vec[1] *= normInv;
vec[2] *= normInv;
vec[3] *= normInv;
}
return norm;
}
//------------------------------ vector operations -------------------------------------------------
// res = 0
void mju_zero(mjtNum* res, int n) {
if (n>0) {
memset(res, 0, n*sizeof(mjtNum));
}
}
// res = vec
void mju_copy(mjtNum* res, const mjtNum* vec, int n) {
if (n>0) {
memcpy(res, vec, n*sizeof(mjtNum));
}
}
// sum(vec)
mjtNum mju_sum(const mjtNum* vec, int n) {
mjtNum res = 0;
for (int i=0; i<n; i++) {
res += vec[i];
}
return res;
}
// sum(abs(vec))
mjtNum mju_L1(const mjtNum* vec, int n) {
mjtNum res = 0;
for (int i=0; i<n; i++) {
res += mju_abs(vec[i]);
}
return res;
}
// res = vec*scl
void mju_scl(mjtNum* res, const mjtNum* vec, mjtNum scl, int n) {
int i = 0;
#ifdef mjUSEAVX
int n_4 = n - 4;
// vector part
if (n_4>=0) {
__m256d sclpar, val1, val1scl;
// init
sclpar = _mm256_set1_pd(scl);
// parallel computation
while (i<=n_4) {
val1 = _mm256_loadu_pd(vec+i);
val1scl = _mm256_mul_pd(val1, sclpar);
_mm256_storeu_pd(res+i, val1scl);
i += 4;
}
}
// process remaining
int n_i = n - i;
if (n_i==3) {
res[i] = vec[i]*scl;
res[i+1] = vec[i+1]*scl;
res[i+2] = vec[i+2]*scl;
} else if (n_i==2) {
res[i] = vec[i]*scl;
res[i+1] = vec[i+1]*scl;
} else if (n_i==1) {
res[i] = vec[i]*scl;
}
#else
for (; i<n; i++) {
res[i] = vec[i]*scl;
}
#endif
}
// res = vec1 + vec2
void mju_add(mjtNum* res, const mjtNum* vec1, const mjtNum* vec2, int n) {
int i = 0;
#ifdef mjUSEAVX
int n_4 = n - 4;
// vector part
if (n_4>=0) {
__m256d sum, val1, val2;
// parallel computation
while (i<=n_4) {
val1 = _mm256_loadu_pd(vec1+i);
val2 = _mm256_loadu_pd(vec2+i);
sum = _mm256_add_pd(val1, val2);
_mm256_storeu_pd(res+i, sum);
i += 4;
}
}
// process remaining
int n_i = n - i;
if (n_i==3) {
res[i] = vec1[i] + vec2[i];
res[i+1] = vec1[i+1] + vec2[i+1];
res[i+2] = vec1[i+2] + vec2[i+2];
} else if (n_i==2) {
res[i] = vec1[i] + vec2[i];
res[i+1] = vec1[i+1] + vec2[i+1];
} else if (n_i==1) {
res[i] = vec1[i] + vec2[i];
}
#else
for (; i<n; i++) {
res[i] = vec1[i] + vec2[i];
}
#endif
}
// res = vec1 - vec2
void mju_sub(mjtNum* res, const mjtNum* vec1, const mjtNum* vec2, int n) {
int i = 0;
#ifdef mjUSEAVX
int n_4 = n - 4;
// vector part
if (n_4>=0) {
__m256d dif, val1, val2;
// parallel computation
while (i<=n_4) {
val1 = _mm256_loadu_pd(vec1+i);
val2 = _mm256_loadu_pd(vec2+i);
dif = _mm256_sub_pd(val1, val2);
_mm256_storeu_pd(res+i, dif);
i += 4;
}
}
// process remaining
int n_i = n - i;
if (n_i==3) {
res[i] = vec1[i] - vec2[i];
res[i+1] = vec1[i+1] - vec2[i+1];
res[i+2] = vec1[i+2] - vec2[i+2];
} else if (n_i==2) {
res[i] = vec1[i] - vec2[i];
res[i+1] = vec1[i+1] - vec2[i+1];
} else if (n_i==1) {
res[i] = vec1[i] - vec2[i];
}
#else
for (; i<n; i++) {
res[i] = vec1[i] - vec2[i];
}
#endif
}
// res += vec
void mju_addTo(mjtNum* res, const mjtNum* vec, int n) {
int i = 0;
#ifdef mjUSEAVX
int n_4 = n - 4;
// vector part
if (n_4>=0) {
__m256d sum, val1, val2;
// parallel computation
while (i<=n_4) {
val1 = _mm256_loadu_pd(res+i);
val2 = _mm256_loadu_pd(vec+i);
sum = _mm256_add_pd(val1, val2);
_mm256_storeu_pd(res+i, sum);
i += 4;
}
}
// process remaining
int n_i = n - i;
if (n_i==3) {
res[i] += vec[i];
res[i+1] += vec[i+1];
res[i+2] += vec[i+2];
} else if (n_i==2) {
res[i] += vec[i];
res[i+1] += vec[i+1];
} else if (n_i==1) {
res[i] += vec[i];
}
#else
for (; i<n; i++) {
res[i] += vec[i];
}
#endif
}
// res -= vec
void mju_subFrom(mjtNum* res, const mjtNum* vec, int n) {
int i = 0;
#ifdef mjUSEAVX
int n_4 = n - 4;
// vector part
if (n_4>=0) {
__m256d dif, val1, val2;
// parallel computation
while (i<=n_4) {
val1 = _mm256_loadu_pd(res+i);
val2 = _mm256_loadu_pd(vec+i);
dif = _mm256_sub_pd(val1, val2);
_mm256_storeu_pd(res+i, dif);
i += 4;
}
}
// process remaining
int n_i = n - i;
if (n_i==3) {
res[i] -= vec[i];
res[i+1] -= vec[i+1];
res[i+2] -= vec[i+2];
} else if (n_i==2) {
res[i] -= vec[i];
res[i+1] -= vec[i+1];
} else if (n_i==1) {
res[i] -= vec[i];
}
#else
for (; i<n; i++) {
res[i] -= vec[i];
}
#endif
}
// res += vec*scl
void mju_addToScl(mjtNum* res, const mjtNum* vec, mjtNum scl, int n) {
int i = 0;
#ifdef mjUSEAVX
int n_4 = n - 4;
// vector part
if (n_4>=0) {
__m256d sclpar, sum, val1, val2, val2scl;
// init
sclpar = _mm256_set1_pd(scl);
// parallel computation
while (i<=n_4) {
val1 = _mm256_loadu_pd(res+i);
val2 = _mm256_loadu_pd(vec+i);
val2scl = _mm256_mul_pd(val2, sclpar);
sum = _mm256_add_pd(val1, val2scl);
_mm256_storeu_pd(res+i, sum);
i += 4;
}
}
// process remaining
int n_i = n - i;
if (n_i==3) {
res[i] += vec[i]*scl;
res[i+1] += vec[i+1]*scl;
res[i+2] += vec[i+2]*scl;
} else if (n_i==2) {
res[i] += vec[i]*scl;
res[i+1] += vec[i+1]*scl;
} else if (n_i==1) {
res[i] += vec[i]*scl;
}
#else
for (; i<n; i++) {
res[i] += vec[i]*scl;
}
#endif
}
// res = vec1 + vec2*scl
void mju_addScl(mjtNum* res, const mjtNum* vec1, const mjtNum* vec2, mjtNum scl, int n) {
int i = 0;
#if defined(__AVX__) && defined(mjUSEAVX) && defined(mjUSEDOUBLE)
int n_4 = n - 4;
// vector part
if (n_4>=0) {
__m256d sclpar, sum, val1, val2, val2scl;
// init
sclpar = _mm256_set1_pd(scl);
// parallel computation
while (i<=n_4) {
val1 = _mm256_loadu_pd(vec1+i);
val2 = _mm256_loadu_pd(vec2+i);
val2scl = _mm256_mul_pd(val2, sclpar);
sum = _mm256_add_pd(val1, val2scl);
_mm256_storeu_pd(res+i, sum);
i += 4;
}
}
// process remaining
int n_i = n - i;
if (n_i==3) {
res[i] = vec1[i] + vec2[i]*scl;
res[i+1] = vec1[i+1] + vec2[i+1]*scl;
res[i+2] = vec1[i+2] + vec2[i+2]*scl;
} else if (n_i==2) {
res[i] = vec1[i] + vec2[i]*scl;
res[i+1] = vec1[i+1] + vec2[i+1]*scl;
} else if (n_i==1) {
res[i] = vec1[i] + vec2[i]*scl;
}
#else
for (; i<n; i++) {
res[i] = vec1[i] + vec2[i]*scl;
}
#endif
}
// normalize vector, return length before normalization
mjtNum mju_normalize(mjtNum* res, int n) {
mjtNum norm = (mjtNum)mju_sqrt(mju_dot(res, res, n));
mjtNum normInv;
if (norm<mjMINVAL) {
res[0] = 1;
for (int i=1; i<n; i++) {
res[i] = 0;
}
} else {
normInv = 1/norm;
for (int i=0; i<n; i++) {
res[i] *= normInv;
}
}
return norm;
}
// compute vector length (without normalizing)
mjtNum mju_norm(const mjtNum* res, int n) {
return mju_sqrt(mju_dot(res, res, n));
}
// vector dot-product
mjtNum mju_dot(const mjtNum* vec1, const mjtNum* vec2, const int n) {
mjtNum res = 0;
int i = 0;
int n_4 = n - 4;
#ifdef mjUSEAVX
// vector part
if (n_4>=0) {
__m256d sum, prod, val1, val2;
__m128d vlow, vhigh, high64;
// init
val1 = _mm256_loadu_pd(vec1);
val2 = _mm256_loadu_pd(vec2);
sum = _mm256_mul_pd(val1, val2);
i = 4;
// parallel computation
while (i<=n_4) {
val1 = _mm256_loadu_pd(vec1+i);
val2 = _mm256_loadu_pd(vec2+i);
prod = _mm256_mul_pd(val1, val2);
sum = _mm256_add_pd(sum, prod);
i += 4;
}
// reduce
vlow = _mm256_castpd256_pd128(sum);
vhigh = _mm256_extractf128_pd(sum, 1);
vlow = _mm_add_pd(vlow, vhigh);
high64 = _mm_unpackhi_pd(vlow, vlow);
res = _mm_cvtsd_f64(_mm_add_sd(vlow, high64));
}
#else
// do the same order of additions as the AVX intrinsics implementation.
// this is faster than the simple for loop you'd expect for a dot product,
// and produces exactly the same results.
mjtNum res0 = 0;
mjtNum res1 = 0;
mjtNum res2 = 0;
mjtNum res3 = 0;
for (; i<=n_4; i+=4) {
res0 += vec1[i] * vec2[i];
res1 += vec1[i+1] * vec2[i+1];
res2 += vec1[i+2] * vec2[i+2];
res3 += vec1[i+3] * vec2[i+3];
}
res = (res0 + res2) + (res1 + res3);
#endif
// process remaining
int n_i = n - i;
if (n_i==3) {
res += vec1[i]*vec2[i] + vec1[i+1]*vec2[i+1] + vec1[i+2]*vec2[i+2];
} else if (n_i==2) {
res += vec1[i]*vec2[i] + vec1[i+1]*vec2[i+1];
} else if (n_i==1) {
res += vec1[i]*vec2[i];
}
return res;
}
//------------------------------ matrix-vector operations ------------------------------------------
// multiply matrix and vector
void mju_mulMatVec(mjtNum* res, const mjtNum* mat, const mjtNum* vec,
int nr, int nc) {
for (int r=0; r<nr; r++) {
res[r] = mju_dot(mat + r*nc, vec, nc);
}
}
// multiply transposed matrix and vector
void mju_mulMatTVec(mjtNum* res, const mjtNum* mat, const mjtNum* vec,
int nr, int nc) {
mjtNum tmp;
mju_zero(res, nc);
for (int r=0; r<nr; r++) {
if ((tmp = vec[r])) {
mju_addToScl(res, mat+r*nc, tmp, nc);
}
}
}
//------------------------------ matrix-matrix operations ------------------------------------------
// transpose matrix
void mju_transpose(mjtNum* res, const mjtNum* mat, int nr, int nc) {
for (int i=0; i<nr; i++) {
for (int j=0; j<nc; j++) {
res[j*nr+i] = mat[i*nc+j];
}
}
}
// multiply matrices, exploit sparsity of mat1
void mju_mulMatMat(mjtNum* res, const mjtNum* mat1, const mjtNum* mat2,
int r1, int c1, int c2) {
mjtNum tmp;
mju_zero(res, r1*c2);
for (int i=0; i<r1; i++) {
for (int k=0; k<c1; k++) {
if ((tmp = mat1[i*c1+k])) {
mju_addToScl(res+i*c2, mat2+k*c2, tmp, c2);
}
}
}
}
// multiply matrices, second argument transposed
void mju_mulMatMatT(mjtNum* res, const mjtNum* mat1, const mjtNum* mat2,
int r1, int c1, int r2) {
for (int i=0; i<r1; i++) {
for (int j=0; j<r2; j++) {
res[i*r2+j] = mju_dot(mat1+i*c1, mat2+j*c1, c1);
}
}
}
// compute M'*diag*M (diag=NULL: compute M'*M)
void mju_sqrMatTD(mjtNum* res, const mjtNum* mat, const mjtNum* diag, int nr, int nc) {
mjtNum tmp;
// half of MatMat routine: only lower triangle
mju_zero(res, nc*nc);
if (diag) {
for (int j=0; j<nr; j++) {
if (diag[j]) {
for (int i=0; i<nc; i++) {
if ((tmp = mat[j*nc+i])) {
mju_addToScl(res+i*nc, mat+j*nc, tmp*diag[j], i+1);
}
}
}
}
} else {
for (int i=0; i<nc; i++) {
for (int j=0; j<nr; j++) {
if ((tmp = mat[j*nc+i])) {
mju_addToScl(res+i*nc, mat+j*nc, tmp, i+1);
}
}
}
}
// make symmetric
for (int i=0; i<nc; i++) {
for (int j=i+1; j<nc; j++) {
res[i*nc+j] = res[j*nc+i];
}
}
}
// multiply matrices, first argument transposed
void mju_mulMatTMat(mjtNum* res, const mjtNum* mat1, const mjtNum* mat2,
int r1, int c1, int c2) {
mjtNum tmp;
mju_zero(res, c1*c2);
for (int i=0; i<r1; i++) {
for (int j=0; j<c1; j++) {
if ((tmp = mat1[i*c1+j])) {
mju_addToScl(res+j*c2, mat2+i*c2, tmp, c2);
}
}
}
}
+207
View File
@@ -0,0 +1,207 @@
// 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_SRC_ENGINE_ENGINE_UTIL_BLAS_H_
#define MUJOCO_SRC_ENGINE_ENGINE_UTIL_BLAS_H_
#include <math.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
//------------------------------ standard library fuctions -----------------------------------------
#ifdef mjUSEDOUBLE
#define mju_sqrt sqrt
#define mju_exp exp
#define mju_sin sin
#define mju_cos cos
#define mju_tan tan
#define mju_asin asin
#define mju_acos acos
#define mju_atan2 atan2
#define mju_tanh tanh
#define mju_pow pow
#define mju_abs fabs
#define mju_log log
#define mju_log10 log10
#define mju_floor floor
#define mju_ceil ceil
#else
#define mju_sqrt sqrtf
#define mju_exp expf
#define mju_sin sinf
#define mju_cos cosf
#define mju_tan tanf
#define mju_asin asinf
#define mju_acos acosf
#define mju_atan2 atan2f
#define mju_tanh tanhf
#define mju_pow powf
#define mju_abs fabsf
#define mju_log logf
#define mju_log10 log10f
#define mju_floor floorf
#define mju_ceil ceilf
#endif
//------------------------------ 3D vector and matrix-vector operations ----------------------------
// res = 0
MJAPI void mju_zero3(mjtNum res[3]);
// res = vec
MJAPI void mju_copy3(mjtNum res[3], const mjtNum data[3]);
// res = vec*scl
MJAPI void mju_scl3(mjtNum res[3], const mjtNum vec[3], mjtNum scl);
// res = vec1 + vec2
MJAPI void mju_add3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3]);
// res = vec1 - vec2
MJAPI void mju_sub3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3]);
// res += vec
MJAPI void mju_addTo3(mjtNum res[3], const mjtNum vec[3]);
// res -= vec
MJAPI void mju_subFrom3(mjtNum res[3], const mjtNum vec[3]);
// res += vec*scl
MJAPI void mju_addToScl3(mjtNum res[3], const mjtNum vec[3], mjtNum scl);
// res = vec1 + vec2*scl
MJAPI void mju_addScl3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3], mjtNum scl);
// normalize vector, return length before normalization
MJAPI mjtNum mju_normalize3(mjtNum vec[3]);
// compute vector length (without normalizing)
MJAPI mjtNum mju_norm3(const mjtNum vec[3]);
// vector dot-product
MJAPI mjtNum mju_dot3(const mjtNum vec1[3], const mjtNum vec2[3]);
// Cartesian distance between 3D vectors
MJAPI mjtNum mju_dist3(const mjtNum pos1[3], const mjtNum pos2[3]);
// multiply vector by 3D rotation matrix
MJAPI void mju_rotVecMat(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]);
// multiply vector by transposed 3D rotation matrix
MJAPI void mju_rotVecMatT(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]);
//------------------------------ 4D/quaternion operations ------------------------------------------
// res = 0
MJAPI void mju_zero4(mjtNum res[4]);
// res = (1,0,0,0)
MJAPI void mju_unit4(mjtNum res[4]);
// res = vec
MJAPI void mju_copy4(mjtNum res[4], const mjtNum data[4]);
// normalize vector, return length before normalization
MJAPI mjtNum mju_normalize4(mjtNum vec[4]);
//------------------------------ general vector operations -----------------------------------------
// res = 0
MJAPI void mju_zero(mjtNum* res, int n);
// res = vec
MJAPI void mju_copy(mjtNum* res, const mjtNum* vec, int n);
// sum(vec)
MJAPI mjtNum mju_sum(const mjtNum* vec, int n);
// sum(abs(vec))
MJAPI mjtNum mju_L1(const mjtNum* vec, int n);
// res = vec*scl
MJAPI void mju_scl(mjtNum* res, const mjtNum* vec, mjtNum scl, int n);
// res = vec1 + vec2
MJAPI void mju_add(mjtNum* res, const mjtNum* vec1, const mjtNum* vec2, int n);
// res = vec1 - vec2
MJAPI void mju_sub(mjtNum* res, const mjtNum* vec1, const mjtNum* vec2, int n);
// res += vec
MJAPI void mju_addTo(mjtNum* res, const mjtNum* vec, int n);
// res -= vec
MJAPI void mju_subFrom(mjtNum* res, const mjtNum* vec, int n);
// res += vec*scl
MJAPI void mju_addToScl(mjtNum* res, const mjtNum* vec, mjtNum scl, int n);
// res = vec1 + vec2*scl
MJAPI void mju_addScl(mjtNum* res, const mjtNum* vec1, const mjtNum* vec2, mjtNum scl, int n);
// normalize vector, return length before normalization
MJAPI mjtNum mju_normalize(mjtNum* res, int n);
// compute vector length (without normalizing)
MJAPI mjtNum mju_norm(const mjtNum* res, int n);
// vector dot-product
MJAPI mjtNum mju_dot(const mjtNum* vec1, const mjtNum* vec2, const int n);
//------------------------------ matrix-vector operations ------------------------------------------
// multiply matrix and vector
MJAPI void mju_mulMatVec(mjtNum* res, const mjtNum* mat, const mjtNum* vec,
int nr, int nc);
// multiply transposed matrix and vector
MJAPI void mju_mulMatTVec(mjtNum* res, const mjtNum* mat, const mjtNum* vec,
int nr, int nc);
//------------------------------ matrix-matrix operations ------------------------------------------
// transpose matrix
MJAPI void mju_transpose(mjtNum* res, const mjtNum* mat, int nr, int nc);
// multiply matrices
MJAPI void mju_mulMatMat(mjtNum* res, const mjtNum* mat1, const mjtNum* mat2,
int r1, int c1, int c2);
// multiply matrices, second argument transposed
MJAPI void mju_mulMatMatT(mjtNum* res, const mjtNum* mat1, const mjtNum* mat2,
int r1, int c1, int r2);
// multiply matrices, first argument transposed
MJAPI void mju_mulMatTMat(mjtNum* res, const mjtNum* mat1, const mjtNum* mat2,
int r1, int c1, int c2);
// compute M'*diag*M (diag=NULL: compute M'*M)
MJAPI void mju_sqrMatTD(mjtNum* res, const mjtNum* mat, const mjtNum* diag, int nr, int nc);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_UTIL_BLAS_H_
+232
View File
@@ -0,0 +1,232 @@
// 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.
#include "engine/engine_util_errmem.h"
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include <unistd.h>
#endif
//------------------------- cross-platform aligned malloc/free -------------------------------------
static inline void* mju_alignedMalloc(size_t size, size_t align) {
#ifdef _WIN32
return _aligned_malloc(size, align);
#elif defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L
// Prefer posix_memalign since C11 aligned_alloc isn't available on macOS < 10.15.
void* ptr;
const int err = posix_memalign(&ptr, align, size);
if (err) {
ptr = NULL;
errno = err;
}
return ptr;
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
return aligned_alloc(align, size);
#endif
}
static inline void mju_alignedFree(void* ptr) {
#ifdef _WIN32
_aligned_free(ptr);
#else
free(ptr);
#endif
}
//------------------------- default user handlers --------------------------------------------------
// define and clear handlers
void (*mju_user_error) (const char*) = 0;
void (*mju_user_warning) (const char*) = 0;
void* (*mju_user_malloc) (size_t) = 0;
void (*mju_user_free) (void*) = 0;
// restore default processing
void mju_clearHandlers(void) {
mju_user_error = 0;
mju_user_warning = 0;
mju_user_malloc = 0;
mju_user_free = 0;
}
//------------------------- internal-only handlers -------------------------------------------------
typedef void (*callback_fn)(const char*);
#ifdef _MSC_VER
#define mjTHREADLOCAL __declspec(thread)
#else
#define mjTHREADLOCAL _Thread_local
#endif
static mjTHREADLOCAL callback_fn _mjPRIVATE_tls_error_fn = NULL;
static mjTHREADLOCAL callback_fn _mjPRIVATE_tls_warning_fn = NULL;
callback_fn _mjPRIVATE__get_tls_error_fn() {
return _mjPRIVATE_tls_error_fn;
}
void _mjPRIVATE__set_tls_error_fn(callback_fn h) {
_mjPRIVATE_tls_error_fn = h;
}
callback_fn _mjPRIVATE__get_tls_warning_fn() {
return _mjPRIVATE_tls_warning_fn;
}
void _mjPRIVATE__set_tls_warning_fn(callback_fn h) {
_mjPRIVATE_tls_warning_fn = h;
}
//------------------------------ error hadling -----------------------------------------------------
// write datetime, type: message to MUJOCO_LOG.TXT
void mju_writeLog(const char* type, const char* msg) {
time_t rawtime;
struct tm *timeinfo;
FILE* fp = fopen("MUJOCO_LOG.TXT", "a+t");
if (fp) {
// get time
time(&rawtime);
timeinfo = localtime(&rawtime);
// write to log file
fprintf(fp, "%s%s: %s\n\n", asctime(timeinfo), type, msg);
fclose(fp);
}
}
// write message to logfile and console, pause and exit
void mju_error(const char* msg) {
if (_mjPRIVATE_tls_error_fn) {
_mjPRIVATE_tls_error_fn(msg);
} else if (mju_user_error) {
mju_user_error(msg);
} else {
// write to log and console
mju_writeLog("ERROR", msg);
printf("ERROR: %s\n\nPress Enter to exit ...", msg);
// pause, exit
getchar();
exit(1);
}
}
// write message to logfile and console
void mju_warning(const char* msg) {
if (_mjPRIVATE_tls_warning_fn) {
_mjPRIVATE_tls_warning_fn(msg);
} else if (mju_user_warning) {
mju_user_warning(msg);
} else {
// write to log file and console
mju_writeLog("WARNING", msg);
printf("WARNING: %s\n\n", msg);
}
}
// error with int argument
void mju_error_i(const char* msg, int i) {
char errmsg[1000];
snprintf(errmsg, 1000, msg, i);
errmsg[999] = '\0';
mju_error(errmsg);
}
// warning with int argument
void mju_warning_i(const char* msg, int i) {
char wrnmsg[1000];
snprintf(wrnmsg, 1000, msg, i);
wrnmsg[999] = '\0';
mju_warning(wrnmsg);
}
// error string argument
void mju_error_s(const char* msg, const char* text) {
char errmsg[1000];
snprintf(errmsg, 1000, msg, text);
errmsg[999] = '\0';
mju_error(errmsg);
}
// warning string argument
void mju_warning_s(const char* msg, const char* text) {
char wrnmsg[1000];
snprintf(wrnmsg, 1000, msg, text);
wrnmsg[999] = '\0';
mju_warning(wrnmsg);
}
//------------------------------ malloc and free ---------------------------------------------------
// allocate memory; byte-align on 8; pad size to multiple of 8
void* mju_malloc(size_t size) {
void* ptr = 0;
// user allocator
if (mju_user_malloc) {
ptr = mju_user_malloc(size);
}
// default allocator
else {
// pad size to multiple of 8
if ((size%8)) {
size += 8 - (size%8);
}
// allocate
ptr = mju_alignedMalloc(size, 8);
}
// error if null pointer
if (!ptr) {
mju_error("Could not allocate memory");
}
return ptr;
}
// free memory
void mju_free(void* ptr) {
// return if null
if (!ptr) {
return;
}
// free with user or built-in function
if (mju_user_free) {
mju_user_free(ptr);
} else {
mju_alignedFree(ptr);
}
}
+69
View File
@@ -0,0 +1,69 @@
// 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_SRC_ENGINE_ENGINE_UTIL_ERRMEM_H_
#define MUJOCO_SRC_ENGINE_ENGINE_UTIL_ERRMEM_H_
#include <stddef.h>
#include <mujoco/mjexport.h>
#ifdef __cplusplus
extern "C" {
#endif
//------------------------------ user handlers -----------------------------------------------------
MJAPI extern void (*mju_user_error)(const char*);
MJAPI extern void (*mju_user_warning)(const char*);
MJAPI extern void* (*mju_user_malloc)(size_t);
MJAPI extern void (*mju_user_free)(void*);
// clear user handlers; restore default processing
MJAPI void mju_clearHandlers(void);
// gets/sets thread-local error/warning handlers for internal use
MJAPI void (*_mjPRIVATE__get_tls_error_fn(void))(const char*);
MJAPI void _mjPRIVATE__set_tls_error_fn(void (*h)(const char*));
MJAPI void (*_mjPRIVATE__get_tls_warning_fn(void))(const char*);
MJAPI void _mjPRIVATE__set_tls_warning_fn(void (*h)(const char*));
//------------------------------ errors and warnings -----------------------------------------------
// errors
MJAPI void mju_error(const char* msg);
MJAPI void mju_error_i(const char* msg, int i);
MJAPI void mju_error_s(const char* msg, const char* text);
// warnings
MJAPI void mju_warning(const char* msg);
MJAPI void mju_warning_i(const char* msg, int i);
MJAPI void mju_warning_s(const char* msg, const char* text);
// write [datetime, type: message] to MUJOCO_LOG.TXT
MJAPI void mju_writeLog(const char* type, const char* msg);
//------------------------------ malloc and free ---------------------------------------------------
// allocate memory; byte-align on 8; pad size to multiple of 8
MJAPI void* mju_malloc(size_t size);
// free memory with free() by default
MJAPI void mju_free(void* ptr);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_UTIL_ERRMEM_H_
File diff suppressed because it is too large Load Diff
+126
View File
@@ -0,0 +1,126 @@
// 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_SRC_ENGINE_ENGINE_UTIL_MISC_H_
#define MUJOCO_SRC_ENGINE_ENGINE_UTIL_MISC_H_
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
//------------------------------ tendons and actuators ---------------------------------------------
// wrap tendons around spheres and cylinders
mjtNum mju_wrap(mjtNum* wpnt, const mjtNum* x0, const mjtNum* x1,
const mjtNum* xpos, const mjtNum* xmat, const mjtNum* size,
int type, const mjtNum* side);
// muscle active force, prm = (range[2], force, scale, lmin, lmax, vmax, fpmax, fvmax)
MJAPI mjtNum mju_muscleGain(mjtNum len, mjtNum vel, const mjtNum lengthrange[2],
mjtNum acc0, const mjtNum prm[9]);
// muscle passive force, prm = (range[2], force, scale, lmin, lmax, vmax, fpmax, fvmax)
MJAPI mjtNum mju_muscleBias(mjtNum len, const mjtNum lengthrange[2],
mjtNum acc0, const mjtNum prm[9]);
// muscle activation dynamics, prm = (tau_act, tau_deact)
MJAPI mjtNum mju_muscleDynamics(mjtNum ctrl, mjtNum act, const mjtNum prm[2]);
//------------------------------ misclellaneous ----------------------------------------------------
// convert contact force to pyramid representation
MJAPI void mju_encodePyramid(mjtNum* pyramid, const mjtNum* force,
const mjtNum* mu, int dim);
// convert pyramid representation to contact force
MJAPI void mju_decodePyramid(mjtNum* force, const mjtNum* pyramid,
const mjtNum* mu, int dim);
// integrate spring-damper analytically, return pos(dt)
MJAPI mjtNum mju_springDamper(mjtNum pos0, mjtNum vel0, mjtNum Kp, mjtNum Kv, mjtNum dt);
// print matrix
MJAPI void mju_printMat(const mjtNum* mat, int nr, int nc);
// print sparse matrix to screen
MJAPI void mju_printMatSparse(const mjtNum* mat, int nr,
const int* rownnz, const int* rowadr,
const int* colind);
// min function, single evaluation of a and b
MJAPI mjtNum mju_min(mjtNum a, mjtNum b);
// max function, single evaluation of a and b
MJAPI mjtNum mju_max(mjtNum a, mjtNum b);
// sign function
MJAPI mjtNum mju_sign(mjtNum x);
// round to nearest integer
MJAPI int mju_round(mjtNum x);
// convert type id (mjtObj) to type name
MJAPI const char* mju_type2Str(int type);
// convert type name to type id (mjtObj)
MJAPI int mju_str2Type(const char* str);
// warning text
MJAPI const char* mju_warningText(int warning, int info);
// return 1 if nan or abs(x)>mjMAXVAL, 0 otherwise
MJAPI int mju_isBad(mjtNum x);
// return 1 if all elements are 0
MJAPI int mju_isZero(mjtNum* vec, int n);
// standard normal random number generator (optional second number)
MJAPI mjtNum mju_standardNormal(mjtNum* num2);
// convert from float to mjtNum
MJAPI void mju_f2n(mjtNum* res, const float* vec, int n);
// convert from mjtNum to float
MJAPI void mju_n2f(float* res, const mjtNum* vec, int n);
// convert from double to mjtNum
MJAPI void mju_d2n(mjtNum* res, const double* vec, int n);
// convert from mjtNum to double
MJAPI void mju_n2d(double* res, const mjtNum* vec, int n);
// insertion sort, increasing order
MJAPI void mju_insertionSort(mjtNum* list, int n);
// integer insertion sort, increasing order
MJAPI void mju_insertionSortInt(int* list, int n);
// Halton sequence
MJAPI mjtNum mju_Halton(int index, int base);
// Call strncpy, then set dst[n-1] = 0.
MJAPI char* mju_strncpy(char *dst, const char *src, int n);
// Sigmoid function over 0<=x<=1 constructed from half-quadratics.
MJAPI mjtNum mju_sigmoid(mjtNum x);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_UTIL_MISC_H_
+632
View File
@@ -0,0 +1,632 @@
// 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.
#include "engine/engine_util_solve.h"
#include <math.h>
#include <string.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjmodel.h>
#include "engine/engine_io.h"
#include "engine/engine_macro.h"
#include "engine/engine_util_blas.h"
#include "engine/engine_util_errmem.h"
#include "engine/engine_util_sparse.h"
#include "engine/engine_util_spatial.h"
//---------------------------- dense Cholesky ------------------------------------------------------
// Cholesky decomposition: mat = L*L'; return 'rank'
int mju_cholFactor(mjtNum* mat, int n, mjtNum mindiag) {
int rank = n;
mjtNum tmp;
// in-place Cholesky factorization
for (int j=0; j<n; j++) {
// compute new diagonal
tmp = mat[j*(n+1)];
if (j) {
tmp -= mju_dot(mat+j*n, mat+j*n, j);
}
// correct diagonal values below threshold
if (tmp<mindiag) {
tmp = mindiag;
rank--;
}
// save diagonal
mat[j*(n+1)] = mju_sqrt(tmp);
// process off-diagonal entries
tmp = 1/mat[j*(n+1)];
for (int i=j+1; i<n; i++) {
mat[i*n+j] = (mat[i*n+j] - mju_dot(mat+i*n, mat+j*n, j)) * tmp;
}
}
return rank;
}
// Cholesky solve
void mju_cholSolve(mjtNum* res, const mjtNum* mat, const mjtNum* vec, int n) {
// copy if source and destination are different
if (res!=vec) {
mju_copy(res, vec, n);
}
// forward substitution: solve L*res = vec
for (int i=0; i<n; i++) {
if (i) {
res[i] -= mju_dot(mat+i*n, res, i);
}
// diagonal
res[i] /= mat[i*(n+1)];
}
// backward substitution: solve L'*res = res
for (int i=n-1; i>=0; i--) {
if (i<n-1) {
for (int j=i+1; j<n; j++) {
res[i] -= mat[j*n+i] * res[j];
}
}
// diagonal
res[i] /= mat[i*(n+1)];
}
}
// Cholesky rank-one update: L*L' +/- x*x'; return rank
int mju_cholUpdate(mjtNum* mat, mjtNum* x, int n, int flg_plus) {
int rank = n;
mjtNum r, c, cinv, s, Lkk, tmp;
for (int k=0; k<n; k++) {
if (x[k]) {
// prepare constants
Lkk = mat[k*(n+1)];
tmp = Lkk*Lkk + (flg_plus ? x[k]*x[k] : -x[k]*x[k]);
if (tmp<mjMINVAL) {
tmp = mjMINVAL;
rank--;
}
r = mju_sqrt(tmp);
c = r / Lkk;
cinv = 1 / c;
s = x[k] / Lkk;
// update diagonal
mat[k*(n+1)] = r;
// update mat
if (flg_plus)
for (int i=k+1; i<n; i++) {
mat[i*n+k] = (mat[i*n+k] + s*x[i])*cinv;
} else
for (int i=k+1; i<n; i++) {
mat[i*n+k] = (mat[i*n+k] - s*x[i])*cinv;
}
// update x
for (int i=k+1; i<n; i++) {
x[i] = c*x[i] - s*mat[i*n+k];
}
}
}
return rank;
}
//---------------------------- sparse Cholesky -----------------------------------------------------
// sparse reverse-order Cholesky decomposition: mat = L'*L; return 'rank'
// mat must have uncompressed layout; rownnz is modified to end at diagonal
int mju_cholFactorSparse(mjtNum* mat, int n, mjtNum mindiag,
int* rownnz, int* rowadr, int* colind,
mjData* d) {
int rank = n;
mjMARKSTACK;
int* buf_ind = (int*) mj_stackAlloc(d, n);
mjtNum* sparse_buf = mj_stackAlloc(d, n);
// shrink rows so that rownnz ends at diagonal
for (int r=0; r<n; r++) {
// shrink
while (rownnz[r]>0 && colind[rowadr[r]+rownnz[r]-1]>r) {
rownnz[r]--;
}
// check
if (rownnz[r]==0 || colind[rowadr[r]+rownnz[r]-1]!=r) {
mju_error("Matrix must have non-zero diagonal in mju_cholFactorSparse");
}
}
// backpass over rows
for (int r=n-1; r>=0; r--) {
// get rownnz and rowadr for row r
int nnz = rownnz[r], adr = rowadr[r];
// update row r diagonal
mjtNum tmp = mat[adr+nnz-1];
if (tmp<mindiag) {
tmp = mindiag;
rank--;
}
mat[adr+nnz-1] = mju_sqrt(tmp);
tmp = 1/mat[adr+nnz-1];
// update row r before diagonal
for (int i=0; i<nnz-1; i++) {
mat[adr+i] *= tmp;
}
// update row c<r where mat(r,c)!=0
for (int i=0; i<nnz-1; i++) {
// get column index
int c = colind[adr+i];
// mat(c,0:c) = mat(c,0:c) - mat(r,c) * mat(r,0:c)
int nnz_c = mju_combineSparse(mat + rowadr[c], mat+rowadr[r], c + 1, 1, -mat[adr+i],
rownnz[c], i+1, colind+rowadr[c], colind+rowadr[r],
sparse_buf, buf_ind);
// assign new nnz to row c
rownnz[c] = nnz_c;
}
}
mjFREESTACK;
return rank;
}
// sparse reverse-order Cholesky solve
void mju_cholSolveSparse(mjtNum* res, const mjtNum* mat, const mjtNum* vec, int n,
const int* rownnz, const int* rowadr, const int* colind) {
// copy input into result
mju_copy(res, vec, n);
// vec <- L^-T vec
for (int i=n-1; i>=0; i--) {
if (res[i]) {
// get rowadr[i], rownnz[i]
const int adr = rowadr[i], nnz = rownnz[i];
// x(i) /= L(i,i)
res[i] /= mat[adr+nnz-1];
mjtNum tmp = res[i];
// x(j) -= L(i,j)*x(i), j=0:i-1
for (int j=0; j<nnz-1; j++) {
res[colind[adr+j]] -= mat[adr+j]*tmp;
}
}
}
// vec <- L^-1 vec
for (int i=0; i<n; i++) {
// get rowadr[i], rownnz[i]
const int adr = rowadr[i], nnz = rownnz[i];
// x(i) -= sum_j L(i,j)*x(j), j=0:i-1
if (nnz>1) {
res[i] -= mju_dotSparse(mat+adr, res, nnz-1, colind+adr);
// modulo AVX, the above line does
// for (int j=0; j<nnz-1; j++)
// res[i] -= mat[adr+j]*res[colind[adr+j]];
}
// x(i) /= L(i,i)
res[i] /= mat[adr+nnz-1];
}
}
// sparse reverse-order Cholesky rank-one update: L'*L +/- x*x'; return rank
// x is sparse, change in sparsity pattern of mat is not allowed
int mju_cholUpdateSparse(mjtNum* mat, mjtNum* x, int n, int flg_plus,
int* rownnz, int* rowadr, int* colind, int x_nnz, int* x_ind,
mjData* d) {
mjMARKSTACK;
int* buf_ind = (int*) mj_stackAlloc(d, n);
mjtNum* sparse_buf = mj_stackAlloc(d, n);
// backpass over rows corresponding to non-zero x(r)
int rank = n, i = x_nnz - 1;
while (i>=0) {
// get rownnz and rowadr for this row
int nnz = rownnz[x_ind[i]], adr = rowadr[x_ind[i]];
// compute quantities
mjtNum tmp = mat[adr+nnz-1]*mat[adr+nnz-1] + (flg_plus ? x[i]*x[i] : -x[i]*x[i]);
if (tmp<mjMINVAL) {
tmp = mjMINVAL;
rank--;
}
mjtNum r = mju_sqrt(tmp);
mjtNum c = r / mat[adr+nnz-1];
mjtNum s = x[i] / mat[adr+nnz-1];
// update diagonal
mat[adr+nnz-1] = r;
// update row: mat(r,1:r-1) = (mat(r,1:r-1) + s*x(1:r-1)) / c
int new_nnz = mju_combineSparse(mat + adr, x, n, 1 / c, (flg_plus ? s / c : -s / c),
nnz-1, i, colind + adr, x_ind,
sparse_buf, buf_ind);
// check for size change
if (new_nnz!=nnz-1) {
mju_error("Varying sparsity pattern in mju_cholUpdateSparse");
}
// update x: x(1:r-1) = c*x(1:r-1) - s*mat(r,1:r-1)
int new_x_nnz = mju_combineSparse(x, mat+adr, n, c, -s,
i, nnz-1, x_ind, colind+adr,
sparse_buf, buf_ind);
// update i, correct for changing x
i = i - 1 + (new_x_nnz - i);
}
mjFREESTACK;
return rank;
}
//--------------------------- eigen decomposition --------------------------------------------------
// eigenvalue decomposition of symmetric 3x3 matrix
static const mjtNum eigEPS = 1E-12;
int mju_eig3(mjtNum* eigval, mjtNum* eigvec, mjtNum quat[4], const mjtNum mat[9]) {
mjtNum D[9], tmp[9];
mjtNum tau, t, c;
int iter, rk, ck, rotk;
// initialize with unit quaternion
quat[0] = 1;
quat[1] = quat[2] = quat[3] = 0;
// Jacobi iteration
for (iter=0; iter<500; iter++) {
// make quaternion matrix eigvec, compute D = eigvec'*mat*eigvec
mju_quat2Mat(eigvec, quat);
mju_mulMatTMat(tmp, eigvec, mat, 3, 3, 3);
mju_mulMatMat(D, tmp, eigvec, 3, 3, 3);
// assign eigenvalues
eigval[0] = D[0];
eigval[1] = D[4];
eigval[2] = D[8];
// find max off-diagonal element, set indices
if (fabs(D[1])>fabs(D[2]) && fabs(D[1])>fabs(D[5])) {
rk = 0; // row
ck = 1; // column
rotk = 2; // rotation axis
} else if (fabs(D[2])>fabs(D[5])) {
rk = 0;
ck = 2;
rotk = 1;
} else {
rk = 1;
ck = 2;
rotk = 0;
}
// terminate if max off-diagonal element too small
if (fabs(D[3*rk+ck])<eigEPS) {
break;
}
// 2x2 symmetric Schur decomposition
tau = (D[4*ck]-D[4*rk])/(2*D[3*rk+ck]);
if (tau>=0) {
t = 1.0/(tau + mju_sqrt(1 + tau*tau));
} else {
t = -1.0/(-tau + mju_sqrt(1 + tau*tau));
}
c = 1.0/mju_sqrt(1 + t*t);
// terminate if cosine too close to 1
if (c>1.0-eigEPS) {
break;
}
// express rotation as quaternion
tmp[1] = tmp[2] = tmp[3] = 0;
tmp[rotk+1] = (tau>=0 ? -mju_sqrt(0.5-0.5*c) : mju_sqrt(0.5-0.5*c));
if (rotk==1) {
tmp[rotk+1] = -tmp[rotk+1];
}
tmp[0] = mju_sqrt(1.0 - tmp[rotk+1]*tmp[rotk+1]);
mju_normalize4(tmp);
// accumulate quaternion rotation
mju_mulQuat(tmp+4, quat, tmp);
mju_copy4(quat, tmp+4);
mju_normalize4(quat);
}
// sort eigenvalues in decreasing order (bubblesort: 0, 1, 0)
for (int j=0; j<3; j++) {
int j1 = j%2; // lead index
if (eigval[j1] < eigval[j1+1]) {
// swap eigenvalues
t = eigval[j1];
eigval[j1] = eigval[j1+1];
eigval[j1+1] = t;
// rotate quaternion
tmp[0] = 0.707106781186548; // mju_cos(pi/4) = mju_sin(pi/4)
tmp[1] = tmp[2] = tmp[3] = 0;
tmp[(j1+2)%3+1] = tmp[0];
mju_mulQuat(tmp+4, quat, tmp);
mju_copy4(quat, tmp+4);
mju_normalize4(quat);
}
}
// recompute eigvec
mju_quat2Mat(eigvec, quat);
return iter;
}
//---------------------------------- QCQP ----------------------------------------------------------
// solve QCQP in 2 dimensions:
// min 0.5*x'*A*x + x'*b s.t. sum (xi/di)^2 <= r^2
// return 0 if unconstrained, 1 if constrained
int mju_QCQP2(mjtNum* res, const mjtNum* Ain, const mjtNum* bin,
const mjtNum* d, mjtNum r) {
mjtNum A11, A22, A12, b1, b2;
mjtNum P11, P22, P12, det, detinv, v1, v2, la, val, deriv;
// scale A,b so that constraint becomes x'*x <= r*r
b1 = bin[0]*d[0];
b2 = bin[1]*d[1];
A11 = Ain[0]*d[0]*d[0];
A22 = Ain[3]*d[1]*d[1];
A12 = Ain[1]*d[0]*d[1];
// Newton iteration
la = 0;
for (int iter=0; iter<20; iter++) {
// det(A+la)
det = (A11+la)*(A22+la) - A12*A12;
// check SPD, with 1e-10 threshold
if (det<1e-10) {
res[0] = 0;
res[1] = 0;
return 0;
}
// P = inv(A+la)
detinv = 1/det;
P11 = (A22+la)*detinv;
P22 = (A11+la)*detinv;
P12 = -A12*detinv;
// v = -P*b
v1 = -P11*b1 - P12*b2;
v2 = -P12*b1 - P22*b2;
// val = v'*v - r*r
val = v1*v1 + v2*v2 - r*r;
// check for convergence, or initial solution inside constraint set
if (val<1e-10) {
break;
}
// deriv = -2 * v' * P * v
deriv = -2.0*(P11*v1*v1 + 2.0*P12*v1*v2 + P22*v2*v2);
// compute update, exit if too small
mjtNum delta = -val/deriv;
if (delta<1e-10) {
break;
}
// update
la += delta;
}
// undo scaling
res[0] = v1*d[0];
res[1] = v2*d[1];
return (la!=0);
}
// solve QCQP in 3 dimensions:
// min 0.5*x'*A*x + x'*b s.t. sum (xi/di)^2 <= r^2
// return 0 if unconstrained, 1 if constrained
int mju_QCQP3(mjtNum* res, const mjtNum* Ain, const mjtNum* bin,
const mjtNum* d, mjtNum r) {
mjtNum A11, A22, A33, A12, A13, A23, b1, b2, b3;
mjtNum P11, P22, P33, P12, P13, P23, det, detinv, v1, v2, v3, la, val, deriv;
// scale A,b so that constraint becomes x'*x <= r*r
b1 = bin[0]*d[0];
b2 = bin[1]*d[1];
b3 = bin[2]*d[2];
A11 = Ain[0]*d[0]*d[0];
A22 = Ain[4]*d[1]*d[1];
A33 = Ain[8]*d[2]*d[2];
A12 = Ain[1]*d[0]*d[1];
A13 = Ain[2]*d[0]*d[2];
A23 = Ain[5]*d[1]*d[2];
// Newton iteration
la = 0;
for (int iter=0; iter<20; iter++) {
// unscaled P
P11 = (A22+la)*(A33+la) - A23*A23;
P22 = (A11+la)*(A33+la) - A13*A13;
P33 = (A11+la)*(A22+la) - A12*A12;
P12 = A13*A23 - A12*(A33+la);
P13 = A12*A23 - A13*(A22+la);
P23 = A12*A13 - A23*(A11+la);
// det(A+la)
det = (A11+la)*P11 + A12*P12 + A13*P13;
// check SPD, with 1e-10 threshold
if (det<1e-10) {
res[0] = 0;
res[1] = 0;
res[2] = 0;
return 0;
}
// detinv
detinv = 1/det;
// final P
P11 *= detinv;
P22 *= detinv;
P33 *= detinv;
P12 *= detinv;
P13 *= detinv;
P23 *= detinv;
// v = -P*b
v1 = -P11*b1 - P12*b2 - P13*b3;
v2 = -P12*b1 - P22*b2 - P23*b3;
v3 = -P13*b1 - P23*b2 - P33*b3;
// val = v'*v - r*r
val = v1*v1 + v2*v2 + v3*v3 - r*r;
// check for convergence, or initial solution inside constraint set
if (val<1e-10) {
break;
}
// deriv = -2 * v' * P * v
deriv = -2.0*(P11*v1*v1 + P22*v2*v2 + P33*v3*v3)
-4.0*(P12*v1*v2 + P13*v1*v3 + P23*v2*v3);
// compute update, exit if too small
mjtNum delta = -val/deriv;
if (delta<1e-10) {
break;
}
// update
la += delta;
}
// undo scaling
res[0] = v1*d[0];
res[1] = v2*d[1];
res[2] = v3*d[2];
return (la!=0);
}
// solve QCQP in n dimensions:
// min 0.5*x'*A*x + x'*b s.t. sum (xi/di)^2 <= r^2
// return 0 if unconstrained, 1 if constrained
int mju_QCQP(mjtNum* res, const mjtNum* Ain, const mjtNum* bin,
const mjtNum* d, mjtNum r, int n) {
mjtNum A[25], Ala[25], b[5];
mjtNum la, val, deriv, tmp[5];
// check size
if (n>5) {
mju_error("mju_QCQP supports n up to 5");
}
// scale A,b so that constraint becomes x'*x <= r*r
for (int i=0; i<n; i++) {
b[i] = bin[i] * d[i];
for (int j=0; j<n; j++) {
A[j+i*n] = Ain[j+i*n] * d[i] * d[j];
}
}
// Newton iteration
la = 0;
for (int iter=0; iter<20; iter++) {
// make A+la
mju_copy(Ala, A, n*n);
for (int i=0; i<n; i++) {
Ala[i*(n+1)] += la;
}
// factorize, check rank with 1e-10 threshold
if (mju_cholFactor(Ala, n, 1e-10) < n) {
mju_zero(res, n);
return 0;
}
// set res = -Ala \ b
mju_cholSolve(res, Ala, b, n);
mju_scl(res, res, -1, n);
// val = b' * Ala^-2 * b - r*r
val = mju_dot(res, res, n) - r*r;
// check for convergence, or initial solution inside constraint set
if (val<1e-10) {
break;
}
// deriv = -2 * b' * Ala^-3 * b
mju_cholSolve(tmp, Ala, res, n);
deriv = -2.0 * mju_dot(res, tmp, n);
// compute update, exit if too small
mjtNum delta = -val/deriv;
if (delta<1e-10) {
break;
}
// update
la += delta;
}
// undo scaling
for (int i=0; i<n; i++) {
res[i] = res[i] * d[i];
}
return (la!=0);
}
+73
View File
@@ -0,0 +1,73 @@
// 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_SRC_ENGINE_ENGINE_UTIL_SOLVE_H_
#define MUJOCO_SRC_ENGINE_ENGINE_UTIL_SOLVE_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
// Cholesky decomposition: mat = L*L'; return rank
MJAPI int mju_cholFactor(mjtNum* mat, int n, mjtNum mindiag);
// Cholesky solve
MJAPI void mju_cholSolve(mjtNum* res, const mjtNum* mat, const mjtNum* vec, int n);
// Cholesky rank-one update: L*L' +/- x*x'; return rank
MJAPI int mju_cholUpdate(mjtNum* mat, mjtNum* x, int n, int flg_plus);
// sparse reverse-order Cholesky decomposition: mat = L'*L; return 'rank'
// mat must have uncompressed layout; rownnz is modified to end at diagonal
int mju_cholFactorSparse(mjtNum* mat, int n, mjtNum mindiag,
int* rownnz, int* rowadr, int* colind,
mjData* d);
// sparse reverse-order Cholesky solve
void mju_cholSolveSparse(mjtNum* res, const mjtNum* mat, const mjtNum* vec, int n,
const int* rownnz, const int* rowadr, const int* colind);
// sparse reverse-order Cholesky rank-one update: L'*L +/i x*x'; return rank
// x is sparse, change in sparsity pattern of mat is not allowed
int mju_cholUpdateSparse(mjtNum* mat, mjtNum* x, int n, int flg_plus,
int* rownnz, int* rowadr, int* colind, int x_nnz, int* x_ind,
mjData* d);
// eigenvalue decomposition of symmetric 3x3 matrix
MJAPI int mju_eig3(mjtNum* eigval, mjtNum* eigvec, mjtNum quat[4], const mjtNum mat[9]);
// solve QCQP in 2 dimensions:
// min 0.5*x'*A*x + x'*b s.t. sum (xi/di)^2 <= r^2
// return 0 if unconstrained, 1 if constrained
MJAPI int mju_QCQP2(mjtNum* res, const mjtNum* Ain, const mjtNum* bin, const mjtNum* d, mjtNum r);
// solve QCQP in 3 dimensions:
// min 0.5*x'*A*x + x'*b s.t. sum (xi/di)^2 <= r^2
// return 0 if unconstrained, 1 if constrained
MJAPI int mju_QCQP3(mjtNum* res, const mjtNum* Ain, const mjtNum* bin, const mjtNum* d, mjtNum r);
// solve QCQP in n<=5 dimensions:
// min 0.5*x'*A*x + x'*b s.t. sum (xi/di)^2 <= r^2
// return 0 if unconstrained, 1 if constrained
int mju_QCQP(mjtNum* res, const mjtNum* Ain, const mjtNum* bin, const mjtNum* d, mjtNum r, int n);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_UTIL_SOLVE_H_
+735
View File
@@ -0,0 +1,735 @@
// 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.
#include "engine/engine_util_sparse.h"
#include <string.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjtnum.h>
#include "engine/engine_io.h"
#include "engine/engine_macro.h"
#include "engine/engine_util_blas.h"
#include "engine/engine_util_errmem.h"
#ifdef mjUSEPLATFORMSIMD
#if defined(__AVX__) && defined(mjUSEDOUBLE)
#define mjUSEAVX
#include "immintrin.h"
#endif
#endif
//------------------------------ sparse operations -------------------------------------------------
// dot-product, first vector is sparse
mjtNum mju_dotSparse(const mjtNum* vec1, const mjtNum* vec2,
const int nnz1, const int* ind1) {
int i = 0;
mjtNum res = 0;
#ifdef mjUSEAVX
int nnz1_4 = nnz1 - 4;
// vector part
if (nnz1_4>=0) {
__m256d sum, prod, val1, val2;
__m128d vlow, vhigh, high64;
// init
val2 = _mm256_set_pd(vec2[ind1[3]],
vec2[ind1[2]],
vec2[ind1[1]],
vec2[ind1[0]]);
val1 = _mm256_loadu_pd(vec1);
sum = _mm256_mul_pd(val1, val2);
i = 4;
// parallel computation
while (i<=nnz1_4) {
val1 = _mm256_loadu_pd(vec1+i);
val2 = _mm256_set_pd(vec2[ind1[i+3]],
vec2[ind1[i+2]],
vec2[ind1[i+1]],
vec2[ind1[i+0]]);
prod = _mm256_mul_pd(val1, val2);
sum = _mm256_add_pd(sum, prod);
i += 4;
}
// reduce
vlow = _mm256_castpd256_pd128(sum);
vhigh = _mm256_extractf128_pd(sum, 1);
vlow = _mm_add_pd(vlow, vhigh);
high64 = _mm_unpackhi_pd(vlow, vlow);
res = _mm_cvtsd_f64(_mm_add_sd(vlow, high64));
}
#endif
// scalar part
for (; i<nnz1; i++) {
res += vec1[i] * vec2[ind1[i]];
}
return res;
}
// dot-productX3, first vector is sparse; supernode of size 3
void mju_dotSparseX3(mjtNum* res0, mjtNum* res1, mjtNum* res2,
const mjtNum* vec10, const mjtNum* vec11, const mjtNum* vec12,
const mjtNum* vec2, const int nnz1, const int* ind1) {
int i = 0;
// clear result
mjtNum RES0 = 0;
mjtNum RES1 = 0;
mjtNum RES2 = 0;
#ifdef mjUSEAVX
int nnz1_4 = nnz1 - 4;
// vector part
if (nnz1_4>=0) {
__m256d sum0, sum1, sum2, prod, val1, val2;
__m128d vlow, vhigh, high64;
// init
val2 = _mm256_set_pd(vec2[ind1[3]],
vec2[ind1[2]],
vec2[ind1[1]],
vec2[ind1[0]]);
val1 = _mm256_loadu_pd(vec10);
sum0 = _mm256_mul_pd(val1, val2);
val1 = _mm256_loadu_pd(vec11);
sum1 = _mm256_mul_pd(val1, val2);
val1 = _mm256_loadu_pd(vec12);
sum2 = _mm256_mul_pd(val1, val2);
i = 4;
// parallel computation
while (i<=nnz1_4) {
// get val2 only once
val2 = _mm256_set_pd(vec2[ind1[i+3]],
vec2[ind1[i+2]],
vec2[ind1[i+1]],
vec2[ind1[i+0]]);
// process each val1
val1 = _mm256_loadu_pd(vec10+i);
prod = _mm256_mul_pd(val1, val2);
sum0 = _mm256_add_pd(sum0, prod);
val1 = _mm256_loadu_pd(vec11+i);
prod = _mm256_mul_pd(val1, val2);
sum1 = _mm256_add_pd(sum1, prod);
val1 = _mm256_loadu_pd(vec12+i);
prod = _mm256_mul_pd(val1, val2);
sum2 = _mm256_add_pd(sum2, prod);
i += 4;
}
// reduce
vlow = _mm256_castpd256_pd128(sum0);
vhigh = _mm256_extractf128_pd(sum0, 1);
vlow = _mm_add_pd(vlow, vhigh);
high64 = _mm_unpackhi_pd(vlow, vlow);
RES0 = _mm_cvtsd_f64(_mm_add_sd(vlow, high64));
vlow = _mm256_castpd256_pd128(sum1);
vhigh = _mm256_extractf128_pd(sum1, 1);
vlow = _mm_add_pd(vlow, vhigh);
high64 = _mm_unpackhi_pd(vlow, vlow);
RES1 = _mm_cvtsd_f64(_mm_add_sd(vlow, high64));
vlow = _mm256_castpd256_pd128(sum2);
vhigh = _mm256_extractf128_pd(sum2, 1);
vlow = _mm_add_pd(vlow, vhigh);
high64 = _mm_unpackhi_pd(vlow, vlow);
RES2 = _mm_cvtsd_f64(_mm_add_sd(vlow, high64));
}
#endif
// scalar part
for (; i<nnz1; i++) {
mjtNum v2 = vec2[ind1[i]];
RES0 += vec10[i] * v2;
RES1 += vec11[i] * v2;
RES2 += vec12[i] * v2;
}
// copy result
*res0 = RES0;
*res1 = RES1;
*res2 = RES2;
}
// dot-product, both vectors are sparse
mjtNum mju_dotSparse2(const mjtNum* vec1, const mjtNum* vec2,
const int nnz1, const int* ind1,
const int nnz2, const int* ind2) {
int i1 = 0, i2 = 0;
mjtNum res = 0;
// check for empty array
if (!nnz1 || !nnz2) {
return 0;
}
while (i1<nnz1 && i2<nnz2) {
// get current indices
int adr1 = ind1[i1], adr2 = ind2[i2];
// match: accumulate result, advance both
if (adr1==adr2) {
res += vec1[i1++] * vec2[i2++];
}
// otherwise advance smaller
else if (adr1<adr2) {
i1++;
} else {
i2++;
}
}
return res;
}
// convert matrix from dense to sparse
void mju_dense2sparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
int* rownnz, int* rowadr, int* colind) {
int adr = 0;
// find non-zeros and construct sparse
for (int r=0; r<nr; r++) {
// init row
rownnz[r] = 0;
rowadr[r] = adr;
// find non-zeros
for (int c=0; c<nc; c++) {
if (mat[r*nc+c]) {
// record index and count
colind[adr] = c;
rownnz[r]++;
// copy element
res[adr++] = mat[r*nc+c];
}
}
}
}
// convert matrix from sparse to dense
void mju_sparse2dense(mjtNum* res, const mjtNum* mat, int nr, int nc,
const int* rownnz, const int* rowadr, const int* colind) {
// clear
mju_zero(res, nr*nc);
// copy non-zeros
for (int r=0; r<nr; r++) {
for (int i=0; i<rownnz[r]; i++) {
res[r*nc + colind[rowadr[r]+i]] = mat[rowadr[r]+i];
}
}
}
// multiply sparse matrix and dense vector: res = mat * vec.
void mju_mulMatVecSparse(mjtNum* res, const mjtNum* mat, const mjtNum* vec,
int nr, const int* rownnz, const int* rowadr,
const int* colind, const int* rowsuper) {
// no supernodes, or no AVX
#ifdef mjUSEAVX
if (!rowsuper)
#endif
{
// regular sparse dot-product
for (int r=0; r<nr; r++) {
res[r] = mju_dotSparse(mat+rowadr[r], vec, rownnz[r], colind+rowadr[r]);
}
return;
}
// regular or supernode
for (int r=0; r<nr; r++) {
if (rowsuper[r]) {
int rs = rowsuper[r]+1;
// handle rows in blocks of 3
while (rs>=3) {
mju_dotSparseX3(res+r, res+r+1, res+r+2,
mat+rowadr[r], mat+rowadr[r+1], mat+rowadr[r+2],
vec, rownnz[r], colind+rowadr[r]);
r += 3;
rs -= 3;
}
// handle remaining rows
while (rs>0) {
res[r] = mju_dotSparse(mat+rowadr[r], vec, rownnz[r], colind+rowadr[r]);
r++;
rs--;
}
// go back one, because of outer for loop
r--;
}
else {
res[r] = mju_dotSparse(mat+rowadr[r], vec, rownnz[r], colind+rowadr[r]);
}
}
}
// res = res*scl1 + vec*scl2
static void mju_addToSclScl(mjtNum* res, const mjtNum* vec, mjtNum scl1, mjtNum scl2, int n) {
int i = 0;
#ifdef mjUSEAVX
int n_4 = n - 4;
// vector part
if (n_4>=0) {
__m256d sclpar1, sclpar2, sum, val1, val2;
// init
sclpar1 = _mm256_set1_pd(scl1);
sclpar2 = _mm256_set1_pd(scl2);
// parallel computation
while (i<=n_4) {
val1 = _mm256_loadu_pd(res+i);
val2 = _mm256_loadu_pd(vec+i);
val1 = _mm256_mul_pd(val1, sclpar1);
val2 = _mm256_mul_pd(val2, sclpar2);
sum = _mm256_add_pd(val1, val2);
_mm256_storeu_pd(res+i, sum);
i += 4;
}
}
// process remaining
int n_i = n - i;
if (n_i==3) {
res[i] = res[i]*scl1 + vec[i]*scl2;
res[i+1] = res[i+1]*scl1 + vec[i+1]*scl2;
res[i+2] = res[i+2]*scl1 + vec[i+2]*scl2;
} else if (n_i==2) {
res[i] = res[i]*scl1 + vec[i]*scl2;
res[i+1] = res[i+1]*scl1 + vec[i+1]*scl2;
} else if (n_i==1) {
res[i] = res[i]*scl1 + vec[i]*scl2;
}
#else
for (; i<n; i++) {
res[i] = res[i]*scl1 + vec[i]*scl2;
}
#endif
}
// return 1 if vec1==vec2, 0 otherwise
static int mju_compare(const int* vec1, const int* vec2, int n) {
int i = 0;
#ifdef mjUSEAVX
int n_4 = n - 4;
// vector part
if (n_4>=0) {
__m128i val1, val2, cmp;
// parallel computation
while (i<=n_4) {
val1 = _mm_loadu_si128((const __m128i*)(vec1+i));
val2 = _mm_loadu_si128((const __m128i*)(vec2+i));
cmp = _mm_cmpeq_epi32(val1, val2);
if (_mm_movemask_epi8(cmp)!= 0xFFFF) {
return 0;
}
i += 4;
}
}
#endif
// scalar part
for (; i<n; i++) {
if (vec1[i]!=vec2[i]) {
return 0;
}
}
return 1;
}
// combine two sparse vectors: dst = a*dst + b*src, return nnz of result
int mju_combineSparse(mjtNum* dst, const mjtNum* src, int n, mjtNum a, mjtNum b,
int dst_nnz, int src_nnz, int* dst_ind, const int* src_ind,
mjtNum* buf, int* buf_ind) {
// check for identical pattern
if (dst_nnz==src_nnz) {
if (mju_compare(dst_ind, src_ind, dst_nnz)) {
// combine mjtNum data directly
mju_addToSclScl(dst, src, a, b, dst_nnz);
return dst_nnz;
}
}
// copy dst into buf
if (dst_nnz) {
memcpy(buf, dst, dst_nnz*sizeof(mjtNum));
memcpy(buf_ind, dst_ind, dst_nnz*sizeof(int));
}
// prepare to merge buf and scr into dst
int bi = 0, si = 0, nnz = 0;
int buf_nnz = dst_nnz;
int badr = bi<buf_nnz ? buf_ind[bi] : n+1;
int sadr = si<src_nnz ? src_ind[si] : n+1;
// merge vectors
while (bi<buf_nnz || si<src_nnz) {
// both
if (badr==sadr) {
dst[nnz] = a*buf[bi++] + b*src[si++];
dst_ind[nnz++] = badr;
badr = bi<buf_nnz ? buf_ind[bi] : n+1;
sadr = si<src_nnz ? src_ind[si] : n+1;
}
// dst only
else if (badr<sadr) {
dst[nnz] = a*buf[bi++];
dst_ind[nnz++] = badr;
badr = bi<buf_nnz ? buf_ind[bi] : n+1;
}
// src only
else {
dst[nnz] = b*src[si++];
dst_ind[nnz++] = sadr;
sadr = si<src_nnz ? src_ind[si] : n+1;
}
}
return nnz;
}
// incomplete combine sparse: dst = a*dst + b*src at common indices
void mju_combineSparseInc(mjtNum* dst, const mjtNum* src, int n, mjtNum a, mjtNum b,
int dst_nnz, int src_nnz, int* dst_ind, const int* src_ind) {
// check for identical pattern
if (dst_nnz==src_nnz) {
if (mju_compare(dst_ind, src_ind, dst_nnz)) {
// combine mjtNum data directly
mju_addToSclScl(dst, src, a, b, dst_nnz);
return;
}
}
// scale dst by a
if (a!=1) {
mju_scl(dst, dst, a, dst_nnz);
}
// prepare to merge
int di = 0, si = 0;
int dadr = di<dst_nnz ? dst_ind[di] : n+1;
int sadr = si<src_nnz ? src_ind[si] : n+1;
// add src*b at common indices
while (di<dst_nnz) {
// both
if (dadr==sadr) {
dst[di++] += b*src[si++];
dadr = di<dst_nnz ? dst_ind[di] : n+1;
sadr = si<src_nnz ? src_ind[si] : n+1;
}
// dst only
else if (dadr<sadr) {
di++;
dadr = di<dst_nnz ? dst_ind[di] : n+1;
}
// src only
else {
si++;
sadr = si<src_nnz ? src_ind[si] : n+1;
}
}
}
// compress layout of sparse matrix
void mju_compressSparse(mjtNum* mat, int nr, int nc, int* rownnz, int* rowadr, int* colind) {
rowadr[0] = 0;
int adr = rownnz[0];
for (int r=1; r<nr; r++) {
// save old rowadr, record new
int rowadr1 = rowadr[r];
rowadr[r] = adr;
// shift mat and mat_colind
for (int adr1=rowadr1; adr1<rowadr1+rownnz[r]; adr1++) {
mat[adr] = mat[adr1];
colind[adr] = colind[adr1];
adr++;
}
}
}
// transpose sparse matrix
void mju_transposeSparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
int* res_rownnz, int* res_rowadr, int* res_colind,
const int* rownnz, const int* rowadr, const int* colind) {
// clear counters for transposed
memset(res_rownnz, 0, nc*sizeof(int));
// set uncompressed layout
for (int rt=0; rt<nc; rt++) {
res_rowadr[rt] = rt*nr;
}
// scan original, compute uncompressed
for (int r=0; r<nr; r++) {
for (int ci=0; ci<rownnz[r]; ci++) {
// get rt=c
int rt = colind[rowadr[r]+ci];
// record index ct=r, assuming uncompressed res_rowadr[rt]=rt*nr
res_colind[rt*nr + res_rownnz[rt]] = r;
// copy data
res[rt*nr + res_rownnz[rt]] = mat[rowadr[r]+ci];
// increase counter for rt
res_rownnz[rt]++;
}
}
// compress
mju_compressSparse(res, nc, nr, res_rownnz, res_rowadr, res_colind);
}
// construct row supernodes
void mju_superSparse(int nr, int* rowsuper,
const int* rownnz, const int* rowadr, const int* colind) {
// no rows: nothing to do
if (!nr) {
return;
}
// find match to child
for (int r=0; r<nr-1; r++) {
// different number of nonzeros: cannot be a match
if (rownnz[r]!=rownnz[r+1]) {
rowsuper[r] = 0;
}
// same number of nonzeros: compare colind vectors
else {
rowsuper[r] = mju_compare(colind+rowadr[r], colind+rowadr[r+1], rownnz[r]);
}
}
// clear last (by definition)
rowsuper[nr-1] = 0;
// accumulate in reverse
for (int r=nr-2; r>=0; r--) {
if (rowsuper[r]) {
rowsuper[r] += rowsuper[r+1];
}
}
}
// compute sparse M'*diag*M (diag=NULL: compute M'*M), res has uncompressed layout
void mju_sqrMatTDSparse(mjtNum* res, const mjtNum* mat, const mjtNum* matT,
const mjtNum* diag, int nr, int nc,
int* res_rownnz, int* res_rowadr, int* res_colind,
const int* rownnz, const int* rowadr,
const int* colind, const int* rowsuper,
const int* rownnzT, const int* rowadrT,
const int* colindT, const int* rowsuperT,
mjData* d) {
// allocate space for accumulation buffer and matT
mjMARKSTACK;
int* chain = (int*) mj_stackAlloc(d, 2*nc);
mjtNum* buffer = mj_stackAlloc(d, nc);
// set uncompressed layout
for (int r=0; r<nc; r++) {
res_rowadr[r] = r*nc;
}
// compute lower-triangular uncompressed layout (nc per row)
for (int r=0; r<nc; r++) {
// copy chain from parent
if (rowsuperT && r>0 && rowsuperT[r-1]>0) {
// copy parent chain
res_rownnz[r] = res_rownnz[r-1];
memcpy(res_colind+res_rowadr[r], res_colind+res_rowadr[r-1],
res_rownnz[r]*sizeof(int));
// add diagonal if rowT is not empty
if (rownnzT[r]) {
res_colind[res_rowadr[r]+res_rownnz[r]] = r;
res_rownnz[r]++;
}
}
// construct chain
else {
// clear chain accumulation buffers
int nchain = 0;
int inew = 0, iold = nc;
int lastadded = -1;
// for each nonzero c in matT_row(r), add nonzeros of mat_row(c) to chain(r)
for (int i=0; i<rownnzT[r]; i++) {
// save c
int c = colindT[rowadrT[r]+i];
// skip if a chain from same supernode was already added
if (rowsuper && lastadded>=0 && (c-lastadded)<=rowsuper[lastadded]) {
continue;
} else {
lastadded = c;
}
// swap chains
int adr = inew;
inew = iold;
iold = adr;
// merge chains
int nnewchain = 0;
adr = 0;
int end = rowadr[c]+rownnz[c];
for (int adr1=rowadr[c]; adr1<end; adr1++) {
// save column index from mat
int col_mat = colind[adr1];
// skip column indices in chain smaller than col_mat
while (adr<nchain && chain[iold + adr]<col_mat && chain[iold + adr]<=r) {
chain[inew + nnewchain++] = chain[iold + adr++];
}
// only lower-triangular
if (col_mat>r) {
break;
}
// existing element: advance chain
if (adr<nchain && chain[iold + adr]==col_mat) {
adr++;
}
// add column index from matT
chain[inew + nnewchain++] = col_mat;
}
// append the rest of the master chain
while (adr<nchain && chain[iold + adr]<=r) {
chain[inew + nnewchain++] = chain[iold + adr++];
}
// assign newchain
nchain = nnewchain;
}
// copy chain
res_rownnz[r] = nchain;
if (nchain) {
memcpy(res_colind+res_rowadr[r], chain+inew, nchain*sizeof(int));
}
}
}
// compute matrix data given uncompressed layout
for (int r=0; r<nc; r++) {
// clear buffer[colind] for this chain
int adr = res_rowadr[r];
for (int i=0; i<res_rownnz[r]; i++) {
buffer[res_colind[adr+i]] = 0;
}
// res_row(r) = sum_c ( matT(r,c) * diag(c) * mat_row(c) )
for (int i=0; i<rownnzT[r]; i++) {
// save c and matT(r,c)*diag(c)
int c = colindT[rowadrT[r]+i];
mjtNum matTrc = matT[rowadrT[r]+i];
if (diag) {
matTrc *= diag[c];
}
// process row
int end = rowadr[c]+rownnz[c];
for (int adr=rowadr[c]; adr<end; adr++) {
// get column index from mat, only lower-triangular
int adr1;
if ((adr1=colind[adr])>r) {
break;
}
// add to buffer
buffer[adr1] += matTrc*mat[adr];
}
}
// copy buffer
adr = res_rowadr[r];
for (int i=0; i<res_rownnz[r]; i++) {
res[adr+i] = buffer[res_colind[adr+i]];
}
}
// make symmetric; uncompressed layout
for (int r=1; r<nc; r++) {
int end = nc*r+res_rownnz[r]-1;
for (int adr=nc*r; adr<end; adr++) {
// add to row given by column index
int adr1 = nc*res_colind[adr] + res_rownnz[res_colind[adr]]++;
res[adr1] = res[adr];
res_colind[adr1] = r;
}
}
mjFREESTACK;
}
+86
View File
@@ -0,0 +1,86 @@
// 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_SRC_ENGINE_ENGINE_UTIL_SPARSE_H_
#define MUJOCO_SRC_ENGINE_ENGINE_UTIL_SPARSE_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjtnum.h>
#ifdef __cplusplus
extern "C" {
#endif
//------------------------------ sparse operations -------------------------------------------------
// dot-product, first vector is sparse
mjtNum mju_dotSparse(const mjtNum* vec1, const mjtNum* vec2,
const int nnz1, const int* ind1);
// dot-product, both vectors are sparse
mjtNum mju_dotSparse2(const mjtNum* vec1, const mjtNum* vec2,
const int nnz1, const int* ind1,
const int nnz2, const int* ind2);
// convert matrix from dense to sparse
void mju_dense2sparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
int* rownnz, int* rowadr, int* colind);
// convert matrix from sparse to dense
void mju_sparse2dense(mjtNum* res, const mjtNum* mat, int nr, int nc,
const int* rownnz, const int* rowadr, const int* colind);
// multiply sparse matrix and dense vector: res = mat * vec
void mju_mulMatVecSparse(mjtNum* res, const mjtNum* mat, const mjtNum* vec,
int nr, const int* rownnz, const int* rowadr,
const int* colind, const int* rowsuper);
// compress layout of sparse matrix
void mju_compressSparse(mjtNum* mat, int nr, int nc,
int* rownnz, int* rowadr, int* colind);
// combine two sparse vectors: dst = a*dst + b*src, return nnz of result
int mju_combineSparse(mjtNum* dst, const mjtNum* src, int n, mjtNum a, mjtNum b,
int dst_nnz, int src_nnz, int* dst_ind, const int* src_ind,
mjtNum* buf, int* buf_ind);
// incomplete combine sparse: dst = a*dst + b*src at common indices
void mju_combineSparseInc(mjtNum* dst, const mjtNum* src, int n, mjtNum a, mjtNum b,
int dst_nnz, int src_nnz, int* dst_ind, const int* src_ind);
// transpose sparse matrix
void mju_transposeSparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
int* res_rownnz, int* res_rowadr, int* res_colind,
const int* rownnz, const int* rowadr, const int* colind);
// construct row supernodes
void mju_superSparse(int nr, int* rowsuper,
const int* rownnz, const int* rowadr, const int* colind);
// compute sparse M'*diag*M (diag=NULL: compute M'*M), res has uncompressed layout
void mju_sqrMatTDSparse(mjtNum* res, const mjtNum* mat, const mjtNum* matT,
const mjtNum* diag, int nr, int nc,
int* res_rownnz, int* res_rowadr, int* res_colind,
const int* rownnz, const int* rowadr,
const int* colind, const int* rowsuper,
const int* rownnzT, const int* rowadrT,
const int* colindT, const int* rowsuperT,
mjData* d);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_UTIL_SPARSE_H_
+485
View File
@@ -0,0 +1,485 @@
// 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.
#include "engine/engine_util_spatial.h"
#include <math.h>
#include <mujoco/mjmodel.h>
#include "engine/engine_util_blas.h"
#include "engine/engine_util_errmem.h"
//------------------------------ quaternion operations ---------------------------------------------
// rotate vector by quaternion
void mju_rotVecQuat(mjtNum res[3], const mjtNum vec[3], const mjtNum quat[4]) {
// null quat: copy vec
if (quat[0]==1 && quat[1]==0 && quat[2]==0 && quat[3]==0) {
mju_copy3(res, vec);
}
// regular processing
else {
mjtNum mat[9];
mju_quat2Mat(mat, quat);
mju_rotVecMat(res, vec, mat);
}
}
// negate quaternion
void mju_negQuat(mjtNum res[4], const mjtNum quat[4]) {
res[0] = quat[0];
res[1] = -quat[1];
res[2] = -quat[2];
res[3] = -quat[3];
}
// multiply quaternions
void mju_mulQuat(mjtNum res[4], const mjtNum qa[4], const mjtNum qb[4]) {
res[0] = qa[0]*qb[0] - qa[1]*qb[1] - qa[2]*qb[2] - qa[3]*qb[3];
res[1] = qa[0]*qb[1] + qa[1]*qb[0] + qa[2]*qb[3] - qa[3]*qb[2];
res[2] = qa[0]*qb[2] - qa[1]*qb[3] + qa[2]*qb[0] + qa[3]*qb[1];
res[3] = qa[0]*qb[3] + qa[1]*qb[2] - qa[2]*qb[1] + qa[3]*qb[0];
}
// multiply quaternion and axis
void mju_mulQuatAxis(mjtNum res[4], const mjtNum quat[4], const mjtNum axis[3]) {
res[0] = - (quat[1]*axis[0] + quat[2]*axis[1] + quat[3]*axis[2]);
res[1] = quat[0]*axis[0] + quat[2]*axis[2] - quat[3]*axis[1];
res[2] = quat[0]*axis[1] + quat[3]*axis[0] - quat[1]*axis[2];
res[3] = quat[0]*axis[2] + quat[1]*axis[1] - quat[2]*axis[0];
}
// convert axisAngle to quaternion
void mju_axisAngle2Quat(mjtNum res[4], const mjtNum axis[3], mjtNum angle) {
// zero angle: null quat
if (angle==0) {
res[0] = 1;
res[1] = 0;
res[2] = 0;
res[3] = 0;
}
// regular processing
else {
mjtNum s = mju_sin(angle*0.5);
res[0] = mju_cos(angle*0.5);
res[1] = axis[0]*s;
res[2] = axis[1]*s;
res[3] = axis[2]*s;
}
}
// convert quaternion (corresponding to orientation difference) to 3D velocity
void mju_quat2Vel(mjtNum res[3], const mjtNum quat[4], mjtNum dt) {
mjtNum axis[3] = {quat[1], quat[2], quat[3]};
mjtNum sin_a_2 = mju_normalize3(axis);
mjtNum speed = 2 * mju_atan2(sin_a_2, quat[0]);
// when axis-angle is larger than pi, rotation is in the opposite direction
if (speed>mjPI) {
speed -= 2*mjPI;
}
speed /= dt;
mju_scl3(res, axis, speed);
}
// Subtract quaternions, express as 3D velocity: qb*quat(res) = qa.
void mju_subQuat(mjtNum res[3], const mjtNum qa[4], const mjtNum qb[4]) {
// qdif = neg(qb)*qa
mjtNum qneg[4], qdif[4];
mju_negQuat(qneg, qb);
mju_mulQuat(qdif, qneg, qa);
// convert to 3D velocity
mju_quat2Vel(res, qdif, 1);
}
// convert quaternion to 3D rotation matrix
void mju_quat2Mat(mjtNum res[9], const mjtNum quat[4]) {
// null quat: identity
if (quat[0]==1 && quat[1]==0 && quat[2]==0 && quat[3]==0) {
res[0] = 1;
res[1] = 0;
res[2] = 0;
res[3] = 0;
res[4] = 1;
res[5] = 0;
res[6] = 0;
res[7] = 0;
res[8] = 1;
}
// regular processing
else {
const mjtNum q00 = quat[0]*quat[0];
const mjtNum q01 = quat[0]*quat[1];
const mjtNum q02 = quat[0]*quat[2];
const mjtNum q03 = quat[0]*quat[3];
const mjtNum q11 = quat[1]*quat[1];
const mjtNum q12 = quat[1]*quat[2];
const mjtNum q13 = quat[1]*quat[3];
const mjtNum q22 = quat[2]*quat[2];
const mjtNum q23 = quat[2]*quat[3];
const mjtNum q33 = quat[3]*quat[3];
res[0] = q00 + q11 - q22 - q33;
res[4] = q00 - q11 + q22 - q33;
res[8] = q00 - q11 - q22 + q33;
res[1] = 2*(q12 - q03);
res[2] = 2*(q13 + q02);
res[3] = 2*(q12 + q03);
res[5] = 2*(q23 - q01);
res[6] = 2*(q13 - q02);
res[7] = 2*(q23 + q01);
}
}
// convert 3D rotation matrix to quaterion
void mju_mat2Quat(mjtNum quat[4], const mjtNum mat[9]) {
// q0 largest
if (mat[0]+mat[4]+mat[8]>0) {
quat[0] = 0.5 * mju_sqrt(1 + mat[0] + mat[4] + mat[8]);
quat[1] = 0.25 * (mat[7] - mat[5]) / quat[0];
quat[2] = 0.25 * (mat[2] - mat[6]) / quat[0];
quat[3] = 0.25 * (mat[3] - mat[1]) / quat[0];
}
// q1 largest
else if (mat[0]>mat[4] && mat[0]>mat[8]) {
quat[1] = 0.5 * mju_sqrt(1 + mat[0] - mat[4] - mat[8]);
quat[0] = 0.25 * (mat[7] - mat[5]) / quat[1];
quat[2] = 0.25 * (mat[1] + mat[3]) / quat[1];
quat[3] = 0.25 * (mat[2] + mat[6]) / quat[1];
}
// q2 largest
else if (mat[4]>mat[8]) {
quat[2] = 0.5 * mju_sqrt(1 - mat[0] + mat[4] - mat[8]);
quat[0] = 0.25 * (mat[2] - mat[6]) / quat[2];
quat[1] = 0.25 * (mat[1] + mat[3]) / quat[2];
quat[3] = 0.25 * (mat[5] + mat[7]) / quat[2];
}
// q3 largest
else {
quat[3] = 0.5 * mju_sqrt(1 - mat[0] - mat[4] + mat[8]);
quat[0] = 0.25 * (mat[3] - mat[1]) / quat[3];
quat[1] = 0.25 * (mat[2] + mat[6]) / quat[3];
quat[2] = 0.25 * (mat[5] + mat[7]) / quat[3];
}
mju_normalize4(quat);
}
// time-derivative of quaternion, given 3D rotational velocity
void mju_derivQuat(mjtNum res[4], const mjtNum quat[4], const mjtNum vel[3]) {
res[0] = 0.5*(-vel[0]*quat[1] - vel[1]*quat[2] - vel[2]*quat[3]);
res[1] = 0.5*( vel[0]*quat[0] + vel[1]*quat[3] - vel[2]*quat[2]);
res[2] = 0.5*(-vel[0]*quat[3] + vel[1]*quat[0] + vel[2]*quat[1]);
res[3] = 0.5*( vel[0]*quat[2] - vel[1]*quat[1] + vel[2]*quat[0]);
}
// integrate quaterion given 3D angular velocity
void mju_quatIntegrate(mjtNum quat[4], const mjtNum vel[3], mjtNum scale) {
mjtNum angle, tmp[4], qrot[4];
// form local rotation quaternion, apply
mju_copy3(tmp, vel);
angle = scale * mju_normalize3(tmp);
mju_axisAngle2Quat(qrot, tmp, angle);
mju_mulQuat(tmp, quat, qrot);
mju_normalize4(tmp);
mju_copy4(quat, tmp);
}
// compute quaternion performing rotation from z-axis to given vector
void mju_quatZ2Vec(mjtNum quat[4], const mjtNum vec[3]) {
mjtNum axis[3], a, vn[3] = {vec[0], vec[1], vec[2]}, z[3] = {0, 0, 1};
// set default result to no-rotation quaternion
quat[0] = 1;
mju_zero3(quat+1);
// normalize vector; if too small, no rotation
if (mju_normalize3(vn)<mjMINVAL) {
return;
}
// compute angle and axis
mju_cross(axis, z, vn);
a = mju_normalize3(axis);
// almost parallel
if (fabs(a)<mjMINVAL) {
// opposite: 180 deg rotation around x axis
if (mju_dot3(vn, z) < 0) {
quat[0] = 0;
quat[1] = 1;
}
return;
}
// make quaterion from angle and axis
a = mju_atan2(a, mju_dot3(vn, z));
mju_axisAngle2Quat(quat, axis, a);
}
//------------------------------ pose operations (quat, pos) ---------------------------------------
// multiply two poses
void mju_mulPose(mjtNum posres[3], mjtNum quatres[4],
const mjtNum pos1[3], const mjtNum quat1[4],
const mjtNum pos2[3], const mjtNum quat2[4]) {
// quatres = quat1*quat2
mju_mulQuat(quatres, quat1, quat2);
mju_normalize4(quatres);
// posres = quat1*pos2 + pos1
mju_rotVecQuat(posres, pos2, quat1);
mju_addTo3(posres, pos1);
}
// negate pose
void mju_negPose(mjtNum posres[3], mjtNum quatres[4], const mjtNum pos[3], const mjtNum quat[4]) {
// qres = neg(quat)
mju_negQuat(quatres, quat);
// pres = -neg(quat)*pos
mju_rotVecQuat(posres, pos, quatres);
mju_scl3(posres, posres, -1);
}
// transform vector by pose
void mju_trnVecPose(mjtNum res[3], const mjtNum pos[3], const mjtNum quat[4], const mjtNum vec[3]) {
// res = quat*vec + pos
mju_rotVecQuat(res, vec, quat);
mju_addTo3(res, pos);
}
//------------------------------ spatial algebra ---------------------------------------------------
// vector cross-product, 3D
void mju_cross(mjtNum res[3], const mjtNum a[3], const mjtNum b[3]) {
res[0] = a[1]*b[2] - a[2]*b[1];
res[1] = a[2]*b[0] - a[0]*b[2];
res[2] = a[0]*b[1] - a[1]*b[0];
}
// cross-product for motion vector
void mju_crossMotion(mjtNum res[6], const mjtNum vel[6], const mjtNum v[6]) {
res[0] = -vel[2]*v[1] + vel[1]*v[2];
res[1] = vel[2]*v[0] - vel[0]*v[2];
res[2] = -vel[1]*v[0] + vel[0]*v[1];
res[3] = -vel[2]*v[4] + vel[1]*v[5];
res[4] = vel[2]*v[3] - vel[0]*v[5];
res[5] = -vel[1]*v[3] + vel[0]*v[4];
res[3] += -vel[5]*v[1] + vel[4]*v[2];
res[4] += vel[5]*v[0] - vel[3]*v[2];
res[5] += -vel[4]*v[0] + vel[3]*v[1];
}
// cross-product for force vectors
void mju_crossForce(mjtNum res[6], const mjtNum vel[6], const mjtNum f[6]) {
res[0] = -vel[2]*f[1] + vel[1]*f[2];
res[1] = vel[2]*f[0] - vel[0]*f[2];
res[2] = -vel[1]*f[0] + vel[0]*f[1];
res[3] = -vel[2]*f[4] + vel[1]*f[5];
res[4] = vel[2]*f[3] - vel[0]*f[5];
res[5] = -vel[1]*f[3] + vel[0]*f[4];
res[0] += -vel[5]*f[4] + vel[4]*f[5];
res[1] += vel[5]*f[3] - vel[3]*f[5];
res[2] += -vel[4]*f[3] + vel[3]*f[4];
}
// express inertia in com-based frame
void mju_inertCom(mjtNum res[10], const mjtNum inert[3], const mjtNum mat[9],
const mjtNum dif[3], mjtNum mass) {
// tmp = diag(inert) * mat' (mat is local-to-global rotation)
mjtNum tmp[9] = {mat[0]*inert[0], mat[3]*inert[0], mat[6]*inert[0],
mat[1]*inert[1], mat[4]*inert[1], mat[7]*inert[1],
mat[2]*inert[2], mat[5]*inert[2], mat[8]*inert[2]
};
// res_rot = mat * diag(inert) * mat'
res[0] = mat[0]*tmp[0] + mat[1]*tmp[3] + mat[2]*tmp[6];
res[1] = mat[3]*tmp[1] + mat[4]*tmp[4] + mat[5]*tmp[7];
res[2] = mat[6]*tmp[2] + mat[7]*tmp[5] + mat[8]*tmp[8];
res[3] = mat[0]*tmp[1] + mat[1]*tmp[4] + mat[2]*tmp[7];
res[4] = mat[0]*tmp[2] + mat[1]*tmp[5] + mat[2]*tmp[8];
res[5] = mat[3]*tmp[2] + mat[4]*tmp[5] + mat[5]*tmp[8];
// res_rot -= mass * dif_cross * dif_cross
res[0] += mass*(dif[1]*dif[1] + dif[2]*dif[2]);
res[1] += mass*(dif[0]*dif[0] + dif[2]*dif[2]);
res[2] += mass*(dif[0]*dif[0] + dif[1]*dif[1]);
res[3] -= mass*dif[0]*dif[1];
res[4] -= mass*dif[0]*dif[2];
res[5] -= mass*dif[1]*dif[2];
// res_tran = mass * dif
res[6] = mass*dif[0];
res[7] = mass*dif[1];
res[8] = mass*dif[2];
// res_mass = mass
res[9] = mass;
}
// multiply 6D vector (rotation, translation) by 6D inertia matrix
void mju_mulInertVec(mjtNum res[6], const mjtNum i[10], const mjtNum v[6]) {
res[0] = i[0]*v[0] + i[3]*v[1] + i[4]*v[2] - i[8]*v[4] + i[7]*v[5];
res[1] = i[3]*v[0] + i[1]*v[1] + i[5]*v[2] + i[8]*v[3] - i[6]*v[5];
res[2] = i[4]*v[0] + i[5]*v[1] + i[2]*v[2] - i[7]*v[3] + i[6]*v[4];
res[3] = i[8]*v[1] - i[7]*v[2] + i[9]*v[3];
res[4] = i[6]*v[2] - i[8]*v[0] + i[9]*v[4];
res[5] = i[7]*v[0] - i[6]*v[1] + i[9]*v[5];
}
// express motion axis in com-based frame
void mju_dofCom(mjtNum res[6], const mjtNum axis[3], const mjtNum offset[3]) {
// hinge
if (offset) {
mju_copy3(res, axis);
mju_cross(res+3, axis, offset);
}
// slide
else {
mju_zero3(res);
mju_copy3(res+3, axis);
}
}
// multiply dof matrix (6-by-n, transposed) by vector (n-by-1)
void mju_mulDofVec(mjtNum* res, const mjtNum* dof, const mjtNum* vec, int n) {
if (n==1) {
mju_scl(res, dof, vec[0], 6);
} else if (n<=0) {
mju_zero(res, 6);
} else {
mju_mulMatTVec(res, dof, vec, n, 6);
}
}
// transform 6D motion or force vector between frames
// rot is 3-by-3 matrix; flg_force determines vector type (motion or force)
void mju_transformSpatial(mjtNum res[6], const mjtNum vec[6], int flg_force,
const mjtNum newpos[3], const mjtNum oldpos[3],
const mjtNum rotnew2old[9]) {
mjtNum cros[3], dif[3], tran[6];
// apply translation
mju_copy(tran, vec, 6);
mju_sub3(dif, newpos, oldpos);
if (flg_force) {
mju_cross(cros, dif, vec+3);
mju_sub3(tran, vec, cros);
} else {
mju_cross(cros, dif, vec);
mju_sub3(tran+3, vec+3, cros);
}
// apply rotation if provided
if (rotnew2old) {
mju_rotVecMatT(res, tran, rotnew2old);
mju_rotVecMatT(res+3, tran+3, rotnew2old);
}
// otherwise copy
else {
mju_copy(res, tran, 6);
}
}
// make 3D frame given X axis (normal) and possibly Y axis (tangent 1)
void mju_makeFrame(mjtNum frame[9]) {
mjtNum tmp[3];
// normalize xaxis
if (mju_normalize3(frame) < 0.5) {
mju_error("xaxis of contact frame undefined");
}
// if yaxis undefined, set yaxis to (0,1,0) if possible, otherwize (0,0,1)
if (mju_norm3(frame+3) < 0.5) {
mju_zero3(frame+3);
if (frame[1]<0.5 && frame[1]>-0.5) {
frame[4] = 1;
} else {
frame[5] = 1;
}
}
// make yaxis orthogonal to xaxis
mju_scl3(tmp, frame, mju_dot3(frame, frame+3));
mju_subFrom3(frame+3, tmp);
mju_normalize3(frame+3);
// zaxis = cross(xaxis, yaxis)
mju_cross(frame+6, frame, frame+3);
}
+115
View File
@@ -0,0 +1,115 @@
// 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_SRC_ENGINE_ENGINE_UTIL_SPATIAL_H_
#define MUJOCO_SRC_ENGINE_ENGINE_UTIL_SPATIAL_H_
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
//------------------------------ quaternion operations ---------------------------------------------
// rotate vector by quaternion
MJAPI void mju_rotVecQuat(mjtNum res[3], const mjtNum vec[3], const mjtNum quat[4]);
// compute conjugate quaternion, corresponding to opposite rotation
MJAPI void mju_negQuat(mjtNum res[4], const mjtNum quat[4]);
// multiply quaternions
MJAPI void mju_mulQuat(mjtNum res[4], const mjtNum quat1[4], const mjtNum quat2[4]);
// multiply quaternion and axis
MJAPI void mju_mulQuatAxis(mjtNum res[4], const mjtNum quat[4], const mjtNum axis[3]);
// convert axisAngle to quaternion
MJAPI void mju_axisAngle2Quat(mjtNum res[4], const mjtNum axis[3], mjtNum angle);
// convert quaternion (corresponding to orientation difference) to 3D velocity
MJAPI void mju_quat2Vel(mjtNum res[3], const mjtNum quat[4], mjtNum dt);
// subtract quaternions, convert to 3D velocity: qb*quat(res) = qa
MJAPI void mju_subQuat(mjtNum res[3], const mjtNum qa[4], const mjtNum qb[4]);
// convert quaternion to 3D rotation matrix
MJAPI void mju_quat2Mat(mjtNum res[9], const mjtNum quat[4]);
// convert 3D rotation matrix to quaterion
MJAPI void mju_mat2Quat(mjtNum quat[4], const mjtNum mat[9]);
// time-derivative of quaternion, given 3D rotational velocity
MJAPI void mju_derivQuat(mjtNum res[4], const mjtNum quat[4], const mjtNum vel[3]);
// integrate quaterion given 3D angular velocity
MJAPI void mju_quatIntegrate(mjtNum quat[4], const mjtNum vel[3], mjtNum scale);
// compute quaternion performing rotation from z-axis to given vector
MJAPI void mju_quatZ2Vec(mjtNum quat[4], const mjtNum vec[3]);
//------------------------------ pose operations (pos, quat) ---------------------------------------
// multiply two poses
MJAPI void mju_mulPose(mjtNum posres[3], mjtNum quatres[4],
const mjtNum pos1[3], const mjtNum quat1[4],
const mjtNum pos2[3], const mjtNum quat2[4]);
// compute conjugate pose, corresponding to the opposite spatial transformation
MJAPI void mju_negPose(mjtNum posres[3], mjtNum quatres[4],
const mjtNum pos[3], const mjtNum quat[4]);
// transform vector by pose
MJAPI void mju_trnVecPose(mjtNum res[3], const mjtNum pos[3], const mjtNum quat[4],
const mjtNum vec[3]);
//------------------------------ spatial algebra ---------------------------------------------------
// vector cross-product, 3D
MJAPI void mju_cross(mjtNum res[3], const mjtNum a[3], const mjtNum b[3]);
// cross-product for motion vector
void mju_crossMotion(mjtNum res[6], const mjtNum vel[6], const mjtNum v[6]);
// cross-product for force vectors
void mju_crossForce(mjtNum res[6], const mjtNum vel[6], const mjtNum f[6]);
// express inertia in com-based frame
void mju_inertCom(mjtNum res[10], const mjtNum inert[3], const mjtNum mat[9],
const mjtNum dif[3], mjtNum mass);
// express motion axis in com-based frame
void mju_dofCom(mjtNum res[6], const mjtNum axis[3], const mjtNum offset[3]);
// multiply 6D vector (rotation, translation) by 6D inertia matrix
void mju_mulInertVec(mjtNum res[6], const mjtNum inert[10], const mjtNum vec[6]);
// multiply dof matrix by vector
void mju_mulDofVec(mjtNum* res, const mjtNum* mat, const mjtNum* vec, int n);
// coordinate transform of 6D motion or force vector in rotation:translation format
// rotnew2old is 3-by-3, NULL means no rotation; flg_force specifies force or motion type
MJAPI void mju_transformSpatial(mjtNum res[6], const mjtNum vec[6], int flg_force,
const mjtNum newpos[3], const mjtNum oldpos[3],
const mjtNum rotnew2old[9]);
// make 3D frame given X axis (and possibly Y axis)
void mju_makeFrame(mjtNum frame[9]);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_UTIL_SPATIAL_H_
+211
View File
@@ -0,0 +1,211 @@
// 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.
#include "engine/engine_vfs.h"
#include <string.h>
#include <stdlib.h>
#include "engine/engine_array_safety.h"
#include "engine/engine_file.h"
#include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h"
// strip path prefix from filename
static void vfs_strippath(char* newname, const char* oldname) {
int i, sz = strlen(oldname);
// find last delimiter
for (i=sz-1; i>=0; i--) {
if (oldname[i]=='\\' || oldname[i]=='/') {
break;
}
}
// check resulting length
if (sz-(i+1)>=mjMAXVFSNAME) {
mju_error("Filename too long in VFS");
}
if (sz-(i+1)<=0) {
mju_error("Empty filename in VFS");
}
// copy
mju_strncpy(newname, oldname+i+1, mjMAXVFSNAME);
// make lowercase
for (i=strlen(newname)-1; i>=0; i--) {
if (newname[i]>='A' && newname[i]<='Z') {
newname[i] = (char)(((int)newname[i]) +'a' - 'A');
}
}
}
// initialize to empty (no deallocation)
void mj_defaultVFS(mjVFS* vfs) {
memset(vfs, 0, sizeof(mjVFS));
}
// add file to VFS, return 0: success, 1: full, 2: repeated name, -1: failed to load
int mj_addFileVFS(mjVFS* vfs, const char* directory, const char* filename) {
// check vfs size
if (vfs->nfile>=mjMAXVFS-1) {
return 1;
}
// make full name
char fullname[1000];
if (directory) {
mjSTRNCPY(fullname, directory);
mjSTRNCAT(fullname, filename);
} else {
mjSTRNCPY(fullname, filename);
}
// strip path
char newname[mjMAXVFSNAME];
vfs_strippath(newname, filename);
// check for repeated name
for (int i=0; i<vfs->nfile; i++) {
if (strncmp(newname, vfs->filename[i], mjMAXVFSNAME)==0) {
return 2;
}
}
// assign name
mjSTRNCPY(vfs->filename[vfs->nfile], newname);
// allocate and read
int filesize = 0;
vfs->filedata[vfs->nfile] = mju_fileToMemory(filename, &filesize);
if (!vfs->filedata[vfs->nfile]) {
return -1;
}
// assign size and count
vfs->filesize[vfs->nfile] = filesize;
vfs->nfile++;
return 0;
}
// make empty file in VFS, return 0: success, 1: full, 2: repeated name
int mj_makeEmptyFileVFS(mjVFS* vfs, const char* filename, int filesize) {
// check vfs size
if (vfs->nfile>=mjMAXVFS-1) {
return 1;
}
// check filesize
if (filesize<=0) {
mju_error("mj_makeEmptyFileVFS expects positive filesize");
}
// strip path
char newname[mjMAXVFSNAME];
vfs_strippath(newname, filename);
// check for repeated name
for (int i=0; i<vfs->nfile; i++) {
if (strncmp(newname, vfs->filename[i], mjMAXVFSNAME)==0) {
return 2;
}
}
// assign name
mjSTRNCPY(vfs->filename[vfs->nfile], newname);
// allocate and clear
vfs->filedata[vfs->nfile] = mju_malloc(filesize);
if (!vfs->filedata[vfs->nfile]) {
mju_error("mj_makeEmptyFileVFS: could not allocate memory");
}
memset(vfs->filedata[vfs->nfile], 0, filesize);
// assign size and count
vfs->filesize[vfs->nfile] = filesize;
vfs->nfile++;
return 0;
}
// return file index in VFS, or -1 if not found in VFS
int mj_findFileVFS(const mjVFS* vfs, const char* filename) {
// strip path
char newname[mjMAXVFSNAME];
vfs_strippath(newname, filename);
// find specific file
for (int i=0; i<vfs->nfile; i++) {
if (strncmp(newname, vfs->filename[i], mjMAXVFSNAME)==0) {
return i;
}
}
return -1;
}
// delete file from VFS, return 0: success, -1: not found in VFS
int mj_deleteFileVFS(mjVFS* vfs, const char* filename) {
// strip path
char newname[mjMAXVFSNAME];
vfs_strippath(newname, filename);
// find specified file
for (int i=0; i<vfs->nfile; i++) {
if (strncmp(newname, vfs->filename[i], mjMAXVFSNAME)==0) {
// free buffer
mju_free(vfs->filedata[i]);
// scroll remaining files forward
while (i<vfs->nfile-1) {
mjSTRNCPY(vfs->filename[i], vfs->filename[i+1]);
vfs->filesize[i] = vfs->filesize[i+1];
vfs->filedata[i] = vfs->filedata[i+1];
}
// set last to 0, for style
vfs->filename[vfs->nfile-1][0] = 0;
vfs->filesize[vfs->nfile-1] = 0;
vfs->filedata[vfs->nfile-1] = NULL;
// decrease counter
vfs->nfile--;
return 0;
}
}
return -1;
}
// delete all files from VFS
void mj_deleteVFS(mjVFS* vfs) {
for (int i=0; i<vfs->nfile; i++) {
mju_free(vfs->filedata[i]);
}
memset(vfs, 0, sizeof(mjVFS));
}
+47
View File
@@ -0,0 +1,47 @@
// 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_SRC_ENGINE_ENGINE_VFS_H_
#define MUJOCO_SRC_ENGINE_ENGINE_VFS_H_
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#ifdef __cplusplus
extern "C" {
#endif
// initialize to empty (no deallocation)
MJAPI void mj_defaultVFS(mjVFS* vfs);
// add file to VFS, return 0: success, 1: full, 2: repeated name, -1: not found on disk
MJAPI int mj_addFileVFS(mjVFS* vfs, const char* directory, const char* filename);
// make empty file in VFS, return 0: success, 1: full, 2: repeated name
MJAPI int mj_makeEmptyFileVFS(mjVFS* vfs, const char* filename, int filesize);
// return file index in VFS, or -1 if not found in VFS
MJAPI int mj_findFileVFS(const mjVFS* vfs, const char* filename);
// delete file from VFS, return 0: success, -1: not found in VFS
MJAPI int mj_deleteFileVFS(mjVFS* vfs, const char* filename);
// delete all files from VFS
MJAPI void mj_deleteVFS(mjVFS* vfs);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_VFS_H_
+349
View File
@@ -0,0 +1,349 @@
// 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.
#include "engine/engine_vis_init.h"
#include <math.h>
#include <string.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mjvisualize.h>
#include "engine/engine_array_safety.h"
#include "engine/engine_macro.h"
#include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h"
#ifdef _MSC_VER
#pragma warning (disable: 4305) // tell MSVC to not complain that float x = 0.1 should be 0.1f
#endif
//--------------------------------- Strings --------------------------------------------------------
// label names
const char* mjLABELSTRING[mjNLABEL] = {
"None",
"Body",
"Joint",
"Geom",
"Site",
"Camera",
"Light",
"Tendon",
"Actuator",
"Constraint",
"Skin",
"Selection",
"SelPoint",
"ContactForce"
};
// frame names
const char* mjFRAMESTRING[mjNFRAME] = {
"None",
"Body",
"Geom",
"Site",
"Camera",
"Light",
"Contact",
"World"
};
// visual opptions: {name, initial value, shortcut}
const char* mjVISSTRING[mjNVISFLAG][3] = {
{"Convex &Hull", "0", "H"},
{"Te&xture", "1", "X"},
{"&Joint", "0", "J"},
{"Act&uator", "0", "U"},
{"Camera", "0", "Q"},
{"Light", "0", "Z"},
{"Tendon", "1", "V"},
{"Range Finder", "1", "Y"},
{"Co&nstraint", "0", "N"},
{"&Inertia", "0", "I"},
{"Scale Inertia", "0", "'"},
{"Pertur&b Force", "0", "B"},
{"Perturb &Object", "1", "O"},
{"&Contact Point", "0", "C"},
{"Contact &Force", "0", "F"},
{"Contact S&plit", "0", "P"},
{"&Transparent", "0", "T"},
{"&Auto Connect", "0", "A"},
{"Center of &Mass", "0", "M"},
{"S&elect Point", "0", "E"},
{"Static Bo&dy", "1", "D"},
{"Skin", "1", ";"}
};
// render options: {name, initial value, shortcut}
const char* mjRNDSTRING[mjNRNDFLAG][3] = {
{"Shadow", "1", "S"},
{"Wireframe", "0", "W"},
{"Reflection", "1", "R"},
{"Additive", "0", "L"},
{"Skybox", "1", "K"},
{"Fog", "0", "G"},
{"Haze", "1", "/"},
{"Segment", "0", ","},
{"Id Color", "0", "."}
};
//--------------------------------- Implementation -------------------------------------------------
// allocate and init abstract scene
void mjv_makeScene(const mjModel* m, mjvScene* scn, int maxgeom) {
// free previous
mjv_freeScene(scn);
// allocate geom buffers
if (maxgeom>0) {
// allocate
scn->maxgeom = maxgeom;
scn->geoms = (mjvGeom*) mju_malloc(maxgeom*sizeof(mjvGeom));
scn->geomorder = (int*) mju_malloc(maxgeom*sizeof(int));
// check allocation
if (!scn->geoms || !scn->geomorder) {
mju_error("Could not allocate geom buffers");
}
}
// set default OpenGL options
for (int i=0; i<mjNRNDFLAG; i++) {
scn->flags[i] = (mjRNDSTRING[i][1][0]=='1');
}
// set default model transformation
scn->scale = 1;
scn->rotate[0] = 1;
// set number of skins
if (m) {
scn->nskin = m->nskin;
} else {
scn->nskin = 0;
}
// allocate skin data
if (scn->nskin) {
// compute number of vertices in all skins
int nskin = m->nskin;
int totvert = 0;
for (int i=0; i<nskin; i++) {
totvert += m->skin_vertnum[i];
}
// allocate
scn->skinfacenum = (int*) mju_malloc(nskin*sizeof(int));
scn->skinvertadr = (int*) mju_malloc(nskin*sizeof(int));
scn->skinvertnum = (int*) mju_malloc(nskin*sizeof(int));
scn->skinvert = (float*) mju_malloc(3*totvert*sizeof(float));
scn->skinnormal = (float*) mju_malloc(3*totvert*sizeof(float));
// check allocation
if (!scn->skinfacenum ||
!scn->skinvertadr ||
!scn->skinvertnum ||
!scn->skinvert ||
!scn->skinnormal) {
mju_error("Could not allocate skin buffers");
}
// copy constant data
for (int i=0; i<nskin; i++) {
scn->skinfacenum[i] = m->skin_facenum[i];
scn->skinvertadr[i] = m->skin_vertadr[i];
scn->skinvertnum[i] = m->skin_vertnum[i];
}
}
// mjvGeom, mjvLight, mjvGLCamera objects are invalid
}
// free abstract scene
void mjv_freeScene(mjvScene* scn) {
// free buffers allocated by mjv_makeScene
mju_free(scn->geoms);
mju_free(scn->geomorder);
mju_free(scn->skinfacenum);
mju_free(scn->skinvertadr);
mju_free(scn->skinvertnum);
mju_free(scn->skinvert);
mju_free(scn->skinnormal);
// clear data structure
mjv_defaultScene(scn);
}
// set default scene
void mjv_defaultScene(mjvScene* scn) {
memset(scn, 0, sizeof(mjvScene));
}
// set default visualization options
void mjv_defaultOption(mjvOption* vopt) {
vopt->label = mjLABEL_NONE;
vopt->frame = mjFRAME_NONE;
for (int i=0; i<mjNGROUP; i++) {
int state = (i<3 ? 1 : 0);
vopt->geomgroup[i] = state;
vopt->sitegroup[i] = state;
vopt->jointgroup[i] = state;
vopt->tendongroup[i] = state;
vopt->actuatorgroup[i] = state;
}
for (int i=0; i<mjNVISFLAG; i++) {
vopt->flags[i] = (mjVISSTRING[i][1][0]=='1');
}
}
// set default camera
void mjv_defaultCamera(mjvCamera* cam) {
memset(cam, 0, sizeof(mjvCamera));
cam->type = mjCAMERA_FREE;
cam->fixedcamid = -1;
cam->trackbodyid = -1;
cam->distance = 2;
cam->azimuth = 90;
cam->elevation = -45;
}
// set default perturbation
void mjv_defaultPerturb(mjvPerturb* pert) {
memset(pert, 0, sizeof(mjvPerturb));
pert->skinselect = -1;
pert->refquat[0] = 1;
pert->scale = 1;
}
// predefined line colors
static const float _linergb[8][3] = {
{1.0, 0.3, 0.3},
{0.1, 1.0, 0.1},
{0.3, 0.3, 1.0},
{0.1, 1.0, 1.0},
{1.0, 0.2, 1.0},
{1.0, 1.0, 0.1},
{1.0, 0.6, 0.2},
{0.6, 0.7, 0.4}
};
// set default figure
void mjv_defaultFigure(mjvFigure* fig) {
// set everything to zero
memset(fig, 0, sizeof(mjvFigure));
// disable highlight
fig->highlightid = -1;
// set enable flags
fig->flg_legend = 1;
fig->flg_ticklabel[0] = 1;
fig->flg_ticklabel[1] = 1;
fig->flg_extend = 1;
// set style
fig->linewidth = 3;
fig->gridwidth = 1;
fig->gridsize[0] = 2;
fig->gridsize[1] = 2;
fig->gridrgb[0] = 0.4f;
fig->gridrgb[1] = 0.4f;
fig->gridrgb[2] = 0.4f;
fig->figurergba[3] = 1;
fig->panergba[3] = 1;
fig->legendrgba[3] = 0.3f;
fig->textrgb[0] = 1;
fig->textrgb[1] = 1;
fig->textrgb[2] = 1;
fig->range[0][0] = 0;
fig->range[0][1] = 1;
fig->range[1][0] = 0;
fig->range[1][1] = 1;
mjSTRNCPY(fig->xformat, "%.0f");
mjSTRNCPY(fig->yformat, "%.2g");
mjSTRNCPY(fig->minwidth, "XXX");
// set line colors
for (int n=0; n<mjMAXLINE; n++) {
// predefined colors
if (n<8) {
fig->linergb[n][0] = _linergb[n][0];
fig->linergb[n][1] = _linergb[n][1];
fig->linergb[n][2] = _linergb[n][2];
}
// automatically generated colors: Halton sequence
else {
fig->linergb[n][0] = 0.1f + 0.8f*mju_Halton(n, 2);
fig->linergb[n][1] = 0.1f + 0.8f*mju_Halton(n, 3);
fig->linergb[n][2] = 0.1f + 0.8f*mju_Halton(n, 5);
}
}
}
// compute rbound for mjvGeom
float mjv_rbound(const mjvGeom* geom) {
// model geom: return
if (geom->objtype==mjOBJ_GEOM) {
return geom->modelrbound;
}
// compute rbound according to type
const float* s = geom->size;
switch (geom->type) {
case mjGEOM_SPHERE:
return s[0];
case mjGEOM_CAPSULE:
return (s[0]+s[2]);
case mjGEOM_CYLINDER:
return sqrtf(s[0]*s[0] + s[2]*s[2]);
case mjGEOM_BOX:
return sqrtf(s[0]*s[0] + s[1]*s[1] + s[2]*s[2]);
break;
default: // not accurate for arrows, but they are not transparent
return mjMAX(s[0], mjMAX(s[1], s[2]));
}
}
+62
View File
@@ -0,0 +1,62 @@
// 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_SRC_ENGINE_ENGINE_VIS_INIT_H_
#define MUJOCO_SRC_ENGINE_ENGINE_VIS_INIT_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mjvisualize.h>
#ifdef __cplusplus
extern "C" {
#endif
// strings
MJAPI extern const char* mjLABELSTRING[mjNLABEL];
MJAPI extern const char* mjFRAMESTRING[mjNFRAME];
MJAPI extern const char* mjVISSTRING[mjNVISFLAG][3];
MJAPI extern const char* mjRNDSTRING[mjNRNDFLAG][3];
// set default scene
MJAPI void mjv_defaultScene(mjvScene* scn);
// allocate and init abstract scene
MJAPI void mjv_makeScene(const mjModel* m, mjvScene* scn, int maxgeom);
// free abstract scene
MJAPI void mjv_freeScene(mjvScene* scn);
// set default visualization options
MJAPI void mjv_defaultOption(mjvOption* vopt);
// set default camera
MJAPI void mjv_defaultCamera(mjvCamera* cam);
// set default perturbation
MJAPI void mjv_defaultPerturb(mjvPerturb* pert);
// set default figure
MJAPI void mjv_defaultFigure(mjvFigure* fig);
// compute rbound for mjvGeom
float mjv_rbound(const mjvGeom* geom);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_VIS_INIT_H_
+767
View File
@@ -0,0 +1,767 @@
// 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.
#include "engine/engine_vis_interact.h"
#include <math.h>
#include <stddef.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mjvisualize.h>
#include "engine/engine_ray.h"
#include "engine/engine_support.h"
#include "engine/engine_util_blas.h"
#include "engine/engine_util_errmem.h"
#include "engine/engine_util_misc.h"
#include "engine/engine_util_spatial.h"
// transform pose from room to model space
void mjv_room2model(mjtNum* modelpos, mjtNum* modelquat, const mjtNum* roompos,
const mjtNum* roomquat, const mjvScene* scn) {
mjtNum translate[3], rotate[4], invpos[3], invquat[4];
// check scale
if (scn->scale<mjMINVAL) {
mju_error("mjvScene scale too small in mjv_room2model");
}
// enabled: transform
if (scn->enabletransform) {
// convert translate, rotate to mjtNum
mju_f2n(translate, scn->translate, 3);
mju_f2n(rotate, scn->rotate, 4);
// invert model pose (without scale)
mju_negPose(invpos, invquat, translate, rotate);
// map from room to model space
mju_mulPose(modelpos, modelquat, invpos, invquat, roompos, roomquat);
// divide position by scale
mju_scl3(modelpos, modelpos, 1.0/scn->scale);
}
// disabled: copy
else {
mju_copy3(modelpos, roompos);
mju_copy4(modelquat, roomquat);
}
}
// transform pose from model to room space
void mjv_model2room(mjtNum* roompos, mjtNum* roomquat, const mjtNum* modelpos,
const mjtNum* modelquat, const mjvScene* scn) {
mjtNum translate[3], rotate[4];
// check scale
if (scn->scale<mjMINVAL) {
mju_error("mjvScene scale too small in mjv_model2room");
}
// enabled: transform
if (scn->enabletransform) {
// convert translate, rotate to mjtNum
mju_f2n(translate, scn->translate, 3);
mju_f2n(rotate, scn->rotate, 4);
// map from model to room space
mju_mulPose(roompos, roomquat, translate, rotate, modelpos, modelquat);
// scale position
mju_scl3(roompos, roompos, scn->scale);
}
// disabled: copy
else {
mju_copy3(roompos, modelpos);
mju_copy4(roomquat, modelquat);
}
}
// get camera info in model space: average left and right OpenGL cameras
void mjv_cameraInModel(mjtNum* headpos, mjtNum* forward, mjtNum* up, const mjvScene* scn) {
mjtNum pos[3], fwd[3], u[3], quat[4];
mjtNum modelpos[3], modelquat[4], modelmat[9];
// check znear
if (scn->camera[0].frustum_near<mjMINVAL || scn->camera[1].frustum_near<mjMINVAL) {
mju_error("mjvScene frustum_near too small in mjv_cameraInModel");
}
// clear results
if (headpos) {
mju_zero3(headpos);
}
if (forward) {
mju_zero3(forward);
}
if (up) {
mju_zero3(up);
}
// average over cameras
for (int n=0; n<2; n++) {
// convert pos, fwd, u
mju_f2n(pos, scn->camera[n].pos, 3);
mju_f2n(fwd, scn->camera[n].forward, 3);
mju_f2n(u, scn->camera[n].up, 3);
// normalize just in case
mju_normalize3(fwd);
mju_normalize3(u);
// make orientation matrix: x = left, y = up, z = forward
mjtNum left[3];
mju_cross(left, u, fwd);
mju_normalize3(left);
mjtNum mat[9] = {
left[0], u[0], fwd[0],
left[1], u[1], fwd[1],
left[2], u[2], fwd[2]
};
mju_mat2Quat(quat, mat);
// convert to model space, make orientation matrix
mjv_room2model(modelpos, modelquat, pos, quat, scn);
mju_quat2Mat(modelmat, modelquat);
// finalize results
if (headpos) {
mju_addToScl3(headpos, modelpos, 0.5);
}
if (forward) {
forward[0] += 0.5*modelmat[2];
forward[1] += 0.5*modelmat[5];
forward[2] += 0.5*modelmat[8];
}
if (up) {
up[0] += 0.5*modelmat[1];
up[1] += 0.5*modelmat[4];
up[2] += 0.5*modelmat[7];
}
}
// normalize forward and up
if (forward) {
mju_normalize3(forward);
}
if (up) {
mju_normalize3(up);
}
}
// get camera info in room space: average left and right OpenGL cameras
void mjv_cameraInRoom(mjtNum* headpos, mjtNum* forward, mjtNum* up, const mjvScene* scn) {
mjtNum pos[3], fwd[3], u[3];
// check znear
if (scn->camera[0].frustum_near<mjMINVAL || scn->camera[1].frustum_near<mjMINVAL) {
mju_error("mjvScene frustum_near too small in mjv_cameraInRoom");
}
// clear results
if (headpos) {
mju_zero3(headpos);
}
if (forward) {
mju_zero3(forward);
}
if (up) {
mju_zero3(up);
}
// average over cameras
for (int n=0; n<2; n++) {
// convert pos, fwd, u
mju_f2n(pos, scn->camera[n].pos, 3);
mju_f2n(fwd, scn->camera[n].forward, 3);
mju_f2n(u, scn->camera[n].up, 3);
// finalize results
if (headpos) {
mju_addToScl3(headpos, pos, 0.5);
}
if (forward) {
mju_addToScl3(forward, fwd, 0.5);
}
if (up) {
mju_addToScl3(up, u, 0.5);
}
}
// normalize
if (forward) {
mju_normalize3(forward);
}
if (up) {
mju_normalize3(up);
}
}
// get frustum height at unit distance from camera; average left and right OpenGL cameras
mjtNum mjv_frustumHeight(const mjvScene* scn) {
mjtNum height;
// check znear
if (scn->camera[0].frustum_near<mjMINVAL || scn->camera[1].frustum_near<mjMINVAL) {
mju_error("mjvScene frustum_near too small in mjv_frustumHeight");
}
// add normalized height for left and right cameras
height = (scn->camera[0].frustum_top-scn->camera[0].frustum_bottom)/scn->camera[0].frustum_near +
(scn->camera[1].frustum_top-scn->camera[1].frustum_bottom)/scn->camera[1].frustum_near;
// average
return 0.5*height;
}
// rotate 3D vec in horizontal plane by angle between (0,1) and (forward_x,forward_y)
MJAPI void mjv_alignToCamera(mjtNum* res, const mjtNum* vec, const mjtNum* forward) {
mjtNum xaxis[2], yaxis[2];
// fotward-aligned y-axis
mju_copy(yaxis, forward, 2);
mju_normalize(yaxis, 2);
// corresponding x-axis
xaxis[0] = yaxis[1];
xaxis[1] = -yaxis[0];
// apply horizontal rotation
res[0] = vec[0]*xaxis[0] + vec[1]*yaxis[0];
res[1] = vec[0]*xaxis[1] + vec[1]*yaxis[1];
res[2] = vec[2];
}
// convert 2D mouse motion to z-aligned 3D world coordinates
static void convert2D(mjtNum* res, int action, mjtNum dx, mjtNum dy, const mjtNum* forward) {
mjtNum vec[3];
// construct 3D vector
switch (action) {
case mjMOUSE_ROTATE_V:
vec[0] = dy;
vec[1] = 0;
vec[2] = dx;
break;
case mjMOUSE_ROTATE_H:
vec[0] = dy;
vec[1] = dx;
vec[2] = 0;
break;
case mjMOUSE_MOVE_V:
vec[0] = dx;
vec[1] = 0;
vec[2] = -dy;
break;
case mjMOUSE_MOVE_H:
vec[0] = dx;
vec[1] = -dy;
vec[2] = 0;
break;
case mjMOUSE_ZOOM:
break;
default:
mju_error_i("Unexpected mouse action %d in convert2D", action);
}
// call 3D converter
mjv_alignToCamera(res, vec, forward);
}
// move camera with mouse; action is mjtMouse
void mjv_moveCamera(const mjModel* m, int action, mjtNum reldx, mjtNum reldy,
const mjvScene* scn, mjvCamera* cam) {
mjtNum headpos[3], forward[3];
mjtNum vec[3], dif[3], scl;
// fixed camera: nothing to do
if (cam->type==mjCAMERA_FIXED) {
return;
}
// process action
switch (action) {
case mjMOUSE_ROTATE_V:
case mjMOUSE_ROTATE_H:
cam->azimuth -= reldx * 180.0;
cam->elevation -= reldy * 180.0;
break;
case mjMOUSE_MOVE_V:
case mjMOUSE_MOVE_H:
// do not move lookat point of tracking camera
if (cam->type==mjCAMERA_TRACKING) {
return;
}
// get camera info and align
mjv_cameraInModel(headpos, forward, NULL, scn);
convert2D(vec, action, reldx, reldy, forward);
// compute scaling: rendered lookat displacement = mouse displacement
mju_sub3(dif, cam->lookat, headpos);
scl = mjv_frustumHeight(scn) * mju_dot3(dif, forward);
// move lookat point in opposite direction
mju_addToScl3(cam->lookat, vec, -scl);
break;
case mjMOUSE_ZOOM:
cam->distance -= mju_log(1 + cam->distance/m->stat.extent/3) * reldy * 9 * m->stat.extent;
break;
default:
mju_error_i("Unexpected action %d in mjv_moveCamera", action);
}
// clamp camera parameters
if (cam->azimuth > 180) {
cam->azimuth -= 360;
}
if (cam->azimuth < -180) {
cam->azimuth += 360;
}
if (cam->elevation > 89) {
cam->elevation = 89;
}
if (cam->elevation < -89) {
cam->elevation = -89;
}
if (cam->distance < 0.01*m->stat.extent) {
cam->distance = 0.01*m->stat.extent;
}
if (cam->distance > 100*m->stat.extent) {
cam->distance = 100*m->stat.extent;
}
}
// move perturb object with mouse; action is mjtMouse
void mjv_movePerturb(const mjModel* m, const mjData* d, int action, mjtNum reldx,
mjtNum reldy, const mjvScene* scn, mjvPerturb* pert) {
int sel = pert->select;
mjtNum forward[3], vec[3], dif[3], scl, q1[4], q2[4], xiquat[4];
// get camera info and align
mjv_cameraInModel(NULL, forward, NULL, scn);
convert2D(vec, action, reldx, reldy, forward);
// process action
switch (action) {
case mjMOUSE_MOVE_V:
case mjMOUSE_MOVE_H:
mju_addToScl3(pert->refpos, vec, pert->scale);
break;
case mjMOUSE_ROTATE_V:
case mjMOUSE_ROTATE_H:
// normalize vector, get length
scl = mju_normalize3(vec);
// make quaternion and apply
mju_axisAngle2Quat(q1, vec, scl*mjPI*2);
mju_mulQuat(q2, q1, pert->refquat);
mju_copy4(pert->refquat, q2);
mju_normalize4(pert->refquat);
// compute xiquat
mju_mulQuat(xiquat, d->xquat+4*sel, m->body_iquat+4*sel);
// limit rotation relative to selected body
if (sel>0 && sel<m->nbody) {
// q2 = neg(selbody) * refquat
mju_negQuat(q1, xiquat);
mju_mulQuat(q2, q1, pert->refquat);
// convert q2 to axis-angle
mju_quat2Vel(dif, q2, 1);
scl = mju_normalize3(dif);
// check limit: +/- 90 deg allowed
if (scl<-mjPI*0.5 || scl>mjPI*0.5) {
// clamp angle
scl = mju_max(-mjPI*0.5, mju_min(mjPI*0.5, scl));
// reconstruct q2
mju_axisAngle2Quat(q2, dif, scl);
// set refquat = selbody * q2_new
mju_mulQuat(pert->refquat, xiquat, q2);
}
}
break;
case mjMOUSE_ZOOM:
break;
default:
mju_error_i("Unexpected mouse action %d in mjv_movePerturb", action);
}
}
// move model with mouse; action is mjtMouse
void mjv_moveModel(const mjModel* m, int action, mjtNum reldx, mjtNum reldy,
const mjtNum roomup[3], mjvScene* scn) {
mjtNum roomforward[3], roomright[3], camforward[3];
mjtNum vec[3], scl, quat[4], rotate[4], result[4];
// transformation disabled: nothing to do
if (!scn->enabletransform) {
return;
}
// get camera forward in room space
mjv_cameraInRoom(NULL, camforward, NULL, scn);
// make orthogonal to roomright
mju_addScl3(roomforward, camforward, roomup, -mju_dot3(camforward, roomup));
mju_normalize3(roomforward);
// compute roomright
mju_cross(roomright, roomforward, roomup);
mju_normalize3(roomright);
// process action
switch (action) {
case mjMOUSE_ROTATE_V:
case mjMOUSE_ROTATE_H:
// construct rotation vector
for (int i=0; i<3; i++) {
if (action==mjMOUSE_ROTATE_V) {
vec[i] = roomup[i]*reldx + roomright[i]*reldy;
} else {
vec[i] = roomforward[i]*reldx + roomright[i]*reldy;
}
}
// make quaternion from angle-axis
scl = mju_normalize3(vec);
mju_axisAngle2Quat(quat, vec, scl*mjPI);
// get current model rotation
mju_f2n(rotate, scn->rotate, 4);
// compose rotation, normalize and and set
mju_mulQuat(result, quat, rotate);
mju_normalize4(result);
mju_n2f(scn->rotate, result, 4);
break;
case mjMOUSE_MOVE_V:
for (int i=0; i<3; i++) {
scn->translate[i] += (float)(roomright[i]*reldx - roomup[i]*reldy) * m->stat.extent;
}
break;
case mjMOUSE_MOVE_H:
for (int i=0; i<3; i++) {
scn->translate[i] += (float)(roomright[i]*reldx - roomforward[i]*reldy) * m->stat.extent;
}
break;
case mjMOUSE_ZOOM:
scn->scale += (float)(mju_log(1 + scn->scale/3) * reldy * 3);
if (scn->scale<0.01f) {
scn->scale = 0.01f;
} else if (scn->scale>100.0f) {
scn->scale = 100.0f;
}
break;
default:
mju_error_i("Unexpected action %d in mjv_moveModel", action);
}
}
// copy perturb pos,quat from selected body; set scale for perturbation
void mjv_initPerturb(const mjModel* m, const mjData* d, const mjvScene* scn, mjvPerturb* pert) {
int sel = pert->select;
mjtNum headpos[3], forward[3], dif[3];
// invalid selected body: return
if (sel<=0 || sel>=m->nbody) {
return;
}
// copy
mju_copy3(pert->refpos, d->xipos + 3*sel);
mju_mulQuat(pert->refquat, d->xquat + 4*sel, m->body_iquat + 4*sel);
// get camera info
mjv_cameraInModel(headpos, forward, NULL, scn);
// compute scaling: rendered pert->refpos displacement = mouse displacement
mju_sub3(dif, pert->refpos, headpos);
pert->scale = mjv_frustumHeight(scn) * mju_dot3(dif, forward);
}
// set perturb pos,quat in d->mocap when selected body is mocap, and in d->qpos otherwise
// d->qpos written only if flg_paused and subtree root for selected body has free joint
void mjv_applyPerturbPose(const mjModel* m, mjData* d, const mjvPerturb* pert,
int flg_paused) {
int rootid = 0, sel = pert->select;
mjtNum pos1[3], quat1[4], pos2[3], quat2[4], refpos[3], refquat[4];
mjtNum *Rpos, *Rquat, *Cpos, *Cquat;
// exit if nothing to do
if (sel<=0 || sel>=m->nbody || !(pert->active | pert->active2)) {
return;
}
// get rootid above selected body
rootid = m->body_rootid[sel];
// transform refpos,refquat from I-frame to X-frame of body[sel]
mju_negPose(pos1, quat1, m->body_ipos+3*sel, m->body_iquat+4*sel);
mju_mulPose(refpos, refquat, pert->refpos, pert->refquat, pos1, quat1);
// mocap body
if (m->body_mocapid[sel]>=0) {
// copy ref pose into mocap pose
mju_copy3(d->mocap_pos + 3*m->body_mocapid[sel], refpos);
mju_copy4(d->mocap_quat + 4*m->body_mocapid[sel], refquat);
}
// floating body, paused
else if (flg_paused && m->body_jntnum[sel]==1 &&
m->jnt_type[m->body_jntadr[sel]]==mjJNT_FREE) {
// copy ref pose into qpos
mju_copy3(d->qpos + m->jnt_qposadr[m->body_jntadr[sel]], refpos);
mju_copy4(d->qpos + m->jnt_qposadr[m->body_jntadr[sel]] + 3, refquat);
}
// child of floating body, paused
else if (flg_paused && m->body_jntnum[rootid]==1 &&
m->jnt_type[m->body_jntadr[rootid]]==mjJNT_FREE) {
// get pointers to root
Rpos = d->qpos + m->jnt_qposadr[m->body_jntadr[rootid]];
Rquat = Rpos + 3;
// get pointers to child
Cpos = d->xpos + 3*sel;
Cquat = d->xquat + 4*sel;
// set root <- ref*neg(child)*root
mju_negPose(pos1, quat1, Cpos, Cquat); // neg(child)
mju_mulPose(pos2, quat2, pos1, quat1, Rpos, Rquat); // neg(child)*root
mju_mulPose(Rpos, Rquat, refpos, refquat, pos2, quat2); // ref*neg(child)*root
}
}
// set perturb force,torque in d->xfrc_applied, if selected body is dynamic
void mjv_applyPerturbForce(const mjModel* m, mjData* d, const mjvPerturb* pert) {
mjtNum xiquat[4], difquat[4], bvel[6], mass, stiffness, *result;
int sel = pert->select;
// exit if nothing to do
if (sel<0 ||sel>=m->nbody || !(pert->active | pert->active2)) {
return;
}
// get pointer to body xfrc_applied
result = d->xfrc_applied + 6*sel;
// global selbody velocity
mj_objectVelocity(m, d, mjOBJ_BODY, sel, bvel, 0);
// spring perturbation, with critical damping
// - force
stiffness = m->vis.map.stiffness;
mass = 1.0/mju_max(mjMINVAL, m->body_invweight0[2*sel]);
mju_sub3(result, pert->refpos, d->xipos+3*sel);
mju_scl3(result, result, stiffness*mass);
mju_addToScl3(result, bvel+3, -sqrtf(stiffness)*mass);
// - torque
stiffness = m->vis.map.stiffnessrot;
mass = 1.0/mju_max(mjMINVAL, m->body_invweight0[2*sel+1]);
mju_mulQuat(xiquat, d->xquat+4*sel, m->body_iquat+4*sel);
mju_negQuat(xiquat, xiquat);
mju_mulQuat(difquat, pert->refquat, xiquat);
mju_quat2Vel(result+3, difquat, 1.0/(stiffness*mass));
mju_addToScl3(result+3, bvel, -sqrtf(stiffness)*mass);
// mask
if (!((pert->active | pert->active2) & mjPERT_TRANSLATE)) {
mju_zero3(result);
}
if (!((pert->active | pert->active2) & mjPERT_ROTATE)) {
mju_zero3(result+3);
}
}
// return the average of two OpenGL cameras
mjvGLCamera mjv_averageCamera(const mjvGLCamera* cam1, const mjvGLCamera* cam2) {
mjtNum pos[3], forward[3], up[3], projection, tmp1[3], tmp2[3];
mjvGLCamera cam;
// compute pos
mju_f2n(tmp1, cam1->pos, 3);
mju_f2n(tmp2, cam2->pos, 3);
mju_add3(pos, tmp1, tmp2);
mju_scl3(pos, pos, 0.5);
// compute forward
mju_f2n(tmp1, cam1->forward, 3);
mju_f2n(tmp2, cam2->forward, 3);
mju_add3(forward, tmp1, tmp2);
mju_normalize3(forward);
// compute up, make it orthogonal to forward
mju_f2n(tmp1, cam1->up, 3);
mju_f2n(tmp2, cam2->up, 3);
mju_add3(up, tmp1, tmp2);
projection = mju_dot3(up, forward);
mju_addToScl3(up, forward, -projection);
mju_normalize3(up);
// assign 3d quantities
mju_n2f(cam.pos, pos, 3);
mju_n2f(cam.forward, forward, 3);
mju_n2f(cam.up, up, 3);
// average frustum
cam.frustum_bottom = 0.5f * (cam1->frustum_bottom + cam2->frustum_bottom);
cam.frustum_top = 0.5f * (cam1->frustum_top + cam2->frustum_top);
cam.frustum_center = 0.5f * (cam1->frustum_center + cam2->frustum_center);
cam.frustum_near = 0.5f * (cam1->frustum_near + cam2->frustum_near);
cam.frustum_far = 0.5f * (cam1->frustum_far + cam2->frustum_far);
return cam;
}
// Select geom or skin with mouse, return bodyid; -1: none selected.
int mjv_select(const mjModel* m, const mjData* d, const mjvOption* vopt,
mjtNum aspectratio, mjtNum relx, mjtNum rely,
const mjvScene* scn, mjtNum selpnt[3], int geomid[1], int skinid[1]) {
// get average camera
mjvGLCamera cam = mjv_averageCamera(scn->camera, scn->camera+1);
// get camera pose in model space
mjtNum pos[3], forward[3], up[3], left[3];
mjv_cameraInModel(pos, forward, up, scn);
mju_cross(left, up, forward);
mju_normalize3(left);
// compute frustum halfwidth so as to match viewport aspect ratio
mjtNum halfwidth = 0.5*aspectratio*(cam.frustum_top - cam.frustum_bottom);
// construct ray
mjtNum ray[3];
mju_scl3(ray, forward, cam.frustum_near);
mju_addToScl3(ray, up, cam.frustum_bottom + rely*(cam.frustum_top-cam.frustum_bottom));
mju_addToScl3(ray, left, -(cam.frustum_center + (2*relx-1)*halfwidth));
mju_normalize3(ray);
// find intersection with geoms
*geomid = -1;
mjtNum geomdist = mj_ray(m, d, pos, ray, vopt->geomgroup,
vopt->flags[mjVIS_STATIC], -1, geomid);
// find intersection with skins
int bodyid = -1;
mjtNum skindist = -1;
*skinid = -1;
if (vopt->flags[mjVIS_SKIN]) {
for (int i=0; i<m->nskin; i++) {
// process one skin
int vertid;
mjtNum newdist = mju_raySkin(m->skin_facenum[i], m->skin_vertnum[i],
m->skin_face + 3*m->skin_faceadr[i],
scn->skinvert + 3*m->skin_vertadr[i],
pos, ray, &vertid);
// update if closer intersection found
if (newdist>=0 && (newdist<skindist || skindist<0)) {
// assign result
skindist = newdist;
// find body with largest weight for this vertex
float bestweight = -1;
for (int j=m->skin_boneadr[i];
j<m->skin_boneadr[i]+m->skin_bonenum[i];
j++) {
for (int k=m->skin_bonevertadr[j];
k<m->skin_bonevertadr[j]+m->skin_bonevertnum[j];
k++) {
// get vertex id and weight
int vid = m->skin_bonevertid[k];
float vweight = m->skin_bonevertweight[k];
// update if matching id and bigger weight
if (vid==vertid && vweight>bestweight) {
bestweight = vweight;
bodyid = m->skin_bonebodyid[j];
*skinid = i;
}
}
}
}
}
}
// no intersection
if (geomdist<0 && skindist<0) {
return -1;
}
// geom only, or geom closer than skin
else if (geomdist>=0 && (skindist<0 || skindist>geomdist)) {
mju_addScl3(selpnt, pos, ray, geomdist);
*skinid = -1;
return m->geom_bodyid[*geomid];
}
// skin
else {
mju_addScl3(selpnt, pos, ray, skindist);
*geomid = -1;
return bodyid;
}
}
+85
View File
@@ -0,0 +1,85 @@
// 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_SRC_ENGINE_ENGINE_VIS_INTERACT_H_
#define MUJOCO_SRC_ENGINE_ENGINE_VIS_INTERACT_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mjvisualize.h>
#ifdef __cplusplus
extern "C" {
#endif
// transform pose from room to model space
MJAPI void mjv_room2model(mjtNum* modelpos, mjtNum* modelquat, const mjtNum* roompos,
const mjtNum* roomquat, const mjvScene* scn);
// transform pose from model to room space
MJAPI void mjv_model2room(mjtNum* roompos, mjtNum* roomquat, const mjtNum* modelpos,
const mjtNum* modelquat, const mjvScene* scn);
// get camera info in model space: average left and right OpenGL cameras
MJAPI void mjv_cameraInModel(mjtNum* headpos, mjtNum* forward, mjtNum* up,
const mjvScene* scn);
// get camera info in room space: average left and right OpenGL cameras
MJAPI void mjv_cameraInRoom(mjtNum* headpos, mjtNum* forward, mjtNum* up,
const mjvScene* scn);
// get frustum height at unit distance from camera; average left and right OpenGL cameras
MJAPI mjtNum mjv_frustumHeight(const mjvScene* scn);
// rotate 3D vec in horizontal plane by angle between (0,1) and (forward_x,forward_y)
MJAPI void mjv_alignToCamera(mjtNum* res, const mjtNum* vec, const mjtNum* forward);
// move camera with mouse; action is mjtMouse
MJAPI void mjv_moveCamera(const mjModel* m, int action, mjtNum reldx, mjtNum reldy,
const mjvScene* scn, mjvCamera* cam);
// move perturb object with mouse; action is mjtMouse
MJAPI void mjv_movePerturb(const mjModel* m, const mjData* d, int action, mjtNum reldx,
mjtNum reldy, const mjvScene* scn, mjvPerturb* pert);
// move model with mouse; action is mjtMouse
MJAPI void mjv_moveModel(const mjModel* m, int action, mjtNum reldx, mjtNum reldy,
const mjtNum roomup[3], mjvScene* scn);
// copy perturb pos,quat from selected body; set scale perturbation
MJAPI void mjv_initPerturb(const mjModel* m, const mjData* d,
const mjvScene* scn, mjvPerturb* pert);
// set perturb pos,quat in d->mocap when selected body is mocap, and in d->qpos otherwise
// d->qpos written only if flg_paused and subtree root for selected body has free joint
MJAPI void mjv_applyPerturbPose(const mjModel* m, mjData* d, const mjvPerturb* pert,
int flg_paused);
// set perturb force,torque in d->xfrc_applied, if selected body is dynamic
MJAPI void mjv_applyPerturbForce(const mjModel* m, mjData* d, const mjvPerturb* pert);
// return the average of two OpenGL cameras
MJAPI mjvGLCamera mjv_averageCamera(const mjvGLCamera* cam1, const mjvGLCamera* cam2);
// Select geom or skin with mouse, return bodyid; -1: none selected.
MJAPI int mjv_select(const mjModel* m, const mjData* d, const mjvOption* vopt,
mjtNum aspectratio, mjtNum relx, mjtNum rely,
const mjvScene* scn, mjtNum selpnt[3], int geomid[1], int skinid[1]);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_VIS_INTERACT_H_
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
// 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_SRC_ENGINE_ENGINE_VIS_VISUALIZE_H_
#define MUJOCO_SRC_ENGINE_ENGINE_VIS_VISUALIZE_H_
#include <mujoco/mjdata.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mjvisualize.h>
#ifdef __cplusplus
extern "C" {
#endif
// set (type, size, pos, mat) connector-type geom between given points
// assume that mjv_initGeom was already called to set all other properties
MJAPI void mjv_makeConnector(mjvGeom* geom, int type, mjtNum width,
mjtNum a0, mjtNum a1, mjtNum a2,
mjtNum b0, mjtNum b1, mjtNum b2);
// initialize given fields when not NULL, set the rest to their default values
MJAPI void mjv_initGeom(mjvGeom* geom, int type, const mjtNum* size,
const mjtNum* pos, const mjtNum* mat, const float* rgba);
// update entire scene
MJAPI void mjv_updateScene(const mjModel* m, mjData* d, const mjvOption* opt,
const mjvPerturb* pert, mjvCamera* cam, int catmask, mjvScene* scn);
// add geoms from selected categories to existing scene
MJAPI void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* opt,
const mjvPerturb* pert, int catmask, mjvScene* scn);
// make list of lights only
MJAPI void mjv_makeLights(const mjModel* m, mjData* d, mjvScene* scn);
// update camera only
MJAPI void mjv_updateCamera(const mjModel* m, mjData* d, mjvCamera* cam, mjvScene* scn);
// update skins only
MJAPI void mjv_updateSkin(const mjModel* m, mjData* d, mjvScene* scn);
#ifdef __cplusplus
}
#endif
#endif // MUJOCO_SRC_ENGINE_ENGINE_VIS_VISUALIZE_H_