How publish\subscribe an array topic in c++(such as ros::publish pub[i])

asked 2020-06-06 09:20:31 -0500

Heho gravatar image

updated 2020-06-07 09:15:10 -0500

Hi everyone, I am a new person to C++. Recently, I have notice another way to publish/subscribe a series of topics. Like:

std_msgs::Float64 msg[5];
ros::Publisher pub[5];
ros::Subscriber pub[5];
string name[5] = {"0", "1", "2", "3","4"}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "topic");
  ros::NodeHandle nh;
  for(int i=0; i<5 ;i++)
  {
    pub_cmd[i]=_n.advertise<std_msgs::Float64>(name[i], 1);
    sub_js[i]=_n.subscribe(name[i], 1, callback);
  }
.....
.....

This seems to make the code more simple.ROS WIKI don't seems to mention this method.It look quiet different from the example. How does this work?

In my reference learning routine, whatever how many pub has been subscribe, there is only one callback funtion for subscriber.

5 subscriber

for(int i=0; i<4; i++)
{
std::string joint_state_name="arm_joint_";
std::string joint_num=boost::lexical_cast<std::string>(i+1);
joint_state_name.append(joint_num);
joint_state_name.append("_controller/state");
sub_js[i]=_n.subscribe(joint_state_name, 1, &ArmHWInterface::read, this);
}

but 1 callback funtion

void ArmHWInterface::read(const dynamixel_msgs::JointStateConstPtr &msg)
{
   ......
}

Thank you!

edit retag flag offensive close merge delete

Comments

Hi @Heho,

I was not able to understand you in this part: "If I want to assign another_msg[5] from msg[5] which was subscribe from the talker(value should obey the array index), how do I edit the callback funtion?"

What are you trying to achieve, 5 publisher that publish each msg position value and 5 subscriber with different calback processing functions?

Weasfas gravatar image Weasfas  ( 2020-06-07 08:26:13 -0500 )edit

@Weasfas I want to subscribe 5 topic with only one callback funtion(like the example I just update in question).And I don't know how to distinguish whether sub_js[1] or sub_js[3] triger the callback funtion. And I try to write such like for(xxx;xxx;xxx)pos[i]=msg->data; in callback to get the msg value but fail to compile.

Heho gravatar image Heho  ( 2020-06-07 09:23:02 -0500 )edit

So, if you want to process 5 topic inputs with one callback you can just declare 5 subscriber with the same callback, however, since you want to distinguish between callback something tells me that you want to process data in different way thus you will need more than one callback signature. From this forum: "The usual ROS practice is to distinguish topics, but decouple subscribers from worrying about which node published each message". Hence, there is no way to differenciate the incoming msgs unless your msg data type contains a univocal key that tells you its origin (this can be done generating a custom msg or wraping a standard msg type with you custom source flag).

On the other hand, have you considered the message_filter approach?

Weasfas gravatar image Weasfas  ( 2020-06-07 09:46:49 -0500 )edit

While I agree it's not wise to implement nodes which depend on knowledge about the publishers of a message, it is actually technically possible to figure out which node published a message without the need for custom messages.

See wiki/roscpp/Overview/Publishers and Subscribers - MessageEvent.

gvdhoorn gravatar image gvdhoorn  ( 2020-06-07 09:58:34 -0500 )edit

Hi @Weasfas .I have succeed in the way to use msg data type contains a univocal key. But fail with the "message_filter" approach. It seems can not filter by only one type of message? My code doesn't even compile.

message_filters::Subscriber<geometry_msgs::Vector3> sub0(nh, name[0], 1);   //Plan B to subscribe
message_filters::Subscriber<geometry_msgs::Vector3> sub1(nh, name[1], 1);   //Plan B to subscribe
message_filters::Subscriber<geometry_msgs::Vector3> sub2(nh, name[2], 1);   //Plan B to subscribe
TimeSynchronizer<geometry_msgs::Vector3> sync(sub0, sub1, sub2, 10);
sync.registerCallback(boost::bind(&subcallback, _1, _2, _3));

withvoid subcallback(const geometry_msgs::Vector3::ConstPtr& msg0, const geometry_msgs::Vector3::ConstPtr& msg1, const geometry_msgs::Vector3::ConstPtr& msg2)

Heho gravatar image Heho  ( 2020-06-08 10:42:03 -0500 )edit

Hi @Heho, Of course you can use a messsage_filter with multiple topics with the same msg type. The main problem of the code you provided is that you have to declare the TimeSynchronizer to accept every incoming msg. That is, if you have three different topics with the same message type:

TimeSynchronizer<geometry_msgs::Vector3, geometry_msgs::Vector3, geometry_msgs::Vector3> sync(sub0, sub1, sub2, 10);

In addition, take into account that the message_filter only works if the message definition contains a stamp. That is because the message_filter implementation synchronizes incoming channels by the timestamps contained in their headers. So appart from changing the TimeSynchronizer declaration you will need to add a stamp to those msgs.

Weasfas gravatar image Weasfas  ( 2020-06-08 12:07:57 -0500 )edit

@Weasfas But how could I add a stamp to the msgs? I have seen there is no a type named "stamp" include the Message Definition of geometry_msgs::Vector3. Should I change the publish msgs type into geometry_msgs/Vector3Stamped ? I thought maybe stamp can be set up in .header? However, I even don't understand what does the header do in msg........Orz

By the way , as @gvhoorn mention about MessageEvent.If all topics are publish from a same publisher, the method .getPublisherName() may fail to distingish the msgs. So, it's the same idea to use .getConnectionHeader() to distingish the msgs? Could you mind giving me a simple example about this?Thanks!

Heho gravatar image Heho  ( 2020-06-09 09:26:01 -0500 )edit

@Heho, yes, when I mentioned stamp I was refering to the std_msgs/Header. And yes, in my case when I needed a stamped message I just wrap the standard message definition to add a std_msgs/Header type, like you said geometry_msgs/Vector3Stamped, that is the geometry_msgs/Vector3 definition but with a header. As for the second question I am sure @gvdhoorn knows more about the topic but I think he was thinkin about different nodes; there is a possibility to use the ConnectionHeader and decode the bytes, but since you are using the same topic and the same node to publish I think there is no way you can distinguish between callbacks.

Weasfas gravatar image Weasfas  ( 2020-06-09 16:59:37 -0500 )edit