Program Listing for File trajectory_spline_sampler.h

Return to documentation for file (/tmp/ws/src/robot_controllers/robot_controllers/include/robot_controllers/trajectory_spline_sampler.h)

/*********************************************************************
 *  Software License Agreement (BSD License)
 *
 *  Copyright (c) 2014, Fetch Robotics Inc.
 *  Copyright (c) 2013, Unbounded Robotics Inc.
 *  Copyright (c) 2009, Willow Garage, Inc.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of Unbounded Robotics, Willow Garage nor the names
 *     of their contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************/

// Portions are based on splines.h of arm_navigation
// Author: Michael Ferguson, Mrinal Kalakrishnan (splines.h)

#ifndef ROBOT_CONTROLLERS__TRAJECTORY_SPLINE_SAMPLER_H_
#define ROBOT_CONTROLLERS__TRAJECTORY_SPLINE_SAMPLER_H_

#include <vector>

#include "robot_controllers/trajectory.h"

namespace robot_controllers
{

struct Spline
{
  double coef[6];
};

enum SplineType
{
  QUINTIC,
  CUBIC,
  LINEAR
};

static inline void generatePowers(int n, double x, double* powers)
{
  powers[0] = 1.0;
  for (int i = 1; i <= n; ++i)
  {
    powers[i] = powers[i-1]*x;
  }
}

static void QuinticSpline(double p0, double v0, double a0,
                          double p1, double v1, double a1,
                          double t, Spline& s)
{
  if (t == 0.0)
  {
    s.coef[0] = p1;
    s.coef[1] = v1;
    s.coef[2] = 0.5*a1;
    s.coef[3] = 0.0;
    s.coef[4] = 0.0;
    s.coef[5] = 0.0;
  }
  else
  {
    double T[6];
    generatePowers(5, t, T);

    s.coef[0] = p0;
    s.coef[1] = v0;
    s.coef[2] = 0.5*a0;
    s.coef[3] = (-20.0*p0 + 20.0*p1 - 3.0*a0*T[2] + a1*T[2] -
                 12.0*v0*T[1] - 8.0*v1*T[1]) / (2.0*T[3]);
    s.coef[4] = (30.0*p0 - 30.0*p1 + 3.0*a0*T[2] - 2.0*a1*T[2] +
                 16.0*v0*T[1] + 14.0*v1*T[1]) / (2.0*T[4]);
    s.coef[5] = (-12.0*p0 + 12.0*p1 - a0*T[2] + a1*T[2] -
                 6.0*v0*T[1] - 6.0*v1*T[1]) / (2.0*T[5]);
  }
}

static void sampleQuinticSpline(Spline& s, double t,
                                double& position, double& velocity, double& acceleration)
{
  double T[6];
  generatePowers(5, t, T);

  position = T[0]*s.coef[0] +
             T[1]*s.coef[1] +
             T[2]*s.coef[2] +
             T[3]*s.coef[3] +
             T[4]*s.coef[4] +
             T[5]*s.coef[5];

  velocity = T[0]*s.coef[1] +
         2.0*T[1]*s.coef[2] +
         3.0*T[2]*s.coef[3] +
         4.0*T[3]*s.coef[4] +
         5.0*T[4]*s.coef[5];

  acceleration = 2.0*T[0]*s.coef[2] +
                 6.0*T[1]*s.coef[3] +
                12.0*T[2]*s.coef[4] +
                20.0*T[3]*s.coef[5];
}

static void CubicSpline(double p0, double v0, double p1, double v1, double t, Spline& s)
{
  if (t == 0.0)
  {
    s.coef[0] = p1;
    s.coef[1] = v1;
    s.coef[2] = 0.0;
    s.coef[3] = 0.0;
  }
  else
  {
    double T[4];
    generatePowers(3, t, T);

    s.coef[0] = p0;
    s.coef[1] = v0;
    s.coef[2] = (3.0 * p1 - 3.0 * p0 - 2.0 *v0*T[1] - v1*T[1]) / T[2];
    s.coef[3] = (-2.0 * p1 + 2.0 * p0 + v0*T[1] + v1*T[1]) / T[3];
  }
}

static void sampleCubicSpline(Spline s, double t, double& position, double& velocity)
{
  double T[4];
  generatePowers(3, t, T);

  position = T[0]*s.coef[0] +
             T[1]*s.coef[1] +
             T[2]*s.coef[2] +
             T[3]*s.coef[3];

  velocity = T[0]*s.coef[1] +
         2.0*T[1]*s.coef[2] +
         3.0*T[2]*s.coef[3];
}

static void LinearSpline(double p0, double p1, double t, Spline& s)
{
  s.coef[0] = p0;
  s.coef[1] = p1 - p0;
  s.coef[2] = t;
}

static void sampleLinearSpline(Spline& s, double t, double& position)
{
  position = s.coef[0] + (s.coef[1] * t/s.coef[2]);
}

class SplineTrajectorySampler : public TrajectorySampler
{
  struct Segment
  {
    double start_time;
    double end_time;
    int type;
    std::vector<Spline> splines;
  };

public:
  explicit SplineTrajectorySampler(const Trajectory& trajectory) :
    trajectory_(trajectory)
  {
    // Check for invalid trajectory size
    if (trajectory.size() == 0)
    {
      throw 0;
    }

    // Check for number of joints
    size_t num_joints = trajectory.points[0].q.size();

    // Trajectory length one is special
    if (trajectory.size() == 1)
    {
      segments_.resize(1);
      segments_[0].start_time = segments_[0].end_time = trajectory.points[0].time;

      // Set up spline
      segments_[0].splines.resize(num_joints);
      for (size_t j = 0; j < num_joints; ++j)
      {
        LinearSpline(trajectory.points[0].q[j],
                     trajectory.points[0].q[j],
                     0.0,
                     segments_[0].splines[j]);
      }
      segments_[0].type = LINEAR;
      result.q.resize(num_joints);
    }
    else
    {
      // We put a segment in between each pair of points
      segments_.resize(trajectory.size()-1);

      // Setup up the segments
      for (size_t p = 0; p < segments_.size(); ++p)
      {
        // This segment is from p to p+1
        segments_[p].start_time = trajectory.points[p].time;
        segments_[p].end_time = trajectory.points[p+1].time;

        // Set up spline
        segments_[p].splines.resize(num_joints);
        if (trajectory.points[p].qdd.size() == trajectory.points[p].q.size())
        {
          result.q.resize(num_joints);
          result.qd.resize(num_joints);
          result.qdd.resize(num_joints);

          // Have accelerations, will use Quintic.
          for (size_t j = 0; j < num_joints; ++j)
          {
            QuinticSpline(trajectory.points[p].q[j],
                          trajectory.points[p].qd[j],
                          trajectory.points[p].qdd[j],
                          trajectory.points[p+1].q[j],
                          trajectory.points[p+1].qd[j],
                          trajectory.points[p+1].qdd[j],
                          segments_[p].end_time - segments_[p].start_time,
                          segments_[p].splines[j]);
          }
          segments_[p].type = QUINTIC;
        }
        else if (trajectory.points[p].qd.size() == trajectory.points[p].q.size())
        {
          result.q.resize(num_joints);
          result.qd.resize(num_joints);

          // Velocities + Positions, do Cubic.
          for (size_t j = 0; j < num_joints; ++j)
          {
            CubicSpline(trajectory.points[p].q[j],
                        trajectory.points[p].qd[j],
                        trajectory.points[p+1].q[j],
                        trajectory.points[p+1].qd[j],
                        segments_[p].end_time - segments_[p].start_time,
                        segments_[p].splines[j]);
          }
          segments_[p].type = CUBIC;
        }
        else
        {
          result.q.resize(num_joints);

          // Lame -- only positions do linear
          for (size_t j = 0; j < num_joints; ++j)
          {
            LinearSpline(trajectory.points[p].q[j],
                         trajectory.points[p+1].q[j],
                         segments_[p].end_time - segments_[p].start_time,
                         segments_[p].splines[j]);
          }
          segments_[p].type = LINEAR;
        }
      }
    }
    seg_ = -1;
  }

