============================================================================== Steps the world and asserts (a) the linkage stays finite the whole time and (b) the closed loop stays closed: the loop-closure anchor on the last link must not drift away from its anchor on the base by more than kMaxLoopClosureError. Without the fix, the Dantzig-only configuration scrubs the singular solution to zero an
| 157 | // kMaxLoopClosureError. Without the fix, the Dantzig-only configuration scrubs |
| 158 | // the singular solution to zero and the loop drifts open, violating (b). |
| 159 | void stepAndCheckLoopClosed( |
| 160 | const WorldPtr& world, |
| 161 | const SkeletonPtr& skel, |
| 162 | BodyNode* base, |
| 163 | BodyNode* lastLink, |
| 164 | const Eigen::Vector3d& loopAnchor) |
| 165 | { |
| 166 | const Eigen::Vector3d off1 |
| 167 | = lastLink->getWorldTransform().inverse() * loopAnchor; |
| 168 | const Eigen::Vector3d off2 = base->getWorldTransform().inverse() * loopAnchor; |
| 169 | const std::size_t numSteps = 100; |
| 170 | for (std::size_t i = 0; i < numSteps; ++i) { |
| 171 | // Apply a small actuation/perturbation on the first revolute joint. |
| 172 | skel->getJoint(1)->setForce(0, 0.01); |
| 173 | |
| 174 | world->step(); |
| 175 | |
| 176 | ASSERT_TRUE(skel->getPositions().allFinite()) |
| 177 | << "Non-finite positions at step " << i << ": " |
| 178 | << skel->getPositions().transpose(); |
| 179 | ASSERT_TRUE(skel->getVelocities().allFinite()) |
| 180 | << "Non-finite velocities at step " << i << ": " |
| 181 | << skel->getVelocities().transpose(); |
| 182 | const Eigen::Vector3d p1 = lastLink->getWorldTransform() * off1; |
| 183 | const Eigen::Vector3d p2 = base->getWorldTransform() * off2; |
| 184 | const double gap = (p1 - p2).norm(); |
| 185 | ASSERT_LT(gap, kMaxLoopClosureError) |
| 186 | << "Loop drifted open at step " << i << ": gap=" << gap; |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | //============================================================================== |
| 191 | // Exercises PART A: the default World solver is a BoxedLcpConstraintSolver with |
no test coverage detected