00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 package tfjava;
00032 
00033 import ros.*;
00034 import ros.communication.*;
00035 
00036 import ros.pkg.tf.msg.tfMessage;
00037 import ros.pkg.geometry_msgs.msg.TransformStamped;
00038 
00039 import javax.vecmath.Quat4d;
00040 import javax.vecmath.Vector3d;
00041 import javax.vecmath.Point3d;
00042 import javax.vecmath.Matrix4d;
00043 
00044 import java.util.HashMap;
00045 import java.util.PriorityQueue;
00046 import java.util.LinkedList;
00047 
00068 public class TFListener {
00069     
00071     public static final long MAX_STORAGE_TIME = (new Duration(10, 0)).totalNsecs(); 
00072     
00074     protected static Ros ros;
00076     protected static NodeHandle rosNode;
00078     protected static TFListener instance;    
00080     protected HashMap<String, Frame> frames;
00082     protected String tfPrefix = "";
00083     
00087     public synchronized static TFListener getInstance() {
00088         if (instance == null) {
00089             instance = new TFListener();
00090         }
00091         return instance;
00092     }
00093     
00094     
00095 
00096      
00097     
00102     protected TFListener() {
00103                 initRos("tfjava_listener");
00104                 
00105                 if (initListener()) {
00106                     spinInSeperateThread();
00107                 }
00108         }       
00109         
00113         protected void initRos(String node_name) {
00114                 ros = Ros.getInstance();
00115 
00116                 if(!ros.isInitialized()) {
00117                         ros.init(node_name);
00118                 }
00119                 rosNode = ros.createNodeHandle();
00120         }
00121         
00125         protected boolean initListener() {
00126         frames = new HashMap<String, Frame>();    
00127             
00128             try {           
00129             TFCallback callback = new TFCallback();         
00130             Subscriber<tfMessage> sub = rosNode.subscribe("/tf", new tfMessage(), callback, 10);  
00131             
00132         } catch (Exception e) {
00133             ros.logError("Main: subscribe to /tf failed");
00134             return false;
00135         }           
00136         return true;
00137         }
00138         
00142     protected class TFCallback implements Subscriber.Callback<tfMessage> {
00143         public void call(tfMessage tfm) {
00144             if (tfm != null) {
00145                 for(TransformStamped tf : tfm.transforms) {                 
00146                     setTransform(tf);               
00147                 }
00148             }
00149         }
00150     }           
00151         
00156     protected void spinInSeperateThread() {
00157             Thread t = new Thread() {
00158                 public void run() {
00159                     rosNode.spin();
00160                 }
00161             };      
00162             t.start();
00163         }       
00164         
00165     
00166 
00167         
00168         
00172     protected boolean setTransform(TransformStamped transform) {
00173             
00174         String childFrameID = assertResolved(tfPrefix, transform.child_frame_id);
00175             String frameID = assertResolved(tfPrefix, transform.header.frame_id);
00176  
00177             boolean errorExists = false;
00178             if (childFrameID == frameID) {
00179                 ros.logError("TF_SELF_TRANSFORM: Ignoring transform with frame_id and child_frame_id  \"" + childFrameID + "\" because they are the same");
00180                 errorExists = true;
00181             }
00182 
00183             if (childFrameID == "/") { 
00184                 ros.logError("TF_NO_CHILD_FRAME_ID: Ignoring transform because child_frame_id not set ");
00185                 errorExists = true;
00186             }
00187 
00188             if (frameID == "/") { 
00189                 ros.logError("TF_NO_FRAME_ID: Ignoring transform with child_frame_id \"" + childFrameID + "\" because frame_id not set");
00190                 errorExists = true;
00191             }
00192 
00193             if (errorExists) return false;          
00194             
00195             
00196             Frame frame = lookupOrInsertFrame(childFrameID);
00197             
00198             
00199             transform.child_frame_id = childFrameID;
00200             transform.header.frame_id = frameID; 
00201             TransformStorage tf = transformStampedMsgToTF(transform);
00202             
00203             
00204             if (!frame.insertData(tf)) {
00205                 ros.logWarn("TF_OLD_DATA ignoring data from the past for frame \"" + childFrameID + "\" at time " + ((double)tf.getTimeStamp() / 1E9));
00206             return false;
00207             }
00208 
00209         return true;
00210         }
00211         
00216         protected Frame lookupOrInsertFrame(String frameID) {
00217         Frame frame = frames.get(frameID);
00218         if (frame == null) {
00219             frame = new Frame(frameID, MAX_STORAGE_TIME);
00220             frames.put(frameID, frame);
00221         }           
00222         return frame;
00223         }
00224     
00225     
00226 
00227  
00228         
00232         public void transformPoint(String targetFrameID, Stamped<Point3d> stampedIn, Stamped<Point3d> stampedOut) {
00233             StampedTransform transform = lookupTransform(targetFrameID, stampedIn.frameID, stampedIn.timeStamp);
00234             transform.transformPoint(stampedIn.getData(), stampedOut.getData());
00235             stampedOut.frameID = targetFrameID;
00236             stampedOut.timeStamp = stampedIn.timeStamp;
00237         }
00238         
00243         public void transformPoint(String targetFrameID, Time targetTime, Stamped<Point3d> stampedIn,
00244                                                     String fixedFrameID, Stamped<Point3d> stampedOut) {
00245             StampedTransform transform = lookupTransform(targetFrameID, targetTime, stampedIn.frameID, stampedIn.timeStamp, fixedFrameID);
00246         transform.transformPoint(stampedIn.getData(), stampedOut.getData()); 
00247         stampedOut.frameID = targetFrameID;
00248         stampedOut.timeStamp = stampedIn.timeStamp;
00249         }
00253         public void transformPose(String targetFrameID, Stamped<Matrix4d> stampedIn, Stamped<Matrix4d> stampedOut) {
00254             StampedTransform transform = lookupTransform(targetFrameID, stampedIn.frameID, stampedIn.timeStamp);
00255             transform.transformPose(stampedIn.getData(), stampedOut.getData());     
00256         stampedOut.frameID = targetFrameID;
00257         stampedOut.timeStamp = stampedIn.timeStamp;
00258         }
00259          
00264         public void transformPose(String targetFrameID, Time targetTime, Stamped<Matrix4d> stampedIn,
00265                                                     String fixedFrameID, Stamped<Matrix4d> stampedOut) {
00266             StampedTransform transform = lookupTransform(targetFrameID, targetTime, stampedIn.frameID, stampedIn.timeStamp, fixedFrameID);
00267             transform.transformPose(stampedIn.getData(), stampedOut.getData());
00268         stampedOut.frameID = targetFrameID;
00269         stampedOut.timeStamp = stampedIn.timeStamp;
00270     }
00271         
00272     
00273 
00274     
00275 
00280         public StampedTransform lookupTransform(String targetFrameID, String sourceFrameID, Time time) {
00281         
00282             String resolvedTargetID = assertResolved(tfPrefix, targetFrameID);
00283         String resolvedSourceID = assertResolved(tfPrefix, sourceFrameID);
00284         
00285         
00286         if (resolvedSourceID == resolvedTargetID) {
00287             StampedTransform out = StampedTransform.getIdentity();
00288             out.timeStamp = time;
00289             out.frameID = resolvedSourceID;
00290             out.childFrameID = resolvedTargetID;            
00291             return out;
00292         }
00293 
00294         
00295         Frame sourceFrame = frames.get(resolvedSourceID);
00296         Frame targetFrame = frames.get(resolvedTargetID);
00297         
00298         if (sourceFrame == null) {
00299             ros.logError("Cannot transform: source frame \"" + resolvedSourceID + "\" does not exist.");
00300             return null;
00301         }    
00302             
00303         if (targetFrame == null) {
00304             ros.logError("Cannot transform: target frame \"" + resolvedTargetID + "\" does not exist.");
00305             return null;
00306         } 
00307             
00308         
00309             LinkedList<TransformStorage> inverseTransforms = new LinkedList<TransformStorage>();
00310             
00311             LinkedList<TransformStorage> forwardTransforms = new LinkedList<TransformStorage>();
00312             
00313             
00314             if (!lookupLists(targetFrame, sourceFrame, time.totalNsecs(), inverseTransforms, forwardTransforms)) {
00315                 
00316                 ros.logError("Cannot transform: source + \"" + resolvedSourceID + "\" and target \""
00317                                 + resolvedTargetID + "\" are not connected.");
00318                 return null;
00319             }        
00320             
00321             
00322             StampedTransform out = StampedTransform.getIdentity();          
00323             out.timeStamp = time;
00324             
00325         
00326         for(TransformStorage t : inverseTransforms) {           
00327             out.mul(StorageToStampedTransform(t));
00328         }
00329             
00330             
00331             for(TransformStorage t : forwardTransforms) {               
00332                 out.mul(StorageToStampedTransform(t).invert(), out);
00333         }           
00334             
00335             
00336             return out;
00337         }
00338         
00343         public StampedTransform lookupTransform(String targetID, Time targetTime, String sourceID, Time sourceTime, String fixedID) {       
00344             
00345             StampedTransform t1 = lookupTransform(fixedID, sourceID, sourceTime);
00346             
00347             StampedTransform t2 = lookupTransform(targetID, fixedID, targetTime);
00348             
00349             
00350             if (t1 == null || t2 == null) return null;      
00351             
00352             
00353             t2.mul(t1);
00354             return t2;
00355         }
00356         
00370     protected boolean lookupLists(Frame targetFrame, Frame sourceFrame, long time,
00371             LinkedList<TransformStorage> inverseTransforms, LinkedList<TransformStorage> forwardTransforms) {
00372         
00373         
00374         SearchNode<Frame> sourceNode = new SearchNode<Frame>(sourceFrame);
00375         SearchNode<Frame> targetNode = new SearchNode<Frame>(targetFrame);
00376         
00377         
00378         sourceNode.backwardStep = sourceNode;
00379         
00380         targetNode.forwardStep = targetNode;        
00381         
00382         
00383         
00384         HashMap<Frame, SearchNode<Frame>> frameToNode = new HashMap<Frame, SearchNode<Frame>>();
00385         
00386         
00387         frameToNode.put(sourceFrame, sourceNode);
00388         frameToNode.put(targetFrame, targetNode);
00389         
00390         
00391         PriorityQueue<SearchNode<Frame>> Q = new PriorityQueue<SearchNode<Frame>>();
00392         
00393         
00394         Q.add(sourceNode); 
00395         Q.add(targetNode);
00396         
00397         
00398         while(!Q.isEmpty()) {
00399             
00400             SearchNode<Frame> frameNode = Q.poll();
00401             Frame frame = frameNode.content;
00402             
00403             
00404             if (frameNode.backwardStep != null && frameNode.forwardStep != null) {
00405                 
00406                 
00407                 
00408                 SearchNode<Frame> node = frameNode;                
00409                 while(node.content != sourceNode.content) {                    
00410                     inverseTransforms.addLast(node.backwardStep.content.getData(time, node.content));
00411                     node = node.backwardStep;
00412                 }
00413                 
00414                 
00415                 node = frameNode;
00416                 while(node.content != targetNode.content) {
00417                     forwardTransforms.addLast(node.forwardStep.content.getData(time, node.content));
00418                     node = node.forwardStep;
00419                 }
00420                 return true;
00421             }
00422             
00423             
00424             for(Frame parentFrame : frame.getParentFrames()) {                
00425                 SearchNode<Frame> parentFrameNode = frameToNode.get(parentFrame);
00426                                 
00427                 boolean addToQueue = false;
00428                 if (parentFrameNode == null) {
00429                     
00430                     parentFrameNode = new SearchNode<Frame>(parentFrame);                    
00431                     frameToNode.put(parentFrame, parentFrameNode);
00432                     addToQueue = true;
00433                 } else {
00434                     
00435                     if ((parentFrameNode.backwardStep == null && frameNode.forwardStep == null)
00436                             || (parentFrameNode.forwardStep == null && frameNode.backwardStep == null)) {
00437                         
00438                         
00439                         
00440                         parentFrameNode = new SearchNode<Frame>(parentFrameNode);
00441                         addToQueue = true;                        
00442                     }
00443                 }                
00444                                 
00445                 
00446                 if (addToQueue) {
00447                     
00448                     TimeCache cache = frame.getTimeCache(parentFrame);
00449                     parentFrameNode.cost = Math.max((double)cache.timeToNearestTransform(time),
00450                                                 Math.max(parentFrameNode.cost, frameNode.cost));
00451                     
00452                     if (frameNode.backwardStep != null) parentFrameNode.backwardStep = frameNode;
00453                     
00454                     if (frameNode.forwardStep != null) parentFrameNode.forwardStep = frameNode;
00455                     
00456                     Q.add(parentFrameNode);
00457                 }
00458             }
00459         }    
00460         
00461         
00462         return false;
00463     }
00464     
00473     protected class SearchNode<V> implements Comparable<SearchNode<V>> {        
00475         V content;
00477         double cost;
00479         SearchNode<V> backwardStep;
00481         SearchNode<V> forwardStep;        
00482         
00484         SearchNode(V content) {
00485             this.content = content;
00486             this.cost = 0;
00487             this.backwardStep = null;
00488             this.forwardStep = null;
00489         }
00490         
00492         SearchNode(SearchNode<V> orig) {
00493             this.content = orig.content;
00494             this.cost = orig.cost;
00495             this.backwardStep = orig.backwardStep;
00496             this.forwardStep = orig.forwardStep;
00497         }
00498         
00500         public int compareTo(SearchNode<V> other) {
00501             if (this.cost < other.cost) return -1;
00502             if (this.cost > other.cost) return 1;
00503             return 0;
00504         }        
00505         
00506     }
00507         
00508     
00509 
00510         
00511         
00515         protected TransformStorage transformStampedMsgToTF(TransformStamped msg) {
00516             ros.pkg.geometry_msgs.msg.Vector3 tMsg = msg.transform.translation;
00517             ros.pkg.geometry_msgs.msg.Quaternion rMsg = msg.transform.rotation; 
00518             
00519             
00520             Frame childFrame = lookupOrInsertFrame(msg.child_frame_id);
00521             Frame parentFrame = lookupOrInsertFrame(msg.header.frame_id);
00522             
00523             return new TransformStorage(new Vector3d(tMsg.x, tMsg.y, tMsg.z),
00524                                         new Quat4d(rMsg.x, rMsg.y, rMsg.z, rMsg.w),
00525                                         msg.header.stamp.totalNsecs(),
00526                                         parentFrame, childFrame);
00527         }
00528         
00532         protected TransformStamped TFToTransformStampedMsg(TransformStorage tf) {
00533             Vector3d tTF = tf.getTranslation();
00534             Quat4d rTF = tf.getRotation();
00535             
00536             
00537             ros.pkg.geometry_msgs.msg.Vector3 tMsg = new ros.pkg.geometry_msgs.msg.Vector3();
00538             ros.pkg.geometry_msgs.msg.Quaternion rMsg = new ros.pkg.geometry_msgs.msg.Quaternion();
00539             tMsg.x = tTF.x; tMsg.y = tTF.y; tMsg.z = tTF.z;
00540         rMsg.x = rTF.x; rMsg.y = rTF.y; rMsg.z = rTF.z; rMsg.w = rTF.w;
00541         
00542         
00543             TransformStamped msg = new TransformStamped();          
00544             msg.header.frame_id = tf.getParentFrame().getFrameID();
00545             msg.header.stamp = new Time(tf.getTimeStamp());
00546             msg.child_frame_id = tf.getChildFrame().getFrameID();
00547             msg.transform.translation = tMsg;
00548             msg.transform.rotation = rMsg;
00549             
00550             return msg;
00551         }
00552         
00557         protected StampedTransform StorageToStampedTransform(TransformStorage ts) {
00558             return new StampedTransform(ts.getTranslation(), ts.getRotation(), new Time(ts.getTimeStamp()), 
00559                             ts.getParentFrame().getFrameID(), ts.getChildFrame().getFrameID());
00560         }
00561         
00566         private String assertResolved(String prefix, String frameID) {
00567           if (!frameID.startsWith("/"))     
00568               ros.logDebug("TF operating on not fully resolved frame id " + frameID +", resolving using local prefix " + prefix);
00569           return resolve(prefix, frameID);
00570         }
00571         
00575         private static String resolve(String prefix, String frameID)    {                           
00576             if (frameID.startsWith("/")) {
00577                         return frameID;                 
00578                 }         
00579                 
00580                 if (prefix.length() > 0) {
00581                         if (prefix.startsWith("/")) {
00582                                 return prefix + "/" + frameID;
00583                         } else {
00584                             return "/" + prefix + "/" + frameID;
00585                         }
00586                 }  else {                   
00587                         return "/" + frameID;                   
00588                 }
00589         }       
00590         
00591         
00592 
00593 
00594 
00595 
00596 
00597 
00598 
00599 
00600 
00601 
00602 
00603 
00604 
00605       
00606         
00607 }