Skip to content

Commit 205c46c

Browse files
committed
EPA: fix convergence criterion
1 parent 802ef59 commit 205c46c

File tree

2 files changed

+92
-10
lines changed

2 files changed

+92
-10
lines changed

include/hpp/fcl/narrowphase/gjk.h

+3
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,9 @@ struct HPP_FCL_DLLAPI EPA {
491491
/// @brief the goal is to add a face connecting vertex w and face edge f[e]
492492
bool expand(size_t pass, const SimplexVertex& w, SimplexFace* f, size_t e,
493493
SimplexHorizon& horizon);
494+
495+
// @brief Use this function to debug expand if needed.
496+
// void PrintExpandLooping(const SimplexFace* f, const SimplexVertex& w);
494497
};
495498

496499
} // namespace details

src/narrowphase/gjk.cpp

+89-10
Original file line numberDiff line numberDiff line change
@@ -1576,6 +1576,9 @@ EPA::SimplexFace* EPA::findClosestFace() {
15761576
EPA::Status EPA::evaluate(GJK& gjk, const Vec3f& guess) {
15771577
GJK::Simplex& simplex = *gjk.getSimplex();
15781578
support_func_guess_t hint(gjk.support_hint);
1579+
1580+
// TODO(louis): we might want to start with a hexahedron if the
1581+
// simplex given by GJK is of rank <= 3.
15791582
bool enclosed_origin = gjk.encloseOrigin();
15801583
if ((simplex.rank > 1) && enclosed_origin) {
15811584
assert(simplex.rank == 4 &&
@@ -1633,27 +1636,55 @@ EPA::Status EPA::evaluate(GJK& gjk, const Vec3f& guess) {
16331636
break;
16341637
}
16351638

1639+
// Step 1: find the support point in the direction of the closest_face
1640+
// normal.
1641+
// --------------------------------------------------------------------------
16361642
SimplexHorizon horizon;
16371643
SimplexVertex& w = sv_store[num_vertices++];
16381644
bool valid = true;
16391645
closest_face->pass = ++pass;
16401646
// At the moment, SimplexF.n is always normalized. This could be revised
16411647
// in the future...
16421648
gjk.getSupport(closest_face->n, true, w, hint);
1643-
FCL_REAL wdist = closest_face->n.dot(w.w) - closest_face->d;
1644-
if (wdist <= tolerance) {
1649+
1650+
// Step 2: check for convergence.
1651+
// ------------------------------
1652+
// Preambule to understand the convergence criterion of EPA:
1653+
// the support we just added is in the direction of the normal of
1654+
// the closest_face. Therefore, the support point will **always**
1655+
// lie "after" the closest_face, i.e closest_face.n.dot(w.w) > 0.
1656+
assert(closest_face->n.dot(w.w) > 0 &&
1657+
"The support is not in the right direction.");
1658+
//
1659+
// 1) First check: `fdist` (see below) is an upper bound of how much
1660+
// more penetration depth we can expect to "gain" by adding `w` to EPA's
1661+
// polytope. This first check, as any convergence check, should be both
1662+
// absolute and relative. This allows to adapt the tolerance to the
1663+
// scale of the objects.
1664+
const SimplexVertex& vf1 = sv_store[closest_face->vertex_id[0]];
1665+
const SimplexVertex& vf2 = sv_store[closest_face->vertex_id[1]];
1666+
const SimplexVertex& vf3 = sv_store[closest_face->vertex_id[2]];
1667+
FCL_REAL fdist = closest_face->n.dot(w.w - vf1.w);
1668+
FCL_REAL wnorm = w.w.norm();
1669+
// TODO(louis): we might want to use tol_abs and tol_rel; this might
1670+
// obfuscate the code for the user though.
1671+
if (fdist <= tolerance + tolerance * wnorm) {
1672+
status = AccuracyReached;
1673+
break;
1674+
}
1675+
// 2) Second check: the expand function **assumes** that the support we
1676+
// just computed is not a vertex of the face. We make sure that this
1677+
// is the case:
1678+
// TODO(louis): should we use squaredNorm everywhere instead of norm?
1679+
if ((w.w - vf1.w).norm() <= tolerance + tolerance * wnorm ||
1680+
(w.w - vf2.w).norm() <= tolerance + tolerance * wnorm ||
1681+
(w.w - vf3.w).norm() <= tolerance + tolerance * wnorm) {
16451682
status = AccuracyReached;
16461683
break;
16471684
}
1648-
// The computed support cannot be a vertex of the `closest_face`,
1649-
// otherwise EPA should have exited with `wdist <= tolerance`.
1650-
assert((w.w - sv_store[closest_face->vertex_id[0]].w).norm() >
1651-
std::numeric_limits<FCL_REAL>::epsilon());
1652-
assert((w.w - sv_store[closest_face->vertex_id[1]].w).norm() >
1653-
std::numeric_limits<FCL_REAL>::epsilon());
1654-
assert((w.w - sv_store[closest_face->vertex_id[2]].w).norm() >
1655-
std::numeric_limits<FCL_REAL>::epsilon());
16561685

1686+
// Step 3: expand the polytope
1687+
// ---------------------------
16571688
for (size_t j = 0; (j < 3) && valid; ++j)
16581689
valid &= expand(pass, w, closest_face->adjacent_faces[j],
16591690
closest_face->adjacent_edge[j], horizon);
@@ -1702,6 +1733,48 @@ EPA::Status EPA::evaluate(GJK& gjk, const Vec3f& guess) {
17021733
return status;
17031734
}
17041735

1736+
// Use this function to debug `EPA::expand` if needed.
1737+
// void EPA::PrintExpandLooping(const SimplexFace* f, const SimplexVertex& w) {
1738+
// std::cout << "Vertices:\n";
1739+
// for (size_t i = 0; i < num_vertices; ++i) {
1740+
// std::cout << "[";
1741+
// std::cout << sv_store[i].w(0) << ", ";
1742+
// std::cout << sv_store[i].w(1) << ", ";
1743+
// std::cout << sv_store[i].w(2) << "]\n";
1744+
// }
1745+
// //
1746+
// std::cout << "\nTriangles:\n";
1747+
// SimplexFace* face = hull.root;
1748+
// for (size_t i = 0; i < hull.count; ++i) {
1749+
// std::cout << "[";
1750+
// std::cout << face->vertex_id[0] << ", ";
1751+
// std::cout << face->vertex_id[1] << ", ";
1752+
// std::cout << face->vertex_id[2] << "]\n";
1753+
// face = face->next_face;
1754+
// }
1755+
// //
1756+
// std::cout << "\nNormals:\n";
1757+
// face = hull.root;
1758+
// for (size_t i = 0; i < hull.count; ++i) {
1759+
// std::cout << "[";
1760+
// std::cout << face->n(0) << ", ";
1761+
// std::cout << face->n(1) << ", ";
1762+
// std::cout << face->n(2) << "]\n";
1763+
// face = face->next_face;
1764+
// }
1765+
// //
1766+
// std::cout << "\nClosest face:\n";
1767+
// face = hull.root;
1768+
// for (size_t i = 0; i < hull.count; ++i) {
1769+
// if (face == closest_face) {
1770+
// std::cout << i << "\n";
1771+
// }
1772+
// face = face->next_face;
1773+
// }
1774+
// std::cout << "\nSupport point:\n";
1775+
// std::cout << "[" << w.w(0) << ", " << w.w(1) << ", " << w.w(2) << "]\n";
1776+
// }
1777+
17051778
/** @brief the goal is to add a face connecting vertex w and face edge f[e] */
17061779
bool EPA::expand(size_t pass, const SimplexVertex& w, SimplexFace* f, size_t e,
17071780
SimplexHorizon& horizon) {
@@ -1712,6 +1785,12 @@ bool EPA::expand(size_t pass, const SimplexVertex& w, SimplexFace* f, size_t e,
17121785

17131786
// Check if we loop through expand indefinitely.
17141787
if (f->pass == pass) {
1788+
// Uncomment the following line and the associated EPA method
1789+
// to debug the infinite loop if needed.
1790+
// EPAPrintExpandLooping(this, f);
1791+
if (f == closest_face) {
1792+
assert(false && "EPA is looping indefinitely.");
1793+
}
17151794
status = InvalidHull;
17161795
return false;
17171796
}

0 commit comments

Comments
 (0)