0
$\begingroup$

Rosanswers logo

Follow up with my previous question #q283013, currently I have another problem with tf broadcaster with same error of lookup into the past. I am really confused now and I hope that you can help me understand a proper way to set up tf broadcaster/listener. I followed an example here #q90246 and fixed my listener. Here is the code:

#include <...my stuffs..>
class cloudHandler{
public:
    cloudHandler():
    {
        main_sub = nh.subscribe("pico_flexx/points",1,&cloudHandler::mainCB,this);  
        rail_plane_pub = nh.advertise<sensor_msgs::PointCloud2>("rail_plane",1);
        fit_rails_sub = nh.subscribe("rail_plane",1,&cloudHandler::fit_railsCB,this);   
    }
    void mainCB(const sensor_msgs::PointCloud2ConstPtr& input){ 

        <...my stuff...>     
    }
    void rail_plane(pcl::PointCloud<pcl::PointXYZ>::Ptr input, pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_projected){
        <...my stff...>
    }

    void fit_railsCB(const sensor_msgs::PointCloud2ConstPtr& input_from_camera_frame){              
        try{
            cam_bl.waitForTransform("base_link",input_from_camera_frame->header.frame_id,input_from_camera_frame->header.stamp,ros::Duration(2));
        }
        catch(tf::TransformException &ex){
            ROS_WARN("%s",ex.what());
        };

        sensor_msgs::PointCloud2 input_in_bl; 
    pcl_ros::transformPointCloud("base_link",*input_from_camera_frame,input_in_bl,cam_bl); // this is ok to transform now
        
    <...processing my stuff...>

    /*Now I tried to published my result*/
    tf::Vector3 rail_origin(p.x,p.y,p.z);
    static tf::TransformBroadcaster rail_br;
    tf::Transform rail_tf_;
    rail_tf_.setOrigin(rail_origin);
    rail_tf_.setRotation(q);
    rail_br.sendTransform(tf::StampedTransform(rail_tf_,ros::Time::now(),"base_link","rail")); /*Can't send broadcast here, the node keeps dieing and restarting which cause a broken tf*/

    geometry_msgs::PoseStamped r_p2;

    r_p2.header.stamp = ros::Time::now();
    r_p2.header.frame_id = "rail";

    r_p2.pose.position.x = p.x;
    r_p2.pose.position.y = p.y;
    r_p2.pose.position.z = p.z;

    r_p2.pose.orientation = tf::createQuaternionMsgFromYaw(yaw_rail);

    rail_pose_pub2.publish(r_p2);/*Can't even publish this message*/
  }
private:
  tf::TransformListener cam_bl;
  ros::NodeHandle nh;
  ros::Subscriber main_sub, fit_rails_sub;
  ros::Publisher  rail_pose_pub;  
};
int main(int argc, char **argv){
    ros::init(argc, argv, "pico_rails_node");
    cloudHandler handler;
    ros::spin();
    return 0;
}

I have no idea why I can't send my tf broadcast. I believe it is proper to stamp my broadcast with ros::Time::now() since it requires sometime to process all my stuff. I also tried to stamp my broadcaster with the time stamp of the incoming message but the error still happened.

[ERROR] [1518989417.344710428, 1518732057.962978755]: Lookup would require extrapolation into the past.  Requested time 1518732055.923351000 but the earliest data is at time 1518732056.005952182, when looking up transform from frame [pico_flexx_optical_frame] to frame [base_link]

Obviously, I have missed something about tf here. I understand that:

  1. a tf listener requires some time to cache up all the transforms, so in callback, the first few calls should just return;

  2. if we can look up transform, it means that the tf tree is set up properly, then a broadcaster can broadcast anytime with correct transformations between frame_ids. It is logical to stamp this broadcaster with a current time.

So my question is :

  1. Could you show me what I misunderstand and/or miss here?

  2. Is there a proper way set up the tf to avoid this situation? Example for my class, I put every publishers and subscribers in class constructor, and all callbacks are for processing incoming messages. So how should I set up tf in a callback so that I can lookup/broadcast my transform whenever I want?

