Skip to content

Commit 105586e

Browse files
authored
[BUG FIX] Fix constraint solver mujoco consistency. (#1509)
1 parent 2c83a92 commit 105586e

File tree

2 files changed

+57
-67
lines changed

2 files changed

+57
-67
lines changed

genesis/engine/solvers/rigid/constraint_solver_decomp.py

Lines changed: 54 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,14 +1229,6 @@ def func_solve(
12291229
if constraint_state.improved[i_b] < 1:
12301230
break
12311231

1232-
gradient = gs.ti_float(0.0)
1233-
for i_d in range(n_dofs):
1234-
gradient += constraint_state.grad[i_d, i_b] * constraint_state.grad[i_d, i_b]
1235-
gradient = ti.sqrt(gradient)
1236-
improvement = constraint_state.prev_cost[i_b] - constraint_state.cost[i_b]
1237-
if gradient < tol_scaled or improvement < tol_scaled:
1238-
break
1239-
12401232

12411233
@ti.func
12421234
def func_ls_init(
@@ -1301,10 +1293,10 @@ def func_ls_point_fn(
13011293
alpha,
13021294
constraint_state: array_class.ConstraintState,
13031295
):
1304-
tmp_quad_total0, tmp_quad_total1, tmp_quad_total2 = gs.ti_float(0.0), gs.ti_float(0.0), gs.ti_float(0.0)
1305-
tmp_quad_total0 = constraint_state.quad_gauss[0, i_b]
1306-
tmp_quad_total1 = constraint_state.quad_gauss[1, i_b]
1307-
tmp_quad_total2 = constraint_state.quad_gauss[2, i_b]
1296+
tmp_quad_total_0, tmp_quad_total_1, tmp_quad_total_2 = gs.ti_float(0.0), gs.ti_float(0.0), gs.ti_float(0.0)
1297+
tmp_quad_total_0 = constraint_state.quad_gauss[0, i_b]
1298+
tmp_quad_total_1 = constraint_state.quad_gauss[1, i_b]
1299+
tmp_quad_total_2 = constraint_state.quad_gauss[2, i_b]
13081300
for i_c in range(constraint_state.n_constraints[i_b]):
13091301
x = constraint_state.Jaref[i_c, i_b] + alpha * constraint_state.jv[i_c, i_b]
13101302
active = 1
@@ -1331,14 +1323,16 @@ def func_ls_point_fn(
13311323
elif i_c >= nef:
13321324
active = x < 0
13331325

1334-
tmp_quad_total0 += qf_0 * active
1335-
tmp_quad_total1 += qf_1 * active
1336-
tmp_quad_total2 += qf_2 * active
1326+
tmp_quad_total_0 += qf_0 * active
1327+
tmp_quad_total_1 += qf_1 * active
1328+
tmp_quad_total_2 += qf_2 * active
13371329

1338-
cost = alpha * alpha * tmp_quad_total2 + alpha * tmp_quad_total1 + tmp_quad_total0
1330+
cost = alpha * alpha * tmp_quad_total_2 + alpha * tmp_quad_total_1 + tmp_quad_total_0
13391331

1340-
deriv_0 = 2 * alpha * tmp_quad_total2 + tmp_quad_total1
1341-
deriv_1 = 2 * tmp_quad_total2 + gs.EPS * (ti.abs(tmp_quad_total2) < gs.EPS)
1332+
deriv_0 = 2 * alpha * tmp_quad_total_2 + tmp_quad_total_1
1333+
deriv_1 = 2 * tmp_quad_total_2
1334+
if deriv_1 <= 0.0:
1335+
deriv_1 = gs.EPS
13421336

13431337
constraint_state.ls_its[i_b] = constraint_state.ls_its[i_b] + 1
13441338

@@ -1615,7 +1609,6 @@ def func_solve_body(
16151609
if ti.abs(alpha) < gs.EPS:
16161610
constraint_state.improved[i_b] = 0
16171611
else:
1618-
constraint_state.improved[i_b] = 1
16191612
for i_d in range(n_dofs):
16201613
constraint_state.qacc[i_d, i_b] = (
16211614
constraint_state.qacc[i_d, i_b] + constraint_state.search[i_d, i_b] * alpha
@@ -1640,56 +1633,58 @@ def func_solve_body(
16401633
static_rigid_sim_config=static_rigid_sim_config,
16411634
)
16421635

1643-
if ti.static(static_rigid_sim_config.solver_type == gs.constraint_solver.CG):
1644-
func_update_gradient(
1636+
if ti.static(static_rigid_sim_config.solver_type == gs.constraint_solver.Newton):
1637+
func_nt_hessian_incremental(
16451638
i_b,
1646-
dofs_state=dofs_state,
16471639
entities_info=entities_info,
1648-
rigid_global_info=rigid_global_info,
16491640
constraint_state=constraint_state,
1641+
rigid_global_info=rigid_global_info,
16501642
static_rigid_sim_config=static_rigid_sim_config,
16511643
)
16521644

1653-
constraint_state.cg_beta[i_b] = gs.ti_float(0.0)
1654-
constraint_state.cg_pg_dot_pMg[i_b] = gs.ti_float(0.0)
1645+
func_update_gradient(
1646+
i_b,
1647+
dofs_state=dofs_state,
1648+
entities_info=entities_info,
1649+
rigid_global_info=rigid_global_info,
1650+
constraint_state=constraint_state,
1651+
static_rigid_sim_config=static_rigid_sim_config,
1652+
)
16551653

1656-
for i_d in range(n_dofs):
1657-
constraint_state.cg_beta[i_b] += constraint_state.grad[i_d, i_b] * (
1658-
constraint_state.Mgrad[i_d, i_b] - constraint_state.cg_prev_Mgrad[i_d, i_b]
1659-
)
1660-
constraint_state.cg_pg_dot_pMg[i_b] += (
1661-
constraint_state.cg_prev_Mgrad[i_d, i_b] * constraint_state.cg_prev_grad[i_d, i_b]
1662-
)
1654+
tol_scaled = (rigid_global_info.meaninertia[i_b] * ti.max(1, n_dofs)) * static_rigid_sim_config.tolerance
1655+
improvement = constraint_state.prev_cost[i_b] - constraint_state.cost[i_b]
1656+
gradient = gs.ti_float(0.0)
1657+
for i_d in range(n_dofs):
1658+
gradient += constraint_state.grad[i_d, i_b] * constraint_state.grad[i_d, i_b]
1659+
gradient = ti.sqrt(gradient)
1660+
if gradient < tol_scaled or improvement < tol_scaled:
1661+
constraint_state.improved[i_b] = 0
1662+
else:
1663+
constraint_state.improved[i_b] = 1
16631664

1664-
constraint_state.cg_beta[i_b] = ti.max(
1665-
0.0, constraint_state.cg_beta[i_b] / ti.max(gs.EPS, constraint_state.cg_pg_dot_pMg[i_b])
1666-
)
1667-
for i_d in range(n_dofs):
1668-
constraint_state.search[i_d, i_b] = (
1669-
-constraint_state.Mgrad[i_d, i_b]
1670-
+ constraint_state.cg_beta[i_b] * constraint_state.search[i_d, i_b]
1671-
)
1665+
if ti.static(static_rigid_sim_config.solver_type == gs.constraint_solver.Newton):
1666+
for i_d in range(n_dofs):
1667+
constraint_state.search[i_d, i_b] = -constraint_state.Mgrad[i_d, i_b]
1668+
else:
1669+
constraint_state.cg_beta[i_b] = gs.ti_float(0.0)
1670+
constraint_state.cg_pg_dot_pMg[i_b] = gs.ti_float(0.0)
16721671

1673-
elif ti.static(static_rigid_sim_config.solver_type == gs.constraint_solver.Newton):
1674-
improvement = constraint_state.prev_cost[i_b] - constraint_state.cost[i_b]
1675-
if improvement > 0:
1676-
func_nt_hessian_incremental(
1677-
i_b,
1678-
entities_info=entities_info,
1679-
constraint_state=constraint_state,
1680-
rigid_global_info=rigid_global_info,
1681-
static_rigid_sim_config=static_rigid_sim_config,
1682-
)
1683-
func_update_gradient(
1684-
i_b,
1685-
dofs_state=dofs_state,
1686-
entities_info=entities_info,
1687-
rigid_global_info=rigid_global_info,
1688-
constraint_state=constraint_state,
1689-
static_rigid_sim_config=static_rigid_sim_config,
1672+
for i_d in range(n_dofs):
1673+
constraint_state.cg_beta[i_b] += constraint_state.grad[i_d, i_b] * (
1674+
constraint_state.Mgrad[i_d, i_b] - constraint_state.cg_prev_Mgrad[i_d, i_b]
1675+
)
1676+
constraint_state.cg_pg_dot_pMg[i_b] += (
1677+
constraint_state.cg_prev_Mgrad[i_d, i_b] * constraint_state.cg_prev_grad[i_d, i_b]
1678+
)
1679+
1680+
constraint_state.cg_beta[i_b] = ti.max(
1681+
0.0, constraint_state.cg_beta[i_b] / ti.max(gs.EPS, constraint_state.cg_pg_dot_pMg[i_b])
16901682
)
16911683
for i_d in range(n_dofs):
1692-
constraint_state.search[i_d, i_b] = -constraint_state.Mgrad[i_d, i_b]
1684+
constraint_state.search[i_d, i_b] = (
1685+
-constraint_state.Mgrad[i_d, i_b]
1686+
+ constraint_state.cg_beta[i_b] * constraint_state.search[i_d, i_b]
1687+
)
16931688

16941689

16951690
@ti.func
@@ -1792,10 +1787,7 @@ def func_update_gradient(
17921787
)
17931788

17941789
elif ti.static(static_rigid_sim_config.solver_type == gs.constraint_solver.Newton):
1795-
func_nt_chol_solve(
1796-
i_b,
1797-
constraint_state=constraint_state,
1798-
)
1790+
func_nt_chol_solve(i_b, constraint_state=constraint_state)
17991791

18001792

18011793
@ti.func

tests/test_rigid_physics.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ def test_simple_kinematic_chain(gs_sim, mj_sim, tol):
357357
@pytest.mark.parametrize("backend", [gs.cpu])
358358
def test_frictionloss(gs_sim, mj_sim, tol):
359359
qvel = np.array([0.7, -0.9])
360-
simulate_and_check_mujoco_consistency(gs_sim, mj_sim, qvel=qvel, num_steps=400, tol=1e-7)
360+
simulate_and_check_mujoco_consistency(gs_sim, mj_sim, qvel=qvel, num_steps=2000, tol=tol)
361361

362362
# Check that final velocity is almost zero
363363
gs_qvel = gs_sim.rigid_solver.dofs_state.vel.to_numpy()
@@ -394,14 +394,12 @@ def test_walker(gs_sim, mj_sim, gjk_collision, tol):
394394
@pytest.mark.parametrize("gs_solver", [gs.constraint_solver.CG, gs.constraint_solver.Newton])
395395
@pytest.mark.parametrize("gs_integrator", [gs.integrator.implicitfast, gs.integrator.Euler])
396396
@pytest.mark.parametrize("backend", [gs.cpu])
397-
def test_equality_joint(gs_sim, mj_sim, gs_solver):
397+
def test_equality_joint(gs_sim, mj_sim, gs_solver, tol):
398398
# there is an equality constraint
399399
assert gs_sim.rigid_solver.n_equalities == 1
400400

401401
qpos = np.array((0.0, -1.0))
402402
qvel = np.array((1.0, -0.3))
403-
# Note that it is impossible to be more accurate than this because of the inherent stiffness of the problem.
404-
tol = 2e-8 if gs_solver == gs.constraint_solver.Newton else 1e-8
405403
simulate_and_check_mujoco_consistency(gs_sim, mj_sim, qpos, qvel, num_steps=300, tol=tol)
406404

407405
# check if the two joints are equal
@@ -435,7 +433,7 @@ def test_equality_weld(gs_sim, mj_sim, gs_solver):
435433
# apply transform internally) is about 1e-15. This is fine and not surprising as it is consistent with machine
436434
# precision. These rounding errors are then amplified by 1e8 when computing the forces resulting from the kinematic
437435
# constraints. The constraints could be made softer by changing its impede parameters.
438-
tol = 1e-7 if gs_solver == gs.constraint_solver.Newton else 2e-5
436+
tol = 1e-7 if gs_solver == gs.constraint_solver.Newton else 5e-6
439437
simulate_and_check_mujoco_consistency(gs_sim, mj_sim, qpos, num_steps=300, tol=tol)
440438

441439

0 commit comments

Comments
 (0)