Revision: 8251
          http://playerstage.svn.sourceforge.net/playerstage/?rev=8251&view=rev
Author:   gbiggs
Date:     2009-09-08 01:49:06 +0000 (Tue, 08 Sep 2009)

Log Message:
-----------
Applied patch #2844290: New rangerposeinterpolator driver

Modified Paths:
--------------
    code/player/trunk/server/drivers/ranger/CMakeLists.txt

Added Paths:
-----------
    code/player/trunk/server/drivers/ranger/rangerposeinterpolator.cc

Modified: code/player/trunk/server/drivers/ranger/CMakeLists.txt
===================================================================
--- code/player/trunk/server/drivers/ranger/CMakeLists.txt      2009-09-08 
01:47:22 UTC (rev 8250)
+++ code/player/trunk/server/drivers/ranger/CMakeLists.txt      2009-09-08 
01:49:06 UTC (rev 8251)
@@ -25,3 +25,5 @@
 PLAYERDRIVER_OPTION (rangertolaser build_rangertolaser ON)
 PLAYERDRIVER_ADD_DRIVER (rangertolaser build_rangertolaser SOURCES 
fromranger.cc rangertolaser.cc)
 
+PLAYERDRIVER_OPTION (rangerposeinterpolator build_rangerposeinterpolator ON)
+PLAYERDRIVER_ADD_DRIVER (rangerposeinterpolator build_rangerposeinterpolator 
SOURCES rangerposeinterpolator.cc)

Added: code/player/trunk/server/drivers/ranger/rangerposeinterpolator.cc
===================================================================
--- code/player/trunk/server/drivers/ranger/rangerposeinterpolator.cc           
                (rev 0)
