Add python wrapper for trajectory.
Reformatted several files and added some useful comments on how to use
the python wrappers.
Change-Id: I9fd89977e0d4f16fa24d0e3b3b7c6657df865b32
diff --git a/frc971/control_loops/python/lib_spline_test.py b/frc971/control_loops/python/lib_spline_test.py
index 2c77d62..4e725a0 100644
--- a/frc971/control_loops/python/lib_spline_test.py
+++ b/frc971/control_loops/python/lib_spline_test.py
@@ -5,7 +5,7 @@
from numpy.testing import *
import unittest
-from libspline import Spline, DistanceSpline
+from libspline import Spline, DistanceSpline, Trajectory
class TestSpline(unittest.TestCase):
def testSimpleSpline(self):
@@ -23,5 +23,29 @@
dSpline = DistanceSpline([spline])
assert_almost_equal(dSpline.Length(), 5 * math.sqrt(2))
+class TestTrajectory(unittest.TestCase):
+ def testTrajectory(self):
+ points = np.array([[1.0, 2.0, 3.0, 4.0, 5.0, 5.0],
+ [2.0, 3.0, 4.0, 5.0, 6.0, 7.0]])
+ spline = Spline(points)
+ dSpline = DistanceSpline([spline])
+ trajectory = Trajectory(dSpline)
+ trajectory.Plan()
+ plan = trajectory.GetPlanXVA(5.05*1e-3)
+ assert(plan.shape == (3, 611))
+
+ def testLimits(self):
+ """ A plan with a lower limit should take longer. """
+ points = np.array([[1.0, 2.0, 3.0, 4.0, 5.0, 5.0],
+ [2.0, 3.0, 4.0, 5.0, 6.0, 7.0]])
+ spline = Spline(points)
+ dSpline = DistanceSpline([spline])
+ trajectory = Trajectory(dSpline)
+ trajectory.LimitVelocity(0, trajectory.Length(), 3)
+ trajectory.Plan()
+ plan = trajectory.GetPlanXVA(5.05*1e-3)
+ self.assertEqual(plan.shape, (3, 644))
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/frc971/control_loops/python/libspline.py b/frc971/control_loops/python/libspline.py
index ca20f61..fd44cb4 100644
--- a/frc971/control_loops/python/libspline.py
+++ b/frc971/control_loops/python/libspline.py
@@ -24,10 +24,18 @@
libSpline.DistanceSplineDThetaDt.restype = ct.c_double
libSpline.DistanceSplineDDTheta.restype = ct.c_double
libSpline.DistanceSplineLength.restype = ct.c_double
+libSpline.TrajectoryLength.restype = ct.c_double
+libSpline.TrajectoryDistance.resType = ct.c_double
+# Required for trajectory
+libSpline.SetUpLogging()
class Spline:
- """A wrapper around spline.h/cc through libspline.cc."""
+ """
+ A wrapper around spline.h/cc through libspline.cc.
+ The functions return values parameterized by alpha, a number that varies
+ between 0 and 1 along the length of the spline.
+ """
def __init__(self, points):
assert points.shape == (2, 6)
@@ -83,18 +91,22 @@
def ControlPoints(self):
return self.__points
- def Spline(self):
+ def GetSplinePtr(self):
return self.__spline
class DistanceSpline:
- """A wrapper around distance_spline.h/cc through libdistancespline.cc."""
+ """
+ A wrapper around distance_spline.h/cc through libspline.cc.
+ The functions return values parameterized by the distance along the spline,
+ starting at 0 and and ending at the value returned by Length().
+ """
def __init__(self, splines):
self.__spline = None
spline_ptrs = []
for spline in splines:
- spline_ptrs.append(spline.Spline())
+ spline_ptrs.append(spline.GetSplinePtr())
spline_ptrs = np.array(spline_ptrs)
spline_array = np.ctypeslib.as_ctypes(spline_ptrs)
@@ -141,3 +153,89 @@
def Length(self):
return libSpline.DistanceSplineLength(self.__spline)
+
+ def GetSplinePtr(self):
+ return self.__spline
+
+
+class Trajectory:
+ """A wrapper around trajectory.h/cc through libspline.cc."""
+
+ def __init__(self, distance_spline, vmax=10, num_distance=0):
+ self.__trajectory = libSpline.NewTrajectory(
+ distance_spline.GetSplinePtr(), ct.c_double(vmax), num_distance)
+
+ def __del__(self):
+ libSpline.deleteTrajectory(self.__trajectory)
+
+ def SetLongitudalAcceleration(self, accel):
+ libSpline.TrajectorySetLongitualAcceleration(self.__trajectory,
+ ct.c_double(accel))
+
+ def SetLateralAcceleration(self, accel):
+ libSpline.TrajectorySetLateralAcceleration(self.__trajectory,
+ ct.c_double(accel))
+
+ def SetVoltageLimit(self, limit):
+ libSpline.TrajectorySetVoltageLimit(self.__trajectory,
+ ct.c_double(limit))
+
+ def LimitVelocity(self, start_distance, end_distance, max_vel):
+ libSpline.TrajectoryLimitVelocity(self.__trajectory,
+ ct.c_double(start_distance),
+ ct.c_double(end_distance),
+ ct.c_double(max_vel))
+
+ def Plan(self):
+ """
+ Call this to compute the plan, if any of the limits change, a new plan
+ must be generated.
+ """
+ libSpline.TrajectoryPlan(self.__trajectory)
+
+ def Length(self):
+ return libSpline.TrajectoryLength(self.__trajectory)
+
+ def Distance(self, index):
+ return libSpline.TrajectoryDistance(self.__trajectory, index)
+
+ def Distances(self):
+ """
+ Returns an array of distances used to compute the plan. The linear
+ distance between each distance is equal.
+ """
+ path_length = libSpline.TrajectoryGetPathLength(self.__trajectory)
+ distances = numpy.zeros(path_length)
+ libSpline.TrajectoryDistances(self.__trajectory,
+ np.ctypeslib.as_ctypes(distances))
+ return distances
+
+ def GetPlan(self):
+ """
+ Returns the plan as an array of velocities matched to each distance
+ from Distances.
+ """
+ path_length = libSpline.TrajectoryGetPathLength(self.__trajectory)
+ velocities = numpy.zeros(path_length)
+ libSpline.TrajectoryGetPlan(self.__trajectory,
+ np.ctypeslib.as_ctypes(velocities))
+ return velocities
+
+ def GetPlanXVA(self, dt):
+ """
+ dt is in seconds
+ Returns the position, velocity, and acceleration as a function of time.
+ This is returned as a 3xN numpy array where N is proportional to how
+ long it takes to run the path.
+ This is slow so don't call more than once with the same data.
+ """
+ XVAPtr = libSpline.TrajectoryGetPlanXVAPtr(self.__trajectory, int(dt*1e9))
+ XVALength = libSpline.TrajectoryGetVectorLength(XVAPtr)
+ X = np.zeros(XVALength)
+ V = np.zeros(XVALength)
+ A = np.zeros(XVALength)
+ libSpline.TrajectoryGetPlanXVA(XVAPtr, np.ctypeslib.as_ctypes(X),
+ np.ctypeslib.as_ctypes(V),
+ np.ctypeslib.as_ctypes(A))
+ libSpline.TrajectoryDeleteVector(XVAPtr)
+ return np.vstack([X, V, A])