errolflynn
errolflynn

Reputation: 641

Why won't Gazebo start ROS?

I am working through the Gazebo tutorial to connect a Gazebo sensor to ROS and pass messages. http://gazebosim.org/tutorials?cat=guided_i&tut=guided_i6

This program builds a Gazebo ModelPlugin object, and initializes ROS from within this object. Then, it creates a ROS node, subscriber, queue, and a standard thread to run the ROS queue. This program works using Gazebo's transport objects, but when I try to add in ROS transport objects (as noted previously), the program does not work. My issue stems from the fact that the roscore nodes (including rosmaster) do not initialize.

My code for the sensor plugin is below. ROS integration begins on line 70:

#ifndef _VELODYNE_PLUGIN_HH_
#define _VELODYNE_PLUGIN_HH_

#include <gazebo/gazebo.hh>
#include <gazebo/msgs/msgs.hh>
#include <gazebo/physics/physics.hh>
#include <gazebo/transport/transport.hh>
#include <ros/ros.h>
#include <ros/callback_queue.h>
#include <ros/subscribe_options.h>
#include <thread>
#include <std_msgs/Float32.h>

namespace gazebo
{
  /// \brief A plugin to control a Velodyne sensor.
  class VelodynePlugin : public ModelPlugin
  {
    /// \brief Constructor
    public: VelodynePlugin() {}

    /// \brief The load function is called by Gazebo when the plugin is
    /// inserted into simulation
    /// \param[in] _model A pointer to the model that this plugin is
    /// attached to.
    /// \param[in] _sdf A pointer to the plugin's SDF element.
    public: virtual void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf) {
            gzwarn << "HERE";
      if (_model->GetJointCount() == 0) {
        std::cerr <<
          "Invalid joint count, Velodyne plugin not loaded\n";
      }

      // Store the model pointer for convenience.
      this->model = _model;

      // Get the first joint. We are making an assumption about the
      // model having one joint that is the rotational joint.
      this->joint = _model->GetJoints()[0];

      // Setup a P-controller with a gain of 0.1.
      this->pid=common::PID(0.1,0,0);

      // Apply the P-controller to the joint.
      this->model->GetJointController()->SetVelocityPID(
          this->joint->GetScopedName(), this->pid);

      // Default to zero velocity
      double velocity=0;

      // Check that the velocity element exists, then read the value
      if (_sdf->HasElement("velocity"))
        velocity=_sdf->Get<double>("velocity");

      this->SetVelocity(velocity);

      // Create the node
      this->node = transport::NodePtr(new transport::Node());
      this->node->Init(this->model->GetWorld()->GetName());

      // Create a topic name
      std::string topicName = "~/" + this->model->GetName() +
        "/vel_cmd";

      // Subscribe to the topic, and register a callback.
      this->sub = this->node->Subscribe(topicName,
          &VelodynePlugin::OnMsg, this);

            // Initialize ros, if it has not already been initialized.
            if (!ros::isInitialized()) {
                std::cout << "initializing ros" << std::endl;
                int argc = 0;
                char **argv=NULL;
                ros::init(argc,argv,"gazebo_client",
                    ros::init_options::NoSigintHandler);
            } else { std::cout << "NOT initializing ros" << std::endl; }

            // Create our ROS node. This acts in a similar manner to the
            // Gazebo node.
            this->rosNode.reset(new ros::NodeHandle("gazebo_client"));

      // Create a named topic, and subscribe to it.
      ros::SubscribeOptions so =
        ros::SubscribeOptions::create<std_msgs::Float32>(
            "/"+this->model->GetName()+"/vel_cmd",
            1,
            boost::bind(&VelodynePlugin::OnRosMsg, this, _1),
            ros::VoidPtr(), &this->rosQueue);

      this->rosSub = this->rosNode->subscribe(so);

      // Spin up the queue helper thread
      this->rosQueueThread = 
        std::thread(std::bind(&VelodynePlugin::QueueThread,this));
    }

    /// \brief Set the velocity of the Velodyne
    /// \param[in] _vel New target velocity
    public: void SetVelocity(const double &_vel) {
      // Set the joint's target velocity.
      this->model->GetJointController()->SetVelocityTarget(
          this->joint->GetScopedName(), _vel);
    }

    /// \brief Handle incoming message
    /// \param[in] _msg Repurpose a vector3 message. This function will
    /// only use the x component.
    private: void OnMsg(ConstVector3dPtr &_msg) {
      this->SetVelocity(_msg->x());
    }

