Add vertex-based flex constraints (dim=2).
PiperOrigin-RevId: 859552050 Change-Id: I61d4b9dc40f041c5b930e5f8810a803e8bccb87a
This commit is contained in:
committed by
Copybara-Service
parent
2cc2e579f1
commit
54dd623650
@@ -274,6 +274,8 @@ struct mjData_ {
|
||||
mjtNum* flexelem_aabb; // flex element bounding boxes (center, size) (nflexelem x 6)
|
||||
mjtNum* flexedge_J; // flex edge Jacobian (nJfe x 1)
|
||||
mjtNum* flexedge_length; // flex edge lengths (nflexedge x 1)
|
||||
mjtNum* flexvert_J; // flex vertex Jacobian (nJfv x 2)
|
||||
mjtNum* flexvert_length; // flex vertex lengths (nflexvert x 2)
|
||||
mjtNum* bvh_aabb_dyn; // global bounding box (center, size) (nbvhdynamic x 6)
|
||||
|
||||
// computed by mj_fwdPosition/mj_tendon
|
||||
@@ -1034,6 +1036,7 @@ struct mjModel_ {
|
||||
int nflexevpair; // number of element-vertex pairs in all flexes
|
||||
int nflextexcoord; // number of vertices with texture coordinates
|
||||
int nJfe; // number of non-zeros in sparse flexedge Jacobian matrix
|
||||
int nJfv; // number of non-zeros in sparse flexvert Jacobian matrix
|
||||
int nmesh; // number of meshes
|
||||
int nmeshvert; // number of vertices in all meshes
|
||||
int nmeshnormal; // number of normals in all meshes
|
||||
@@ -1328,12 +1331,13 @@ struct mjModel_ {
|
||||
mjtNum* flexedge_length0; // edge lengths in qpos0 (nflexedge x 1)
|
||||
mjtNum* flexedge_invweight0; // edge inv. weight in qpos0 (nflexedge x 1)
|
||||
mjtNum* flex_radius; // radius around primitive element (nflex x 1)
|
||||
mjtNum* flex_size; // vertex bounding box half sizes in qpos0 (nflex x 3)
|
||||
mjtNum* flex_stiffness; // finite element stiffness matrix (nflexelem x 21)
|
||||
mjtNum* flex_bending; // bending stiffness (nflexedge x 17)
|
||||
mjtNum* flex_damping; // Rayleigh's damping coefficient (nflex x 1)
|
||||
mjtNum* flex_edgestiffness; // edge stiffness (nflex x 1)
|
||||
mjtNum* flex_edgedamping; // edge damping (nflex x 1)
|
||||
mjtByte* flex_edgeequality; // is edge equality constraint defined (nflex x 1)
|
||||
int* flex_edgeequality; // 0: none, 1: edges, 2: vertices (nflex x 1)
|
||||
mjtByte* flex_rigid; // are all vertices in the same body (nflex x 1)
|
||||
mjtByte* flexedge_rigid; // are both edge vertices in same body (nflexedge x 1)
|
||||
mjtByte* flex_centered; // are all vertex coordinates (0,0,0) (nflex x 1)
|
||||
@@ -1343,6 +1347,9 @@ struct mjModel_ {
|
||||
int* flexedge_J_rownnz; // number of non-zeros in Jacobian row (nflexedge x 1)
|
||||
int* flexedge_J_rowadr; // row start address in colind array (nflexedge x 1)
|
||||
int* flexedge_J_colind; // column indices in sparse Jacobian (nJfe x 1)
|
||||
int* flexvert_J_rownnz; // number of non-zeros in Jacobian row (nflexvert x 2)
|
||||
int* flexvert_J_rowadr; // row start address in colind array (nflexvert x 2)
|
||||
int* flexvert_J_colind; // column indices in sparse Jacobian (nJfv x 2)
|
||||
float* flex_rgba; // rgba when material is omitted (nflex x 4)
|
||||
float* flex_texcoord; // vertex texture coordinates (nflextexcoord x 2)
|
||||
|
||||
@@ -2186,6 +2193,7 @@ typedef struct mjsFlex_ { // flex specification
|
||||
// other properties
|
||||
int dim; // element dimensionality
|
||||
double radius; // radius around primitive element
|
||||
double size[3]; // vertex bounding box half sizes in qpos0
|
||||
mjtByte internal; // enable internal collisions
|
||||
mjtByte flatskin; // render flex skin with flat shading
|
||||
int selfcollide; // mode for flex self collision
|
||||
|
||||
@@ -308,6 +308,8 @@ struct mjData_ {
|
||||
mjtNum* flexelem_aabb; // flex element bounding boxes (center, size) (nflexelem x 6)
|
||||
mjtNum* flexedge_J; // flex edge Jacobian (nJfe x 1)
|
||||
mjtNum* flexedge_length; // flex edge lengths (nflexedge x 1)
|
||||
mjtNum* flexvert_J; // flex vertex Jacobian (nJfv x 2)
|
||||
mjtNum* flexvert_length; // flex vertex lengths (nflexvert x 2)
|
||||
mjtNum* bvh_aabb_dyn; // global bounding box (center, size) (nbvhdynamic x 6)
|
||||
|
||||
// computed by mj_fwdPosition/mj_tendon
|
||||
|
||||
@@ -702,6 +702,7 @@ struct mjModel_ {
|
||||
int nflexevpair; // number of element-vertex pairs in all flexes
|
||||
int nflextexcoord; // number of vertices with texture coordinates
|
||||
int nJfe; // number of non-zeros in sparse flexedge Jacobian matrix
|
||||
int nJfv; // number of non-zeros in sparse flexvert Jacobian matrix
|
||||
int nmesh; // number of meshes
|
||||
int nmeshvert; // number of vertices in all meshes
|
||||
int nmeshnormal; // number of normals in all meshes
|
||||
@@ -996,12 +997,13 @@ struct mjModel_ {
|
||||
mjtNum* flexedge_length0; // edge lengths in qpos0 (nflexedge x 1)
|
||||
mjtNum* flexedge_invweight0; // edge inv. weight in qpos0 (nflexedge x 1)
|
||||
mjtNum* flex_radius; // radius around primitive element (nflex x 1)
|
||||
mjtNum* flex_size; // vertex bounding box half sizes in qpos0 (nflex x 3)
|
||||
mjtNum* flex_stiffness; // finite element stiffness matrix (nflexelem x 21)
|
||||
mjtNum* flex_bending; // bending stiffness (nflexedge x 17)
|
||||
mjtNum* flex_damping; // Rayleigh's damping coefficient (nflex x 1)
|
||||
mjtNum* flex_edgestiffness; // edge stiffness (nflex x 1)
|
||||
mjtNum* flex_edgedamping; // edge damping (nflex x 1)
|
||||
mjtByte* flex_edgeequality; // is edge equality constraint defined (nflex x 1)
|
||||
int* flex_edgeequality; // 0: none, 1: edges, 2: vertices (nflex x 1)
|
||||
mjtByte* flex_rigid; // are all vertices in the same body (nflex x 1)
|
||||
mjtByte* flexedge_rigid; // are both edge vertices in same body (nflexedge x 1)
|
||||
mjtByte* flex_centered; // are all vertex coordinates (0,0,0) (nflex x 1)
|
||||
@@ -1011,6 +1013,9 @@ struct mjModel_ {
|
||||
int* flexedge_J_rownnz; // number of non-zeros in Jacobian row (nflexedge x 1)
|
||||
int* flexedge_J_rowadr; // row start address in colind array (nflexedge x 1)
|
||||
int* flexedge_J_colind; // column indices in sparse Jacobian (nJfe x 1)
|
||||
int* flexvert_J_rownnz; // number of non-zeros in Jacobian row (nflexvert x 2)
|
||||
int* flexvert_J_rowadr; // row start address in colind array (nflexvert x 2)
|
||||
int* flexvert_J_colind; // column indices in sparse Jacobian (nJfv x 2)
|
||||
float* flex_rgba; // rgba when material is omitted (nflex x 4)
|
||||
float* flex_texcoord; // vertex texture coordinates (nflextexcoord x 2)
|
||||
|
||||
|
||||
@@ -438,6 +438,7 @@ typedef struct mjsFlex_ { // flex specification
|
||||
// other properties
|
||||
int dim; // element dimensionality
|
||||
double radius; // radius around primitive element
|
||||
double size[3]; // vertex bounding box half sizes in qpos0
|
||||
mjtByte internal; // enable internal collisions
|
||||
mjtByte flatskin; // render flex skin with flat shading
|
||||
int selfcollide; // mode for flex self collision
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
X( nflexevpair ) \
|
||||
X( nflextexcoord ) \
|
||||
X( nJfe ) \
|
||||
X( nJfv ) \
|
||||
X( nmesh ) \
|
||||
X( nmeshvert ) \
|
||||
X( nmeshnormal ) \
|
||||
@@ -386,12 +387,13 @@
|
||||
X ( mjtNum, flexedge_length0, nflexedge, 1 ) \
|
||||
X ( mjtNum, flexedge_invweight0, nflexedge, 1 ) \
|
||||
X ( mjtNum, flex_radius, nflex, 1 ) \
|
||||
X ( mjtNum, flex_size, nflex, 3 ) \
|
||||
X ( mjtNum, flex_stiffness, nflexelem, 21 ) \
|
||||
X ( mjtNum, flex_bending, nflexedge, 17 ) \
|
||||
X ( mjtNum, flex_damping, nflex, 1 ) \
|
||||
X ( mjtNum, flex_edgestiffness, nflex, 1 ) \
|
||||
X ( mjtNum, flex_edgedamping, nflex, 1 ) \
|
||||
X ( mjtByte, flex_edgeequality, nflex, 1 ) \
|
||||
X ( int, flex_edgeequality, nflex, 1 ) \
|
||||
X ( mjtByte, flex_rigid, nflex, 1 ) \
|
||||
X ( mjtByte, flexedge_rigid, nflexedge, 1 ) \
|
||||
X ( mjtByte, flex_centered, nflex, 1 ) \
|
||||
@@ -401,6 +403,9 @@
|
||||
X ( int, flexedge_J_rownnz, nflexedge, 1 ) \
|
||||
X ( int, flexedge_J_rowadr, nflexedge, 1 ) \
|
||||
X ( int, flexedge_J_colind, nJfe, 1 ) \
|
||||
X ( int, flexvert_J_rownnz, nflexvert, 2 ) \
|
||||
X ( int, flexvert_J_rowadr, nflexvert, 2 ) \
|
||||
X ( int, flexvert_J_colind, nJfv, 2 ) \
|
||||
X ( float, flex_rgba, nflex, 4 ) \
|
||||
X ( float, flex_texcoord, nflextexcoord, 2 )
|
||||
|
||||
@@ -736,6 +741,8 @@
|
||||
X ( mjtNum, flexelem_aabb, nflexelem, 6 ) \
|
||||
X ( mjtNum, flexedge_J, nJfe, 1 ) \
|
||||
X ( mjtNum, flexedge_length, nflexedge, 1 ) \
|
||||
X ( mjtNum, flexvert_J, nJfv, 2 ) \
|
||||
X ( mjtNum, flexvert_length, nflexvert, 2 ) \
|
||||
X ( mjtNum, bvh_aabb_dyn, nbvhdynamic, 6 ) \
|
||||
X ( int, ten_wrapadr, ntendon, 1 ) \
|
||||
X ( int, ten_wrapnum, ntendon, 1 ) \
|
||||
|
||||
@@ -997,6 +997,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
type=ValueType(name='int'),
|
||||
doc='number of non-zeros in sparse flexedge Jacobian matrix',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='nJfv',
|
||||
type=ValueType(name='int'),
|
||||
doc='number of non-zeros in sparse flexvert Jacobian matrix',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='nmesh',
|
||||
type=ValueType(name='int'),
|
||||
@@ -2880,6 +2885,14 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
doc='radius around primitive element',
|
||||
array_extent=('nflex',),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='flex_size',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtNum'),
|
||||
),
|
||||
doc='vertex bounding box half sizes in qpos0',
|
||||
array_extent=('nflex', 3),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='flex_stiffness',
|
||||
type=PointerType(
|
||||
@@ -2923,9 +2936,9 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
StructFieldDecl(
|
||||
name='flex_edgeequality',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtByte'),
|
||||
inner_type=ValueType(name='int'),
|
||||
),
|
||||
doc='is edge equality constraint defined',
|
||||
doc='0: none, 1: edges, 2: vertices',
|
||||
array_extent=('nflex',),
|
||||
),
|
||||
StructFieldDecl(
|
||||
@@ -3000,6 +3013,30 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
doc='column indices in sparse Jacobian',
|
||||
array_extent=('nJfe',),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='flexvert_J_rownnz',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='int'),
|
||||
),
|
||||
doc='number of non-zeros in Jacobian row',
|
||||
array_extent=('nflexvert', 2),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='flexvert_J_rowadr',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='int'),
|
||||
),
|
||||
doc='row start address in colind array',
|
||||
array_extent=('nflexvert', 2),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='flexvert_J_colind',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='int'),
|
||||
),
|
||||
doc='column indices in sparse Jacobian',
|
||||
array_extent=('nJfv', 2),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='flex_rgba',
|
||||
type=PointerType(
|
||||
@@ -5609,6 +5646,22 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
doc='flex edge lengths',
|
||||
array_extent=('nflexedge',),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='flexvert_J',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtNum'),
|
||||
),
|
||||
doc='flex vertex Jacobian',
|
||||
array_extent=('nJfv', 2),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='flexvert_length',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjtNum'),
|
||||
),
|
||||
doc='flex vertex lengths',
|
||||
array_extent=('nflexvert', 2),
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='bvh_aabb_dyn',
|
||||
type=PointerType(
|
||||
@@ -7879,6 +7932,14 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
type=ValueType(name='double'),
|
||||
doc='radius around primitive element',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='size',
|
||||
type=ArrayType(
|
||||
inner_type=ValueType(name='double'),
|
||||
extents=(3,),
|
||||
),
|
||||
doc='vertex bounding box half sizes in qpos0',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='internal',
|
||||
type=ValueType(name='mjtByte'),
|
||||
|
||||
@@ -537,6 +537,7 @@ void mj_updateDynamicBVH(const mjModel* m, mjData* d, int bvhadr, int bvhnum) {
|
||||
void mj_flex(const mjModel* m, mjData* d) {
|
||||
int nv = m->nv;
|
||||
int* rowadr = m->flexedge_J_rowadr, *rownnz = m->flexedge_J_rownnz;
|
||||
int* vrowadr = m->flexvert_J_rowadr, *vrownnz = m->flexvert_J_rownnz;
|
||||
|
||||
// skip if no flexes
|
||||
if (!m->nflex) {
|
||||
@@ -659,6 +660,9 @@ void mj_flex(const mjModel* m, mjData* d) {
|
||||
// clear Jacobian
|
||||
mju_zeroInt(rowadr, m->nflexedge);
|
||||
mju_zeroInt(rownnz, m->nflexedge);
|
||||
mju_zeroInt(vrowadr, 2*m->nflexvert);
|
||||
mju_zeroInt(vrownnz, 2*m->nflexvert);
|
||||
mju_zero(d->flexvert_J, 2*m->nJfv);
|
||||
|
||||
// compute lengths and Jacobians of edges
|
||||
for (int f=0; f < m->nflex; f++) {
|
||||
@@ -715,6 +719,321 @@ void mj_flex(const mjModel* m, mjData* d) {
|
||||
rownnz[ebase+e] = NV;
|
||||
mju_copyInt(m->flexedge_J_colind + rowadr[ebase+e], chain, NV);
|
||||
}
|
||||
|
||||
// if dim=2 and constraints are active we use the vertex-based constraint defined in
|
||||
// Chen, Kry, and Vouga, "Locking-free Simulation of Isometric Thin Plates", 2019.
|
||||
if (m->flex_dim[f] == 2 && m->flex_edgeequality[f] == 2) {
|
||||
int nvert = m->flex_vertnum[f];
|
||||
mjtNum edge1[3], edge2[3], normal[3];
|
||||
mjtNum quat[4], mat[9];
|
||||
int t_adr, t0, t1, t2;
|
||||
|
||||
mj_markStack(d);
|
||||
int* buf_ind = mjSTACKALLOC(d, nv, int);
|
||||
|
||||
// compute normal from first element
|
||||
t_adr = m->flex_elemdataadr[f];
|
||||
t0 = m->flex_elem[t_adr];
|
||||
t1 = m->flex_elem[t_adr+1];
|
||||
t2 = m->flex_elem[t_adr+2];
|
||||
|
||||
mju_sub3(edge1, m->flex_vert0 + 3*(vbase+t1), m->flex_vert0 + 3*(vbase+t0));
|
||||
mju_sub3(edge2, m->flex_vert0 + 3*(vbase+t2), m->flex_vert0 + 3*(vbase+t0));
|
||||
mji_cross(normal, edge1, edge2);
|
||||
mju_normalize3(normal);
|
||||
|
||||
// compute rotation to Z
|
||||
mju_quatZ2Vec(quat, normal);
|
||||
mju_quat2Mat(mat, quat);
|
||||
|
||||
// compute edge vectors
|
||||
mjtNum* edge_dx = mjSTACKALLOC(d, 3*m->flex_edgenum[f], mjtNum);
|
||||
mjtNum* edge_dy = mjSTACKALLOC(d, 3*m->flex_edgenum[f], mjtNum);
|
||||
for (int e=0; e < m->flex_edgenum[f]; e++) {
|
||||
int v1 = m->flex_edge[2*(ebase+e)];
|
||||
int v2 = m->flex_edge[2*(ebase+e)+1];
|
||||
mjtNum dx3[3];
|
||||
mju_sub3(dx3, m->flex_vert0 + 3*(vbase+v2), m->flex_vert0 + 3*(vbase+v1));
|
||||
dx3[0] *= 2*m->flex_size[3*f+0];
|
||||
dx3[1] *= 2*m->flex_size[3*f+1];
|
||||
dx3[2] *= 2*m->flex_size[3*f+2];
|
||||
mji_mulMatTVec3(edge_dx+3*e, mat, dx3);
|
||||
if (mju_abs((edge_dx+3*e)[2]) > mjMINVAL) {
|
||||
mjERROR("flex vertices are not in the same plane"); // SHOULD NOT OCCUR
|
||||
}
|
||||
mju_sub3(edge_dy+3*e, d->flexvert_xpos+3*(vbase+v2), d->flexvert_xpos+3*(vbase+v1));
|
||||
}
|
||||
|
||||
// build vertex adjacency list
|
||||
int* v_edge_cnt = mjSTACKALLOC(d, nvert, int);
|
||||
int* v_edge_adr = mjSTACKALLOC(d, nvert, int);
|
||||
int* adj_edges = mjSTACKALLOC(d, 2*m->flex_edgenum[f], int);
|
||||
mju_zeroInt(v_edge_cnt, nvert);
|
||||
for (int e = 0; e < m->flex_edgenum[f]; ++e) {
|
||||
v_edge_cnt[m->flex_edge[2*(ebase+e)+0]]++;
|
||||
v_edge_cnt[m->flex_edge[2*(ebase+e)+1]]++;
|
||||
}
|
||||
int total_adj_edges = 0;
|
||||
for (int v = 0; v < nvert; ++v) {
|
||||
v_edge_adr[v] = total_adj_edges;
|
||||
total_adj_edges += v_edge_cnt[v];
|
||||
}
|
||||
int* v_edge_fill = mjSTACKALLOC(d, nvert, int);
|
||||
mju_zeroInt(v_edge_fill, nvert);
|
||||
for (int e = 0; e < m->flex_edgenum[f]; ++e) {
|
||||
int v1 = m->flex_edge[2*(ebase+e)+0];
|
||||
int v2 = m->flex_edge[2*(ebase+e)+1];
|
||||
adj_edges[v_edge_adr[v1] + v_edge_fill[v1]] = e;
|
||||
v_edge_fill[v1]++;
|
||||
adj_edges[v_edge_adr[v2] + v_edge_fill[v2]] = e;
|
||||
v_edge_fill[v2]++;
|
||||
}
|
||||
|
||||
mjtNum* F_vert = mjSTACKALLOC(d, 6*nvert, mjtNum);
|
||||
mjtNum* Binv_vert = mjSTACKALLOC(d, 4*nvert, mjtNum);
|
||||
|
||||
// compute averaged Cauchy strain tensors for each vertex
|
||||
for (int v=0; v < nvert; v++) {
|
||||
mjtNum A[6] = {0}, B[4] = {0};
|
||||
int k, edge_idx;
|
||||
|
||||
for (k=0; k<v_edge_cnt[v]; k++) {
|
||||
edge_idx = adj_edges[v_edge_adr[v]+k];
|
||||
mjtNum* dx = edge_dx+3*edge_idx;
|
||||
mjtNum* dy = edge_dy+3*edge_idx;
|
||||
|
||||
// get mass of neighbor vertex
|
||||
mjtNum weight = 1.0;
|
||||
int v1 = m->flex_edge[2 * (ebase + edge_idx)];
|
||||
int v2 = m->flex_edge[2 * (ebase + edge_idx) + 1];
|
||||
int neighbor_v = (v == v1) ? v2 : v1;
|
||||
int b_neighbor = m->flex_vertbodyid[vbase + neighbor_v];
|
||||
if (b_neighbor >= 0) {
|
||||
weight = m->body_mass[b_neighbor];
|
||||
if (weight < mjMINVAL) weight = mjMINVAL;
|
||||
}
|
||||
|
||||
// accumulate A += w * dy * dx', B += w * dx * dx'
|
||||
for (int row=0; row < 3; row++) {
|
||||
for (int col=0; col < 2; col++) {
|
||||
A[2 * row + col] += weight * dy[row] * dx[col];
|
||||
}
|
||||
}
|
||||
for (int row=0; row < 2; row++) {
|
||||
for (int col=0; col < 2; col++) {
|
||||
B[2 * row + col] += weight * dx[row] * dx[col];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int vadr = vbase+v;
|
||||
mjtNum* F = F_vert + 6*v;
|
||||
mjtNum* Binv = Binv_vert + 4*v;
|
||||
mjtNum cauchy[2][2];
|
||||
|
||||
// compute Binv = B^-1
|
||||
mjtNum det = B[0]*B[3] - B[1]*B[2];
|
||||
if (mju_abs(det) < mjMINVAL) {
|
||||
mju_zero(Binv, 4);
|
||||
} else {
|
||||
mjtNum invdet = 1/det;
|
||||
Binv[0] = B[3]*invdet;
|
||||
Binv[1] = -B[1]*invdet;
|
||||
Binv[2] = -B[2]*invdet;
|
||||
Binv[3] = B[0]*invdet;
|
||||
}
|
||||
|
||||
// compute deformation gradient F = A * Binv
|
||||
mju_mulMatMat(F, A, Binv, 3, 2, 2);
|
||||
|
||||
// compute Cauchy strain tensor F^T F
|
||||
cauchy[0][0] = F[0]*F[0] + F[2]*F[2] + F[4]*F[4];
|
||||
cauchy[0][1] = F[0]*F[1] + F[2]*F[3] + F[4]*F[5];
|
||||
cauchy[1][0] = F[1]*F[0] + F[3]*F[2] + F[5]*F[4];
|
||||
cauchy[1][1] = F[1]*F[1] + F[3]*F[3] + F[5]*F[5];
|
||||
|
||||
// compute tensor invariants
|
||||
d->flexvert_length[2*vadr+0] = cauchy[0][0] + cauchy[1][1] - 2;
|
||||
d->flexvert_length[2*vadr+1] = cauchy[0][0] * cauchy[1][1] -
|
||||
cauchy[0][1] * cauchy[1][0] - 1;
|
||||
}
|
||||
|
||||
// 1st pass: compute vrownnz
|
||||
int* chain1 = mjSTACKALLOC(d, nv, int);
|
||||
int* chain2 = mjSTACKALLOC(d, nv, int);
|
||||
|
||||
// determine start address for this flex
|
||||
int v0_base = 2*vbase;
|
||||
int current_adr = 0;
|
||||
if (v0_base > 0) {
|
||||
current_adr = vrowadr[v0_base - 1] + vrownnz[v0_base - 1];
|
||||
}
|
||||
vrowadr[v0_base] = current_adr;
|
||||
|
||||
for (int v=0; v<nvert; ++v) {
|
||||
// clear buf_ind
|
||||
mju_zeroInt(buf_ind, nv);
|
||||
int current_nnz = 0;
|
||||
for (int i=0; i<v_edge_cnt[v]; ++i) {
|
||||
int e = adj_edges[v_edge_adr[v]+i];
|
||||
int v1 = m->flex_edge[2*(ebase+e)];
|
||||
int v2 = m->flex_edge[2*(ebase+e)+1];
|
||||
|
||||
// chains from edge e
|
||||
int b1 = m->flex_vertbodyid[vbase+v1];
|
||||
int b2 = m->flex_vertbodyid[vbase+v2];
|
||||
int NV1 = mj_bodyChain(m, b1, chain1);
|
||||
int NV2 = mj_bodyChain(m, b2, chain2);
|
||||
|
||||
for (int j=0; j<NV1; ++j) {
|
||||
if (!buf_ind[chain1[j]]) {
|
||||
buf_ind[chain1[j]] = 1;
|
||||
current_nnz++;
|
||||
}
|
||||
}
|
||||
for (int j=0; j<NV2; ++j) {
|
||||
if (!buf_ind[chain2[j]]) {
|
||||
buf_ind[chain2[j]] = 1;
|
||||
current_nnz++;
|
||||
}
|
||||
}
|
||||
}
|
||||
int row0 = 2*(vbase+v);
|
||||
int row1 = 2*(vbase+v)+1;
|
||||
vrownnz[row0] = vrownnz[row1] = current_nnz;
|
||||
|
||||
// set rowadr for next rows
|
||||
vrowadr[row1] = vrowadr[row0] + current_nnz;
|
||||
if (row1 + 1 < 2*m->nflexvert) {
|
||||
vrowadr[row1+1] = vrowadr[row1] + current_nnz;
|
||||
}
|
||||
|
||||
// fill colind
|
||||
int count = 0;
|
||||
for (int j=0; j<nv; j++) {
|
||||
if (buf_ind[j]) {
|
||||
m->flexvert_J_colind[vrowadr[row0]+count] = j;
|
||||
m->flexvert_J_colind[vrowadr[row1]+count] = j;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2nd pass: clear Jacobian and assemble vertex by vertex
|
||||
mjtNum* J0_dense = mjSTACKALLOC(d, nv, mjtNum);
|
||||
mjtNum* J1_dense = mjSTACKALLOC(d, nv, mjtNum);
|
||||
mjtNum dI1dy1[3], dI1dy2[3], FB[6];
|
||||
mjtNum dI2dy1[3], dI2dy2[3];
|
||||
mjtNum cauchy[4], adj[4], Fadj[6], FadjBinv[6], dI2dy[3];
|
||||
|
||||
for (int v=0; v<nvert; v++) {
|
||||
mju_zero(J0_dense, nv);
|
||||
mju_zero(J1_dense, nv);
|
||||
|
||||
mjtNum* F = F_vert + 6*v;
|
||||
mjtNum* Binv = Binv_vert + 4*v;
|
||||
|
||||
// precompute for I1
|
||||
mju_mulMatMat(FB, F, Binv, 3, 2, 2);
|
||||
|
||||
// precompute for I2
|
||||
cauchy[0] = F[0]*F[0] + F[2]*F[2] + F[4]*F[4]; // c00
|
||||
cauchy[1] = F[0]*F[1] + F[2]*F[3] + F[4]*F[5]; // c01
|
||||
cauchy[3] = F[1]*F[1] + F[3]*F[3] + F[5]*F[5]; // c11
|
||||
adj[0] = cauchy[3];
|
||||
adj[1] = -cauchy[1];
|
||||
adj[2] = -cauchy[1];
|
||||
adj[3] = cauchy[0];
|
||||
mju_mulMatMat(Fadj, F, adj, 3, 2, 2);
|
||||
mju_mulMatMat(FadjBinv, Fadj, Binv, 3, 2, 2);
|
||||
|
||||
for (int i=0; i<v_edge_cnt[v]; ++i) {
|
||||
int e = adj_edges[v_edge_adr[v]+i];
|
||||
int v1 = m->flex_edge[2*(ebase+e)];
|
||||
int v2 = m->flex_edge[2*(ebase+e)+1];
|
||||
|
||||
// reuse precomputed edge vector
|
||||
mjtNum* dx = edge_dx + 3 * e;
|
||||
|
||||
// get mass of neighbor vertex
|
||||
mjtNum weight = 1.0;
|
||||
int neighbor_v = (v == v1) ? v2 : v1;
|
||||
int b_neighbor = m->flex_vertbodyid[vbase + neighbor_v];
|
||||
if (b_neighbor >= 0) {
|
||||
weight = m->body_mass[b_neighbor];
|
||||
if (weight < mjMINVAL) weight = mjMINVAL;
|
||||
}
|
||||
|
||||
// dI1/dy1, dI1/dy2 (scaled by weight)
|
||||
mju_mulMatVec(dI1dy1, FB, dx, 3, 2);
|
||||
mju_scl3(dI1dy1, dI1dy1, -2 * weight);
|
||||
mju_scl3(dI1dy2, dI1dy1, -1); // dI1dy2 = -dI1dy1
|
||||
|
||||
// dI2/dy1, dI2/dy2 (scaled by weight)
|
||||
mju_mulMatVec(dI2dy, FadjBinv, dx, 3, 2);
|
||||
mju_scl3(dI2dy1, dI2dy, -2 * weight);
|
||||
mju_scl3(dI2dy2, dI2dy1, -1); // dI2dy2 = -dI2dy1
|
||||
|
||||
// get endpoint Jacobians
|
||||
int b1 = m->flex_vertbodyid[vbase+v1];
|
||||
int b2 = m->flex_vertbodyid[vbase+v2];
|
||||
int NV1 = mj_bodyChain(m, b1, chain1);
|
||||
mj_jacSparse(m, d, jac1, NULL, d->flexvert_xpos + 3*(vbase+v1), b1, NV1, chain1);
|
||||
int NV2 = mj_bodyChain(m, b2, chain2);
|
||||
mj_jacSparse(m, d, jac2, NULL, d->flexvert_xpos + 3*(vbase+v2), b2, NV2, chain2);
|
||||
|
||||
// accumulate dense Jacobians for vertex v
|
||||
for (int j=0; j<NV1; j++) {
|
||||
J0_dense[chain1[j]] += dI1dy1[0]*jac1[j] + dI1dy1[1]*jac1[j+NV1] + dI1dy1[2]*jac1[j+2*NV1];
|
||||
}
|
||||
for (int j=0; j<NV2; j++) {
|
||||
J0_dense[chain2[j]] += dI1dy2[0]*jac2[j] + dI1dy2[1]*jac2[j+NV2] + dI1dy2[2]*jac2[j+2*NV2];
|
||||
}
|
||||
for (int j=0; j<NV1; j++) {
|
||||
J1_dense[chain1[j]] += dI2dy1[0]*jac1[j] + dI2dy1[1]*jac1[j+NV1] + dI2dy1[2]*jac1[j+2*NV1];
|
||||
}
|
||||
for (int j=0; j<NV2; j++) {
|
||||
J1_dense[chain2[j]] += dI2dy2[0]*jac2[j] + dI2dy2[1]*jac2[j+NV2] + dI2dy2[2]*jac2[j+2*NV2];
|
||||
}
|
||||
}
|
||||
|
||||
// copy to sparse flexvert_J
|
||||
int row0 = 2*(vbase+v);
|
||||
int nnz0 = vrownnz[row0];
|
||||
for (int j=0; j<nnz0; j++) {
|
||||
d->flexvert_J[vrowadr[row0]+j] += J0_dense[m->flexvert_J_colind[vrowadr[row0]+j]];
|
||||
}
|
||||
int row1 = 2*(vbase+v)+1;
|
||||
int nnz1 = vrownnz[row1];
|
||||
for (int j = 0; j < nnz1; j++) {
|
||||
d->flexvert_J[vrowadr[row1] + j] +=
|
||||
J1_dense[m->flexvert_J_colind[vrowadr[row1] + j]];
|
||||
}
|
||||
|
||||
// mass scaling: scale constraint by sqrt(mass) to improve condition
|
||||
// number
|
||||
int b = m->flex_vertbodyid[vbase + v];
|
||||
if (b >= 0) {
|
||||
mjtNum mass = m->body_mass[b];
|
||||
if (mass > mjMINVAL) {
|
||||
mjtNum scale = mju_sqrt(mass);
|
||||
d->flexvert_length[2 * (vbase + v) + 0] *= scale;
|
||||
d->flexvert_length[2 * (vbase + v) + 1] *= scale;
|
||||
|
||||
nnz0 = vrownnz[row0];
|
||||
for (int j = 0; j < nnz0; j++) {
|
||||
d->flexvert_J[vrowadr[row0] + j] *= scale;
|
||||
}
|
||||
nnz1 = vrownnz[row1];
|
||||
for (int j = 0; j < nnz1; j++) {
|
||||
d->flexvert_J[vrowadr[row1] + j] *= scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mj_freeStack(d);
|
||||
}
|
||||
}
|
||||
|
||||
mj_freeStack(d);
|
||||
|
||||
@@ -220,7 +220,7 @@ void mj_makeModel(mjModel** dest,
|
||||
int nbvhstatic, int nbvhdynamic, int noct, int njnt, int ntree,
|
||||
int nM, int nB, int nC, int nD, int ngeom, int nsite, int ncam, int nlight,
|
||||
int nflex, int nflexnode, int nflexvert, int nflexedge, int nflexelem, int nflexelemdata,
|
||||
int nflexelemedge, int nflexshelldata, int nflexevpair, int nflextexcoord, int nJfe,
|
||||
int nflexelemedge, int nflexshelldata, int nflexevpair, int nflextexcoord, int nJfe, int nJfv,
|
||||
int nmesh, int nmeshvert, int nmeshnormal, int nmeshtexcoord, int nmeshface,
|
||||
int nmeshgraph, int nmeshpoly, int nmeshpolyvert, int nmeshpolymap, int nskin, int nskinvert,
|
||||
int nskintexvert, int nskinface,
|
||||
@@ -279,6 +279,7 @@ void mj_makeModel(mjModel** dest,
|
||||
m->nflexevpair = nflexevpair;
|
||||
m->nflextexcoord = nflextexcoord;
|
||||
m->nJfe = nJfe;
|
||||
m->nJfv = nJfv;
|
||||
m->nmesh = nmesh;
|
||||
m->nmeshvert = nmeshvert;
|
||||
m->nmeshnormal = nmeshnormal;
|
||||
@@ -406,8 +407,8 @@ mjModel* mj_copyModel(mjModel* dest, const mjModel* src) {
|
||||
src->nbvhstatic, src->nbvhdynamic, src->noct, src->njnt, src->ntree,
|
||||
src->nM, src->nB, src->nC, src->nD, src->ngeom, src->nsite, src->ncam,
|
||||
src->nlight, src->nflex, src->nflexnode, src->nflexvert, src->nflexedge,
|
||||
src->nflexelem, src->nflexelemdata, src->nflexelemedge,
|
||||
src->nflexshelldata, src->nflexevpair, src->nflextexcoord, src->nJfe, src->nmesh,
|
||||
src->nflexelem, src->nflexelemdata, src->nflexelemedge, src->nflexshelldata,
|
||||
src->nflexevpair, src->nflextexcoord, src->nJfe, src->nJfv, src->nmesh,
|
||||
src->nmeshvert, src->nmeshnormal, src->nmeshtexcoord, src->nmeshface,
|
||||
src->nmeshgraph, src->nmeshpoly, src->nmeshpolyvert, src->nmeshpolymap,
|
||||
src->nskin, src->nskinvert, src->nskintexvert, src->nskinface,
|
||||
@@ -598,7 +599,7 @@ mjModel* mj_loadModelBuffer(const void* buffer, int buffer_sz) {
|
||||
ints[49], ints[50], ints[51], ints[52], ints[53], ints[54], ints[55],
|
||||
ints[56], ints[57], ints[58], ints[59], ints[60], ints[61], ints[62],
|
||||
ints[63], ints[64], ints[65], ints[66], ints[67], ints[68], ints[69],
|
||||
ints[70], ints[71], ints[72], ints[73], ints[74], ints[75]);
|
||||
ints[70], ints[71], ints[72], ints[73], ints[74], ints[75], ints[76]);
|
||||
|
||||
// read mjModel mjtSize fields
|
||||
mjtSize sizes[8];
|
||||
|
||||
@@ -51,14 +51,15 @@ void mj_makeModel(mjModel** dest,
|
||||
int nq, int nv, int nu, int na, int nbody, int nbvh, int nbvhstatic, int nbvhdynamic, int noct,
|
||||
int njnt, int ntree, int nM, int nB, int nC, int nD, int ngeom, int nsite, int ncam, int nlight,
|
||||
int nflex, int nflexnode, int nflexvert, int nflexedge, int nflexelem, int nflexelemdata,
|
||||
int nJfe, int nflexelemedge, int nflexshelldata, int nflexevpair, int nflextexcoord, int nmesh,
|
||||
int nmeshvert, int nmeshnormal, int nmeshtexcoord, int nmeshface, int nmeshgraph, int nmeshpoly,
|
||||
int nmeshpolyvert, int nmeshpolymap, 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 nplugin, int npluginattr, 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, int npaths);
|
||||
int nflexelemedge, int nflexshelldata, int nflexevpair, int nflextexcoord, int nJfe, int nJfv,
|
||||
int nmesh, int nmeshvert, int nmeshnormal, int nmeshtexcoord, int nmeshface, int nmeshgraph,
|
||||
int nmeshpoly, int nmeshpolyvert, int nmeshpolymap, 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 nplugin, int npluginattr, 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, int npaths);
|
||||
|
||||
// copy mjModel; allocate new if dest is NULL
|
||||
MJAPI mjModel* mj_copyModel(mjModel* dest, const mjModel* src);
|
||||
|
||||
@@ -4392,10 +4392,16 @@ void mjCFlex::Compile(const mjVFS* vfs) {
|
||||
// compute bounding box coordinates
|
||||
vert0_.assign(3*nvert, 0);
|
||||
const mjtNum* bvh = tree.Bvh().data();
|
||||
size[0] = bvh[3] - radius;
|
||||
size[1] = bvh[4] - radius;
|
||||
size[2] = bvh[5] - radius;
|
||||
for (int j=0; j < nvert; j++) {
|
||||
for (int k=0; k < 3; k++) {
|
||||
double size = 2*(bvh[k+3] - radius);
|
||||
vert0_[3*j+k] = (vertxpos[3*j+k] - bvh[k]) / size + 0.5;
|
||||
if (size[k] > mjMINVAL) {
|
||||
vert0_[3*j+k] = (vertxpos[3*j+k] - bvh[k]) / (2*size[k]) + 0.5;
|
||||
} else {
|
||||
vert0_[3*j+k] = 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+29
-1
@@ -1155,6 +1155,7 @@ void mjCModel::Clear() {
|
||||
nflexevpair = 0;
|
||||
nflextexcoord = 0;
|
||||
nJfe = 0;
|
||||
nJfv = 0;
|
||||
nmeshvert = 0;
|
||||
nmeshnormal = 0;
|
||||
nmeshtexcoord = 0;
|
||||
@@ -2166,6 +2167,31 @@ void mjCModel::SetSizes() {
|
||||
nJfe += b->dofnum;
|
||||
}
|
||||
}
|
||||
|
||||
// compute nJfv
|
||||
std::vector<std::vector<int>> adj(flexes_[i]->nvert);
|
||||
for (const auto& edge : flexes_[i]->edge) {
|
||||
adj[edge.first].push_back(edge.second);
|
||||
adj[edge.second].push_back(edge.first);
|
||||
}
|
||||
for (int j=0; j < flexes_[i]->nvert; j++) {
|
||||
std::unordered_set<int> vert_bodies;
|
||||
vert_bodies.insert(flexes_[i]->vertbodyid[j]);
|
||||
for (int neighbor : adj[j]) {
|
||||
vert_bodies.insert(flexes_[i]->vertbodyid[neighbor]);
|
||||
}
|
||||
std::unordered_set<mjCBody*> bodies_in_jac;
|
||||
for (int body_id : vert_bodies) {
|
||||
mjCBody* b = bodies_[body_id];
|
||||
while (b) {
|
||||
bodies_in_jac.insert(b);
|
||||
b = b->parent;
|
||||
}
|
||||
}
|
||||
for (mjCBody* b : bodies_in_jac) {
|
||||
nJfv += b->dofnum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mesh counts
|
||||
@@ -3288,6 +3314,7 @@ void mjCModel::CopyObjects(mjModel* m) {
|
||||
mjuu_copyvec(m->flex_solref + mjNREF * i, pfl->solref, mjNREF);
|
||||
mjuu_copyvec(m->flex_solimp + mjNIMP * i, pfl->solimp, mjNIMP);
|
||||
m->flex_radius[i] = (mjtNum)pfl->radius;
|
||||
mjuu_copyvec(m->flex_size + 3 * i, pfl->size, 3);
|
||||
mjuu_copyvec(m->flex_friction + 3 * i, pfl->friction, 3);
|
||||
m->flex_margin[i] = (mjtNum)pfl->margin;
|
||||
m->flex_gap[i] = (mjtNum)pfl->gap;
|
||||
@@ -3363,6 +3390,7 @@ void mjCModel::CopyObjects(mjModel* m) {
|
||||
m->flex_edgeequality[i] = 1;
|
||||
break;
|
||||
}
|
||||
// TODO: support flex_edgeequality = 2
|
||||
}
|
||||
|
||||
// copy bvh data (flex aabb computed dynamically in mjData)
|
||||
@@ -4856,7 +4884,7 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
|
||||
mj_makeModel(&m,
|
||||
nq, nv, nu, na, nbody, nbvh, nbvhstatic, nbvhdynamic, noct, njnt, ntree, nM, nB, nC,
|
||||
nD, ngeom, nsite, ncam, nlight, nflex, nflexnode, nflexvert, nflexedge, nflexelem,
|
||||
nflexelemdata, nflexelemedge, nflexshelldata, nflexevpair, nflextexcoord, nJfe,
|
||||
nflexelemdata, nflexelemedge, nflexshelldata, nflexevpair, nflextexcoord, nJfe, nJfv,
|
||||
nmesh, nmeshvert, nmeshnormal, nmeshtexcoord, nmeshface, nmeshgraph, nmeshpoly,
|
||||
nmeshpolyvert, nmeshpolymap, nskin, nskinvert, nskintexvert, nskinface, nskinbone,
|
||||
nskinbonevert, nhfield, nhfielddata, ntex, ntexdata, nmat, npair, nexclude,
|
||||
|
||||
@@ -99,7 +99,8 @@ class mjCModel_ : public mjsElement {
|
||||
int nflexshelldata; // number of shell fragment vertex ids in all flexes
|
||||
int nflexevpair; // number of element-vertex pairs in all flexes
|
||||
int nflextexcoord; // number of vertex texture coordinates in all flexes
|
||||
int nJfe; // number of non-zeros in sparse flex constraint Jacobian
|
||||
int nJfe; // number of non-zeros in sparse flex edge constraint Jacobian
|
||||
int nJfv; // number of non-zeros in sparse flex vertex constraint Jacobian
|
||||
int nmeshvert; // number of vertices in all meshes
|
||||
int nmeshnormal; // number of normals in all meshes
|
||||
int nmeshtexcoord; // number of texture coordinates in all meshes
|
||||
|
||||
@@ -41,6 +41,7 @@ using ::testing::Eq;
|
||||
using ::testing::Pointwise;
|
||||
using ::testing::DoubleNear;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::Not;
|
||||
using CoreSmoothTest = MujocoTest;
|
||||
|
||||
|
||||
@@ -876,5 +877,205 @@ TEST_F(CoreSmoothTest, FactorIs) {
|
||||
mj_deleteModel(m);
|
||||
}
|
||||
|
||||
TEST_F(CoreSmoothTest, FlexVertLengthScaling) {
|
||||
constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<option jacobian="sparse"/>
|
||||
<worldbody>
|
||||
<flexcomp name="g" type="grid" count="3 3 1" spacing=".5 .5 .5" dim="2" radius=".05">
|
||||
<edge equality="true"/>
|
||||
</flexcomp>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
)";
|
||||
char error[1024];
|
||||
mjModel* m = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(m, NotNull()) << error;
|
||||
mjData* d = mj_makeData(m);
|
||||
|
||||
// check that nJfv is correct:
|
||||
// corner vertices: 2 * (1+3) * 3 + 2 * (1+2) * 3 = 42
|
||||
// edge vertices: 4 * (1+4) * 3 = 60
|
||||
// center vertex: 1 * (1+6) * 3 = 21
|
||||
// nJfv = 42 + 60 + 21 = 123
|
||||
EXPECT_EQ(m->nJfv, 123);
|
||||
|
||||
// Set edge equality to 2
|
||||
m->flex_edgeequality[0] = 2;
|
||||
|
||||
// Run kinematics to populate xpos/xmat initially
|
||||
mj_fwdKinematics(m, d);
|
||||
|
||||
// Check invariants for scale=1
|
||||
// The constraints should be satisfied
|
||||
int nvert = m->flex_vertnum[0];
|
||||
ASSERT_EQ(nvert, 9);
|
||||
for (int i=0; i < nvert; i++) {
|
||||
EXPECT_NEAR(d->flexvert_length[2*i+0], 0.0, 1e-5);
|
||||
EXPECT_NEAR(d->flexvert_length[2*i+1], 0.0, 1e-5);
|
||||
}
|
||||
|
||||
// set qvel to rigid rotation
|
||||
ASSERT_EQ(m->nv, 3*nvert);
|
||||
mju_zero(d->qvel, m->nv);
|
||||
for (int i=0; i < nvert; i++) {
|
||||
const mjtNum* p = d->xpos + 3*m->flex_vertbodyid[i];
|
||||
d->qvel[3*i+0] = -p[1];
|
||||
d->qvel[3*i+1] = p[0];
|
||||
d->qvel[3*i+2] = 1.0;
|
||||
}
|
||||
|
||||
// check that Jacobian times velocity is zero for rigid body motion
|
||||
vector<mjtNum> Jv(2*nvert, 0);
|
||||
for (int i=0; i < 2*nvert; i++) {
|
||||
int row_start = m->flexvert_J_rowadr[i];
|
||||
int row_nnz = m->flexvert_J_rownnz[i];
|
||||
for (int j=0; j < row_nnz; j++) {
|
||||
Jv[i] += d->flexvert_J[row_start + j] *
|
||||
d->qvel[m->flexvert_J_colind[row_start + j]];
|
||||
}
|
||||
}
|
||||
EXPECT_THAT(Jv, Each(DoubleNear(0.0, 1e-9)));
|
||||
|
||||
// check sparsity pattern
|
||||
int corners[] = {0, 2, 6, 8};
|
||||
int edges[] = {1, 3, 5, 7};
|
||||
int center[] = {4};
|
||||
for (int i : corners) {
|
||||
EXPECT_EQ(m->flexvert_J_rownnz[2*i+0], (i == 0 || i == 8) ? 12 : 9);
|
||||
EXPECT_EQ(m->flexvert_J_rownnz[2*i+1], (i == 0 || i == 8) ? 12 : 9);
|
||||
}
|
||||
for (int i : edges) {
|
||||
EXPECT_EQ(m->flexvert_J_rownnz[2*i+0], 15);
|
||||
EXPECT_EQ(m->flexvert_J_rownnz[2*i+1], 15);
|
||||
}
|
||||
for (int i : center) {
|
||||
EXPECT_EQ(m->flexvert_J_rownnz[2*i+0], 21);
|
||||
EXPECT_EQ(m->flexvert_J_rownnz[2*i+1], 21);
|
||||
}
|
||||
|
||||
// check rowadr
|
||||
EXPECT_EQ(m->flexvert_J_rowadr[0], 0);
|
||||
for (int i=1; i < 2*nvert; i++) {
|
||||
EXPECT_EQ(m->flexvert_J_rowadr[i],
|
||||
m->flexvert_J_rowadr[i-1] + m->flexvert_J_rownnz[i-1]);
|
||||
}
|
||||
|
||||
// check that colind are sorted and unique
|
||||
int nnzJ = 0;
|
||||
for (int i = 0; i < 2*nvert; i++) {
|
||||
nnzJ += m->flexvert_J_rownnz[i];
|
||||
}
|
||||
EXPECT_EQ(nnzJ, 2*m->nJfv);
|
||||
for (int i=0; i < 2*nvert; i++) {
|
||||
int row_start = m->flexvert_J_rowadr[i];
|
||||
int row_nnz = m->flexvert_J_rownnz[i];
|
||||
for (int j=0; j < row_nnz-1; j++) {
|
||||
EXPECT_LE(m->flexvert_J_colind[row_start+j],
|
||||
m->flexvert_J_colind[row_start+j+1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Finite-difference check for flexvert_J
|
||||
auto fd_check = [&](double tolerance) {
|
||||
std::vector<mjtNum> qpos0(m->nq);
|
||||
mju_copy(qpos0.data(), d->qpos, m->nq);
|
||||
mj_kinematics(m, d);
|
||||
mj_flex(m, d);
|
||||
|
||||
mjtNum eps = 1e-7;
|
||||
int nflexvert = m->flex_vertnum[0];
|
||||
std::vector<mjtNum> jac_fd(2 * nflexvert * m->nv);
|
||||
std::vector<mjtNum> qpos_backup(m->nq);
|
||||
mju_copy(qpos_backup.data(), d->qpos, m->nq);
|
||||
|
||||
for (int i=0; i < m->nv; ++i) {
|
||||
std::vector<mjtNum> qvel(m->nv, 0);
|
||||
qvel[i] = 1.0;
|
||||
|
||||
// plus
|
||||
mju_copy(d->qpos, qpos_backup.data(), m->nq);
|
||||
mj_integratePos(m, d->qpos, qvel.data(), eps);
|
||||
mj_kinematics(m, d);
|
||||
mj_flex(m, d);
|
||||
std::vector<mjtNum> L_plus(2 * nflexvert);
|
||||
for (int e = 0; e < 2 * nflexvert; ++e) {
|
||||
L_plus[e] = d->flexvert_length[e];
|
||||
}
|
||||
|
||||
// minus
|
||||
mju_copy(d->qpos, qpos_backup.data(), m->nq);
|
||||
mj_integratePos(m, d->qpos, qvel.data(), -eps);
|
||||
mj_kinematics(m, d);
|
||||
mj_flex(m, d);
|
||||
std::vector<mjtNum> L_minus(2 * nflexvert);
|
||||
for (int e = 0; e < 2 * nflexvert; ++e) {
|
||||
L_minus[e] = d->flexvert_length[e];
|
||||
}
|
||||
|
||||
for (int e = 0; e < 2 * nflexvert; ++e) {
|
||||
jac_fd[e*m->nv + i] = (L_plus[e] - L_minus[e]) / (2*eps);
|
||||
}
|
||||
}
|
||||
mju_copy(d->qpos, qpos_backup.data(), m->nq);
|
||||
mj_kinematics(m, d);
|
||||
mj_flex(m, d);
|
||||
|
||||
// Compare with analytic
|
||||
std::vector<mjtNum> jac_analytic(2 * nflexvert * m->nv);
|
||||
mju_zero(jac_analytic.data(), 2 * nflexvert * m->nv);
|
||||
for (int e = 0; e < 2 * nflexvert; ++e) {
|
||||
int row_start = m->flexvert_J_rowadr[e];
|
||||
int row_nnz = m->flexvert_J_rownnz[e];
|
||||
for (int i = 0; i < row_nnz; ++i) {
|
||||
jac_analytic[e*m->nv + m->flexvert_J_colind[row_start+i]] =
|
||||
d->flexvert_J[row_start+i];
|
||||
}
|
||||
}
|
||||
EXPECT_THAT(jac_analytic, Not(Each(Eq(0))));
|
||||
EXPECT_THAT(jac_analytic, Pointwise(DoubleNear(tolerance), jac_fd));
|
||||
|
||||
mju_copy(d->qpos, qpos0.data(), m->nq);
|
||||
mj_kinematics(m, d);
|
||||
mj_flex(m, d);
|
||||
};
|
||||
|
||||
fd_check(5e-5);
|
||||
|
||||
// Set qpos to put flex in scale=2 configuration.
|
||||
for (int i=0; i < nvert; i++) {
|
||||
d->qpos[3*i+0] = d->xpos[3*(i+1)+0];
|
||||
d->qpos[3*i+1] = d->xpos[3*(i+1)+1];
|
||||
d->qpos[3*i+2] = d->xpos[3*(i+1)+2];
|
||||
}
|
||||
mj_fwdKinematics(m, d);
|
||||
|
||||
// Get mass scaling factor
|
||||
mjtNum scale = 1.0;
|
||||
int b = m->flex_vertbodyid[0];
|
||||
if (b >= 0 && m->body_mass[b] > mjMINVAL) {
|
||||
scale = mju_sqrt(m->body_mass[b]);
|
||||
}
|
||||
|
||||
// Check invariants for scale=2
|
||||
// F should be [2, 2]. C = F'F = 4I.
|
||||
// Strain E = C - I = 3I.
|
||||
// Invariant 0: Trace(E) = 3 + 3 = 6
|
||||
// Invariant 1: Det(C) - 1 = 4 * 4 - 1 = 15
|
||||
for (int i=0; i < nvert; i++) {
|
||||
EXPECT_NEAR(d->flexvert_length[2 * i + 0], 6.0 * scale, 1e-5);
|
||||
EXPECT_NEAR(d->flexvert_length[2 * i + 1], 15.0 * scale, 1e-5);
|
||||
}
|
||||
|
||||
// Perturb z-positions so configuration is not flat
|
||||
for (int i=0; i < nvert; i++) {
|
||||
d->qpos[3*i+2] += 0.01 * (i%2 ? 1 : -1);
|
||||
}
|
||||
fd_check(5e-5);
|
||||
|
||||
mj_deleteData(d);
|
||||
mj_deleteModel(m);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mujoco
|
||||
|
||||
@@ -4983,6 +4983,8 @@ public unsafe struct mjData_ {
|
||||
public double* flexelem_aabb;
|
||||
public double* flexedge_J;
|
||||
public double* flexedge_length;
|
||||
public double* flexvert_J;
|
||||
public double* flexvert_length;
|
||||
public double* bvh_aabb_dyn;
|
||||
public int* ten_wrapadr;
|
||||
public int* ten_wrapnum;
|
||||
@@ -5314,6 +5316,7 @@ public unsafe struct mjModel_ {
|
||||
public int nflexevpair;
|
||||
public int nflextexcoord;
|
||||
public int nJfe;
|
||||
public int nJfv;
|
||||
public int nmesh;
|
||||
public int nmeshvert;
|
||||
public int nmeshnormal;
|
||||
@@ -5572,12 +5575,13 @@ public unsafe struct mjModel_ {
|
||||
public double* flexedge_length0;
|
||||
public double* flexedge_invweight0;
|
||||
public double* flex_radius;
|
||||
public double* flex_size;
|
||||
public double* flex_stiffness;
|
||||
public double* flex_bending;
|
||||
public double* flex_damping;
|
||||
public double* flex_edgestiffness;
|
||||
public double* flex_edgedamping;
|
||||
public byte* flex_edgeequality;
|
||||
public int* flex_edgeequality;
|
||||
public byte* flex_rigid;
|
||||
public byte* flexedge_rigid;
|
||||
public byte* flex_centered;
|
||||
@@ -5587,6 +5591,9 @@ public unsafe struct mjModel_ {
|
||||
public int* flexedge_J_rownnz;
|
||||
public int* flexedge_J_rowadr;
|
||||
public int* flexedge_J_colind;
|
||||
public int* flexvert_J_rownnz;
|
||||
public int* flexvert_J_rowadr;
|
||||
public int* flexvert_J_colind;
|
||||
public float* flex_rgba;
|
||||
public float* flex_texcoord;
|
||||
public int* mesh_vertadr;
|
||||
|
||||
@@ -2157,6 +2157,9 @@ struct MjsFlex {
|
||||
void set_radius(double value) {
|
||||
ptr_->radius = value;
|
||||
}
|
||||
emscripten::val size() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(3, ptr_->size));
|
||||
}
|
||||
mjtByte internal() const {
|
||||
return ptr_->internal;
|
||||
}
|
||||
@@ -3585,6 +3588,12 @@ struct MjModel {
|
||||
void set_nJfe(int value) {
|
||||
ptr_->nJfe = value;
|
||||
}
|
||||
int nJfv() const {
|
||||
return ptr_->nJfv;
|
||||
}
|
||||
void set_nJfv(int value) {
|
||||
ptr_->nJfv = value;
|
||||
}
|
||||
int nmesh() const {
|
||||
return ptr_->nmesh;
|
||||
}
|
||||
@@ -4521,6 +4530,9 @@ struct MjModel {
|
||||
emscripten::val flex_radius() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_radius));
|
||||
}
|
||||
emscripten::val flex_size() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(ptr_->nflex * 3, ptr_->flex_size));
|
||||
}
|
||||
emscripten::val flex_stiffness() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(ptr_->nflexelem * 21, ptr_->flex_stiffness));
|
||||
}
|
||||
@@ -4566,6 +4578,15 @@ struct MjModel {
|
||||
emscripten::val flexedge_J_colind() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(ptr_->nJfe, ptr_->flexedge_J_colind));
|
||||
}
|
||||
emscripten::val flexvert_J_rownnz() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(ptr_->nflexvert * 2, ptr_->flexvert_J_rownnz));
|
||||
}
|
||||
emscripten::val flexvert_J_rowadr() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(ptr_->nflexvert * 2, ptr_->flexvert_J_rowadr));
|
||||
}
|
||||
emscripten::val flexvert_J_colind() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(ptr_->nJfv * 2, ptr_->flexvert_J_colind));
|
||||
}
|
||||
emscripten::val flex_rgba() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(ptr_->nflex * 4, ptr_->flex_rgba));
|
||||
}
|
||||
@@ -6306,6 +6327,12 @@ struct MjData {
|
||||
emscripten::val flexedge_length() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(model->nflexedge, ptr_->flexedge_length));
|
||||
}
|
||||
emscripten::val flexvert_J() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(model->nJfv * 2, ptr_->flexvert_J));
|
||||
}
|
||||
emscripten::val flexvert_length() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(model->nflexvert * 2, ptr_->flexvert_length));
|
||||
}
|
||||
emscripten::val bvh_aabb_dyn() const {
|
||||
return emscripten::val(emscripten::typed_memory_view(model->nbvhdynamic * 6, ptr_->bvh_aabb_dyn));
|
||||
}
|
||||
@@ -11019,6 +11046,8 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
.property("flexedge_length", &MjData::flexedge_length)
|
||||
.property("flexedge_velocity", &MjData::flexedge_velocity)
|
||||
.property("flexelem_aabb", &MjData::flexelem_aabb)
|
||||
.property("flexvert_J", &MjData::flexvert_J)
|
||||
.property("flexvert_length", &MjData::flexvert_length)
|
||||
.property("flexvert_xpos", &MjData::flexvert_xpos)
|
||||
.property("geom_xmat", &MjData::geom_xmat)
|
||||
.property("geom_xpos", &MjData::geom_xpos)
|
||||
@@ -11334,6 +11363,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
.property("flex_shell", &MjModel::flex_shell)
|
||||
.property("flex_shelldataadr", &MjModel::flex_shelldataadr)
|
||||
.property("flex_shellnum", &MjModel::flex_shellnum)
|
||||
.property("flex_size", &MjModel::flex_size)
|
||||
.property("flex_solimp", &MjModel::flex_solimp)
|
||||
.property("flex_solmix", &MjModel::flex_solmix)
|
||||
.property("flex_solref", &MjModel::flex_solref)
|
||||
@@ -11351,6 +11381,9 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
.property("flexedge_invweight0", &MjModel::flexedge_invweight0)
|
||||
.property("flexedge_length0", &MjModel::flexedge_length0)
|
||||
.property("flexedge_rigid", &MjModel::flexedge_rigid)
|
||||
.property("flexvert_J_colind", &MjModel::flexvert_J_colind)
|
||||
.property("flexvert_J_rowadr", &MjModel::flexvert_J_rowadr)
|
||||
.property("flexvert_J_rownnz", &MjModel::flexvert_J_rownnz)
|
||||
.property("geom_aabb", &MjModel::geom_aabb)
|
||||
.property("geom_bodyid", &MjModel::geom_bodyid)
|
||||
.property("geom_conaffinity", &MjModel::geom_conaffinity)
|
||||
@@ -11477,6 +11510,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
.property("nC", &MjModel::nC, &MjModel::set_nC, reference())
|
||||
.property("nD", &MjModel::nD, &MjModel::set_nD, reference())
|
||||
.property("nJfe", &MjModel::nJfe, &MjModel::set_nJfe, reference())
|
||||
.property("nJfv", &MjModel::nJfv, &MjModel::set_nJfv, reference())
|
||||
.property("nJmom", &MjModel::nJmom, &MjModel::set_nJmom, reference())
|
||||
.property("nM", &MjModel::nM, &MjModel::set_nM, reference())
|
||||
.property("na", &MjModel::na, &MjModel::set_na, reference())
|
||||
@@ -12048,6 +12082,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
.property("radius", &MjsFlex::radius, &MjsFlex::set_radius, reference())
|
||||
.property("rgba", &MjsFlex::rgba)
|
||||
.property("selfcollide", &MjsFlex::selfcollide, &MjsFlex::set_selfcollide, reference())
|
||||
.property("size", &MjsFlex::size)
|
||||
.property("solimp", &MjsFlex::solimp)
|
||||
.property("solmix", &MjsFlex::solmix, &MjsFlex::set_solmix, reference())
|
||||
.property("solref", &MjsFlex::solref)
|
||||
|
||||
Reference in New Issue
Block a user