Program Listing for File SuctionGripper.cpp

Return to documentation for file (/tmp/ws/src/aerostack2/as2_simulation_assets/as2_gazebo_assets/plugins/SuctionGripper.cpp)

// Copyright 2024 Universidad Politécnica de Madrid
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//
//    * Neither the name of the Universidad Politécnica de Madrid nor the names of its
//      contributors may be used to endorse or promote products derived from
//      this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.


#include <gz/plugin/Register.hh>


#include <gz/transport/Node.hh>

#include <gz/msgs/contacts.pb.hh>

#include <gz/msgs.hh>

#include <gz/gazebo/components.hh>
#include <gz/gazebo/Model.hh>

#include "SuctionGripper.hpp"

// using mbzirc::SuctionGripperPrivate;
// using mbzirc::SuctionGripperPlugin;
// using ignition::gazebo;
// using gazebo::System;

// using ::mbzric::SuctionGripperPrivate;
// using ::mbzric::SuctionGripperPlugin;
// using ::ignition::gazebo::System;

// using namespace mbzirc;
// using namespace ignition;
// using namespace gazebo;

class mbzirc::SuctionGripperPrivate
{

public:
  Entity childItem {kNullEntity};


public:
  std::string linkName;


public:
  Entity joint {kNullEntity};


public:
  Entity gripperEntity {kNullEntity};


public:
  transport::Node node;


public:
  bool suctionOn {false};


public:
  bool pendingJointCreation {false};


public:
  bool jointCreated {false};


public:
  std::mutex mtx;


public:
  std::array<std::array<Entity, 3>, 3> contacts;


public:
  transport::Node::Publisher contactPublisherCenter;

public:
  transport::Node::Publisher contactPublisherLeft;

public:
  transport::Node::Publisher contactPublisherRight;

public:
  transport::Node::Publisher contactPublisherTop;

public:
  transport::Node::Publisher contactPublisherBottom;


public:
  void OnContact(
    int idx0, int idx1,
    const gz::msgs::Contacts & _msg)
  {
    std::lock_guard<std::mutex> lock(this->mtx);

    if (_msg.contact_size()) {
      auto contact = _msg.contact(0);
      this->contacts[idx0][idx1] = contact.collision2().id();
    } else {
      this->contacts[idx0][idx1] = kNullEntity;
    }
  }


public:
  void OnCmd(const gz::msgs::Boolean & _suctionOn)
  {
    std::lock_guard<std::mutex> lock(this->mtx);
    this->suctionOn = _suctionOn.data();
  }
};


SuctionGripperPlugin::SuctionGripperPlugin()
: dataPtr(new SuctionGripperPrivate)
{
  for (size_t ii = 0; ii < 3; ++ii) {
    for (size_t jj = 0; jj < 3; ++jj) {
      this->dataPtr->contacts[ii][jj] = kNullEntity;
    }
  }
}

SuctionGripperPlugin::~SuctionGripperPlugin()
{
}

void SuctionGripperPlugin::Configure(
  const Entity & _entity,
  const std::shared_ptr<const sdf::Element> & _sdf,
  EntityComponentManager & _ecm,
  EventManager & /*_eventMgr*/)
{
  if (_sdf->HasElement("parent_link")) {
    this->dataPtr->linkName = _sdf->Get<std::string>("parent_link");
  } else {
    ignerr << "Please specify a link name" << std::endl;
    return;
  }

  Model model(_entity);
  this->dataPtr->gripperEntity = model.LinkByName(_ecm, this->dataPtr->linkName);
  if (this->dataPtr->gripperEntity == kNullEntity) {
    ignerr << "Could not find link named "
           << this->dataPtr->linkName << std::endl;
    return;
  }

  if (_sdf->HasElement("contact_sensor_topic_prefix")) {
    auto prefix = _sdf->Get<std::string>("contact_sensor_topic_prefix");

    std::function<void(const gz::msgs::Contacts &)> callback_01 =
      std::bind(
      &SuctionGripperPrivate::OnContact, this->dataPtr.get(), 0, 1,
      std::placeholders::_1);
    this->dataPtr->node.Subscribe(prefix + "/contact_sensor_01", callback_01);

    std::function<void(const gz::msgs::Contacts &)> callback_11 =
      std::bind(
      &SuctionGripperPrivate::OnContact, this->dataPtr.get(), 1, 1,
      std::placeholders::_1);
    this->dataPtr->node.Subscribe(prefix + "/contact_sensor_11", callback_11);

    std::function<void(const gz::msgs::Contacts &)> callback_21 =
      std::bind(
      &SuctionGripperPrivate::OnContact, this->dataPtr.get(), 2, 1,
      std::placeholders::_1);
    this->dataPtr->node.Subscribe(prefix + "/contact_sensor_21", callback_21);

    std::function<void(const gz::msgs::Contacts &)> callback_10 =
      std::bind(
      &SuctionGripperPrivate::OnContact, this->dataPtr.get(), 1, 0,
      std::placeholders::_1);
    this->dataPtr->node.Subscribe(prefix + "/contact_sensor_10", callback_10);

    std::function<void(const gz::msgs::Contacts &)> callback_12 =
      std::bind(
      &SuctionGripperPrivate::OnContact, this->dataPtr.get(), 1, 2,
      std::placeholders::_1);
    this->dataPtr->node.Subscribe(prefix + "/contact_sensor_12", callback_12);


    this->dataPtr->contactPublisherCenter =
      this->dataPtr->node.Advertise<msgs::Boolean>(prefix + "/contacts/center");
    this->dataPtr->contactPublisherLeft =
      this->dataPtr->node.Advertise<msgs::Boolean>(prefix + "/contacts/left");
    this->dataPtr->contactPublisherRight =
      this->dataPtr->node.Advertise<msgs::Boolean>(prefix + "/contacts/right");
    this->dataPtr->contactPublisherTop =
      this->dataPtr->node.Advertise<msgs::Boolean>(prefix + "/contacts/top");
    this->dataPtr->contactPublisherBottom =
      this->dataPtr->node.Advertise<msgs::Boolean>(prefix + "/contacts/bottom");
  } else {
    ignerr << "Please specify a contact_sensor_topic_prefix" << std::endl;
    return;
  }

  if (_sdf->HasElement("command_topic")) {
    auto topic = _sdf->Get<std::string>("command_topic");
    this->dataPtr->node.Subscribe(
      topic,
      &SuctionGripperPrivate::OnCmd,
      this->dataPtr.get());
  } else {
    ignerr << "Please specify a command_topic" << std::endl;
    return;
  }
}