  virtual TrajectoryPoint sample(double time)
  {
    // Which segment to sample from.
    while ((seg_ + 1 < static_cast<int>(segments_.size())) &&
           (segments_[seg_ + 1].start_time < time))
    {
      ++seg_;
    }

    // Check beginning of trajectory, return empty trajectory point if not started.
    if (seg_ == -1)
      return TrajectoryPoint();

    // Check end of trajectory, restrict time.
    if (time > end_time())
      time = end_time();

    // Sample segment for each joint.
    for (size_t i = 0; i < segments_[seg_].splines.size(); ++i)
    {
      if (segments_[seg_].type == QUINTIC)
      {
        sampleQuinticSpline(segments_[seg_].splines[i], time - segments_[seg_].start_time,
                            result.q[i], result.qd[i], result.qdd[i]);
      }
      else if(segments_[seg_].type == CUBIC)
      {
        sampleCubicSpline(segments_[seg_].splines[i], time - segments_[seg_].start_time,
                          result.q[i], result.qd[i]);
      }
      else
      {
        sampleLinearSpline(segments_[seg_].splines[i], time - segments_[seg_].start_time,
                           result.q[i]);
      }
    }

    result.time = time;
    return result;
  }

  virtual double end_time()
  {
    return segments_[segments_.size()-1].end_time;
  }

  virtual Trajectory getTrajectory()
  {
    return trajectory_;
  }

private:
  std::vector<Segment> segments_;
  Trajectory trajectory_;
  TrajectoryPoint result;
  int seg_;
};

}  // namespace robot_controllers

#endif  // ROBOT_CONTROLLERS__TRAJECTORY_SPLINE_SAMPLER_H_