ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
8

ros::init() must be called before creating a node handle

asked 2012-04-24 13:33:43 -0600

gavinmachine gravatar image

updated 2012-04-24 14:37:06 -0600

Hello,

I have defined a library (i.e. another ROS package) that node "X" depends upon. In the header file of the library I have a class that declares a node handle. When "X" is being preprocessed, the library's header file is pulled into "X".

When I run the system (via roslaunch), I immediately get this: "ros::init() must be called before creating a node handle" and the node crashes b/c it finds the node handle before it finds main(), which is where ros::init is called.

The question: How do I allow a node handle to exist in the library header file (so the library class can use it) without causing such an error?

Here is an example:

Lib.h:

class Z
{
     public: ...
     private: ros::NodeHandle nh;
};

Node "X" source (.cpp):

#include <Lib/Lib.h>
...
main()
{
     Z z;
     ros::init(...);
}

When I launch "X", which depends upon Lib (and by extension "Z"), the node handle is found before ros::init() is executed, even though the node handle is not used until afterwards...

Again, the runtime error is: "ros::init() must be called before creating a node handle"

Thanks a lot!

edit retag flag offensive close merge delete

5 Answers

Sort by ยป oldest newest most voted
10

answered 2012-04-24 14:47:49 -0600

Patrick Mihelich gravatar image

There is nothing wrong with your example as described; in fact it's a typical usage. The NodeHandle is not created until you create the instance of Z, which happens after the call to ros::init(), so there should be no problem.

Most likely you are declaring a global instance of Z (or some other class containing a NodeHandle) either in your main.cpp or in the library. Since globals are created before main(), ros::init() has not yet been called and you get the error.

Get a stack trace from the failed assertion to see where the NodeHandle is being created.

edit flag offensive delete link more

Comments

1

That makes sense. Would is work to create a node handle/publisher in a member function of Z, and then call the member function to publish something? The reason I'm not just creating Z after init() is because I need Z to be global so that anything (classes included) in node "X" can access it...

gavinmachine gravatar image gavinmachine  ( 2012-04-24 15:31:54 -0600 )edit

I have the same problem you mentioned ^

Could you tell me how you fixed it?

2ROS0 gravatar image 2ROS0  ( 2014-11-25 14:13:53 -0600 )edit
5

answered 2012-04-24 16:53:08 -0600

joq gravatar image

If you want to control when the constructor for Z runs, you can make it a boost::shared_ptr (even a global one) and then wait until after the ros::init() before allocating it.

boost::shared_ptr<Z> z;
main()
{
  ros::init(...);
  z.reset(new Z());
  ...
}
edit flag offensive delete link more

Comments

Using this approaches seems to forbid me from using member functions of Z... for example, using the above syntax, and then calling z.foo() from within a class in node "X" gives the error that "boost::shared_ptr<Z> does not contain function foo"

gavinmachine gravatar image gavinmachine  ( 2012-04-24 19:48:49 -0600 )edit
1

It's now a pointer, so the call looks like z->foo().

joq gravatar image joq  ( 2012-04-24 21:52:01 -0600 )edit
1

answered 2014-02-24 00:14:38 -0600

Kamiccolo gravatar image

updated 2014-02-24 01:59:43 -0600

Personally, using kind a fixed Major's version:

class ROSCommonNode
{
protected:
    ROSCommonNode(int argc, char **argv, const char *node_name)
    {
        ros::init(argc, argv, node_name);
    }
};

class ROSCommonWorker : ROSCommonNode
{
public:
    ROSCommonWorker(int argc, char **argv, const char *node_name) : ROSBaseNode(argc, argv, node_name)
    {
        //register some common publishers/subscribers or deal with global param service, whatever
    }

private:
    ros::NodeHandle node_handle;
};

Motivation: couple of duplicated lines of code saved, also centralized way of changing how all of my common nodes/services/workers/etc. works with logging/dealing with parameters/etc.

edit flag offensive delete link more

Comments

This seems like a really complicated way to wrap calling ros::init as the first line in main(). Is the Base/Common node a singleton?

dornhege gravatar image dornhege  ( 2014-02-24 01:33:56 -0600 )edit

In the context of single ROS node - it's a singleton. Not sure if You were asking that and I understood the question right. The main idea is - why the hell I need to initiate each Node by hand in `main()` if I can wrap it like that and forget? EDIT: fixed a few typos in the code.

Kamiccolo gravatar image Kamiccolo  ( 2014-02-24 02:03:58 -0600 )edit

class ROSCommonNode should have a constructer named ROSCommonNode() instead of ROSBaseNode() but it i like this solution to inherate from this basenode.

major gravatar image major  ( 2014-02-24 02:06:49 -0600 )edit

I think dornehege question about the singleton is becaus of using more nodes so everytime ros::init is called. maybe a static variable (static int is_inited = 0;) or a static mutex could solve this and an IF -Statement in constructer could solve this to get a singleton behavior ...

major gravatar image major  ( 2014-02-24 02:11:02 -0600 )edit

You can't also forget about this one as you still need to initialize your RosCommonWorker node.

dornhege gravatar image dornhege  ( 2014-02-24 03:24:21 -0600 )edit
0

answered 2021-03-04 13:00:46 -0600

RDaneelOlivaw gravatar image

In my case it was because I was trying to launch a ROS2 launch in a terminal session that has previously ROS1 sourcing. To solve it be sure to ONLY source ROS related stuff: source /opt/ros/foxy/setup.bash, and colcon build from scratch your workspace just in case.

edit flag offensive delete link more
0

answered 2014-02-22 04:40:01 -0600

major gravatar image

updated 2014-02-24 01:58:43 -0600

I have done it in this way

class Rosinit
{
    static int is_inited = 0;
public:
    Rosinit(int argc, char **argv, const char *node_name)
    {
        if(!is_inited){

        ros::init(argc,argv,node_name);
        is_inited=1;
        }
    }
};


class Rosnode_parent
{

    Rosinit init;
public:
    ros::NodeHandle n;

    Rosnode_parent(int argc, char **argv, const char *node_name):init(argc, argv, node_name)
    {
       //...
    };
};

the class Rosnode_parent has a memer Rosinit init, in the constructer this member is initializied with with an argumentlist,this is done bevor a NodeHandle Construkter is called.

in class Rosinit the static member "static int is_inited" enusres that there is only on call of ros::init

I use Rosnode_parent as a super calls and inherite , important is that in the childclass in there own constructer the super constructer is called

edit flag offensive delete link more

Comments

This is a bad idea because you're not passing the program arguments to ros::init(). This means that you will not be able to use any of the command-line remapping arguments with your node.

ahendrix gravatar image ahendrix  ( 2014-02-22 08:34:02 -0600 )edit
1

The canonical way to do this is to simply call ros::init() once at the very beginning of your main() function.

ahendrix gravatar image ahendrix  ( 2014-02-22 08:34:27 -0600 )edit
1

@ahendrix ok I edit the post and now the argc.argv are past. In previous it was just a scheme and yes your are right normally it have to be in the main, but if you use a library you didn't have any main and if you want it global like in the question the ros::init would do nothing because the global node is created befor the call

major gravatar image major  ( 2014-02-24 02:02:01 -0600 )edit
2

You should not call ros::init from a library. That should have been done in a node. The library can't and should not know or set a node name.

dornhege gravatar image dornhege  ( 2014-02-24 03:25:07 -0600 )edit

Question Tools

Stats

Asked: 2012-04-24 13:33:43 -0600

Seen: 30,762 times

Last updated: Mar 04 '21