/* * Copyright (C) 2018 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 #include #include "gazebo/common/Events.hh" #include "gazebo/common/UpdateInfo.hh" #include "gazebo/physics/Entity.hh" #include "gazebo/physics/World.hh" #include "gazebo/transport/Node.hh" #include "ContainPlugin.hh" namespace gazebo { namespace physics { typedef boost::weak_ptr EntityWeakPtr; } /// \brief Private data class for the ContainPlugin class class ContainPluginPrivate { /// \brief Connection to world update. public: event::ConnectionPtr updateConnection; /// \brief Pointer to the world. public: physics::WorldPtr world; /// \brief Scoped name of the entity we're checking. public: std::string entityName; /// \brief Pointer to the entity we're checking. public: physics::EntityWeakPtr entity; /// \brief Box representing the volume to check. public: ignition::math::OrientedBoxd box; /// \brief Gazebo transport node for communication. public: transport::NodePtr gzNode; /// \brief pointer to an entity whose pose the geometry will track public: physics::EntityWeakPtr containerEntity; /// \brief scoped name of entity to track public: std::string containerEntityName; /// \brief Publisher which publishes contain / doesn't contain messages. public: transport::PublisherPtr containGzPub; /// \brief Subscriber to enable messages. public: transport::SubscriberPtr enableGzSub; /// \brief Namespace for the topics: /// //contain /// //enable public: std::string ns; /// \brief 1 if contains, 0 if doesn't contain, -1 if unset public: int contain = -1; }; } using namespace gazebo; GZ_REGISTER_WORLD_PLUGIN(ContainPlugin) ///////////////////////////////////////////////// ContainPlugin::ContainPlugin() : WorldPlugin(), dataPtr(new ContainPluginPrivate) { } ///////////////////////////////////////////////// void ContainPlugin::Load(physics::WorldPtr _world, sdf::ElementPtr _sdf) { // Entity name if (!_sdf->HasElement("entity")) { gzerr << "Missing required parameter , plugin will not be " << "initialized." << std::endl; return; } this->dataPtr->entityName = _sdf->Get("entity"); // Namespace if (!_sdf->HasElement("namespace")) { gzerr << "Missing required parameter , plugin will not be " << "initialized." << std::endl; return; } this->dataPtr->ns = _sdf->Get("namespace"); // Pose if (!_sdf->HasElement("pose")) { gzerr << "Missing required parameter , plugin will not be " << "initialized." << std::endl; return; } auto pose = _sdf->Get("pose"); sdf::ParamPtr frameParam = _sdf->GetElement("pose")->GetAttribute("frame"); if (frameParam) { this->dataPtr->containerEntityName = frameParam->GetAsString(); } // Geometry if (!_sdf->HasElement("geometry")) { gzerr << "Missing required parameter , plugin will not be " << "initialized." << std::endl; return; } auto geometryElem = _sdf->GetElement("geometry"); // Only box for now if (!geometryElem->HasElement("box")) { gzerr << "Missing required parameter , plugin will not be " << "initialized." << std::endl; return; } auto boxElem = geometryElem->GetElement("box"); if (!boxElem->HasElement("size")) { gzerr << "Missing required parameter , plugin will not be " << "initialized." << std::endl; return; } auto size = boxElem->Get("size"); this->dataPtr->box = ignition::math::OrientedBoxd(size, pose); this->dataPtr->world = _world; // Start/stop this->dataPtr->gzNode = transport::NodePtr(new transport::Node()); this->dataPtr->gzNode->Init(); this->dataPtr->enableGzSub = this->dataPtr->gzNode->Subscribe("/" + this->dataPtr->ns + "/enable", &ContainPlugin::Enable, this); auto enabled = true; if (_sdf->HasElement("enabled")) enabled = _sdf->Get("enabled"); if (enabled) { boost::shared_ptr msg(new msgs::Int()); msg->set_data(1); this->Enable(msg); } } ////////////////////////////////////////////////// void ContainPlugin::Enable(ConstIntPtr &_msg) { auto enable = _msg->data() == 1; // Already started if (enable && this->dataPtr->updateConnection) { gzwarn << "Contain plugin is already enabled." << std::endl; return; } // Already stopped if (!enable && !this->dataPtr->updateConnection) { gzwarn << "Contain plugin is already disabled." << std::endl; return; } // Start if (enable) { // Start update this->dataPtr->updateConnection = event::Events::ConnectWorldUpdateBegin( std::bind(&ContainPlugin::OnUpdate, this, std::placeholders::_1)); this->dataPtr->containGzPub = this->dataPtr->gzNode->Advertise( "/" + this->dataPtr->ns + "/contain"); gzmsg << "Started contain plugin [" << this->dataPtr->ns << "]" << std::endl; return; } // Stop { this->dataPtr->updateConnection.reset(); this->dataPtr->containGzPub.reset(); this->dataPtr->contain = -1; gzmsg << "Stopped contain plugin [" << this->dataPtr->ns << "]" << std::endl; return; } } ///////////////////////////////////////////////// void ContainPlugin::OnUpdate(const common::UpdateInfo &/*_info*/) { // Only get the entity once physics::EntityPtr entity = this->dataPtr->entity.lock(); if (!entity) { this->dataPtr->entity = this->dataPtr->world->GetEntity( this->dataPtr->entityName); entity = this->dataPtr->entity.lock(); if (!entity) { // Could not find entity being tested this->PublishContains(false); return; } } ignition::math::Vector3d entityInWorldFrame = entity->GetWorldPose().Ign().Pos(); ignition::math::Vector3d entityInBoxFrame; if (!this->dataPtr->containerEntityName.empty()) { physics::EntityPtr referenceEntity = this->dataPtr->containerEntity.lock(); // box is in a potentially moving reference frame if (!referenceEntity) { this->dataPtr->containerEntity = this->dataPtr->world->GetEntity( this->dataPtr->containerEntityName); referenceEntity = this->dataPtr->containerEntity.lock(); if (!referenceEntity) { // Could not find reference entity this->PublishContains(false); return; } } auto worldToBox = referenceEntity->GetWorldPose().Ign(); auto boxToWorld = worldToBox.Inverse(); // Transform the entity vector from world frame to the frame the box is in entityInBoxFrame = (boxToWorld.Rot() * entityInWorldFrame) + boxToWorld.Pos(); } else { // box frame is world frame entityInBoxFrame = entityInWorldFrame; } this->PublishContains(this->dataPtr->box.Contains(entityInBoxFrame)); } ////////////////////////////////////////////////// void ContainPlugin::PublishContains(const bool _contains) { int containNow = _contains ? 1 : 0; if (containNow != this->dataPtr->contain) { this->dataPtr->contain = containNow; msgs::Int msg; msg.set_data(this->dataPtr->contain); this->dataPtr->containGzPub->Publish(msg); } }