Skip to content

Commit 340c0d7

Browse files
kbayescopybara-github
authored andcommitted
Fix a box-box bad collision in NativeCCD.
PiperOrigin-RevId: 711773153 Change-Id: I8a526ef5933be19d863e67f40edeb95b0b1249cd
1 parent 63e2836 commit 340c0d7

File tree

3 files changed

+62
-16
lines changed

3 files changed

+62
-16
lines changed

src/engine/engine_collision_gjk.c

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,12 @@ static int polytope2(Polytope* pt, const mjCCDStatus* status, mjCCDObj* obj1, mj
890890
if (mju_abs(det3(v1, v3, v4)) < mjMINVAL || mju_abs(det3(v1, v3, v5)) < mjMINVAL ||
891891
mju_abs(det3(v1, v3, v5)) < mjMINVAL || mju_abs(det3(v2, v3, v4)) < mjMINVAL ||
892892
mju_abs(det3(v2, v3, v5)) < mjMINVAL || mju_abs(det3(v2, v4, v5)) < mjMINVAL) {
893-
return 2;
893+
return mjEPA_P2_INVALID_FACES;
894+
}
895+
896+
// check that origin is in the hexahedron
897+
if (!testTetra(v1, v3, v4, v5) && !testTetra(v2, v3, v4, v5)) {
898+
return mjEPA_P2_MISSING_ORIGIN;
894899
}
895900

896901
// save vertices and get indices for each one
@@ -914,7 +919,7 @@ static int polytope2(Polytope* pt, const mjCCDStatus* status, mjCCDObj* obj1, mj
914919
pt->map[i] = pt->faces + i;
915920
pt->faces[i].index = i;
916921
if (pt->faces[i].dist < mjMINVAL) {
917-
return 3;
922+
return mjEPA_P2_ORIGIN_ON_FACE;
918923
}
919924
}
920925
pt->nmap = 6;
@@ -1003,7 +1008,7 @@ static int polytope3(Polytope* pt, const mjCCDStatus* status, mjCCDObj* obj1, mj
10031008
cross3(n, diff1, diff2);
10041009
mjtNum n_norm = mju_norm3(n);
10051010
if (n_norm < mjMINVAL) {
1006-
return 4;
1011+
return mjEPA_P3_BAD_NORMAL;
10071012
}
10081013

10091014
// negative of triangle normal n
@@ -1016,7 +1021,7 @@ static int polytope3(Polytope* pt, const mjCCDStatus* status, mjCCDObj* obj1, mj
10161021

10171022
// check that v4 is not contained in the 2-simplex
10181023
if (triPointIntersect(v1, v2, v3, v4)) {
1019-
return 5;
1024+
return mjEPA_P3_INVALID_V4;
10201025
}
10211026

10221027
// get 5th vertex in -n direction
@@ -1026,7 +1031,7 @@ static int polytope3(Polytope* pt, const mjCCDStatus* status, mjCCDObj* obj1, mj
10261031

10271032
// check that v5 is not contained in the 2-simplex
10281033
if (triPointIntersect(v1, v2, v3, v5)) {
1029-
return 6;
1034+
return mjEPA_P3_INVALID_V5;
10301035
}
10311036

10321037
// if origin does not lie on simplex then we need to check that the hexahedron contains the
@@ -1036,7 +1041,7 @@ static int polytope3(Polytope* pt, const mjCCDStatus* status, mjCCDObj* obj1, mj
10361041
// it but within tolerance from it. In that case the hexahedron could possibly be constructed
10371042
// that doesn't contain the origin, but nonetheless there is penetration depth.
10381043
if (status->dist > 10*mjMINVAL && !testTetra(v1, v2, v3, v4) && !testTetra(v1, v2, v3, v5)) {
1039-
return 7;
1044+
return mjEPA_P3_MISSING_ORIGIN;
10401045
}
10411046

10421047
// save vertices and get indices for each one
@@ -1061,7 +1066,7 @@ static int polytope3(Polytope* pt, const mjCCDStatus* status, mjCCDObj* obj1, mj
10611066
pt->map[i] = pt->faces + i;
10621067
pt->faces[i].index = i;
10631068
if (pt->faces[i].dist < mjMINVAL) {
1064-
return 8;
1069+
return mjEPA_P3_ORIGIN_ON_FACE;
10651070
}
10661071
}
10671072
pt->nmap = 6;
@@ -1444,7 +1449,8 @@ mjtNum mjc_ccd(const mjCCDConfig* config, mjCCDStatus* status, mjCCDObj* obj1, m
14441449
obj1->center(status->x1, obj1);
14451450
obj2->center(status->x2, obj2);
14461451
status->gjk_iterations = 0;
1447-
status->epa_iterations = -1;
1452+
status->epa_iterations = 0;
1453+
status->epa_status = mjEPA_NOCONTACT;
14481454
status->tolerance = config->tolerance;
14491455
status->max_iterations = config->max_iterations;
14501456
status->max_contacts = config->max_contacts;
@@ -1551,12 +1557,12 @@ mjtNum mjc_ccd(const mjCCDConfig* config, mjCCDStatus* status, mjCCDObj* obj1, m
15511557
} else {
15521558
ret = polytope4(&pt, status, obj1, obj2);
15531559
}
1560+
status->epa_status = ret;
15541561

15551562
// simplex not on boundary (objects are penetrating)
15561563
if (!ret) {
15571564
dist = -epa(status, &pt, obj1, obj2);
15581565
} else {
1559-
status->epa_iterations = -ret;
15601566
dist = 0;
15611567
}
15621568
mj_freeStack(d);

src/engine/engine_collision_gjk.h

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,30 @@
2525
extern "C" {
2626
#endif
2727

28+
// Status of an EPA run
29+
typedef enum {
30+
mjEPA_NOCONTACT = -1,
31+
mjEPA_SUCCESS = 0,
32+
mjEPA_P2_INVALID_FACES,
33+
mjEPA_P2_MISSING_ORIGIN,
34+
mjEPA_P2_ORIGIN_ON_FACE,
35+
mjEPA_P3_BAD_NORMAL,
36+
mjEPA_P3_INVALID_V4,
37+
mjEPA_P3_INVALID_V5,
38+
mjEPA_P3_MISSING_ORIGIN,
39+
mjEPA_P3_ORIGIN_ON_FACE,
40+
} mjEPAStatus;
41+
2842
// configuration for convex collision detection
29-
struct _mjCCDConfig {
43+
typedef struct {
3044
int max_iterations; // the maximum number of iterations for GJK and EPA
3145
mjtNum tolerance; // tolerance used by GJK and EPA
3246
int max_contacts; // set to max number of contact points to recover
3347
mjtNum dist_cutoff; // set to max geom distance to recover
34-
};
35-
typedef struct _mjCCDConfig mjCCDConfig;
48+
} mjCCDConfig;
3649

3750
// data produced from running GJK and EPA
38-
struct _mjCCDStatus {
51+
typedef struct {
3952
// geom distance information
4053
mjtNum dist; // distance between geoms
4154
mjtNum x1[3 * mjMAXCONPAIR]; // witness points for geom 1
@@ -50,13 +63,13 @@ struct _mjCCDStatus {
5063

5164
// statistics for debugging purposes
5265
int gjk_iterations; // number of iterations that GJK ran
53-
int epa_iterations; // number of iterations that EPA ran (negative if EPA did not run)
66+
int epa_iterations; // number of iterations that EPA ran (zero if EPA did not run)
67+
mjEPAStatus epa_status; // status of the EPA run
5468
mjtNum simplex1[12]; // the simplex that GJK returned for obj1
5569
mjtNum simplex2[12]; // the simplex that GJK returned for obj2
5670
mjtNum simplex[12]; // the simplex that GJK returned for the Minkowski difference
5771
int nsimplex; // size of simplex 1 & 2
58-
};
59-
typedef struct _mjCCDStatus mjCCDStatus;
72+
} mjCCDStatus;
6073

6174
// run general convex collision detection, returns positive for distance, negative for penetration
6275
MJAPI mjtNum mjc_ccd(const mjCCDConfig* config, mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2);

test/engine/engine_collision_gjk_test.cc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,33 @@ TEST_F(MjGjkTest, BoxBoxDepth2) {
335335
mj_deleteModel(model);
336336
}
337337

338+
TEST_F(MjGjkTest, BoxBoxTouching) {
339+
static constexpr char xml[] = R"(
340+
<mujoco>
341+
<worldbody>
342+
<geom name="geom1" type="box" pos="0 0 1.859913200000001376466229885409" size="1 1 1"/>
343+
<geom name="geom2" type="box" pos="0 2 1.859913200000001376466229885409" size="1 1 1"/>
344+
</worldbody>
345+
</mujoco>)";
346+
347+
std::array<char, 1000> error;
348+
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
349+
ASSERT_THAT(model, NotNull()) << "Failed to load model: " << error.data();
350+
351+
mjData* data = mj_makeData(model);
352+
mj_forward(model, data);
353+
354+
int geom1 = mj_name2id(model, mjOBJ_GEOM, "geom1");
355+
int geom2 = mj_name2id(model, mjOBJ_GEOM, "geom2");
356+
mjtNum dir[3], pos[3];
357+
mjtNum dist = Penetration(model, data, geom1, geom2, dir, pos);
358+
359+
EXPECT_EQ(dist, mjMAXVAL);
360+
361+
mj_deleteData(data);
362+
mj_deleteModel(model);
363+
}
364+
338365
TEST_F(MjGjkTest, SmallBoxMesh) {
339366
static constexpr char xml[] = R"(
340367
<mujoco>

0 commit comments

Comments
 (0)