Thank you!


Edit: Hi @gvdhoorn, Thank you for your detailed answer. This is a very long answer so I can't put it in a comment.

We're going to need some more info here: what does "keeps dying" mean? A SEGFAULT, some other error? As this is CPP, I would try to get a GDB backtrace. I included a backtrace from gdb here:

(gdb) bt    
#0  0x0000000000428817 in cloudHandler::fit_railsCB(boost::shared_ptr<sensor_msgs::PointCloud2_<std::allocator<void> > const> const&) ()
    #1  0x0000000000418acc in boost::detail::function::void_function_obj_invoker1<boost::function<void (boost::shared_ptr<sensor_msgs::PointCloud2_<std::allocator<void> > const> const&)>, void, boost::shared_ptr<sensor_msgs::PointCloud2_<std::allocator<void> > const> >::invoke(boost::detail::function::function_buffer&, boost::shared_ptr<sensor_msgs::PointCloud2_<std::allocator<void> > const>) ()
    #2  0x000000000042ac6c in ros::SubscriptionCallbackHelperT<boost::shared_ptr<sensor_msgs::PointCloud2_<std::allocator<void> > const> const&, void>::call(ros::SubscriptionCallbackHelperCallParams&) ()
    #3  0x00007ffff522644d in ros::SubscriptionQueue::call()
        () from /opt/ros/kinetic/lib/libroscpp.so
    #4  0x00007ffff51cc210 in ros::CallbackQueue::callOneCB(ros::CallbackQueue::TLS*) ()
       from /opt/ros/kinetic/lib/libroscpp.so
    #5  0x00007ffff51cd683 in ros::CallbackQueue::callAvailable(ros::WallDuration) ()
       from /opt/ros/kinetic/lib/libroscpp.so
    #6  0x00007ffff522a511 in ros::SingleThreadedSpinner::spin(ros::CallbackQueue*) ()
       from /opt/ros/kinetic/lib/libroscpp.so
    #7  0x00007ffff520f4cb in ros::spin() ()
       from /opt/ros/kinetic/lib/libroscpp.so
    #8  0x0000000000412839 in main ()

By including the broadcaster, I have a SEGFAULT like this. But if I don't broadcast anything, my program works just fine.

Also: any reason to not make rail_br a member variable?

No, there isn't. I honestly didn't remember why I didn't make it a member variable. I changed that.

Finally: you're catch(..)ing the tf::TransformException in fit_railsCB(..), but then just printing the msg. If waitForTransform(..) throws an exception, continuing with the rest of the callback is pointless, so you should perhaps add a return there or something similar.

This is my silly mistake, I copied from my previous question without editing it. Yes, in the catch, I included a return so that the first few calls which obviously fails will stop my callback.

Finally2:

I believe it is proper to stamp my broadcast with ros::Time::now() since it requires sometime to process all mys stuff

that depends: if the frame you attempt to broadcast is the result of processing the incoming cloud -- which was captured at a specific time, then reusing the stamp ..

This makes sense to me. I stamped my broadcaster with the incoming message's stamp.

.. from the cloud that was the input of your process makes more sense to me: how are 'downstream' consumers ever going to be able to correlate your TF frame with the state of the world (ie: at the time that the cloud was captured)? If processing my stuff takes a (hypothetical) 5 mins, rail_tf_ will be broadcast based on 5 min old data. Without the stamp from the cloud that was processed, that will be impossible to detect.

This is the reason why I first thought to stamp the output of callback with the current time, however, it is weird to ask the robot to get to the pose 5 mins ago. But this is a question of how to process my raw pointcloud.


Originally posted by tuandl on ROS Answers with karma: 358 on 2018-02-18

Post score: 0


Original comments

Comment by jayess on 2018-02-18:
Have you gone through the network setup on the wiki? You may have issues with the synchronization between clocks.