void SuctionGripperPlugin::PreUpdate(
  const UpdateInfo & _info,
  EntityComponentManager & _ecm)
{
  if (_info.paused) {return;}
  std::lock_guard<std::mutex> lock(this->dataPtr->mtx);

  msgs::Boolean contact;

  // If the gripper is engaged and holding an object, return contacts as true
  if (this->dataPtr->jointCreated) {
    contact.set_data(true);
    this->dataPtr->contactPublisherCenter.Publish(contact);
    this->dataPtr->contactPublisherLeft.Publish(contact);
    this->dataPtr->contactPublisherRight.Publish(contact);
    this->dataPtr->contactPublisherTop.Publish(contact);
    this->dataPtr->contactPublisherBottom.Publish(contact);
  } else {
    contact.set_data(this->dataPtr->contacts[1][1] != kNullEntity);
    this->dataPtr->contactPublisherCenter.Publish(contact);

    contact.set_data(this->dataPtr->contacts[1][0] != kNullEntity);
    this->dataPtr->contactPublisherLeft.Publish(contact);

    contact.set_data(this->dataPtr->contacts[1][2] != kNullEntity);
    this->dataPtr->contactPublisherRight.Publish(contact);

    contact.set_data(this->dataPtr->contacts[0][1] != kNullEntity);
    this->dataPtr->contactPublisherTop.Publish(contact);

    contact.set_data(this->dataPtr->contacts[2][1] != kNullEntity);
    this->dataPtr->contactPublisherBottom.Publish(contact);
  }


  if (!this->dataPtr->jointCreated && this->dataPtr->suctionOn) {
    // check that two sensors are making contact with the same object
    auto checkContacts =
      [&](std::pair<int, int> idx0, std::pair<int, int> idx1)->bool {
        auto contact0 = this->dataPtr->contacts[idx0.first][idx0.second];
        auto contact1 = this->dataPtr->contacts[idx1.first][idx1.second];
        return
          contact0 != kNullEntity &&
          contact1 != kNullEntity &&
          contact0 == contact1;
      };


    bool contactMade =
      (checkContacts({1, 1}, {1, 0}) ||                   // Center + left
      checkContacts({1, 1}, {1, 2}) ||                    // Center + right
      checkContacts({1, 1}, {0, 1}) ||                    // Center + top
      checkContacts({1, 1}, {2, 1}));                    // Center + bottom

    if (contactMade) {
      this->dataPtr->pendingJointCreation = true;
      this->dataPtr->childItem = this->dataPtr->contacts[1][1];
    } else if (checkContacts({1, 0}, {1, 2})) {         // left + right
      this->dataPtr->pendingJointCreation = true;
      this->dataPtr->childItem = this->dataPtr->contacts[1][0];
    } else if (checkContacts({0, 1}, {2, 1})) {         // top + bottom
      this->dataPtr->pendingJointCreation = true;
      this->dataPtr->childItem = this->dataPtr->contacts[0][1];
    }
  }

  // Clear contacts
  for (size_t ii = 0; ii < 3; ++ii) {
    for (size_t jj = 0; jj < 3; ++jj) {
      this->dataPtr->contacts[ii][jj] = kNullEntity;
    }
  }

  if (this->dataPtr->pendingJointCreation) {
    // If we need to create a new joint
    this->dataPtr->pendingJointCreation = false;
    this->dataPtr->joint = _ecm.CreateEntity();
    auto parentLink = _ecm.ParentEntity(this->dataPtr->childItem);
    _ecm.CreateComponent(
      this->dataPtr->joint,
      components::DetachableJoint(
        {this->dataPtr->gripperEntity,
          parentLink, "fixed"}));
    igndbg << "Created joint between gripper and "
           << this->dataPtr->childItem
           << std::endl << "at time step " << _info.simTime.count() << std::endl;
    this->dataPtr->jointCreated = true;
  }

  if (!this->dataPtr->suctionOn && this->dataPtr->jointCreated) {
    // If we have an item and were commanded to release it
    _ecm.RequestRemoveEntity(this->dataPtr->joint);
    this->dataPtr->joint = kNullEntity;
    this->dataPtr->jointCreated = false;
    igndbg << "Remove joint between gripper and "
           << this->dataPtr->childItem
           << std::endl << "at time step " << _info.simTime.count() << std::endl;
  }
}


IGNITION_ADD_PLUGIN(
  mbzirc::SuctionGripperPlugin,
  ignition::gazebo::System,
  mbzirc::SuctionGripperPlugin::ISystemConfigure,
  mbzirc::SuctionGripperPlugin::ISystemPreUpdate)