|
37 | 37 | #include "gz/sim/components/Model.hh" |
38 | 38 | #include "gz/sim/components/Name.hh" |
39 | 39 | #include "gz/sim/components/Pose.hh" |
| 40 | +#include "gz/sim/components/PoseCmd.hh" |
40 | 41 | #include "gz/sim/components/WindMode.hh" |
| 42 | +#include "gz/sim/components/DetachableJoint.hh" |
41 | 43 |
|
42 | 44 | #include "../helpers/Relay.hh" |
43 | 45 | #include "../helpers/EnvTestFixture.hh" |
@@ -435,3 +437,128 @@ TEST_F(DetachableJointTest, |
435 | 437 | // should be close. |
436 | 438 | EXPECT_TRUE(abs(distTraveledB1 - distTraveledVehicle) < 0.01); |
437 | 439 | } |
| 440 | + |
| 441 | +///////////////////////////////////////////////// |
| 442 | +TEST_F(DetachableJointTest, |
| 443 | + GZ_UTILS_TEST_DISABLED_ON_WIN32(EnforceFixedConstraint)) |
| 444 | +{ |
| 445 | + this->StartServer(common::joinPaths("/test", "worlds", |
| 446 | + "detachable_joint_enforce.sdf")); |
| 447 | + |
| 448 | + Entity modelM1 = kNullEntity; |
| 449 | + Entity modelM3 = kNullEntity; |
| 450 | + bool initialized = false; |
| 451 | + |
| 452 | + test::Relay testSystem; |
| 453 | + testSystem.OnPreUpdate([&](const UpdateInfo &, |
| 454 | + EntityComponentManager &_ecm) |
| 455 | + { |
| 456 | + if (!initialized) |
| 457 | + { |
| 458 | + modelM1 = _ecm.EntityByComponents( |
| 459 | + components::Model(), components::Name("M1")); |
| 460 | + ASSERT_NE(kNullEntity, modelM1); |
| 461 | + modelM3 = _ecm.EntityByComponents( |
| 462 | + components::Model(), components::Name("M3")); |
| 463 | + ASSERT_NE(kNullEntity, modelM3); |
| 464 | + |
| 465 | + // Locate the detachable joints for M1/M2 and M3/M4. |
| 466 | + Entity jointM1M2 = kNullEntity; |
| 467 | + Entity jointM3M4 = kNullEntity; |
| 468 | + |
| 469 | + _ecm.Each<components::DetachableJoint>( |
| 470 | + [&](const Entity &_entity, |
| 471 | + const components::DetachableJoint *_joint) -> bool |
| 472 | + { |
| 473 | + auto parentComp = _ecm.Component<components::Name>( |
| 474 | + _joint->Data().parentLink); |
| 475 | + if (parentComp) |
| 476 | + { |
| 477 | + if (parentComp->Data() == "body") |
| 478 | + jointM1M2 = _entity; |
| 479 | + else if (parentComp->Data() == "body1") |
| 480 | + jointM3M4 = _entity; |
| 481 | + } |
| 482 | + return true; |
| 483 | + }); |
| 484 | + |
| 485 | + ASSERT_NE(kNullEntity, jointM1M2); |
| 486 | + ASSERT_NE(kNullEntity, jointM3M4); |
| 487 | + |
| 488 | + // Enable enforced fixed constraint for the M1/M2 joint. |
| 489 | + _ecm.CreateComponent( |
| 490 | + jointM1M2, |
| 491 | + components::DetachableJointEnforceFixedConstraint(true)); |
| 492 | + |
| 493 | + // Disable enforced fixed constraint for the M3/M4 joint. |
| 494 | + _ecm.CreateComponent( |
| 495 | + jointM3M4, |
| 496 | + components::DetachableJointEnforceFixedConstraint(false)); |
| 497 | + |
| 498 | + initialized = true; |
| 499 | + } |
| 500 | + |
| 501 | + // Teleport the parent models M1 and M3. |
| 502 | + _ecm.SetComponentData<components::WorldPoseCmd>( |
| 503 | + modelM1, math::Pose3d(0, 60, 1, 0, 0, 0)); |
| 504 | + _ecm.SetComponentData<components::WorldPoseCmd>( |
| 505 | + modelM3, math::Pose3d(10, 60, 1, 0, 0, 0)); |
| 506 | + }); |
| 507 | + |
| 508 | + std::vector<math::Pose3d> m1LinkPoses, m2LinkPoses; |
| 509 | + std::vector<math::Pose3d> m3LinkPoses, m4LinkPoses; |
| 510 | + |
| 511 | + testSystem.OnPostUpdate([&](const UpdateInfo &, |
| 512 | + const EntityComponentManager &_ecm) |
| 513 | + { |
| 514 | + _ecm.Each<components::Link>( |
| 515 | + [&](const Entity &_entity, const components::Link *) -> bool |
| 516 | + { |
| 517 | + Link link(_entity); |
| 518 | + auto worldPose = link.WorldPose(_ecm); |
| 519 | + if (!worldPose) |
| 520 | + return true; |
| 521 | + |
| 522 | + auto parentModel = _ecm.ParentEntity(_entity); |
| 523 | + auto parentName = _ecm.ComponentData<components::Name>( |
| 524 | + parentModel).value_or(""); |
| 525 | + |
| 526 | + if (parentName == "M1") |
| 527 | + m1LinkPoses.push_back(*worldPose); |
| 528 | + else if (parentName == "M2") |
| 529 | + m2LinkPoses.push_back(*worldPose); |
| 530 | + else if (parentName == "M3") |
| 531 | + m3LinkPoses.push_back(*worldPose); |
| 532 | + else if (parentName == "M4") |
| 533 | + m4LinkPoses.push_back(*worldPose); |
| 534 | + |
| 535 | + return true; |
| 536 | + }); |
| 537 | + }); |
| 538 | + |
| 539 | + this->server->AddSystem(testSystem.systemPtr); |
| 540 | + |
| 541 | + const std::size_t nIters{10}; |
| 542 | + this->server->Run(true, nIters, false); |
| 543 | + |
| 544 | + ASSERT_EQ(nIters, m1LinkPoses.size()); |
| 545 | + ASSERT_EQ(nIters, m2LinkPoses.size()); |
| 546 | + ASSERT_EQ(nIters, m3LinkPoses.size()); |
| 547 | + ASSERT_EQ(nIters, m4LinkPoses.size()); |
| 548 | + |
| 549 | + // M1/M2 has enforce fixed constraint set to true: |
| 550 | + // the distance should remain at ~2.0m because the child is moved |
| 551 | + // to maintain the rigid child-to-parent pose offset. |
| 552 | + double M1M2Dist = |
| 553 | + (m2LinkPoses.back().Pos() - m1LinkPoses.back().Pos()).Length(); |
| 554 | + EXPECT_NEAR(2.0, M1M2Dist, 1e-5); |
| 555 | + |
| 556 | + // M3/M4 has enforce fixed constraint set to false: |
| 557 | + // The parent-child pose offset is not strictly enforced. Their relative |
| 558 | + // distance is determined by the constraint solver. |
| 559 | + double M3M4Dist = |
| 560 | + (m4LinkPoses.back().Pos() - m3LinkPoses.back().Pos()).Length(); |
| 561 | + |
| 562 | + // There should be non-zero difference between M1/M2 and M3/M4 pose offsets |
| 563 | + EXPECT_GT(std::fabs(M1M2Dist - M3M4Dist), 1e-6); |
| 564 | +} |
0 commit comments