Enable multiccd by default.

PiperOrigin-RevId: 902752921
Change-Id: I8e2085ff17db0ac0db1641b8837415c458e5eca4
This commit is contained in:
Kyle Bayes
2026-04-20 11:59:24 -07:00
committed by Copybara-Service
parent 8e7787ad09
commit 6cb6e5a93f
22 changed files with 43 additions and 47 deletions
+4 -4
View File
@@ -494,8 +494,9 @@ typedef enum mjtDisableBit_ { // disable default feature bitflags
mjDSBL_AUTORESET = 1<<16, // automatic reset when numerical issues are detected
mjDSBL_NATIVECCD = 1<<17, // native convex collision detection
mjDSBL_ISLAND = 1<<18, // constraint island discovery
mjDSBL_MULTICCD = 1<<19, // multiple CCD contact points
mjNDISABLE = 19 // number of disable flags
mjNDISABLE = 20 // number of disable flags
} mjtDisableBit;
typedef enum mjtEnableBit_ { // enable optional feature bitflags
mjENBL_OVERRIDE = 1<<0, // override contact parameters
@@ -503,10 +504,9 @@ typedef enum mjtEnableBit_ { // enable optional feature bitflags
mjENBL_FWDINV = 1<<2, // record solver statistics
mjENBL_INVDISCRETE = 1<<3, // discrete-time inverse dynamics
// experimental features:
mjENBL_MULTICCD = 1<<4, // multi-point convex collision detection
mjENBL_SLEEP = 1<<5, // sleeping
mjENBL_SLEEP = 1<<4, // sleeping
mjNENABLE = 6 // number of enable flags
mjNENABLE = 5 // number of enable flags
} mjtEnableBit;
typedef enum mjtJoint_ { // type of degree of freedom
mjJNT_FREE = 0, // global position and orientation (quat) (7)
+4 -4
View File
@@ -70,8 +70,9 @@ typedef enum mjtDisableBit_ { // disable default feature bitflags
mjDSBL_AUTORESET = 1<<16, // automatic reset when numerical issues are detected
mjDSBL_NATIVECCD = 1<<17, // native convex collision detection
mjDSBL_ISLAND = 1<<18, // constraint island discovery
mjDSBL_MULTICCD = 1<<19, // multiple CCD contact points
mjNDISABLE = 19 // number of disable flags
mjNDISABLE = 20 // number of disable flags
} mjtDisableBit;
@@ -81,10 +82,9 @@ typedef enum mjtEnableBit_ { // enable optional feature bitflags
mjENBL_FWDINV = 1<<2, // record solver statistics
mjENBL_INVDISCRETE = 1<<3, // discrete-time inverse dynamics
// experimental features:
mjENBL_MULTICCD = 1<<4, // multi-point convex collision detection
mjENBL_SLEEP = 1<<5, // sleeping
mjENBL_SLEEP = 1<<4, // sleeping
mjNENABLE = 6 // number of enable flags
mjNENABLE = 5 // number of enable flags
} mjtEnableBit;
-2
View File
@@ -92,8 +92,6 @@ class EnableBit(enum.IntFlag):
INVDISCRETE = mujoco.mjtEnableBit.mjENBL_INVDISCRETE
# unsupported: OVERRIDE, ENERGY, FWDINV, ISLAND
# required by the C implementation only, ignored otherwise: MULTICCD
MULTICCD = mujoco.mjtEnableBit.mjENBL_MULTICCD
SLEEP = mujoco.mjtEnableBit.mjENBL_SLEEP
@@ -36,7 +36,7 @@ from mujoco.mjx.third_party.mujoco_warp._src.types import MJ_MAX_EPAHORIZON
from mujoco.mjx.third_party.mujoco_warp._src.types import MJ_MAXCONPAIR
from mujoco.mjx.third_party.mujoco_warp._src.types import MJ_MAXVAL
from mujoco.mjx.third_party.mujoco_warp._src.types import Data
from mujoco.mjx.third_party.mujoco_warp._src.types import EnableBit
from mujoco.mjx.third_party.mujoco_warp._src.types import DisableBit
from mujoco.mjx.third_party.mujoco_warp._src.types import GeomType
from mujoco.mjx.third_party.mujoco_warp._src.types import Model
from mujoco.mjx.third_party.mujoco_warp._src.types import mat43
@@ -1127,7 +1127,7 @@ def convex_narrowphase(m: Model, d: Data, ctx: CollisionContext, collision_table
epa_iterations = 16 if nboxbox == ncollision else m.opt.ccd_iterations
# set to true to enable multiccd
use_multiccd = m.opt.enableflags & EnableBit.MULTICCD
use_multiccd = m.opt.disableflags & DisableBit.MULTICCD == 0
# need at least 4 (square sides) if there's a box collision needing multiccd
nmaxpolygon = 4 if nboxbox > 0 else 0
+1 -1
View File
@@ -370,7 +370,7 @@ def put_model(mjm: mujoco.MjModel) -> types.Model:
)
# check for unsupported margin + multicontact / box-box CCD combinations
use_multiccd = mjm.opt.enableflags & types.EnableBit.MULTICCD
use_multiccd = (mjm.opt.disableflags & types.DisableBit.MULTICCD) == 0
nativeccd_disabled = mjm.opt.disableflags & types.DisableBit.NATIVECCD
BOX = int(mujoco.mjtGeom.mjGEOM_BOX)
MESH = int(mujoco.mjtGeom.mjGEOM_MESH)
+2 -2
View File
@@ -184,6 +184,7 @@ class DisableBit(enum.IntFlag):
EULERDAMP: implicit damping for Euler integration
NATIVECCD: native convex collision detection (ignored in MJWarp)
ISLAND: constraint islands
MULTICCD: multiple CCD contact points
"""
CONSTRAINT = mujoco.mjtDisableBit.mjDSBL_CONSTRAINT
@@ -203,6 +204,7 @@ class DisableBit(enum.IntFlag):
EULERDAMP = mujoco.mjtDisableBit.mjDSBL_EULERDAMP
NATIVECCD = mujoco.mjtDisableBit.mjDSBL_NATIVECCD
ISLAND = mujoco.mjtDisableBit.mjDSBL_ISLAND
MULTICCD = mujoco.mjtDisableBit.mjDSBL_MULTICCD
# unsupported: MIDPHASE, AUTORESET
@@ -212,12 +214,10 @@ class EnableBit(enum.IntFlag):
Attributes:
ENERGY: energy computation
INVDISCRETE: discrete-time inverse dynamics
MULTICCD: multiple contacts with CCD
"""
ENERGY = mujoco.mjtEnableBit.mjENBL_ENERGY
INVDISCRETE = mujoco.mjtEnableBit.mjENBL_INVDISCRETE
MULTICCD = mujoco.mjtEnableBit.mjENBL_MULTICCD
# unsupported: OVERRIDE, FWDINV, ISLAND
+1 -1
View File
@@ -704,7 +704,7 @@ void ParseUsdPhysicsScene(mjSpec* spec,
bool multiccd_flag;
mjc_physics_scene.GetMultiCCDFlagAttr().Get(&multiccd_flag);
spec->option.enableflags |= (multiccd_flag ? mjENBL_MULTICCD : 0);
spec->option.disableflags |= (!multiccd_flag ? mjDSBL_MULTICCD : 0);
// Compiler attributes
auto auto_limits_attr = mjc_physics_scene.GetAutoLimitsAttr();
+1 -1
View File
@@ -966,7 +966,7 @@ Euler integrator, semi-implicit in velocity.
self.assertEqual(mujoco.mjtEnableBit.mjENBL_OVERRIDE, 1 << 0)
self.assertEqual(mujoco.mjtEnableBit.mjENBL_ENERGY, 1 << 1)
self.assertEqual(mujoco.mjtEnableBit.mjENBL_FWDINV, 1 << 2)
self.assertEqual(mujoco.mjtEnableBit.mjNENABLE, 6)
self.assertEqual(mujoco.mjtEnableBit.mjNENABLE, 5)
self.assertEqual(mujoco.mjtGeom.mjGEOM_PLANE, 0)
self.assertEqual(mujoco.mjtGeom.mjGEOM_HFIELD, 1)
self.assertEqual(mujoco.mjtGeom.mjGEOM_SPHERE, 2)
+4 -4
View File
@@ -46,7 +46,8 @@ ENUMS: Mapping[str, EnumDecl] = dict([
('mjDSBL_AUTORESET', 65536),
('mjDSBL_NATIVECCD', 131072),
('mjDSBL_ISLAND', 262144),
('mjNDISABLE', 19),
('mjDSBL_MULTICCD', 524288),
('mjNDISABLE', 20),
]),
)),
('mjtEnableBit',
@@ -58,9 +59,8 @@ ENUMS: Mapping[str, EnumDecl] = dict([
('mjENBL_ENERGY', 2),
('mjENBL_FWDINV', 4),
('mjENBL_INVDISCRETE', 8),
('mjENBL_MULTICCD', 16),
('mjENBL_SLEEP', 32),
('mjNENABLE', 6),
('mjENBL_SLEEP', 16),
('mjNENABLE', 5),
]),
)),
('mjtJoint',
+2 -3
View File
@@ -42,9 +42,8 @@ class EnumsTest(absltest.TestCase):
('mjENBL_ENERGY', 1<<1),
('mjENBL_FWDINV', 1<<2),
('mjENBL_INVDISCRETE', 1<<3),
('mjENBL_MULTICCD', 1<<4),
('mjENBL_SLEEP', 1<<5),
('mjNENABLE', 6)))
('mjENBL_SLEEP', 1<<4),
('mjNENABLE', 5)))
# values mostly increment by one with occasional overrides
def test_mjtGeom(self): # pylint: disable=invalid-name
+2 -2
View File
@@ -828,7 +828,7 @@ static int maxContacts(const mjModel* m, const mjCCDObj* obj1, const mjCCDObj* o
// reduce mesh collisions to 4 contacts max
if (type1 == mjGEOM_BOX || type1 == mjGEOM_MESH) {
if (type2 == mjGEOM_BOX || type2 == mjGEOM_MESH) {
return mjENABLED(mjENBL_MULTICCD) ? 4 : 1;
return mjDISABLED(mjDSBL_MULTICCD) ? 1 : 4;
}
}
@@ -857,7 +857,7 @@ int mjc_Convex(const mjModel* m, mjData* d, mjContact* con, int g1, int g2, mjtN
}
// look for additional contacts
if (ncon == 1 && mjENABLED(mjENBL_MULTICCD) // TODO(tassa) leave as bitflag or make geom attribute (?)
if (ncon == 1 && !mjDISABLED(mjDSBL_MULTICCD) // TODO(tassa) leave as bitflag or make geom attribute (?)
&& m->geom_type[g1] != mjGEOM_ELLIPSOID && m->geom_type[g1] != mjGEOM_SPHERE
&& m->geom_type[g2] != mjGEOM_ELLIPSOID && m->geom_type[g2] != mjGEOM_SPHERE) {
// multiCCD parameters
+2 -2
View File
@@ -66,7 +66,8 @@ const char* mjDISABLESTRING[mjNDISABLE] = {
"Eulerdamp",
"AutoReset",
"NativeCCD",
"Island"
"Island",
"MultiCCD"
};
@@ -76,7 +77,6 @@ const char* mjENABLESTRING[mjNENABLE] = {
"Energy",
"Fwdinv",
"InvDiscrete",
"MultiCCD",
"Sleep"
};
@@ -649,7 +649,6 @@ class ModelWriter {
};
const std::vector<std::pair<pxr::TfToken, int>> enable_flags = {
{MjcPhysicsTokens->mjcFlagMulticcd, mjENBL_MULTICCD},
{MjcPhysicsTokens->mjcFlagFwdinv, mjENBL_FWDINV},
{MjcPhysicsTokens->mjcFlagEnergy, mjENBL_ENERGY},
{MjcPhysicsTokens->mjcFlagOverride, mjENBL_OVERRIDE},
@@ -677,7 +676,8 @@ class ModelWriter {
{MjcPhysicsTokens->mjcFlagEulerdamp, mjDSBL_EULERDAMP},
{MjcPhysicsTokens->mjcFlagAutoreset, mjDSBL_AUTORESET},
{MjcPhysicsTokens->mjcFlagNativeccd, mjDSBL_NATIVECCD},
{MjcPhysicsTokens->mjcFlagIsland, mjDSBL_ISLAND}};
{MjcPhysicsTokens->mjcFlagIsland, mjDSBL_ISLAND},
{MjcPhysicsTokens->mjcFlagMulticcd, mjDSBL_MULTICCD}};
for (const auto &[token, flag] : disable_flags) {
create_flag_attr(token, flag, false);
}
+1 -1
View File
@@ -1284,6 +1284,7 @@ void mjXReader::Option(XMLElement* section, mjOption* opt) {
READDSBL("autoreset", mjDSBL_AUTORESET)
READDSBL("nativeccd", mjDSBL_NATIVECCD)
READDSBL("island", mjDSBL_ISLAND)
READDSBL("multiccd", mjDSBL_MULTICCD)
#undef READDSBL
#define READENBL(NAME, MASK) \
@@ -1295,7 +1296,6 @@ void mjXReader::Option(XMLElement* section, mjOption* opt) {
READENBL("energy", mjENBL_ENERGY)
READENBL("fwdinv", mjENBL_FWDINV)
READENBL("invdiscrete", mjENBL_INVDISCRETE)
READENBL("multiccd", mjENBL_MULTICCD)
READENBL("sleep", mjENBL_SLEEP)
#undef READENBL
}
+3 -3
View File
@@ -1093,7 +1093,7 @@ void mjXWriter::Option(XMLElement* root) {
XMLElement* sub = InsertEnd(section, "flag");
#define WRITEDSBL(NAME, MASK) \
if( model->option.disableflags & MASK ) \
if (model->option.disableflags & MASK) \
WriteAttrKey(sub, NAME, enable_map, 2, 0);
WRITEDSBL("constraint", mjDSBL_CONSTRAINT)
WRITEDSBL("equality", mjDSBL_EQUALITY)
@@ -1114,16 +1114,16 @@ void mjXWriter::Option(XMLElement* root) {
WRITEDSBL("autoreset", mjDSBL_AUTORESET)
WRITEDSBL("nativeccd", mjDSBL_NATIVECCD)
WRITEDSBL("island", mjDSBL_ISLAND)
WRITEDSBL("multiccd", mjDSBL_MULTICCD)
#undef WRITEDSBL
#define WRITEENBL(NAME, MASK) \
if( model->option.enableflags & MASK ) \
if (model->option.enableflags & MASK) \
WriteAttrKey(sub, NAME, enable_map, 2, 1);
WRITEENBL("override", mjENBL_OVERRIDE)
WRITEENBL("energy", mjENBL_ENERGY)
WRITEENBL("fwdinv", mjENBL_FWDINV)
WRITEENBL("invdiscrete", mjENBL_INVDISCRETE)
WRITEENBL("multiccd", mjENBL_MULTICCD)
WRITEENBL("sleep", mjENBL_SLEEP)
#undef WRITEENBL
}
+1 -1
View File
@@ -68,7 +68,7 @@ TEST_F(MjcConvexTest, CylinderBox) {
EXPECT_EQ(data->ncon, 5);
// with multiCCD disabled, should find 1 contact
model->opt.enableflags &= ~mjENBL_MULTICCD;
model->opt.disableflags |= mjDSBL_MULTICCD;
mj_forward(model, data);
EXPECT_EQ(data->ncon, 1);
+1 -1
View File
@@ -1998,7 +1998,7 @@ TEST_F(MjGjkTest, CylinderBoxMargin) {
<mujoco>
<statistic meansize="0.15"/>
<option>
<flag gravity="disable"/>
<flag gravity="disable" multiccd="disable"/>
</option>
<worldbody>
+1
View File
@@ -46,6 +46,7 @@ TEST_F(SolverTest, IslandsEquivalent) {
model->opt.tolerance = 0; // set tolerance to 0
model->opt.ls_tolerance = 0; // set ls_tolerance to 0
model->opt.ccd_tolerance = 0; // set ccd_tolerance to 0
model->opt.disableflags |= mjDSBL_MULTICCD; // disable multiccd
int nv = model->nv;
@@ -179,8 +179,6 @@ TEST_F(MjcPhysicsSceneTest, TestDefaults) {
EXPECT_ENABLE_FLAG_USD_FALLBACK_EQ_MODEL_DEFAULT(FwdinvFlag, mjENBL_FWDINV);
EXPECT_ENABLE_FLAG_USD_FALLBACK_EQ_MODEL_DEFAULT(InvDiscreteFlag,
mjENBL_INVDISCRETE);
EXPECT_ENABLE_FLAG_USD_FALLBACK_EQ_MODEL_DEFAULT(MultiCCDFlag,
mjENBL_MULTICCD);
mj_deleteModel(default_model);
mj_deleteSpec(empty_spec);
+4 -4
View File
@@ -137,16 +137,16 @@ public enum mjtDisableBit : int{
mjDSBL_AUTORESET = 65536,
mjDSBL_NATIVECCD = 131072,
mjDSBL_ISLAND = 262144,
mjNDISABLE = 19,
mjDSBL_MULTICCD = 524288,
mjNDISABLE = 20,
}
public enum mjtEnableBit : int{
mjENBL_OVERRIDE = 1,
mjENBL_ENERGY = 2,
mjENBL_FWDINV = 4,
mjENBL_INVDISCRETE = 8,
mjENBL_MULTICCD = 16,
mjENBL_SLEEP = 32,
mjNENABLE = 6,
mjENBL_SLEEP = 16,
mjNENABLE = 5,
}
public enum mjtJoint : int{
mjJNT_FREE = 0,
+1 -1
View File
@@ -10942,6 +10942,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.value("mjDSBL_AUTORESET", mjDSBL_AUTORESET)
.value("mjDSBL_NATIVECCD", mjDSBL_NATIVECCD)
.value("mjDSBL_ISLAND", mjDSBL_ISLAND)
.value("mjDSBL_MULTICCD", mjDSBL_MULTICCD)
.value("mjNDISABLE", mjNDISABLE);
enum_<mjtDyn>("mjtDyn")
.value("mjDYN_NONE", mjDYN_NONE)
@@ -10956,7 +10957,6 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.value("mjENBL_ENERGY", mjENBL_ENERGY)
.value("mjENBL_FWDINV", mjENBL_FWDINV)
.value("mjENBL_INVDISCRETE", mjENBL_INVDISCRETE)
.value("mjENBL_MULTICCD", mjENBL_MULTICCD)
.value("mjENBL_SLEEP", mjENBL_SLEEP)
.value("mjNENABLE", mjNENABLE);
enum_<mjtEq>("mjtEq")
+4 -4
View File
@@ -684,10 +684,10 @@ describe('MuJoCo WASM Bindings', () => {
it('should check constants values', () => {
expect(mujoco.mjNEQDATA).toBe(11);
expect(mujoco.mjDISABLESTRING).toEqual([
'Constraint', 'Equality', 'Frictionloss', 'Limit', 'Contact', 'Spring',
'Damper', 'Gravity', 'Clampctrl', 'Warmstart', 'Filterparent',
'Actuation', 'Refsafe', 'Sensor', 'Midphase', 'Eulerdamp', 'AutoReset',
'NativeCCD', 'Island'
'Constraint', 'Equality', 'Frictionloss', 'Limit', 'Contact',
'Spring', 'Damper', 'Gravity', 'Clampctrl', 'Warmstart',
'Filterparent', 'Actuation', 'Refsafe', 'Sensor', 'Midphase',
'Eulerdamp', 'AutoReset', 'NativeCCD', 'Island', 'MultiCCD',
]);
expect(mujoco.mjRNDSTRING).toEqual([
['Shadow', '1', 'S'], ['Wireframe', '0', 'W'], ['Reflection', '1', 'R'],