0
$\begingroup$

Rosanswers logo

Hello, I'm currently thinking about throwing an exception directly in a callback method if the message received is an error message (faulty sensor), which has been sent by the corresponding node in charge of reading that sensor.

However, I need to make sure to understand how it exactly works before implementing this idea. Let's say that there are 5 different sensors each performing an initialization phase for 1 second to make sure they are behaving adequately. If it's the case, each node reading its corresponding sensor sends a message with the distance measured by the sensor after this initialization phase. If the sensor is faulty, the node sends a minus value instead (let say -1).

Then, the main node in charge of gathering all the sensors' data first wait for the initialization time + a small additional time to make sure all the messages have been sent on the corresponding topics and call afterwards all the callback with ros::spinOnce(). Let's pretend here for sake of simpliticy that the messages were received by the main node in a ascending order (if the sensors are enumerated from 1 to 5, the messages were received in that exact order). If the second sensor is faulty (hence the node did send a -1 value) and the callback determines its faultiness and throws an exception, what would happen to the other callbacks waiting to process the other messages in the queue ? Would they still be evaluated or would I have to call another time the ros::spinOnce() function in order to do so ?

Thanks in advance for your help.


Originally posted by ta on ROS Answers with karma: 15 on 2017-11-02

Post score: 1

$\endgroup$

1 Answer 1

0
$\begingroup$

Rosanswers logo

Throwing exception in a subscribing node for a bad sensor initialization in a different node is an unusual approach, I'm curious if anyone can point to a major ros node that does that. http://wiki.ros.org/roscpp/Overview/Exceptions says "roscpp throws exceptions only in truly unexpected, programmer error conditions.".

But it is an interesting question so I set up an experiment and noticed some oddities with this- it looks like you can set up a try catch around ros::spin(), and then restart the spin after catching something.

I'm using timer callbacks rather than subscriber callbacks, perhaps they behave completely differently with exceptions.

#include <ros/ros.h>

ros::Time start;
int count = 0;

void printTimeDiff(const int i, const ros::TimerEvent& e)
{
  ROS_INFO_STREAM("Callback " << i << " triggered "
      << ros::Time::now() - start << " "
      << (e.current_expected - start).toSec() << " "
      << (e.current_real - start).toSec());
}

void callback1(const ros::TimerEvent& e)
{
  count++;
  printTimeDiff(1, e);
  if (count == 4)
  { 
    ROS_WARN_STREAM("throwing exception");
    throw 10;
  }
}

void callback2(const ros::TimerEvent& e)
{     
  printTimeDiff(2, e);
}

void callback3(const ros::TimerEvent& e)
{     
  printTimeDiff(3, e);
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "test");
  ros::NodeHandle n;
  start = ros::Time::now();

  ros::Timer timer1 = n.createTimer(ros::Duration(0.1), callback1);
  ros::Timer timer2 = n.createTimer(ros::Duration(0.1), callback2);
  // ros::Timer timer3 = n.createTimer(ros::Duration(0.5), callback3);

  while (ros::ok())
  {
    try {
      ros::spin();
    }
    catch (int e)
    {
      ROS_ERROR_STREAM("caught exception: " << e);
    }
  }
  return 0;
}

When I run the above neither callback is called after the exception, presumably I'd have to set them both up again. But if I change the timer2 duration to trigger the first callback after the exception occurs then it will trigger as expected. Or if I uncomment timer3 (which is first triggered after the exception) then both timer2 and timer3 will be triggered after the exception.

Update using real subscribers

So the timer callbacks don't generalize, ros subscribers behave fine after an exception:

void printTimeDiff(const int i, const int j)
{
  ROS_INFO_STREAM("Callback " << i << " triggered "
      << ros::Time::now() - start << " " << j);
}

void callback1(const std_msgs::Int32::ConstPtr& e)
{
  count++;
  printTimeDiff(1, e->data);
  if (count == 4)
  {
    ROS_WARN_STREAM("throwing exception");
    throw 10;
  }
}

void callback2(const std_msgs::Int32::ConstPtr& e)
{
  printTimeDiff(2, e->data);
}

void callback3(const std_msgs::Int32::ConstPtr& e)
{
  printTimeDiff(3, e->data);
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "test");
  ros::NodeHandle n;
  start = ros::Time::now();

  ros::Subscriber s1 = n.subscribe("topic1", 1, callback1);
  ros::Subscriber s2 = n.subscribe("topic2", 1, callback2);
  ros::Subscriber s3 = n.subscribe("topic3", 1, callback3);
...

And some python to publish:

#!/usr/bin/env python

import rospy

from std_msgs.msg import Int32

rospy.init_node('pub_ints')

p1 = rospy.Publisher("topic1", Int32, queue_size=1)
p2 = rospy.Publisher("topic2", Int32, queue_size=1)
p3 = rospy.Publisher("topic3", Int32, queue_size=1)

count = 0
while not rospy.is_shutdown():
    p1.publish(Int32(1))
    p2.publish(Int32(2))
    rospy.sleep(0.5)
    count += 1
    if (count % 5 == 0):
        p3.publish(Int32(3))

Originally posted by lucasw with karma: 8729 on 2017-11-02

This answer was ACCEPTED on the original site

Post score: 1


Original comments

Comment by ta on 2017-11-03:
Thanks for your answer and taking the time to set up this experiment. It's eyes opening. So, if message callbacks behave similarly (which I expect), I can not use them in my peculiar situation as several messages will be waiting on the topic when calling ros::spinOnce().

Comment by lucasw on 2017-11-03:
I haven't tried it but you could resubscribe every subscriber and probably clear out whatever caused the exception to foul them up, but queued messages could be lost in the process.

Comment by lucasw on 2017-11-10:
I just tried this with real subscribers and they don't have the same problem, so you can throw exceptions in callbacks and catch them from spin and continue on and spin again without re-subscribing.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.