ContainPlugin


Contain Plugin Introduction

ContainPlugin is a world plugin included with gazebo that publishes a message when something enters or exits a volume. It can be used to trigger an action. For example, it may be used to raise an error in a test when a robot enters a keepout zone.

Using in a world

This plugin is a world plugin, so it must be used as a child of <world>. The filename attribute must be set to libContainPlugin.so.

<world name="default">
  <plugin name="MyContainPluginInstance" filename="libContainPlugin.so">
    <!-- plugin parameters go here ... -->

Topics in Gazebo 9+

This plugin uses two ignition transport topics: contain and enable. The ignition transport topics use a message of type ignition.msgs.Boolean.

Plugin parameters

<enabled>

If true the plugin will output data right away, otherwise it needs to be enabled explicitly. If unspecified this parameter defaults to true. Send a true value to the topic called <namespace>/enable to enable the plugin, and false to disable it. While disabled ContainPlugin will not publish any messages.

example: <enabled>false</enabled>

<entity>

This is a scoped name of a link, model, nested model, or light in the world. Every simulation update ContainPlugin checks if the origin of this entity is inside the specified geometry.

example: <entity>robot::hand_link</entity>

<namespace>

This is the prefix given to the topics used by ContainPlugin: contain, and enable.

example: <namespace>/foo/bar</namespace> would result in topics /foo/bar/contain and /foo/bar/enable.

<pose>

This is the location of the center of the specified geometry. If the frame attribute is not specified then the pose is in world frame. If the frame attribute is set to a scoped name, then the pose is relative to the origin of that entity.

example: <pose>1 2 3 0 0 0</pose> in world frame

example: <pose frame="tree::limb">0.2 0 0.4 1.570769 0 0</pose> relative to an entity

<geometry>

This is the geometry which is checked to see if it contains <entity>. The only geometry currently supported by ContainPlugin is <box>. See the sdformat specification for box geometry for more info.

Examples

Using ContainPlugin to Trigger an Action

This example shows how to use ContainPlugin to trigger an action. When a ball rolls under a lamp post, the light will turn on.

Download the example world, plugin source code, and CMake file. Build and run the plugin using this tutorial as an example. Make sure to run source <install_path>/share/gazebo/setup.sh before extending GAZEBO_PLUGIN_PATH. <install_path> is /usr if you installed using apt.

A ContainPlugin instance is configured to watch when a sphere enters a box below the lamp post. When the sphere rolls into or out of the box ContainPlugin will publish a message on the contain topic.

<plugin name='ContainPlugin' filename='libContainPlugin.so'>
      <enabled>true</enabled>
      <entity>unit_sphere::only_link</entity>
      <namespace>contain_example</namespace>
      <pose>0 -3 0.5 0 0 0</pose>
      <geometry>
        <box>
          <size>4 4 1</size>
        </box>
      </geometry>
    </plugin>
    <!-- Launch example plugin -->
    <plugin name='AutomaticLight' filename='libTurnOnLightPlugin.so'/>
</world>
</sdf>

This custom plugin receives messages from ContainPlugin, and turns the light on or off.

#include <ignition/msgs.hh>
#include <ignition/transport/Node.hh>

#include "gazebo/common/Plugin.hh"
#include "gazebo/msgs/msgs.hh"
#include "gazebo/physics/World.hh"
#include "gazebo/transport/Node.hh"

namespace gazebo
{
  class TurnOnLightPlugin : public WorldPlugin
  {
    public: void Load(physics::WorldPtr _world, sdf::ElementPtr _sdf) override
    {
      gzmsg << "Loading Example plugin\n";
      // Transport initialization
      this->gzNode = transport::NodePtr(new transport::Node());
      this->gzNode->Init();

      // Subscribe to ContainPlugin output
      std::string topic("contain_example/contain");
      std::function<void(const ignition::msgs::Boolean &)> cb =
          [=](const ignition::msgs::Boolean &_msg){
        TurnOnLightPlugin::OnContainPluginMsg(_msg);
      };
      const bool containSub = this->node.Subscribe(topic, cb);
      if (!containSub)
      {
        gzerr << "Failed to subscribe to [" << topic << "]\n";
      }

      // Make a publisher for the light topic
      this->lightPub = this->gzNode->Advertise<msgs::Light>("~/light/modify");
    }

    public: void OnContainPluginMsg(const ignition::msgs::Boolean &_msg)
    {
      msgs::Light lightMsg;
      lightMsg.set_name("post_light");
      // Turn light on when the entity enters the box, and off when it leaves
      if (_msg.data())
      {
        gzmsg << "Turning on light\n";
        lightMsg.set_range(15.0);
      }
      else
      {
        gzmsg << "Turning off light\n";
        lightMsg.set_range(0.0);
      }
      this->lightPub->Publish(lightMsg);
    }

    private: ignition::transport::Node node;
    private: transport::NodePtr gzNode;
    private: transport::PublisherPtr lightPub;
  };
  GZ_REGISTER_WORLD_PLUGIN(TurnOnLightPlugin);
}  // namespace gazebo

After building and running the world you should see the ball trigger the light to turn on. Hit CTRL + R to restart the world if you missed it.

ContainPlugin tracking a Moving Volume

The <pose> tag on this plugin can be given relative to another entity. This allows the volume to move with the entity as if it were connected by a fixed joint. Gazebo includes a world demonstrating this.

Start gazebo in paused mode with the moving geometry world.

gazebo --pause --verbose worlds/contain_plugin_moving_demo.world

In another terminal echo the output of the contain topic,

ign topic --echo /gazebo/default/drill/contain

Initially the plugin reports a false value becaues the drill is not inside it. As the volume falls the plugin reports a true value when the drill enters the volume, and a false value later when that is no longer the case.