+++ code/player/trunk/server/drivers/ranger/rangerposeinterpolator.cc   
2009-09-08 01:49:06 UTC (rev 8251)
@@ -0,0 +1,463 @@
+/*
+ *  Player - One Hell of a Robot Server
+ *  Copyright (C) 2005 -
+ *     Brian Gerkey
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/** @ingroup drivers */
+/** @{ */
+/** @defgroup driver_rangerposeinterpolator rangerposeinterpolator
+ * @brief Attach poses to ranger scans
+
+The rangerposeinterpolator driver reads ranger scans from a ranger device
+and poses from a position2d device, linearly interpolates to estimate
+the actual pose from which the scan was taken, then outputs messages
+containing both scan and pose.
+
+...@par Compile-time dependencies
+
+- none
+
+...@par Provides
+
+- @ref interface_ranger : Pose-stamped ranger scans (subtype
+PLAYER_RANGER_DATA_RANGESTAMPED) are published via this interface
+
+...@par Requires
+
+- @ref interface_ranger : Raw ranger scans (subtype PLAYER_RANGER_DATA_RANGE) 
are read from this device
+- @ref interface_position2d : Pose data (subtype PLAYER_POSITION2D_DATA_STATE) 
is read from this device
+
+...@par Configuration requests
+
+- All configuration are forwarded to the underlying @ref interface_ranger 
device for handling.
+
+...@par Configuration file options
+
+- interpolate (integer)
+  - Default: 1
+  - Linearly interpolate between poses for each scan (1), or just attach the
+    most recent pose to each scan (0).
+- max_scans (integer)
+  - Default: 100
+  - Maximum number of scans to buffer while waiting for a second pose in order
+    to interpolate.
+- update_thresh ([length angle] tuple)
+  - Default: [-1.0 -1.0]
+  - Minimum change in pose (translation or rotation) required before
+    a new ranger scan will be published.  Use this option to choke the data 
rate.
+    Set either value to -1.0 to indicate that no threshold should be applied in
+    that dimension (i.e., every scan should be published).
+- update_interval (float, seconds)
+  - Default: -1.0
+  - Interval after which a new scan will be published, regardless of how far
+    the robot has moved.  Set to -1.0 to disable this threshold.
+- send_all_scans (integer)
+  - Default: 1
+  - Whether to stamp and publish every ranger scan.  If set to 1, this option
+    overrides update_thresh and update_interval.
+
+...@par Example
+
+...@verbatim
+driver
+(
+  name "sicklms200"
+  provides ["ranger:0"]
+)
+driver
+(
+  name "p2os"
+  provides ["odometry::position:0"]
+)
+driver
+(
+  name "rangerposeinterpolator"
+  provides ["ranger:1"]
+  requires ["ranger:0" "position2d:0"]
+)
+...@endverbatim
+
+...@author Brian Gerkey (modified from laser to ranger by Patrick Beeson)
+
+*/
+/** @} */
+
+
+#include <config.h>
+
+#include <math.h>
+#include <float.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <libplayercore/playercore.h>
+#include <libplayerinterface/functiontable.h>
+
+#if defined (WIN32)
+  #define hypot _hypot
+#endif
+
+#define DEFAULT_MAXSCANS 100
+
+// computes the signed minimum difference between the two angles.
+static double
+angle_diff(double a, double b)
+{
+  double d1, d2;
+  a = NORMALIZE(a);
+  b = NORMALIZE(b);
+  d1 = a-b;
+  d2 = 2*M_PI - fabs(d1);
+  if(d1 > 0)
+    d2 *= -1.0;
+  if(fabs(d1) < fabs(d2))
+    return(d1);
+  else
+    return(d2);
+}
+
+// The ranger device class.
+class RangerPoseInterp : public Driver
+{
+  public:
+
+    // Constructor
+    RangerPoseInterp(ConfigFile* cf, int section);
+    ~RangerPoseInterp();
+
+    int Setup();
+    int Shutdown();
+
+    // MessageHandler
+    int ProcessMessage(QueuePointer & resp_queue,
+                      player_msghdr * hdr,
+                      void * data);
+  private:
+
+    // device bookkeeping
+    player_devaddr_t ranger_addr;
+    player_devaddr_t position_addr;
+    Device* ranger_device;
+    Device* position_device;
+
+    // interpolation bookkeeping
+    bool interpolate;
+    int maxnumscans;
+    int numscans;
+    player_ranger_data_range_t* scans;
+    double* scantimes;
+    player_position2d_data_t lastpose;
+    double lastposetime;
+    player_pose3d_t lastpublishpose;
+    double lastpublishposetime;
+    double update_thresh[2];
+    double update_interval;
+    bool send_all_scans;
+};
+
+// a factory creation function
+Driver* RangerPoseInterp_Init(ConfigFile* cf, int section)
+{
+  return((Driver*)(new RangerPoseInterp(cf, section)));
+}
+
+// a driver registration function
+void rangerposeinterpolator_Register(DriverTable* table)
+{
+  table->AddDriver("rangerposeinterpolator", RangerPoseInterp_Init);
+}
+
+RangerPoseInterp::RangerPoseInterp(ConfigFile* cf, int section)
+    : Driver(cf, section, true, PLAYER_MSGQUEUE_DEFAULT_MAXLEN,
+             PLAYER_RANGER_CODE)
+{
+  // Must have an input ranger
+  if (cf->ReadDeviceAddr(&this->ranger_addr, section, "requires",
+                         PLAYER_RANGER_CODE, -1, NULL) != 0)
+  {
+    this->SetError(-1);
+    return;
+  }
+  this->ranger_device = NULL;
+
+  // Must have an input position
+  if (cf->ReadDeviceAddr(&this->position_addr, section, "requires",
+                         PLAYER_POSITION2D_CODE, -1, NULL) != 0)
+  {
+    this->SetError(-1);
+    return;
+  }
+  this->position_device = NULL;
+
+  this->interpolate = cf->ReadInt(section, "interpolate", 1) != 0 ? true : 
false;
+  this->maxnumscans = cf->ReadInt(section, "max_scans", DEFAULT_MAXSCANS);
+  this->update_thresh[0] = cf->ReadTupleLength(section, "update_thresh",
+                                               0, -1.0);
+  this->update_thresh[1] = cf->ReadTupleAngle(section, "update_thresh",
+                                              1, -1.0);
+  this->update_interval = cf->ReadFloat(section, "update_interval", -1.0);
+  this->send_all_scans = cf->ReadInt(section, "send_all_scans", 1) != 0 ? true 
: false;
+
+  this->scans = (player_ranger_data_range_t*)calloc(this->maxnumscans,
+                                                   
sizeof(player_ranger_data_range_t));
+  assert(this->scans);
+  this->scantimes = (double*)calloc(this->maxnumscans, sizeof(double));
+  assert(this->scantimes);
+
+  return;
+}
+
+RangerPoseInterp::~RangerPoseInterp()
+{
+  for (int i=0; i< maxnumscans; i++)
+    if (this->scans[i].ranges != NULL)
+      delete [] this->scans[i].ranges;
+  free(this->scans);
+  free(this->scantimes);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Set up the device
+int RangerPoseInterp::Setup()
+{
+  // Subscribe to the ranger.
+  if(Device::MatchDeviceAddress(this->ranger_addr, this->device_addr))
+  {
+    PLAYER_ERROR("attempt to subscribe to self");
+    return(-1);
+  }
+  if(!(this->ranger_device = deviceTable->GetDevice(this->ranger_addr)))
+  {
+    PLAYER_ERROR("unable to locate suitable ranger device");
+    return(-1);
+  }
+  if(this->ranger_device->Subscribe(this->InQueue) != 0)
+  {
+    PLAYER_ERROR("unable to subscribe to ranger device");
+    return(-1);
+  }
+
+  // Subscribe to the position.
+  if(!(this->position_device = deviceTable->GetDevice(this->position_addr)))
+  {
+    PLAYER_ERROR("unable to locate suitable position device");
+    return(-1);
+  }
+  if(this->position_device->Subscribe(this->InQueue) != 0)
+  {
+    PLAYER_ERROR("unable to subscribe to position device");
+    return(-1);
+  }
+
+  this->numscans = 0;
+  this->lastposetime = -1;
+  this->lastpublishposetime = -1;
+
+  return(0);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Shutdown the device
+int RangerPoseInterp::Shutdown()
+{
+
+  this->ranger_device->Unsubscribe(this->InQueue);
+  this->position_device->Unsubscribe(this->InQueue);
+  return(0);
+}
+
+
+int
+RangerPoseInterp::ProcessMessage(QueuePointer & resp_queue,
+                                player_msghdr * hdr,
+                                void * data)
+{
+  // Is it a ranger scan?
+  if(Message::MatchMessage(hdr, PLAYER_MSGTYPE_DATA,
+                           PLAYER_RANGER_DATA_RANGE,
+                           this->ranger_addr))
+  {
+    // are we interpolating?
+    if(!this->interpolate)
+    {
+      // make sure we've gotten at least one pose
+      if(this->lastposetime < 0)
+        return(0);
+
+      // Tag this scan with the last received pose and push it out
+      player_ranger_data_rangestamped_t scanpose;
+      scanpose.have_config = false;
+      scanpose.have_geom = true;
+      scanpose.geom.pose.px = this->lastpose.pos.px;
+      scanpose.geom.pose.py = this->lastpose.pos.py;
+      scanpose.geom.pose.pyaw = this->lastpose.pos.pa;
+      scanpose.geom.element_poses_count = 0;
+      scanpose.geom.element_sizes_count = 0;
+      scanpose.data.ranges_count = 
((player_ranger_data_range_t*)data)->ranges_count;
+      scanpose.data.ranges = ((player_ranger_data_range_t*)data)->ranges;
+
+      this->Publish(this->device_addr,
+                   PLAYER_MSGTYPE_DATA, PLAYER_RANGER_DATA_RANGESTAMPED,
+                   (void*)&scanpose, sizeof(scanpose), &hdr->timestamp);
+      return(0);
+    }
+    else
+    {
+      // Buffer the scan to be pushed out later.
+
+      // is there room?
+      if(this->numscans >= this->maxnumscans)
+      {
+        PLAYER_WARN1("exceeded maximum number of scans to buffer (%d)",
+                     this->maxnumscans);
+        return(0);
+      }
+      // store the scan and timestamp, make sure we deep copy the data
+         player_copy_fn_t copyfunc;
+      if (!(copyfunc = playerxdr_get_copyfunc (PLAYER_RANGER_CODE, 
PLAYER_MSGTYPE_DATA, PLAYER_RANGER_DATA_RANGE)))
+      {
+        PLAYER_ERROR ("Couldn't fund copy function to copy ranger data");
+        return(-1);
+      }
+      if (this->scans[numscans].ranges != NULL) {
+       delete [] this->scans[numscans].ranges;
+       this->scans[numscans].ranges = NULL;
+      }
+      
+      this->scans[numscans].ranges = new double [ 
((player_ranger_data_range_t*)data)->ranges_count ];
+
+      (*copyfunc) (&this->scans[this->numscans],data);
+      //this->scans[this->numscans] = *((player_ranger_data_t*)data);
+      this->scantimes[this->numscans] = hdr->timestamp;
+      this->numscans++;
+      return(0);
+    }
+  }
+  // Is it a new pose?
+  else if(Message::MatchMessage(hdr, PLAYER_MSGTYPE_DATA,
+                                PLAYER_POSITION2D_DATA_STATE,
+                                this->position_addr))
+  {
+    player_position2d_data_t newpose = *((player_position2d_data_t*)data);
+    // Is it the first pose?
+    if(this->lastposetime < 0)
+    {
+      // Just store it.
+      this->lastpose = newpose;
+      this->lastposetime = hdr->timestamp;
+    }
+    else
+    {
+      // are we interpolating?
+      if(this->interpolate)
+      {
+        // Interpolate pose for all buffered scans and send them out
+        double t1 = hdr->timestamp - this->lastposetime;
+        for(int i=0;i<this->numscans;i++)
+        {
+          double t0 = this->scantimes[i] - this->lastposetime;
+          player_ranger_data_rangestamped_t scanpose;
+
+          scanpose.geom.pose.px = this->lastpose.pos.px + t0 *
+                  (newpose.pos.px - this->lastpose.pos.px) / t1;
+          scanpose.geom.pose.py = this->lastpose.pos.py + t0 *
+                  (newpose.pos.py - this->lastpose.pos.py) / t1;
+          scanpose.geom.pose.pyaw = NORMALIZE(this->lastpose.pos.pa + t0 *
+                                             angle_diff(newpose.pos.pa,
+                                                        this->lastpose.pos.pa) 
/ t1);
+         scanpose.have_config = false;
+         scanpose.have_geom = true;
+         scanpose.geom.element_poses_count = 1;
+         scanpose.geom.element_poses = &(scanpose.geom.pose);
+         scanpose.geom.element_sizes_count = 0;
+         
+         scanpose.data.ranges_count = this->scans[i].ranges_count;
+         scanpose.data.ranges = this->scans[i].ranges;
+
+         //          scanpose.data = this->scans[i];
+
+          // Should we publish this scan?  Take account of all the various
+          // thresholds that the user can set.
+          if((this->send_all_scans) ||
+             (this->lastpublishposetime < 0.0) ||
+             ((this->update_thresh[0] >= 0.0) &&
+              (hypot(scanpose.geom.pose.px-this->lastpublishpose.px,
+                     scanpose.geom.pose.py-this->lastpublishpose.py) >=
+               this->update_thresh[0])) ||
+             ((this->update_thresh[1] >= 0.0) &&
+              
(fabs(angle_diff(scanpose.geom.pose.pyaw,this->lastpublishpose.pyaw)) >=
+               this->update_thresh[1])) ||
+             ((this->update_interval >= 0.0) &&
+              ((this->scantimes[i] - this->lastpublishposetime) >=
+               this->update_interval)))
+          {
+           this->Publish(this->device_addr,
+                         PLAYER_MSGTYPE_DATA, PLAYER_RANGER_DATA_RANGESTAMPED,
+                         (void*)&scanpose, sizeof(scanpose),
+                         this->scantimes + i);
+           
+            this->lastpublishposetime = this->scantimes[i];
+            this->lastpublishpose = scanpose.geom.pose;
+          }
+        }
+        this->numscans = 0;
+      }
+      this->lastpose = newpose;
+      this->lastposetime = hdr->timestamp;
+    }
+    return(0);
+  }
+  // Forward any request to the ranger
+  else if(Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, -1, 
this->device_addr))
+  {
+    // Forward the message
+    ranger_device->PutMsg(this->InQueue, hdr, data);
+    // Store the return address for later use
+    this->ret_queue = resp_queue;
+    // Set the message filter to look for the response
+    this->InQueue->SetFilter(this->ranger_addr.host,
+                             this->ranger_addr.robot,
+                             this->ranger_addr.interf,
+                             this->ranger_addr.index,
+                             -1,
+                             hdr->subtype);
+    // No response now; it will come later after we hear back from the
+    // ranger
+    return(0);
+  }
+  // Forward response (success or failure) from the ranger
+  else if((Message::MatchMessage(hdr, PLAYER_MSGTYPE_RESP_ACK,
+                            -1, this->ranger_addr)) ||
+     (Message::MatchMessage(hdr, PLAYER_MSGTYPE_RESP_NACK,
+                            -1, this->ranger_addr)))
+  {
+    // Copy in our address and forward the response
+    hdr->addr = this->device_addr;
+    this->Publish(this->ret_queue, hdr, data);
+    // Clear the filter
+    this->InQueue->ClearFilter();
+
+    return(0);
+  }
+  // Don't know how to handle this message.
+  return(-1);
+}
+


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with 
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
Playerstage-commit mailing list
Playerstage-commit@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/playerstage-commit

Reply via email to