/* * Copyright (C) 2012 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include #include "gazebo/physics/simbody/SimbodyTypes.hh" #include "gazebo/physics/simbody/SimbodyModel.hh" #include "gazebo/physics/simbody/SimbodyLink.hh" #include "gazebo/physics/simbody/SimbodyJoint.hh" #include "gazebo/physics/simbody/SimbodyCollision.hh" #include "gazebo/physics/simbody/SimbodyPlaneShape.hh" #include "gazebo/physics/simbody/SimbodySphereShape.hh" #include "gazebo/physics/simbody/SimbodyHeightmapShape.hh" #include "gazebo/physics/simbody/SimbodyMultiRayShape.hh" #include "gazebo/physics/simbody/SimbodyBoxShape.hh" #include "gazebo/physics/simbody/SimbodyCylinderShape.hh" #include "gazebo/physics/simbody/SimbodyMeshShape.hh" #include "gazebo/physics/simbody/SimbodyPolylineShape.hh" #include "gazebo/physics/simbody/SimbodyRayShape.hh" #include "gazebo/physics/simbody/SimbodyHingeJoint.hh" #include "gazebo/physics/simbody/SimbodyUniversalJoint.hh" #include "gazebo/physics/simbody/SimbodyBallJoint.hh" #include "gazebo/physics/simbody/SimbodySliderJoint.hh" #include "gazebo/physics/simbody/SimbodyHinge2Joint.hh" #include "gazebo/physics/simbody/SimbodyScrewJoint.hh" #include "gazebo/physics/simbody/SimbodyFixedJoint.hh" #include "gazebo/physics/ContactManager.hh" #include "gazebo/physics/PhysicsTypes.hh" #include "gazebo/physics/PhysicsFactory.hh" #include "gazebo/physics/World.hh" #include "gazebo/physics/WorldPrivate.hh" #include "gazebo/physics/Entity.hh" #include "gazebo/physics/Model.hh" #include "gazebo/physics/SurfaceParams.hh" #include "gazebo/physics/MapShape.hh" #include "gazebo/common/Assert.hh" #include "gazebo/common/Console.hh" #include "gazebo/common/Exception.hh" #include "gazebo/math/Vector3.hh" #include "gazebo/transport/Publisher.hh" #include "gazebo/physics/simbody/SimbodyPhysics.hh" typedef boost::shared_ptr SimbodyJointPtr; using namespace gazebo; using namespace physics; using namespace SimTK; GZ_REGISTER_PHYSICS_ENGINE("simbody", SimbodyPhysics) ////////////////////////////////////////////////// SimbodyPhysics::SimbodyPhysics(WorldPtr _world) : PhysicsEngine(_world), system(), matter(system), forces(system), gravity(forces, matter, -SimTK::ZAxis, 0), discreteForces(forces, matter), tracker(system), contact(system, tracker), integ(NULL) , contactMaterialStiffness(0.0) , contactMaterialDissipation(0.0) , contactMaterialPlasticCoefRestitution(0.0) , contactMaterialPlasticImpactVelocity(0.0) , contactMaterialStaticFriction(0.0) , contactMaterialDynamicFriction(0.0) , contactMaterialViscousFriction(0.0) , contactImpactCaptureVelocity(0.0) , contactStictionTransitionVelocity(0.0) , dynamicsWorld(NULL) , stepTimeDouble(0.0) { // Instantiate the Multibody System // Instantiate the Simbody Matter Subsystem // Instantiate the Simbody General Force Subsystem this->simbodyPhysicsInitialized = false; this->simbodyPhysicsStepped = false; } ////////////////////////////////////////////////// SimbodyPhysics::~SimbodyPhysics() { } ////////////////////////////////////////////////// ModelPtr SimbodyPhysics::CreateModel(BasePtr _parent) { // set physics as uninitialized this->simbodyPhysicsInitialized = false; SimbodyModelPtr model(new SimbodyModel(_parent)); return model; } ////////////////////////////////////////////////// void SimbodyPhysics::Load(sdf::ElementPtr _sdf) { PhysicsEngine::Load(_sdf); // Create an integrator /// \TODO: get from sdf for simbody physics /// \TODO: use this when pgs rigid body solver is implemented this->solverType = "elastic_foundation"; /// \TODO: get from sdf for simbody physics this->integratorType = "semi_explicit_euler"; if (this->integratorType == "rk_merson") this->integ = new SimTK::RungeKuttaMersonIntegrator(system); else if (this->integratorType == "rk3") this->integ = new SimTK::RungeKutta3Integrator(system); else if (this->integratorType == "rk2") this->integ = new SimTK::RungeKutta2Integrator(system); else if (this->integratorType == "semi_explicit_euler") this->integ = new SimTK::SemiExplicitEuler2Integrator(system); else { gzerr << "type not specified, using SemiExplicitEuler2Integrator.\n"; this->integ = new SimTK::SemiExplicitEuler2Integrator(system); } this->stepTimeDouble = this->GetMaxStepSize(); sdf::ElementPtr simbodyElem = this->sdf->GetElement("simbody"); // Set integrator accuracy (measured with Richardson Extrapolation) this->integ->setAccuracy( simbodyElem->Get("accuracy")); // Set stiction max slip velocity to make it less stiff. this->contact.setTransitionVelocity( simbodyElem->Get("max_transient_velocity")); sdf::ElementPtr simbodyContactElem = simbodyElem->GetElement("contact"); // system wide contact properties, assigned in AddCollisionsToLink() this->contactMaterialStiffness = simbodyContactElem->Get("stiffness"); this->contactMaterialDissipation = simbodyContactElem->Get("dissipation"); this->contactMaterialStaticFriction = simbodyContactElem->Get("static_friction"); this->contactMaterialDynamicFriction = simbodyContactElem->Get("dynamic_friction"); this->contactMaterialViscousFriction = simbodyContactElem->Get("viscous_friction"); // below are not used yet, but should work it into the system this->contactMaterialPlasticCoefRestitution = simbodyContactElem->Get("plastic_coef_restitution"); this->contactMaterialPlasticImpactVelocity = simbodyContactElem->Get("plastic_impact_velocity"); this->contactImpactCaptureVelocity = simbodyContactElem->Get("override_impact_capture_velocity"); this->contactStictionTransitionVelocity = simbodyContactElem->Get("override_stiction_transition_velocity"); } ///////////////////////////////////////////////// void SimbodyPhysics::OnRequest(ConstRequestPtr &_msg) { msgs::Response response; response.set_id(_msg->id()); response.set_request(_msg->request()); response.set_response("success"); std::string *serializedData = response.mutable_serialized_data(); if (_msg->request() == "physics_info") { msgs::Physics physicsMsg; physicsMsg.set_type(msgs::Physics::SIMBODY); // min_step_size is defined but not yet used physicsMsg.set_min_step_size(this->GetMaxStepSize()); physicsMsg.set_enable_physics(this->world->GetEnablePhysicsEngine()); physicsMsg.mutable_gravity()->CopyFrom( msgs::Convert(this->world->Gravity())); physicsMsg.mutable_magnetic_field()->CopyFrom( msgs::Convert(this->MagneticField())); physicsMsg.set_real_time_update_rate(this->realTimeUpdateRate); physicsMsg.set_real_time_factor(this->targetRealTimeFactor); physicsMsg.set_max_step_size(this->maxStepSize); response.set_type(physicsMsg.GetTypeName()); physicsMsg.SerializeToString(serializedData); this->responsePub->Publish(response); } } ///////////////////////////////////////////////// void SimbodyPhysics::OnPhysicsMsg(ConstPhysicsPtr &_msg) { // Parent class handles many generic parameters // This should be done first so that the profile settings // can be over-ridden by other message parameters. PhysicsEngine::OnPhysicsMsg(_msg); if (_msg->has_enable_physics()) this->world->EnablePhysicsEngine(_msg->enable_physics()); if (_msg->has_gravity()) this->SetGravity(msgs::ConvertIgn(_msg->gravity())); if (_msg->has_real_time_factor()) this->SetTargetRealTimeFactor(_msg->real_time_factor()); if (_msg->has_real_time_update_rate()) this->SetRealTimeUpdateRate(_msg->real_time_update_rate()); if (_msg->has_max_step_size()) this->SetMaxStepSize(_msg->max_step_size()); /* below will set accuracy for simbody if the messages exist // Set integrator accuracy (measured with Richardson Extrapolation) if (_msg->has_accuracy()) { this->integ->setAccuracy(_msg->simbody().accuracy()); } // Set stiction max slip velocity to make it less stiff. if (_msg->has_max_transient_velocity()) { this->contact.setTransitionVelocity( _msg->simbody().max_transient_velocity()); } */ /// Make sure all models get at least on update cycle. this->world->EnableAllModels(); } ////////////////////////////////////////////////// void SimbodyPhysics::Reset() { this->integ->initialize(this->system.getDefaultState()); // restore potentially user run-time modified gravity this->SetGravity(this->world->Gravity()); } ////////////////////////////////////////////////// void SimbodyPhysics::Init() { this->simbodyPhysicsInitialized = true; } ////////////////////////////////////////////////// void SimbodyPhysics::InitModel(const physics::ModelPtr _model) { // Before building a new system, transfer all joints in existing // models, save Simbody joint states in Gazebo Model. const SimTK::State& currentState = this->integ->getState(); double stateTime = 0; bool simbodyStateSaved = false; if (currentState.getSystemStage() != SimTK::Stage::Empty) { stateTime = currentState.getTime(); physics::Model_V models = this->world->GetModels(); for (physics::Model_V::iterator mi = models.begin(); mi != models.end(); ++mi) { if ((*mi) != _model) { physics::Joint_V joints = (*mi)->GetJoints(); for (physics::Joint_V::iterator jx = joints.begin(); jx != joints.end(); ++jx) { SimbodyJointPtr simbodyJoint = boost::dynamic_pointer_cast(*jx); simbodyJoint->SaveSimbodyState(currentState); } physics::Link_V links = (*mi)->GetLinks(); for (physics::Link_V::iterator lx = links.begin(); lx != links.end(); ++lx) { SimbodyLinkPtr simbodyLink = boost::dynamic_pointer_cast(*lx); simbodyLink->SaveSimbodyState(currentState); } } } simbodyStateSaved = true; } try { //------------------------ CREATE SIMBODY SYSTEM --------------------------- // Add to Simbody System and populate it with new links and joints if (_model->IsStatic()) { SimbodyPhysics::AddStaticModelToSimbodySystem(_model); } else { //---------------------- GENERATE MULTIBODY GRAPH ------------------------ MultibodyGraphMaker mbgraph; this->CreateMultibodyGraph(mbgraph, _model); // Optional: dump the graph to stdout for debugging or curiosity. // mbgraph.dumpGraph(gzdbg); SimbodyPhysics::AddDynamicModelToSimbodySystem(mbgraph, _model); } } catch(const std::exception& e) { gzthrow(std::string("Simbody build EXCEPTION: ") + e.what()); } try { //------------------------ CREATE SIMBODY SYSTEM --------------------------- // Create a Simbody System and populate it with Subsystems we'll need. SimbodyPhysics::InitSimbodySystem(); } catch(const std::exception& e) { gzthrow(std::string("Simbody init EXCEPTION: ") + e.what()); } SimTK::State state = this->system.realizeTopology(); // Restore Gazebo saved Joint states // back into Simbody state. if (simbodyStateSaved) { // set/retsore state time. state.setTime(stateTime); physics::Model_V models = this->world->GetModels(); for (physics::Model_V::iterator mi = models.begin(); mi != models.end(); ++mi) { physics::Joint_V joints = (*mi)->GetJoints(); for (physics::Joint_V::iterator jx = joints.begin(); jx != joints.end(); ++jx) { SimbodyJointPtr simbodyJoint = boost::dynamic_pointer_cast(*jx); simbodyJoint->RestoreSimbodyState(state); } physics::Link_V links = (*mi)->GetLinks(); for (physics::Link_V::iterator lx = links.begin(); lx != links.end(); ++lx) { SimbodyLinkPtr simbodyLink = boost::dynamic_pointer_cast(*lx); simbodyLink->RestoreSimbodyState(state); } } } // initialize integrator from state this->integ->initialize(state); // mark links as initialized Link_V links = _model->GetLinks(); for (Link_V::iterator li = links.begin(); li != links.end(); ++li) { physics::SimbodyLinkPtr simbodyLink = boost::dynamic_pointer_cast(*li); if (simbodyLink) simbodyLink->physicsInitialized = true; else gzerr << "failed to cast link [" << (*li)->GetName() << "] as simbody link\n"; } // mark joints as initialized physics::Joint_V joints = _model->GetJoints(); for (physics::Joint_V::iterator ji = joints.begin(); ji != joints.end(); ++ji) { SimbodyJointPtr simbodyJoint = boost::dynamic_pointer_cast(*ji); if (simbodyJoint) simbodyJoint->physicsInitialized = true; else gzerr << "simbodyJoint [" << (*ji)->GetName() << "]is not a SimbodyJointPtr\n"; } this->simbodyPhysicsInitialized = true; } ////////////////////////////////////////////////// void SimbodyPhysics::InitForThread() { } ////////////////////////////////////////////////// void SimbodyPhysics::UpdateCollision() { boost::recursive_mutex::scoped_lock lock(*this->physicsUpdateMutex); this->contactManager->ResetCount(); // Get all contacts from Simbody const SimTK::State &state = this->integ->getState(); // The tracker cannot generate a snapshot without a subsystem if (state.getNumSubsystems() == 0) return; // get contact snapshot const SimTK::ContactSnapshot &contactSnapshot = this->tracker.getActiveContacts(state); int numc = contactSnapshot.getNumContacts(); int count = 0; for (int j = 0; j < numc; ++j) { // get contact stuff from Simbody const SimTK::Contact &simbodyContact = contactSnapshot.getContact(j); { SimTK::ContactSurfaceIndex csi1 = simbodyContact.getSurface1(); SimTK::ContactSurfaceIndex csi2 = simbodyContact.getSurface2(); const SimTK::ContactSurface &cs1 = this->tracker.getContactSurface(csi1); const SimTK::ContactSurface &cs2 = this->tracker.getContactSurface(csi2); /// \TODO: See issue #1584 /// \TODO: below, get collision data from simbody contacts Collision *collision1 = NULL; Collision *collision2 = NULL; physics::LinkPtr link1 = NULL; physics::LinkPtr link2 = NULL; /// \TODO: get SimTK::ContactGeometry* from ContactForce somehow const SimTK::ContactGeometry &cg1 = cs1.getShape(); const SimTK::ContactGeometry &cg2 = cs2.getShape(); /// \TODO: proof of concept only /// loop through all link->all collisions and find /// this is going to be very very slow, we'll need /// something with a void* pointer in simbody /// to support something like this. physics::Model_V models = this->world->GetModels(); for (physics::Model_V::iterator mi = models.begin(); mi != models.end(); ++mi) { physics::Link_V links = (*mi)->GetLinks(); for (Link_V::iterator li = links.begin(); li != links.end(); ++li) { Collision_V collisions = (*li)->GetCollisions(); for (Collision_V::iterator ci = collisions.begin(); ci != collisions.end(); ++ci) { /// compare SimbodyCollision::GetCollisionShape() to /// ContactGeometry from SimTK::ContactForce SimbodyCollisionPtr sc = boost::dynamic_pointer_cast(*ci); if (sc->GetCollisionShape() == &cg1) { collision1 = (*ci).get(); link1 = (*li); } else if (sc->GetCollisionShape() == &cg2) { collision2 = (*ci).get(); link2 = (*li); } } } } // add contacts to the manager. This will return NULL if no one is // listening for contact information. Contact *contactFeedback = this->contactManager->NewContact(collision1, collision2, this->world->GetSimTime()); if (contactFeedback) { const bool useContactPatch = true; if (useContactPatch) { // get contact patch to get detailed contacts // see https://github.com/simbody/simbody/blob/master/examples/ExampleContactPlayground.cpp#L110 SimTK::ContactPatch patch; this->system.realize(state, SimTK::Stage::Velocity); const bool found = this->contact.calcContactPatchDetailsById( state, simbodyContact.getContactId(), patch); // loop through details of patch if (found) { for (int i = 0; i < patch.getNumDetails(); ++i) { if (count >= MAX_CONTACT_JOINTS) { gzerr << "max contact count [" << MAX_CONTACT_JOINTS << "] exceeded. truncating info.\n"; continue; } // gzerr << "count: " << count << "\n"; // get detail const SimTK::ContactDetail &detail = patch.getContactDetail(i); // get contact information from simbody and // add them to contactFeedback. // Store the contact depth contactFeedback->depths[count] = detail.getDeformation(); // Store the contact position contactFeedback->positions[count].Set( detail.getContactPoint()[0], detail.getContactPoint()[1], detail.getContactPoint()[2]); // Store the contact normal contactFeedback->normals[count].Set( detail.getContactNormal()[0], detail.getContactNormal()[1], detail.getContactNormal()[2]); // Store the contact forces const SimTK::Vec3 f2 = detail.getForceOnSurface2(); const SimTK::SpatialVec s2 = SimTK::SpatialVec(SimTK::Vec3(0, 0, 0), f2); /// Get transform from point to CG. /// detail.getContactPoint() returns in body frame /// per gazebo contact feedback convention. const SimTK::Vec3 offset2 = -detail.getContactPoint(); SimTK::SpatialVec s2cg = SimTK::shiftForceBy(s2, offset2); SimTK::Vec3 t2cg = s2cg[0]; SimTK::Vec3 f2cg = s2cg[1]; /// shift for body 1 /// \TODO: generalize wrench shifting below later and add /// it to JointWrench class for shifting wrenches around /// arbitrarily based on frames. /// /// shift forces to link1 frame without rotating it first math::Pose pose1 = link1->GetWorldPose(); math::Pose pose2 = link2->GetWorldPose(); const SimTK::Vec3 offset1 = -detail.getContactPoint() + SimbodyPhysics::Vector3ToVec3(pose1.pos - pose2.pos); SimTK::SpatialVec s1cg = SimTK::shiftForceBy(-s2, offset1); /// get torque and force components SimTK::Vec3 t1cg = s1cg[0]; SimTK::Vec3 f1cg = s1cg[1]; /* actually don't need to do this? confirm that everything is in the world frame! /// \TODO: rotate it into link 1 frame, there must be /// a clean way to do this in simbody... /// my gazebo way of rotating frames for now, to replace with /// clean simbody function calls. /// rotation from link2 to link1 frame specified in link2 frame math::Quaternion rot21 = (pose1 - pose2).rot; t1cg = SimbodyPhysics::Vector3ToVec3( rot21.RotateVectorReverse(SimbodyPhysics::Vec3ToVector3(t1cg))); f1cg = SimbodyPhysics::Vector3ToVec3( rot21.RotateVectorReverse(SimbodyPhysics::Vec3ToVector3(f1cg))); gzerr << "numc: " << j << "\n"; gzerr << "count: " << count << "\n"; gzerr << "index: " << i << "\n"; gzerr << "offset 2: " << detail.getContactPoint() << "\n"; gzerr << "s2: " << s2 << "\n"; gzerr << "s2cg: " << s2cg << "\n"; gzerr << "f2cg: " << f2cg << "\n"; gzerr << "t2cg: " << t2cg << "\n"; gzerr << "offset 1: " << detail.getContactPoint() << "\n"; gzerr << "s1cg: " << s1cg << "\n"; gzerr << "f1cg: " << f1cg << "\n"; gzerr << "t1cg: " << t1cg << "\n"; */ // copy. contactFeedback->wrench[count].body1Force.Set( f1cg[0], f1cg[1], f1cg[2]); contactFeedback->wrench[count].body2Force.Set( f2cg[0], f2cg[1], f2cg[2]); contactFeedback->wrench[count].body1Torque.Set( t1cg[0], t1cg[1], t1cg[2]); contactFeedback->wrench[count].body2Torque.Set( t2cg[0], t2cg[1], t2cg[2]); // Increase the counters ++count; contactFeedback->count = count; } } } else // use single ContactForce { // // get contact information from simbody ContactForce and // // add it to contactFeedback. // /// \TODO: confirm the contact depth is zero? // contactFeedback->depths[count] = 0.0; // // Store the contact position // contactFeedback->positions[count].Set( // contactForce.getContactPoint()[0], // contactForce.getContactPoint()[1], // contactForce.getContactPoint()[2]); // // Store the contact normal // contactFeedback->normals[j].Set( // 0, 0, 0); // // contactForce.getContactNormal()[0], // // contactForce.getContactNormal()[1], // // contactForce.getContactNormal()[2]); // // Increase the counters // ++count; // contactFeedback->count = count; } } } } } ////////////////////////////////////////////////// void SimbodyPhysics::UpdatePhysics() { // need to lock, otherwise might conflict with world resetting boost::recursive_mutex::scoped_lock lock(*this->physicsUpdateMutex); common::Time currTime = this->world->GetRealTime(); // Simbody cannot step the integrator without a subsystem const SimTK::State &s = this->integ->getState(); if (s.getNumSubsystems() == 0) return; bool trying = true; while (trying && integ->getTime() < this->world->GetSimTime().Double()) { try { this->integ->stepTo(this->world->GetSimTime().Double(), this->world->GetSimTime().Double()); } catch(const std::exception& e) { gzerr << "simbody stepTo() failed with message:\n" << e.what() << "\nWill stop trying now.\n"; trying = false; } } this->simbodyPhysicsStepped = true; // debug // gzerr << "time [" << s.getTime() // << "] q [" << s.getQ() // << "] u [" << s.getU() // << "] dt [" << this->stepTimeDouble // << "] t [" << this->world->GetSimTime().Double() // << "]\n"; // this->lastUpdateTime = currTime; // pushing new entity pose into dirtyPoses for visualization physics::Model_V models = this->world->GetModels(); for (physics::Model_V::iterator mi = models.begin(); mi != models.end(); ++mi) { physics::Link_V links = (*mi)->GetLinks(); for (physics::Link_V::iterator lx = links.begin(); lx != links.end(); ++lx) { physics::SimbodyLinkPtr simbodyLink = boost::dynamic_pointer_cast(*lx); math::Pose pose = SimbodyPhysics::Transform2Pose( simbodyLink->masterMobod.getBodyTransform(s)); simbodyLink->SetDirtyPose(pose); this->world->dataPtr->dirtyPoses.push_back( boost::static_pointer_cast(*lx).get()); } physics::Joint_V joints = (*mi)->GetJoints(); for (physics::Joint_V::iterator jx = joints.begin(); jx != joints.end(); ++jx) { SimbodyJointPtr simbodyJoint = boost::dynamic_pointer_cast(*jx); simbodyJoint->CacheForceTorque(); } } // FIXME: this needs to happen before forces are applied for the next step // FIXME: but after we've gotten everything from current state this->discreteForces.clearAllForces(this->integ->updAdvancedState()); } ////////////////////////////////////////////////// void SimbodyPhysics::Fini() { } ////////////////////////////////////////////////// LinkPtr SimbodyPhysics::CreateLink(ModelPtr _parent) { if (_parent == NULL) gzthrow("Link must have a parent\n"); SimbodyLinkPtr link(new SimbodyLink(_parent)); link->SetWorld(_parent->GetWorld()); return link; } ////////////////////////////////////////////////// CollisionPtr SimbodyPhysics::CreateCollision(const std::string &_type, LinkPtr _parent) { SimbodyCollisionPtr collision(new SimbodyCollision(_parent)); ShapePtr shape = this->CreateShape(_type, collision); collision->SetShape(shape); shape->SetWorld(_parent->GetWorld()); return collision; } ////////////////////////////////////////////////// ShapePtr SimbodyPhysics::CreateShape(const std::string &_type, CollisionPtr _collision) { ShapePtr shape; SimbodyCollisionPtr collision = boost::dynamic_pointer_cast(_collision); if (_type == "plane") shape.reset(new SimbodyPlaneShape(collision)); else if (_type == "sphere") shape.reset(new SimbodySphereShape(collision)); else if (_type == "box") shape.reset(new SimbodyBoxShape(collision)); else if (_type == "cylinder") shape.reset(new SimbodyCylinderShape(collision)); else if (_type == "mesh" || _type == "trimesh") shape.reset(new SimbodyMeshShape(collision)); else if (_type == "polyline") shape.reset(new SimbodyPolylineShape(collision)); else if (_type == "heightmap") shape.reset(new SimbodyHeightmapShape(collision)); else if (_type == "multiray") shape.reset(new SimbodyMultiRayShape(collision)); else if (_type == "ray") if (_collision) shape.reset(new SimbodyRayShape(_collision)); else shape.reset(new SimbodyRayShape(this->world->GetPhysicsEngine())); else gzerr << "Unable to create collision of type[" << _type << "]\n"; // else if (_type == "map" || _type == "image") // shape.reset(new MapShape(collision)); return shape; } ////////////////////////////////////////////////// JointPtr SimbodyPhysics::CreateJoint(const std::string &_type, ModelPtr _parent) { JointPtr joint; if (_type == "revolute") joint.reset(new SimbodyHingeJoint(this->dynamicsWorld, _parent)); else if (_type == "universal") joint.reset(new SimbodyUniversalJoint(this->dynamicsWorld, _parent)); else if (_type == "ball") joint.reset(new SimbodyBallJoint(this->dynamicsWorld, _parent)); else if (_type == "prismatic") joint.reset(new SimbodySliderJoint(this->dynamicsWorld, _parent)); else if (_type == "revolute2") joint.reset(new SimbodyHinge2Joint(this->dynamicsWorld, _parent)); else if (_type == "screw") joint.reset(new SimbodyScrewJoint(this->dynamicsWorld, _parent)); else if (_type == "fixed") joint.reset(new SimbodyFixedJoint(this->dynamicsWorld, _parent)); else gzthrow("Unable to create joint of type[" << _type << "]"); return joint; } ////////////////////////////////////////////////// void SimbodyPhysics::SetGravity(const gazebo::math::Vector3 &_gravity) { this->world->SetGravitySDF(_gravity.Ign()); { boost::recursive_mutex::scoped_lock lock(*this->physicsUpdateMutex); if (this->simbodyPhysicsInitialized && this->world->GetModelCount() > 0) this->gravity.setGravityVector(this->integ->updAdvancedState(), SimbodyPhysics::Vector3ToVec3(_gravity)); else this->gravity.setDefaultGravityVector( SimbodyPhysics::Vector3ToVec3(_gravity)); } } ////////////////////////////////////////////////// void SimbodyPhysics::DebugPrint() const { } ////////////////////////////////////////////////// void SimbodyPhysics::CreateMultibodyGraph( SimTK::MultibodyGraphMaker &_mbgraph, const physics::ModelPtr _model) { // Step 1: Tell MultibodyGraphMaker about joints it should know about. // Note: "weld" and "free" are always predefined at 0 and 6 dofs, resp. // Gazebo name #dofs Simbody equivalent _mbgraph.addJointType(GetTypeString(physics::Base::HINGE_JOINT), 1); _mbgraph.addJointType(GetTypeString(physics::Base::HINGE2_JOINT), 2); _mbgraph.addJointType(GetTypeString(physics::Base::SLIDER_JOINT), 1); _mbgraph.addJointType(GetTypeString(physics::Base::UNIVERSAL_JOINT), 2); _mbgraph.addJointType(GetTypeString(physics::Base::SCREW_JOINT), 1); _mbgraph.addJointType(GetTypeString(physics::Base::FIXED_JOINT), 0); // Simbody has a Ball constraint that is a good choice if you need to // break a loop at a ball joint. // _mbgraph.addJointType(GetTypeString(physics::Base::BALL_JOINT), 3, true); // skip loop joints for now _mbgraph.addJointType(GetTypeString(physics::Base::BALL_JOINT), 3, false); // Step 2: Tell it about all the links we read from the input file, // starting with world, and provide a reference pointer. _mbgraph.addBody("world", SimTK::Infinity, false); physics::Link_V links = _model->GetLinks(); for (physics::Link_V::iterator li = links.begin(); li != links.end(); ++li) { SimbodyLinkPtr simbodyLink = boost::dynamic_pointer_cast(*li); // gzerr << "debug : " << (*li)->GetName() << "\n"; if (simbodyLink) _mbgraph.addBody((*li)->GetName(), (*li)->GetInertial()->GetMass(), simbodyLink->mustBeBaseLink, (*li).get()); else gzerr << "simbodyLink [" << (*li)->GetName() << "]is not a SimbodyLinkPtr\n"; } // Step 3: Tell it about all the joints we read from the input file, // and provide a reference pointer. physics::Joint_V joints = _model->GetJoints(); for (physics::Joint_V::iterator ji = joints.begin(); ji != joints.end(); ++ji) { SimbodyJointPtr simbodyJoint = boost::dynamic_pointer_cast(*ji); if (simbodyJoint) if ((*ji)->GetParent() && (*ji)->GetChild()) _mbgraph.addJoint((*ji)->GetName(), GetTypeString((*ji)->GetType()), (*ji)->GetParent()->GetName(), (*ji)->GetChild()->GetName(), simbodyJoint->mustBreakLoopHere, (*ji).get()); else if ((*ji)->GetChild()) _mbgraph.addJoint((*ji)->GetName(), GetTypeString((*ji)->GetType()), "world", (*ji)->GetChild()->GetName(), simbodyJoint->mustBreakLoopHere, (*ji).get()); else gzerr << "simbodyJoint [" << (*ji)->GetName() << "] does not have a valid child link, which is required\n"; else gzerr << "simbodyJoint [" << (*ji)->GetName() << "]is not a SimbodyJointPtr\n"; } // Setp 4. Generate the multibody graph. _mbgraph.generateGraph(); } ////////////////////////////////////////////////// void SimbodyPhysics::InitSimbodySystem() { // Set stiction max slip velocity to make it less stiff. // this->contact.setTransitionVelocity(0.01); // now done in Load using sdf // Specify gravity (read in above from world). if (!math::equal(this->world->Gravity().Length(), 0.0)) this->gravity.setDefaultGravityVector( SimbodyPhysics::Vector3ToVec3(this->world->Gravity())); else this->gravity.setDefaultMagnitude(0.0); } ////////////////////////////////////////////////// void SimbodyPhysics::AddStaticModelToSimbodySystem( const physics::ModelPtr _model) { physics::Link_V links = _model->GetLinks(); for (physics::Link_V::iterator li = links.begin(); li != links.end(); ++li) { SimbodyLinkPtr simbodyLink = boost::dynamic_pointer_cast(*li); if (simbodyLink) { this->AddCollisionsToLink(simbodyLink.get(), this->matter.updGround(), ContactCliqueId()); simbodyLink->masterMobod = this->matter.updGround(); } else gzerr << "simbodyLink [" << (*li)->GetName() << "]is not a SimbodyLinkPtr\n"; } } ////////////////////////////////////////////////// void SimbodyPhysics::AddDynamicModelToSimbodySystem( const SimTK::MultibodyGraphMaker &_mbgraph, const physics::ModelPtr /*_model*/) { // Generate a contact clique we can put collision geometry in to prevent // self-collisions. // \TODO: put this in a gazebo::physics::SimbodyModel class ContactCliqueId modelClique = ContactSurface::createNewContactClique(); // Will specify explicitly when needed // Record the MobilizedBody for the World link. // model.links.updLink(0).masterMobod = this->matter.Ground(); // Run through all the mobilizers in the multibody graph, adding a Simbody // MobilizedBody for each one. Also add visual and collision geometry to the // bodies when they are mobilized. for (int mobNum = 0; mobNum < _mbgraph.getNumMobilizers(); ++mobNum) { // Get a mobilizer from the graph, then extract its corresponding // joint and bodies. Note that these don't necessarily have equivalents // in the GazeboLink and GazeboJoint inputs. const MultibodyGraphMaker::Mobilizer& mob = _mbgraph.getMobilizer(mobNum); const std::string& type = mob.getJointTypeName(); // The inboard body always corresponds to one of the input links, // because a slave link is always the outboard body of a mobilizer. // The outboard body may be slave, but its master body is one of the // Gazebo input links. const bool isSlave = mob.isSlaveMobilizer(); // note: do not use boost shared pointer here, on scope out the // original pointer get scrambled SimbodyLink* gzInb = static_cast(mob.getInboardBodyRef()); SimbodyLink* gzOutb = static_cast(mob.getOutboardMasterBodyRef()); const MassProperties massProps = gzOutb->GetEffectiveMassProps(mob.getNumFragments()); // debug // if (gzInb) // gzerr << "debug: Inb: " << gzInb->GetName() << "\n"; // if (gzOutb) // gzerr << "debug: Outb: " << gzOutb->GetName() // << " mass: " << gzOutb->GetInertial()->GetMass() // << " efm: " << massProps // << "\n"; // This will reference the new mobilized body once we create it. MobilizedBody mobod; MobilizedBody parentMobod = gzInb == NULL ? this->matter.Ground() : gzInb->masterMobod; if (mob.isAddedBaseMobilizer()) { // There is no corresponding Gazebo joint for this mobilizer. // Create the joint and set its default position to be the default // pose of the base link relative to the Ground frame. // Currently only `free` is allowed, we may add more types later GZ_ASSERT(type == "free", "type is not 'free', not allowed."); if (type == "free") { MobilizedBody::Free freeJoint( parentMobod, Transform(), massProps, Transform()); SimTK::Transform inboard_X_ML; if (gzInb == NULL) { // GZ_ASSERT(gzOutb, "must be here"); physics::ModelPtr model = gzOutb->GetParentModel(); inboard_X_ML = ~SimbodyPhysics::Pose2Transform(model->GetWorldPose()); } else inboard_X_ML = SimbodyPhysics::Pose2Transform(gzInb->GetRelativePose()); SimTK::Transform outboard_X_ML = SimbodyPhysics::Pose2Transform(gzOutb->GetRelativePose()); // defX_ML link frame specified in model frame freeJoint.setDefaultTransform(~inboard_X_ML*outboard_X_ML); mobod = freeJoint; } } else { // This mobilizer does correspond to one of the input joints. // note: do not use boost shared pointer here, on scope out the // original pointer get scrambled SimbodyJoint* gzJoint = static_cast(mob.getJointRef()); const bool isReversed = mob.isReversedFromJoint(); // Find inboard and outboard frames for the mobilizer; these are // parent and child frames or the reverse. const Transform& X_IF0 = isReversed ? gzJoint->xCB : gzJoint->xPA; const Transform& X_OM0 = isReversed ? gzJoint->xPA : gzJoint->xCB; const MobilizedBody::Direction direction = isReversed ? MobilizedBody::Reverse : MobilizedBody::Forward; if (type == "free") { MobilizedBody::Free freeJoint( parentMobod, X_IF0, massProps, X_OM0, direction); Transform defX_FM = isReversed ? Transform(~gzJoint->defxAB) : gzJoint->defxAB; freeJoint.setDefaultTransform(defX_FM); mobod = freeJoint; } else if (type == "screw") { UnitVec3 axis( SimbodyPhysics::Vector3ToVec3( gzJoint->GetAxisFrameOffset(0).RotateVector( gzJoint->GetLocalAxis(0)))); double pitch = dynamic_cast(gzJoint)->GetThreadPitch(0); if (math::equal(pitch, 0.0)) { gzerr << "thread pitch should not be zero (joint is a slider?)" << " using pitch = 1.0e6\n"; pitch = 1.0e6; } // Simbody's screw joint axis (both rotation and translation) is along Z Rotation R_JZ(axis, ZAxis); Transform X_IF(X_IF0.R()*R_JZ, X_IF0.p()); Transform X_OM(X_OM0.R()*R_JZ, X_OM0.p()); MobilizedBody::Screw screwJoint( parentMobod, X_IF, massProps, X_OM, -1.0/pitch, direction); mobod = screwJoint; gzdbg << "Setting limitForce[0] for [" << gzJoint->GetName() << "]\n"; double low = gzJoint->GetLowerLimit(0u).Radian(); double high = gzJoint->GetUpperLimit(0u).Radian(); // initialize stop stiffness and dissipation from joint parameters gzJoint->limitForce[0] = Force::MobilityLinearStop(this->forces, mobod, SimTK::MobilizerQIndex(0), gzJoint->GetStopStiffness(0), gzJoint->GetStopDissipation(0), low, high); // gzdbg << "SimbodyPhysics SetDamping (" // << gzJoint->GetDampingCoefficient() // << ")\n"; // Create a damper for every joint even if damping coefficient // is zero. This will allow user to change damping coefficients // on the fly. gzJoint->damper[0] = Force::MobilityLinearDamper(this->forces, mobod, 0, gzJoint->GetDamping(0)); // add spring (stiffness proportional to mass) gzJoint->spring[0] = Force::MobilityLinearSpring(this->forces, mobod, 0, gzJoint->GetStiffness(0), gzJoint->GetSpringReferencePosition(0)); } else if (type == "universal") { UnitVec3 axis1(SimbodyPhysics::Vector3ToVec3( gzJoint->GetAxisFrameOffset(0).RotateVector( gzJoint->GetLocalAxis(UniversalJoint::AXIS_PARENT)))); /// \TODO: check if this is right, or GetAxisFrameOffset(1) is needed. UnitVec3 axis2(SimbodyPhysics::Vector3ToVec3( gzJoint->GetAxisFrameOffset(0).RotateVector( gzJoint->GetLocalAxis(UniversalJoint::AXIS_CHILD)))); // Simbody's univeral joint is along axis1=Y and axis2=X // note X and Y are reversed because Simbody defines universal joint // rotation in body-fixed frames, whereas Gazebo/ODE uses space-fixed // frames. Rotation R_JF(axis1, XAxis, axis2, YAxis); Transform X_IF(X_IF0.R()*R_JF, X_IF0.p()); Transform X_OM(X_OM0.R()*R_JF, X_OM0.p()); MobilizedBody::Universal uJoint( parentMobod, X_IF, massProps, X_OM, direction); mobod = uJoint; for (unsigned int nj = 0; nj < 2; ++nj) { double low = gzJoint->GetLowerLimit(nj).Radian(); double high = gzJoint->GetUpperLimit(nj).Radian(); // initialize stop stiffness and dissipation from joint parameters gzJoint->limitForce[nj] = Force::MobilityLinearStop(this->forces, mobod, SimTK::MobilizerQIndex(nj), gzJoint->GetStopStiffness(nj), gzJoint->GetStopDissipation(nj), low, high); // gzdbg << "stop stiffness [" << gzJoint->GetStopStiffness(nj) // << "] low [" << low // << "] high [" << high // << "]\n"; // gzdbg << "SimbodyPhysics SetDamping (" // << gzJoint->GetDampingCoefficient() // << ")\n"; // Create a damper for every joint even if damping coefficient // is zero. This will allow user to change damping coefficients // on the fly. gzJoint->damper[nj] = Force::MobilityLinearDamper(this->forces, mobod, nj, gzJoint->GetDamping(nj)); // add spring (stiffness proportional to mass) gzJoint->spring[nj] = Force::MobilityLinearSpring(this->forces, mobod, nj, gzJoint->GetStiffness(nj), gzJoint->GetSpringReferencePosition(nj)); } } else if (type == "revolute") { // rotation from axis frame to child link frame // simbody assumes links are in child link frame, but gazebo // sdf 1.4 and earlier assumes joint axis are defined in model frame. // Use function Joint::GetAxisFrame() to remedy this situation. // Joint::GetAxisFrame() returns the frame joint axis is defined: // either model frame or child link frame. // simbody always assumes axis is specified in the child link frame. // \TODO: come up with a test case where we might need to // flip transform based on isReversed flag. UnitVec3 axis( SimbodyPhysics::Vector3ToVec3( gzJoint->GetAxisFrameOffset(0).RotateVector( gzJoint->GetLocalAxis(0)))); // gzerr << "[" << gzJoint->GetAxisFrameOffset(0).GetAsEuler() // << "] [" // << gzJoint->GetAxisFrameOffset(0).RotateVector( // gzJoint->GetLocalAxis(0)) << "]\n"; // Simbody's pin is along Z Rotation R_JZ(axis, ZAxis); Transform X_IF(X_IF0.R()*R_JZ, X_IF0.p()); Transform X_OM(X_OM0.R()*R_JZ, X_OM0.p()); MobilizedBody::Pin pinJoint( parentMobod, X_IF, massProps, X_OM, direction); mobod = pinJoint; double low = gzJoint->GetLowerLimit(0u).Radian(); double high = gzJoint->GetUpperLimit(0u).Radian(); // initialize stop stiffness and dissipation from joint parameters gzJoint->limitForce[0] = Force::MobilityLinearStop(this->forces, mobod, SimTK::MobilizerQIndex(0), gzJoint->GetStopStiffness(0), gzJoint->GetStopDissipation(0), low, high); // gzdbg << "SimbodyPhysics SetDamping (" // << gzJoint->GetDampingCoefficient() // << ")\n"; // Create a damper for every joint even if damping coefficient // is zero. This will allow user to change damping coefficients // on the fly. gzJoint->damper[0] = Force::MobilityLinearDamper(this->forces, mobod, 0, gzJoint->GetDamping(0)); // add spring (stiffness proportional to mass) gzJoint->spring[0] = Force::MobilityLinearSpring(this->forces, mobod, 0, gzJoint->GetStiffness(0), gzJoint->GetSpringReferencePosition(0)); } else if (type == "prismatic") { UnitVec3 axis(SimbodyPhysics::Vector3ToVec3( gzJoint->GetAxisFrameOffset(0).RotateVector( gzJoint->GetLocalAxis(0)))); // Simbody's slider is along X Rotation R_JX(axis, XAxis); Transform X_IF(X_IF0.R()*R_JX, X_IF0.p()); Transform X_OM(X_OM0.R()*R_JX, X_OM0.p()); MobilizedBody::Slider sliderJoint( parentMobod, X_IF, massProps, X_OM, direction); mobod = sliderJoint; double low = gzJoint->GetLowerLimit(0u).Radian(); double high = gzJoint->GetUpperLimit(0u).Radian(); // initialize stop stiffness and dissipation from joint parameters gzJoint->limitForce[0] = Force::MobilityLinearStop(this->forces, mobod, SimTK::MobilizerQIndex(0), gzJoint->GetStopStiffness(0), gzJoint->GetStopDissipation(0), low, high); // Create a damper for every joint even if damping coefficient // is zero. This will allow user to change damping coefficients // on the fly. gzJoint->damper[0] = Force::MobilityLinearDamper(this->forces, mobod, 0, gzJoint->GetDamping(0)); // add spring (stiffness proportional to mass) gzJoint->spring[0] = Force::MobilityLinearSpring(this->forces, mobod, 0, gzJoint->GetStiffness(0), gzJoint->GetSpringReferencePosition(0)); } else if (type == "ball") { MobilizedBody::Ball ballJoint( parentMobod, X_IF0, massProps, X_OM0, direction); Rotation defR_FM = isReversed ? Rotation(~gzJoint->defxAB.R()) : gzJoint->defxAB.R(); ballJoint.setDefaultRotation(defR_FM); mobod = ballJoint; } else if (type == "fixed") { MobilizedBody::Weld fixedJoint( parentMobod, X_IF0, massProps, X_OM0); mobod = fixedJoint; } else { gzerr << "Simbody joint type [" << type << "] not implemented.\n"; } // Created a mobilizer that corresponds to gzJoint. Keep track. gzJoint->mobod = mobod; gzJoint->isReversed = isReversed; } // Link gzOutb has been mobilized; keep track for later. if (isSlave) gzOutb->slaveMobods.push_back(mobod); else gzOutb->masterMobod = mobod; // A mobilizer has been created; now add the collision // geometry for the new mobilized body. this->AddCollisionsToLink(gzOutb, mobod, modelClique); } // Weld the slaves to their masters. physics::Model_V models = this->world->GetModels(); for (physics::Model_V::iterator mi = models.begin(); mi != models.end(); ++mi) { physics::Link_V links = (*mi)->GetLinks(); for (physics::Link_V::iterator lx = links.begin(); lx != links.end(); ++lx) { physics::SimbodyLinkPtr link = boost::dynamic_pointer_cast(*lx); if (link->slaveMobods.empty()) continue; for (unsigned i = 0; i < link->slaveMobods.size(); ++i) { Constraint::Weld weld(link->masterMobod, link->slaveMobods[i]); // in case we want to know later link->slaveWelds.push_back(weld); } } } // leave out optimization // // Add the loop joints if any. // for (int lcx=0; lcx < _mbgraph.getNumLoopConstraints(); ++lcx) { // const MultibodyGraphMaker::LoopConstraint& loop = // _mbgraph.getLoopConstraint(lcx); // SimbodyJointPtr joint(loop.getJointRef()); // SimbodyLinkPtr parent(loop.getParentBodyRef()); // SimbodyLinkPtr child(loop.getChildBodyRef()); // if (joint.type == "weld") { // Constraint::Weld weld(parent.masterMobod, joint.xPA, // child.masterMobod, joint.xCB); // joint.constraint = weld; // } else if (joint.type == "ball") { // Constraint::Ball ball(parent.masterMobod, joint.xPA.p(), // child.masterMobod, joint.xCB.p()); // joint.constraint = ball; // } else if (joint.type == "free") { // // A "free" loop constraint is no constraint at all so we can // // just ignore it. It might be more convenient if there were // // a 0-constraint Constraint::Free, just as there is a 0-mobility // // MobilizedBody::Weld. // } else // throw std::runtime_error( // "Unrecognized loop constraint type '" + joint.type + "'."); // } } std::string SimbodyPhysics::GetTypeString(physics::Base::EntityType _type) { // switch (_type) // { // case physics::Base::BALL_JOINT: // gzerr << "here\n"; // return "ball"; // break; // case physics::Base::HINGE2_JOINT: // return "revolute2"; // break; // case physics::Base::HINGE_JOINT: // return "revolute"; // break; // case physics::Base::SLIDER_JOINT: // return "prismatic"; // break; // case physics::Base::SCREW_JOINT: // return "screw"; // break; // case physics::Base::UNIVERSAL_JOINT: // return "universal"; // break; // default: // gzerr << "Unrecognized joint type\n"; // return "UNRECOGNIZED"; // } if (_type & physics::Base::BALL_JOINT) return "ball"; else if (_type & physics::Base::HINGE2_JOINT) return "revolute2"; else if (_type & physics::Base::HINGE_JOINT) return "revolute"; else if (_type & physics::Base::SLIDER_JOINT) return "prismatic"; else if (_type & physics::Base::SCREW_JOINT) return "screw"; else if (_type & physics::Base::UNIVERSAL_JOINT) return "universal"; else if (_type & physics::Base::FIXED_JOINT) return "fixed"; gzerr << "Unrecognized joint type\n"; return "UNRECOGNIZED"; } ///////////////////////////////////////////////// void SimbodyPhysics::SetSeed(uint32_t /*_seed*/) { gzerr << "SimbodyPhysics::SetSeed not implemented\n"; } ///////////////////////////////////////////////// void SimbodyPhysics::AddCollisionsToLink(const physics::SimbodyLink *_link, MobilizedBody &_mobod, ContactCliqueId _modelClique) { // TODO: Edit physics::Surface class to support these properties // Define a material to use for contact. This is not very stiff. // use stiffness of 1e8 and dissipation of 1000.0 to approximate inelastic // collision. but 1e6 and 10 seems sufficient when TransitionVelocity is // reduced from 0.1 to 0.01 SimTK::ContactMaterial material(this->contactMaterialStiffness, this->contactMaterialDissipation, this->contactMaterialStaticFriction, this->contactMaterialDynamicFriction, this->contactMaterialViscousFriction); // Debug: works for SpawnDrop // SimTK::ContactMaterial material(1e6, // stiffness // 10.0, // dissipation // 0.7, // mu_static // 0.5, // mu_dynamic // 0.5); // mu_viscous bool addModelClique = _modelClique.isValid() && !_link->GetSelfCollide(); // COLLISION Collision_V collisions = _link->GetCollisions(); for (Collision_V::iterator ci = collisions.begin(); ci != collisions.end(); ++ci) { Transform X_LC = SimbodyPhysics::Pose2Transform((*ci)->GetRelativePose()); // use pointer to store CollisionGeometry SimbodyCollisionPtr sc = boost::dynamic_pointer_cast(*ci); switch ((*ci)->GetShapeType() & (~physics::Entity::SHAPE)) { case physics::Entity::PLANE_SHAPE: { boost::shared_ptr p = boost::dynamic_pointer_cast((*ci)->GetShape()); // by default, simbody HalfSpace normal is in the -X direction // rotate it based on normal vector specified by user // Create a rotation whos x-axis is in the // negative normal vector direction Vec3 normal = SimbodyPhysics::Vector3ToVec3(p->GetNormal()); Rotation R_XN(-UnitVec3(normal), XAxis); ContactSurface surface(ContactGeometry::HalfSpace(), material); if (addModelClique) surface.joinClique(_modelClique); int surfNum = _mobod.updBody().addContactSurface(R_XN, surface); // store ContactGeometry pointer in SimbodyCollision object SimTK::ContactSurface &contactSurf = _mobod.updBody().updContactSurface(surfNum); sc->SetCollisionShape(&contactSurf.updShape()); } break; case physics::Entity::SPHERE_SHAPE: { boost::shared_ptr s = boost::dynamic_pointer_cast((*ci)->GetShape()); double r = s->GetRadius(); ContactSurface surface(ContactGeometry::Sphere(r), material); if (addModelClique) surface.joinClique(_modelClique); int surfNum = _mobod.updBody().addContactSurface(X_LC, surface); // store ContactGeometry pointer in SimbodyCollision object SimTK::ContactSurface &contactSurf = _mobod.updBody().updContactSurface(surfNum); sc->SetCollisionShape(&contactSurf.updShape()); } break; case physics::Entity::CYLINDER_SHAPE: { boost::shared_ptr c = boost::dynamic_pointer_cast( (*ci)->GetShape()); double r = c->GetRadius(); double len = c->GetLength(); // chunky hexagonal shape const int resolution = 1; const PolygonalMesh mesh = PolygonalMesh:: createCylinderMesh(ZAxis, r, len/2, resolution); const ContactGeometry::TriangleMesh triMesh(mesh); ContactSurface surface(triMesh, material, 1 /*Thickness*/); // Vec3 esz = Vec3(r, r, len/2); // Use ellipsoid instead // ContactSurface surface(ContactGeometry::Ellipsoid(esz), // material); if (addModelClique) surface.joinClique(_modelClique); int surfNum = _mobod.updBody().addContactSurface(X_LC, surface); // store ContactGeometry pointer in SimbodyCollision object SimTK::ContactSurface &contactSurf = _mobod.updBody().updContactSurface(surfNum); sc->SetCollisionShape(&contactSurf.updShape()); } break; case physics::Entity::BOX_SHAPE: { Vec3 hsz = SimbodyPhysics::Vector3ToVec3( (boost::dynamic_pointer_cast( (*ci)->GetShape()))->GetSize())/2; /// \TODO: harcoded resolution, make collision resolution /// an adjustable parameter (#980) // number times to chop the longest side. const int resolution = 6; // const int resolution = 10 * (int)(max(hsz)/min(hsz) + 0.5); const PolygonalMesh mesh = PolygonalMesh:: createBrickMesh(hsz, resolution); const ContactGeometry::TriangleMesh triMesh(mesh); ContactSurface surface(triMesh, material, 1 /*Thickness*/); // ContactSurface surface(ContactGeometry::Ellipsoid(hsz), // material); if (addModelClique) surface.joinClique(_modelClique); int surfNum = _mobod.updBody().addContactSurface(X_LC, surface); // store ContactGeometry pointer in SimbodyCollision object SimTK::ContactSurface &contactSurf = _mobod.updBody().updContactSurface(surfNum); sc->SetCollisionShape(&contactSurf.updShape()); } break; default: gzerr << "Collision type [" << (*ci)->GetShapeType() << "] unimplemented\n"; break; } } } ///////////////////////////////////////////////// std::string SimbodyPhysics::GetType() const { return "simbody"; } ///////////////////////////////////////////////// SimTK::MultibodySystem *SimbodyPhysics::GetDynamicsWorld() const { return this->dynamicsWorld; } ///////////////////////////////////////////////// SimTK::Quaternion SimbodyPhysics::QuadToQuad(const math::Quaternion &_q) { return SimTK::Quaternion(_q.w, _q.x, _q.y, _q.z); } ///////////////////////////////////////////////// math::Quaternion SimbodyPhysics::QuadToQuad(const SimTK::Quaternion &_q) { return math::Quaternion(_q[0], _q[1], _q[2], _q[3]); } ///////////////////////////////////////////////// SimTK::Vec3 SimbodyPhysics::Vector3ToVec3(const math::Vector3 &_v) { return SimTK::Vec3(_v.x, _v.y, _v.z); } ///////////////////////////////////////////////// math::Vector3 SimbodyPhysics::Vec3ToVector3(const SimTK::Vec3 &_v) { return math::Vector3(_v[0], _v[1], _v[2]); } ///////////////////////////////////////////////// SimTK::Transform SimbodyPhysics::Pose2Transform(const math::Pose &_pose) { SimTK::Quaternion q(_pose.rot.w, _pose.rot.x, _pose.rot.y, _pose.rot.z); SimTK::Vec3 v(_pose.pos.x, _pose.pos.y, _pose.pos.z); SimTK::Transform frame(SimTK::Rotation(q), v); return frame; } ///////////////////////////////////////////////// math::Pose SimbodyPhysics::Transform2Pose(const SimTK::Transform &_xAB) { SimTK::Quaternion q(_xAB.R()); const SimTK::Vec4 &qv = q.asVec4(); return math::Pose(math::Vector3(_xAB.p()[0], _xAB.p()[1], _xAB.p()[2]), math::Quaternion(qv[0], qv[1], qv[2], qv[3])); } ///////////////////////////////////////////////// SimTK::Transform SimbodyPhysics::GetPose(sdf::ElementPtr _element) { const math::Pose pose = _element->Get("pose"); return Pose2Transform(pose); } ///////////////////////////////////////////////// std::string SimbodyPhysics::GetTypeString(unsigned int _type) { return GetTypeString(physics::Base::EntityType(_type)); } ////////////////////////////////////////////////// boost::any SimbodyPhysics::GetParam(const std::string &_key) const { boost::any value; this->GetParam(_key, value); return value; } ////////////////////////////////////////////////// bool SimbodyPhysics::GetParam(const std::string &_key, boost::any &_value) const { if (_key == "solver_type") { _value = std::string("Spatial Algebra and Elastic Foundation"); } else if (_key == "integrator_type") { _value = this->integratorType; } else if (_key == "accuracy") { if (this->integ) _value = this->integ->getAccuracyInUse(); else _value = 0.0f; } else if (_key == "max_transient_velocity") { _value = this->contact.getTransitionVelocity(); } else { return PhysicsEngine::GetParam(_key, _value); } return true; } ////////////////////////////////////////////////// bool SimbodyPhysics::SetParam(const std::string &_key, const boost::any &_value) { try { if (_key == "accuracy") { this->integ->setAccuracy(boost::any_cast(_value)); } else if (_key == "max_transient_velocity") { this->contact.setTransitionVelocity(boost::any_cast(_value)); } else if (_key == "stiffness") { this->contactMaterialStiffness = boost::any_cast(_value); } else if (_key == "dissipation") { this->contactMaterialDissipation = boost::any_cast(_value); } else if (_key == "plastic_coef_restitution") { this->contactMaterialPlasticCoefRestitution = boost::any_cast(_value); } else if (_key == "plastic_impact_velocity") { this->contactMaterialPlasticImpactVelocity = boost::any_cast(_value); } else if (_key == "static_friction") { this->contactMaterialStaticFriction = boost::any_cast(_value); } else if (_key == "dynamic_friction") { this->contactMaterialDynamicFriction = boost::any_cast(_value); } else if (_key == "viscous_friction") { this->contactMaterialViscousFriction = boost::any_cast(_value); } else if (_key == "override_impact_capture_velocity") { this->contactMaterialPlasticImpactVelocity = boost::any_cast(_value); } else if (_key == "override_stiction_transition_velocity") { this->contactImpactCaptureVelocity = boost::any_cast(_value); } else { return PhysicsEngine::SetParam(_key, _value); } } catch(boost::bad_any_cast &e) { gzerr << "SimbodyPhysics::SetParam(" << _key << ") boost::any_cast error: " << e.what() << std::endl; return false; } return true; }