    /// \brief Handle an incoming message from ROS
    /// \param[in] _msg A float value that is used to set the velocity
    /// of the Velodyne.
    public: void OnRosMsg(const std_msgs::Float32ConstPtr &_msg) {
      this->SetVelocity(_msg->data);
    }

    /// \brief ROS helper function that processes messages
    private: void QueueThread() {
      static const double timeout = .01;
      while (this->rosNode->ok()) {
        this->rosQueue.callAvailable(ros::WallDuration(timeout));
      }
    }


    /// \brief Pointer to the model;
    private: physics::ModelPtr model;

    /// \brief Control surfaces joints.
    private: physics::JointPtr joint;

    /// \brief Velocity PID for the propeller.
    private: common::PID pid;

    /// \brief A node used for transport
    private: transport::NodePtr node;

    /// \brief A subscriber to a named topic.
    private: transport::SubscriberPtr sub;

    /// \brief A node used for ROS transport
    private: std::unique_ptr<ros::NodeHandle> rosNode;

    /// \brief A ROS subscriber
    private: ros::Subscriber rosSub;

    /// \brief A ROS callbackqueue that helps process messages
    private: ros::CallbackQueue rosQueue;

    /// \brief A thread that keeps running the rosQueue
    private: std::thread rosQueueThread;

  };

  // Tell Gazebo about this plugin, so that Gazebo can call Load on this plugin.
  GZ_REGISTER_MODEL_PLUGIN(VelodynePlugin)
}
#endif

Let me know how I can be more specific about this question. The first problem is definitely that ROS is not initialized. I can tell this because a call in a different terminal to rostopic list outputs:

ERROR: Unable to communicate with master

Upvotes: 1

Views: 2386

Answers (4)

shimin pan
shimin pan

Reputation: 1

To my understanding, Gazebo is not part of ROS, so it makes sense if it doesn't start roscore.

If you want to start roscore simultaneously as this node starts, try to use roslaunch command instead of rosrun.

Upvotes: 0

tahsin kose
tahsin kose

Reputation: 1

The problem is solved via running roscore beforehand you start Gazebo server. I must accept that the tutorial does, indeed, follows a Gazebo-first pipeline. Nonetheless, I believe it's a deprecated method to make the bridge between ROS and Gazebo. You should inspect the code in gazebo-ros-pkgs for a much easier method.

A Gazebo plugin is a run-time component -shared library in technical terms- that is attached to a specific object or directly to the World instance via SDF/URDF files. A world instance can be simulated through the command gzserver <world_file>. If you are using Gazebo in conjunction with ROS (I highly recommend that if you are going to use ROS at some point), what you need is gazebo-ros node. Again in the above link, there are several examples of how it is done.

In ROS universe, all the processes are represented as ROS nodes and Gazebo simulator is not different. When you use gazebo_ros package (which makes the ROS-Gazebo connection in one-line) Gazebo simulator is initialized as /gazebo node with the specified world file and all the attached Gazebo plugins do operate as components within this node. The first issue resides in here. You should not call ros::init() in a Gazebo plugin since it has already an initialized node. Second problem is in your assumption, such that ros::init() call does not start the roscore node. It only initializes your executable in a way that it can communicate with other ROS nodes under the same master. So, what you need is only a ros::NodeHandle.

I don't know why OSRF hasn't updated the tutorials. Perhaps, they are strictly trying to offer a Gazebo-first aspect in Gazebo website and treat ROS as a secondary component. However I find it rather problematic, especially when one wants to use ROS as primary component. Since roslaunch only launches ROS nodes, which makes gzserver <world_file> command disfunctional, this tutorial would be invalid and even contradicting with the mainstream usage.

Upvotes: 0

Mohammad Ali
Mohammad Ali

Reputation: 574

The problem is indeed roscore but after that you can check the node ur started with rosnode info nodename to check the sub and publisher status or do roswtf to check for error in general.

You can check realtime debug log setting ros debug level with rqt logger level(easiest way) to know if your node is getting data or not

Upvotes: -1

luator
luator

Reputation: 5017

It sounds to me like roscore is not running. You have to run roscore manually before you can start any ROS node.

You can imagine roscore as a server to which all nodes connect and that manages the communication between these nodes. It won't start automatically, so you always have to start roscore as a first step, before you can use any ROS nodes.

An exception to this is, if you are using launch-files. roslaunch will indeed automatically start roscore if it is not already running.

Upvotes: 3

Related Questions