# How do I get out of ros::spin() in another thread?

Hi All,

So what I am doing is enumerated in the points below. After that, I explain my questions in detail.

1) I recently got my hands on an ATI force/torque sensor which is pretty cool. I already had code which publishes the (fx,fy,fz,tx,ty,tz) readings from this sensor on the ROS network.

2) My job is to write the subscriber. The subscriber should have the following functionality:

2.a) I should be able to move around with the subscriber code. This means I want it to be preferably in the form of a single header file or something of the likes.

2.b) I want it to have a start_recording() functionality and stop_recording() functionality. These two functions essentially allow me to log the force/torque data to a text file, and stop the logging.

2.c) I want the data logging to be really fast. The more data I collect, the better for my application. The sensor itself publishes at 33 Hz, so anything in that ballpark is good for me.

3) So what I did was wrote a class called ForceTorqueObject which has some private threaded stuff going on, and some interface functions to allow the user to log/stop-log the data. Here is the class declaration.

class ForceTorqueObject
{
private:
volatile bool recording;
ros::NodeHandle *nh;
FILE *ft_curr_logfile;
ros::Subscriber ft_listener;
queue<robot_comm::robot_ForceLog> ft_q; //shared object

static void *ft_subscribe_main(void *args)
{
ForceTorqueObject *ft_sensor = (ForceTorqueObject *)args;

ft_sensor->ft_subscribe();
//return((void *)0);
}
void ft_subscribe();
void ft_sensor_callback(const robot_comm::robot_ForceLog &fLog);
void ft_queue_dump();

public:
ForceTorqueObject(ros::NodeHandle *n); //Constructor
~ForceTorqueObject();

//User functions
void ft_start_recording(const char *fLogFile);
void ft_stop_recording();
void ft_force_exit();
};


4) As seen from above, I am first creating a subscription thread, in which I have a callback function which checks for a flag "recording". Accordingly, it pumps the rosmsg carrying the force/torque data into a queue. ft_start_recording() just sets "recording" to 1, while ft_stop_recording() sets "recording" to 0, and dumps all contents of the queue into a text file. Thus I get my desired "log/stop-log" functionality.

5) In the separate thread for subscribing to the topic, I am calling ros::spin() from the ForceTorqueObject::ft_subscribe() function.

All this brings me to my set of questions:

1) How do I get out of the ros::spin() in the recording thread!? I wrote a small main() function to test my new class, and found out that once I am done logging and stop-logging, the subscriber thread still continues to live! I then realized that I could just not think of a way in which I can shut down the subscriber thread from the main thread.

2) So I tried changing things a little bit here-and-there. I tried doing

     static void *ft_subscribe_main(void *args)
{
ForceTorqueObject *ft_sensor = (ForceTorqueObject *)args;
while(ros::ok())
{
ft_sensor->ft_subscribe();
ros::spinOnce();
}
cout<<"End of ...
edit retag close merge delete

Sort by » oldest newest most voted

Ok, so there are a few issues here.

1. If you're already publishing the force/torque data over ROS, is there a reason why you're writing your own data logger instead of using rosbag?

2. Assuming ft_sensor->ft_subscribe() is creating a subscriber, when you replace ros::spin() with ros::spinOnce() in a loop, you're creating new subscriptions as fast as possible, over and over. Each time you create a subscriber, you are necessitating communication with the ROS master.

3. If you're already using ROS, then it's not a lot to use boost threads instead of pthreads. On POSIX systems, boost threads are pthreads, but with a more modern c++-like interface which you will find less error-prone.

4. I suspect you're doing something wonky with threading to keep your program from exiting. Once a SIGINT (ctrl-c) is received by the program, ros::shutdown() should be called. If you're doing something to keep that signal from being handled, then when you want ros::ok() to return false or ros::spin() to return, then simply call ros::shutdown() manually as shown here.

more

@jbohren : Thanks for the quick answers! Here are my thoughts on your points: 1) I want to write the data that I log to text files. Can I do that with rosbag? (I am fairly new to it) 2) This is a perfect answer to my corresponding question. I totally agree 3) I'll look into this.

( 2013-03-13 17:31:09 -0500 )edit

@Nishant Yeah, converting ROS bagfiles to text files (usually comma-seprated files) or matlab files is pretty common: 123

( 2013-03-13 17:43:54 -0500 )edit

@jbohren : This is very interesting. I shall try to migrate to rosbag over time. As per your answer (4) and provided links, I have a question which is provided in the comment below (ran out of space on this one)

( 2013-03-14 11:36:45 -0500 )edit

Suppose I am calling ros::spinOnce() in a while(ros::ok()) loop. What is the difference between this setup, and calling ros::spinOnce() from within a while(stayActive) (assuming stayActive is a bool which tells the loop to stay active) ? Can't I just do option (2) and turn stayActive=0 to exit loop?

( 2013-03-14 11:41:03 -0500 )edit

@jbohren : I cannot thank you enough jbohren. I have found out so much in this single answer of yours itself! How callbacks work, about rosbag and boost threads. For now, what I am doing is executing all that is in GlobalCallbackQueue in a while(stayActive) loop in the subscriber thread.

( 2013-03-14 11:55:04 -0500 )edit

When I want the thread to shut down, I do stayActive=0 (using a mutex). I observed over multiple runs, that I get a slightly lesser recording frequency (~32.7 Hz) compared to original 33 Hz. This might be due to the fact that ros::spin() uses some inbuilt optimization which I am not using...correct?

( 2013-03-14 11:58:31 -0500 )edit

There should be no performance difference between spin() and spinOnce() in a loop. What's probably happening is your mutex-protected stayActive is blocking your spin loop sometimes. You don't really need to protect a bool with a mutex when you're using it like that. (http://goo.gl/cK8Qf)

( 2013-03-14 13:05:10 -0500 )edit

Also, if you weren't sure about it, all spinOnce() does is call all of the waiting callbacks (subscriber callbacks, service callbacks, etc). They are all called from that function call and all need to return before that call returns.

( 2013-03-14 13:08:53 -0500 )edit

@add I was spinning within boost::thread which subscribed to a few topics. What I simply forgot was to add the ros::ok() check:

        //subscribe to topics ...

//pump callbacks but check if SIGINT is not set
while(ros::ok()) ros::spin();

more