Comment by tuandl on 2018-02-18:
Yes, I did. I did set up ntp so that the robot's NUC is always synced with the current time. I recorded my data with rosbag and work with it on my laptop. I did set use_sim_time true before playing my bag and launching my node.

Comment by gvdhoorn on 2018-02-19:
We're going to need some more info here: what does "keeps dying" mean? A SEGFAULT, some other error? As this is CPP, I would try to get a GDB backtrace.

Also: any reason to not make rail_br a member variable?

Comment by gvdhoorn on 2018-02-19:
Finally: you're catch(..)ing the tf::TransformException in fit_railsCB(..), but then just printing the msg. If waitForTransform(..) throws an exception, continuing with the rest of the callback is pointless, so you should perhaps add a return there or something similar.

Comment by gvdhoorn on 2018-02-19:
Finally2:

I believe it is proper to stamp my broadcast with ros::Time::now() since it requires sometime to process all my stuff

that depends: if the frame you attempt to broadcast is the result of processing the incoming cloud -- which was captured at a specific time, then reusing the stamp ..

Comment by gvdhoorn on 2018-02-19:
.. from the cloud that was the input of your process makes more sense to me: how are 'downstream' consumers ever going to be able to correlate your TF frame with the state of the world (ie: at the time that the cloud was captured)? If processing my stuff takes a (hypothetical) 5 mins, ..

Comment by gvdhoorn on 2018-02-19:
.. rail_tf_ will be broadcast based on 5 min old data. Without the stamp from the cloud that was processed, that will be impossible to detect.

Comment by gvdhoorn on 2018-02-19:\

Thank you for your detailed answer. This is a very long answer so I can't put it in a comment.

exactly. So editting your original question would have been appropriate.

I've merged your answer into your original question.

Comment by gvdhoorn on 2018-02-19:
re: your gdb trace: you'll need to (re)build your workspace with CMAKE_BUILD_TYPE set to Debug or RelWithDebInfo. Otherwise the backtrace doesn't really help.

Comment by gvdhoorn on 2018-02-19:\

it is weird to ask the robot to get to the pose 5 mins ago

why exactly? This is precisely one of the use-cases that TF was created for. As long as you have a long enough buffer, asking for a TF frame as it was 30 mins ago is no different than asking for one that is only 2 sec old.

Comment by tuandl on 2018-02-19:\

re: your gdb trace: you'll need to (re)build your workspace with CMAKE_BUILD_TYPE set to Debug

This is another stupid mistake I made. I completely forgot to set DCMAKE_BUILD_TYPE=Debug. After rebuild my package with Debug option, I was able to catch a segfault in <...processing stuff...>,...

Comment by tuandl on 2018-02-19:
...,which is strange that it didn't raise any segfault with DCMAKE_BUILD_TYPE=Release. Anyway, I am trying to fix my algorithm. Thank you for your time :).

Comment by tuandl on 2018-02-19:\

why exactly?

I mean it is weird for my robot to ask for a pose that was too far in the past. But I totally understand the purpose of TF is to keep track of every transformation through time.

Comment by gvdhoorn on 2018-02-20:\

I was able to catch a segfault in <...processing stuff...>

Ok. It would be good to post this as an answer (but please add some more detail / explanation, perhaps the backtrace) and then accept your own answer.

That way it's completely clear that your issue has been resolved - and by yourself.

$\endgroup$

1 Answer 1

0
$\begingroup$

Rosanswers logo

re: your gdb trace: you'll need to (re)build your workspace with CMAKE_BUILD_TYPE set to Debug

I was able to catch a segfault in <...process my stuff...> which seems to cause a segfault in a broadcaster object.

I am still curious about my error though: if I build my package in Release mode, the segfault caused by my algorithm wouldn't crash my node without asking a broadcaster to broadcast my result. It would only crash when I ask a broadcaster to broadcast the result.

But this is a different question. Thank you for your help @gvdhoorn. Please close this question.


Originally posted by tuandl with karma: 358 on 2018-02-20

This answer was ACCEPTED on the original site

Post score: 0

$\endgroup$

Your Answer

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