Merge "Add structures for the camera communication"
diff --git a/frc971/control_loops/drivetrain/BUILD b/frc971/control_loops/drivetrain/BUILD
index 8f98953..aee3a37 100644
--- a/frc971/control_loops/drivetrain/BUILD
+++ b/frc971/control_loops/drivetrain/BUILD
@@ -274,7 +274,11 @@
deps = [
":distance_spline",
":spline",
+ ":trajectory",
+ "//aos/logging:implementations",
+ "//aos/network:team_number",
"//third_party/eigen",
+ "//y2019/control_loops/drivetrain:drivetrain_base",
],
linkshared=True,
)
diff --git a/frc971/control_loops/drivetrain/libspline.cc b/frc971/control_loops/drivetrain/libspline.cc
index 117290d..0d14f66 100644
--- a/frc971/control_loops/drivetrain/libspline.cc
+++ b/frc971/control_loops/drivetrain/libspline.cc
@@ -1,62 +1,66 @@
#include <vector>
+#include <string>
#include "Eigen/Dense"
+#include "aos/logging/implementations.h"
+#include "aos/network/team_number.h"
#include "frc971/control_loops/drivetrain/distance_spline.h"
#include "frc971/control_loops/drivetrain/spline.h"
+#include "frc971/control_loops/drivetrain/trajectory.h"
+#include "y2019/control_loops/drivetrain/drivetrain_base.h"
namespace frc971 {
namespace control_loops {
namespace drivetrain {
extern "C" {
- NSpline<6>* NewSpline(double x[6], double y[6]) {
+ // Based on spline.h
+ NSpline<6> *NewSpline(double x[6], double y[6]) {
return new NSpline<6>((::Eigen::Matrix<double, 2, 6>() << x[0], x[1], x[2],
x[3], x[4], x[5], y[0], y[1], y[2], y[3], y[4],
y[5]).finished());
}
- void deleteSpline(NSpline<6>* spline) {
- delete spline;
- }
+ void deleteSpline(NSpline<6> *spline) { delete spline; }
- void SplinePoint(NSpline<6>* spline, double alpha, double* res) {
- double* val = spline->Point(alpha).data();
+ void SplinePoint(NSpline<6> *spline, double alpha, double *res) {
+ double *val = spline->Point(alpha).data();
res[0] = val[0];
res[1] = val[1];
}
- void SplineDPoint(NSpline<6>* spline, double alpha, double* res) {
- double* val = spline->DPoint(alpha).data();
+ void SplineDPoint(NSpline<6> *spline, double alpha, double *res) {
+ double *val = spline->DPoint(alpha).data();
res[0] = val[0];
res[1] = val[1];
}
- void SplineDDPoint(NSpline<6>* spline, double alpha, double* res) {
- double* val = spline->DDPoint(alpha).data();
+ void SplineDDPoint(NSpline<6> *spline, double alpha, double *res) {
+ double *val = spline->DDPoint(alpha).data();
res[0] = val[0];
res[1] = val[1];
}
- void SplineDDDPoint(NSpline<6>* spline, double alpha, double* res) {
- double* val = spline->DDDPoint(alpha).data();
+ void SplineDDDPoint(NSpline<6> *spline, double alpha, double *res) {
+ double *val = spline->DDDPoint(alpha).data();
res[0] = val[0];
res[1] = val[1];
}
- double SplineTheta(NSpline<6>* spline, double alpha) {
+ double SplineTheta(NSpline<6> *spline, double alpha) {
return spline->Theta(alpha);
}
- double SplineDTheta(NSpline<6>* spline, double alpha) {
+ double SplineDTheta(NSpline<6> *spline, double alpha) {
return spline->DTheta(alpha);
}
- double SplineDDTheta(NSpline<6>* spline, double alpha) {
+ double SplineDDTheta(NSpline<6> *spline, double alpha) {
return spline->DDTheta(alpha);
}
- void SplineControlPoints(NSpline<6>* spline, double* x, double* y) {
+ void SplineControlPoints(NSpline<6> *spline, double *x, double *y) {
auto points = spline->control_points();
// Deal with incorrectly strided matrix.
for (int i = 0; i < 6; ++i) {
@@ -65,7 +69,8 @@
}
}
- DistanceSpline* NewDistanceSpline(Spline** splines, int count) {
+ // Based on distance_spline.h
+ DistanceSpline *NewDistanceSpline(Spline **splines, int count) {
::std::vector<Spline> splines_;
for (int i = 0; i < count; ++i) {
splines_.push_back(*splines[i]);
@@ -73,9 +78,7 @@
return new DistanceSpline(::std::vector<Spline>(splines_));
}
- void deleteDistanceSpline(DistanceSpline* spline) {
- delete spline;
- }
+ void deleteDistanceSpline(DistanceSpline *spline) { delete spline; }
void DistanceSplineXY(DistanceSpline *spline, double distance, double *res) {
double *val = spline->XY(distance).data();
@@ -116,6 +119,89 @@
double DistanceSplineLength(DistanceSpline *spline) {
return spline->length();
}
+
+ // Based on trajectory.h
+ Trajectory *NewTrajectory(DistanceSpline *spline, double vmax,
+ int num_distance) {
+ return new Trajectory(
+ spline, ::y2019::control_loops::drivetrain::GetDrivetrainConfig(), vmax,
+ num_distance);
+ }
+
+ void deleteTrajectory(Trajectory *t) { delete t; }
+
+ void TrajectorySetLongitudalAcceleration(Trajectory *t, double accel) {
+ t->set_longitudal_acceleration(accel);
+ }
+
+ void TrajectorySetLateralAcceleration(Trajectory *t, double accel) {
+ t->set_lateral_acceleration(accel);
+ }
+
+ void TrajectorySetVoltageLimit(Trajectory *t, double limit) {
+ t->set_voltage_limit(limit);
+ }
+
+ void TrajectoryLimitVelocity(Trajectory *t, double start, double end,
+ double max) {
+ t->LimitVelocity(start, end, max);
+ }
+
+ void TrajectoryPlan(Trajectory *t) { t->Plan(); }
+
+ double TrajectoryLength(Trajectory *t) { return t->length(); }
+
+ int TrajectoryGetPathLength(Trajectory *t) { return t->plan().size(); }
+
+ // This assumes that res is created in python to be getPathLength() long.
+ // Likely to SEGFAULT otherwise.
+ void Distances(Trajectory *t, double *res) {
+ const ::std::vector<double> &distances = t->Distances();
+ ::std::memcpy(res, distances.data(), sizeof(double) * distances.size());
+ }
+
+ double TrajectoryDistance(Trajectory *t, int index) {
+ return t->Distance(index);
+ }
+
+ // This assumes that res is created in python to be getPathLength() long.
+ // Likely to SEGFAULT otherwise.
+ void TrajectoryGetPlan(Trajectory *t, double *res) {
+ const ::std::vector<double> &velocities = t->plan();
+ ::std::memcpy(res, velocities.data(), sizeof(double) * velocities.size());
+ }
+
+ // Time in in nanoseconds.
+ ::std::vector<::Eigen::Matrix<double, 3, 1>> *TrajectoryGetPlanXVAPtr(
+ Trajectory *t, int dt) {
+ return new ::std::vector<::Eigen::Matrix<double, 3, 1>>(
+ t->PlanXVA(::std::chrono::nanoseconds(dt)));
+ }
+
+ void TrajectoryDeleteVector(
+ ::std::vector<::Eigen::Matrix<double, 3, 1>> *vec) {
+ delete vec;
+ }
+
+ int TrajectoryGetVectorLength(
+ ::std::vector<::Eigen::Matrix<double, 3, 1>> *vec) {
+ return vec->size();
+ }
+
+ void TrajectoryGetPlanXVA(::std::vector<::Eigen::Matrix<double, 3, 1>> *vec,
+ double *X, double *V, double *A) {
+ for (size_t i = 0; i < vec->size(); ++i) {
+ X[i] = (*vec)[0][0];
+ V[i] = (*vec)[0][1];
+ A[i] = (*vec)[0][2];
+ }
+ }
+
+ // Util
+ void SetUpLogging() {
+ ::aos::logging::Init();
+ ::aos::network::OverrideTeamNumber(971);
+ }
}
} // namespace drivetrain
diff --git a/frc971/control_loops/python/angular_system.py b/frc971/control_loops/python/angular_system.py
index e5dd199..8d375f8 100755
--- a/frc971/control_loops/python/angular_system.py
+++ b/frc971/control_loops/python/angular_system.py
@@ -164,7 +164,9 @@
duration=1.0,
use_profile=True,
kick_time=0.5,
- kick_magnitude=0.0):
+ kick_magnitude=0.0,
+ max_velocity=10.0,
+ max_acceleration=70.0):
"""Runs the plant with an initial condition and goal.
Args:
@@ -177,6 +179,8 @@
duration: float, time in seconds to run the simulation for.
kick_time: float, time in seconds to kick the robot.
kick_magnitude: float, disturbance in volts to apply.
+ max_velocity: float, The maximum velocity for the profile.
+ max_acceleration: float, The maximum acceleration for the profile.
"""
t_plot = []
x_plot = []
@@ -197,8 +201,8 @@
(plant.X, numpy.matrix(numpy.zeros((1, 1)))), axis=0)
profile = TrapezoidProfile(plant.dt)
- profile.set_maximum_acceleration(70.0)
- profile.set_maximum_velocity(10.0)
+ profile.set_maximum_acceleration(max_acceleration)
+ profile.set_maximum_velocity(max_velocity)
profile.SetGoal(goal[0, 0])
U_last = numpy.matrix(numpy.zeros((1, 1)))
@@ -326,11 +330,13 @@
kick_magnitude=2.0)
-def PlotMotion(params, R):
+def PlotMotion(params, R, max_velocity=10.0, max_acceleration=70.0):
"""Plots a trapezoidal motion.
Args:
R: numpy.matrix(2, 1), the goal,
+ max_velocity: float, The max velocity of the profile.
+ max_acceleration: float, The max acceleration of the profile.
"""
plant = AngularSystem(params, params.name)
controller = IntegralAngularSystem(params, params.name)
@@ -346,7 +352,9 @@
controller=controller,
observer=observer,
duration=2.0,
- use_profile=True)
+ use_profile=True,
+ max_velocity=max_velocity,
+ max_acceleration=max_acceleration)
def WriteAngularSystem(params, plant_files, controller_files, year_namespaces):
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])
diff --git a/frc971/control_loops/python/linear_system.py b/frc971/control_loops/python/linear_system.py
index 7129e32..322e41a 100755
--- a/frc971/control_loops/python/linear_system.py
+++ b/frc971/control_loops/python/linear_system.py
@@ -171,7 +171,8 @@
use_profile=True,
kick_time=0.5,
kick_magnitude=0.0,
- max_velocity=0.3):
+ max_velocity=0.3,
+ max_acceleration=10.0):
"""Runs the plant with an initial condition and goal.
Args:
@@ -185,6 +186,7 @@
kick_time: float, time in seconds to kick the robot.
kick_magnitude: float, disturbance in volts to apply.
max_velocity: float, the max speed in m/s to profile.
+ max_acceleration: float, the max acceleration in m/s/s to profile.
"""
t_plot = []
x_plot = []
@@ -205,7 +207,7 @@
(plant.X, numpy.matrix(numpy.zeros((1, 1)))), axis=0)
profile = TrapezoidProfile(plant.dt)
- profile.set_maximum_acceleration(10.0)
+ profile.set_maximum_acceleration(max_acceleration)
profile.set_maximum_velocity(max_velocity)
profile.SetGoal(goal[0, 0])
@@ -334,12 +336,13 @@
kick_magnitude=2.0)
-def PlotMotion(params, R, max_velocity=0.3):
+def PlotMotion(params, R, max_velocity=0.3, max_acceleration=10.0):
"""Plots a trapezoidal motion.
Args:
R: numpy.matrix(2, 1), the goal,
max_velocity: float, The max velocity of the profile.
+ max_acceleration: float, The max acceleration of the profile.
"""
plant = LinearSystem(params, params.name)
controller = IntegralLinearSystem(params, params.name)
@@ -356,7 +359,8 @@
observer=observer,
duration=2.0,
use_profile=True,
- max_velocity=max_velocity)
+ max_velocity=max_velocity,
+ max_acceleration=max_acceleration)
def WriteLinearSystem(params, plant_files, controller_files, year_namespaces):
diff --git a/y2019/BUILD b/y2019/BUILD
index 49ae5a5..52c0264 100644
--- a/y2019/BUILD
+++ b/y2019/BUILD
@@ -1,5 +1,23 @@
+cc_library(
+ name = "constants",
+ srcs = [
+ "constants.cc",
+ ],
+ hdrs = [
+ "constants.h",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos:once",
+ "//aos/logging",
+ "//aos/mutex",
+ "//aos/network:team_number",
+ "//frc971:constants",
+ ],
+)
+
py_library(
name = "python_init",
srcs = ["__init__.py"],
visibility = ["//visibility:public"],
-)
+)
\ No newline at end of file
diff --git a/y2019/constants.cc b/y2019/constants.cc
new file mode 100644
index 0000000..0851577
--- /dev/null
+++ b/y2019/constants.cc
@@ -0,0 +1,83 @@
+#include "y2019/constants.h"
+
+#include <inttypes.h>
+
+#include <map>
+
+#if __has_feature(address_sanitizer)
+#include "sanitizer/lsan_interface.h"
+#endif
+
+#include "aos/logging/logging.h"
+#include "aos/mutex/mutex.h"
+#include "aos/network/team_number.h"
+#include "aos/once.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+namespace y2019 {
+namespace constants {
+
+const int Values::kZeroingSampleSize;
+
+namespace {
+
+const uint16_t kCompTeamNumber = 971;
+const uint16_t kPracticeTeamNumber = 9971;
+
+const Values *DoGetValuesForTeam(uint16_t team) {
+ Values *const r = new Values();
+
+ switch (team) {
+ // A set of constants for tests.
+ case 1:
+ break;
+
+ case kCompTeamNumber:
+ break;
+
+ case kPracticeTeamNumber:
+ break;
+
+ default:
+ LOG(FATAL, "unknown team #%" PRIu16 "\n", team);
+ }
+
+ return r;
+}
+
+const Values *DoGetValues() {
+ uint16_t team = ::aos::network::GetTeamNumber();
+ LOG(INFO, "creating a Constants for team %" PRIu16 "\n", team);
+ return DoGetValuesForTeam(team);
+}
+
+} // namespace
+
+const Values &GetValues() {
+ static ::aos::Once<const Values> once(DoGetValues);
+ return *once.Get();
+}
+
+const Values &GetValuesForTeam(uint16_t team_number) {
+ static ::aos::Mutex mutex;
+ ::aos::MutexLocker locker(&mutex);
+
+ // IMPORTANT: This declaration has to stay after the mutex is locked to avoid
+ // race conditions.
+ static ::std::map<uint16_t, const Values *> values;
+
+ if (values.count(team_number) == 0) {
+ values[team_number] = DoGetValuesForTeam(team_number);
+#if __has_feature(address_sanitizer)
+ __lsan_ignore_object(values[team_number]);
+#endif
+ }
+ return *values[team_number];
+}
+
+
+} // namespace constants
+} // namespace y2019
diff --git a/y2019/constants.h b/y2019/constants.h
new file mode 100644
index 0000000..e4dda86
--- /dev/null
+++ b/y2019/constants.h
@@ -0,0 +1,37 @@
+#ifndef Y2019_CONSTANTS_H_
+#define Y2019_CONSTANTS_H_
+
+#include <stdint.h>
+#include <math.h>
+
+#include "frc971/constants.h"
+
+namespace y2019 {
+namespace constants {
+
+// Has all of our "constants", except the ones that come from other places. The
+// ones which change between robots are put together with a workable way to
+// retrieve the values for the current robot.
+
+// Everything is in SI units (volts, radians, meters, seconds, etc).
+// Some of these values are related to the conversion between raw values
+// (encoder counts, voltage, etc) to scaled units (radians, meters, etc).
+//
+// All ratios are from the encoder shaft to the output units.
+
+struct Values {
+ static const int kZeroingSampleSize = 200;
+};
+
+// Creates (once) a Values instance for ::aos::network::GetTeamNumber() and
+// returns a reference to it.
+const Values &GetValues();
+
+// Creates Values instances for each team number it is called with and returns
+// them.
+const Values &GetValuesForTeam(uint16_t team_number);
+
+} // namespace constants
+} // namespace y2019
+
+#endif // Y2019_CONSTANTS_H_
diff --git a/y2019/control_loops/superstructure/BUILD b/y2019/control_loops/superstructure/BUILD
new file mode 100644
index 0000000..79d851a
--- /dev/null
+++ b/y2019/control_loops/superstructure/BUILD
@@ -0,0 +1,40 @@
+package(default_visibility = ["//visibility:public"])
+
+load("//aos/build:queues.bzl", "queue_library")
+
+queue_library(
+ name = "superstructure_queue",
+ srcs = [
+ "superstructure.q",
+ ],
+ deps = [
+ "//aos/controls:control_loop_queues",
+ "//frc971/control_loops:profiled_subsystem_queue",
+ "//frc971/control_loops:queues",
+ ],
+)
+
+cc_library(
+ name = 'superstructure_lib',
+ srcs = [
+ "superstructure.cc",
+ ],
+ hdrs = [
+ "superstructure.h",
+ ],
+ deps = [
+ ":superstructure_queue",
+ "//aos/controls:control_loop",
+ ]
+)
+
+cc_binary(
+ name = "superstructure",
+ srcs = [
+ "superstructure_main.cc",
+ ],
+ deps = [
+ ":superstructure_lib",
+ "//aos:init",
+ ]
+)
\ No newline at end of file
diff --git a/y2019/control_loops/superstructure/superstructure.cc b/y2019/control_loops/superstructure/superstructure.cc
new file mode 100644
index 0000000..2f0832c
--- /dev/null
+++ b/y2019/control_loops/superstructure/superstructure.cc
@@ -0,0 +1,32 @@
+#include "y2019/control_loops/superstructure/superstructure.h"
+
+#include "aos/controls/control_loops.q.h"
+#include "frc971/control_loops/control_loops.q.h"
+
+namespace y2019 {
+namespace control_loops {
+namespace superstructure {
+
+Superstructure::Superstructure(
+ SuperstructureQueue *superstructure_queue)
+ : aos::controls::ControlLoop<SuperstructureQueue>(
+ superstructure_queue) {}
+
+void Superstructure::RunIteration(
+ const SuperstructureQueue::Goal *unsafe_goal,
+ const SuperstructureQueue::Position *position,
+ SuperstructureQueue::Output *output,
+ SuperstructureQueue::Status *status) {
+ (void)unsafe_goal;
+ (void)position;
+ (void)output;
+ (void)status;
+
+ if (WasReset()) {
+ LOG(ERROR, "WPILib reset, restarting\n");
+ }
+}
+
+} // namespace superstructure
+} // namespace control_loops
+} // namespace y2019
\ No newline at end of file
diff --git a/y2019/control_loops/superstructure/superstructure.h b/y2019/control_loops/superstructure/superstructure.h
new file mode 100644
index 0000000..1faa6b4
--- /dev/null
+++ b/y2019/control_loops/superstructure/superstructure.h
@@ -0,0 +1,34 @@
+#ifndef Y2019_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_H_
+#define Y2019_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_H_
+
+#include "aos/controls/control_loop.h"
+#include "y2019/control_loops/superstructure/superstructure.q.h"
+
+namespace y2019 {
+namespace control_loops {
+namespace superstructure {
+
+class Superstructure
+ : public ::aos::controls::ControlLoop<SuperstructureQueue> {
+ public:
+ explicit Superstructure(
+ SuperstructureQueue *my_superstructure =
+ &superstructure_queue);
+
+ protected:
+ virtual void RunIteration(
+ const SuperstructureQueue::Goal *unsafe_goal,
+ const SuperstructureQueue::Position *position,
+ SuperstructureQueue::Output *output,
+ SuperstructureQueue::Status *status) override;
+
+ private:
+
+ DISALLOW_COPY_AND_ASSIGN(Superstructure);
+};
+
+} // namespace superstructure
+} // namespace control_loops
+} // namespace y2019
+
+#endif // Y2019_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_H_
\ No newline at end of file
diff --git a/y2019/control_loops/superstructure/superstructure.q b/y2019/control_loops/superstructure/superstructure.q
new file mode 100644
index 0000000..2cd7238
--- /dev/null
+++ b/y2019/control_loops/superstructure/superstructure.q
@@ -0,0 +1,127 @@
+package y2019.control_loops.superstructure;
+
+import "aos/controls/control_loops.q";
+import "frc971/control_loops/profiled_subsystem.q";
+
+struct ElevatorGoal {
+ // Meters, 0 = lowest position - mechanical hard stop,
+ // positive = upward
+ double height;
+
+ .frc971.ProfileParameters profile_params;
+};
+
+struct IntakeGoal {
+ // Positive is rollers intaking inward.
+ double roller_voltage;
+
+ // 0 = linkage on the sprocket is pointing straight up,
+ // positive = forward
+ double joint_angle;
+
+ .frc971.ProfileParameters profile_params;
+};
+
+struct SuctionGoal {
+ // True = open solenoid (apply suction)
+ // Top/bottom are when wrist is forward
+ bool top;
+ bool bottom;
+};
+
+struct StiltsGoal {
+ // Distance stilts extended out of the bottom of the robot. Positive = down.
+ // 0 is the height such that the bottom of the stilts is tangent to the bottom
+ // of the middle wheels.
+ double height;
+
+ .frc971.ProfileParameters profile_params;
+};
+
+struct WristGoal {
+ // 0 = Straight up parallel to elevator
+ // Positive rotates toward intake from 0
+ double angle;
+ .frc971.ProfileParameters profile_params;
+};
+
+queue_group SuperstructureQueue {
+ implements aos.control_loops.ControlLoop;
+
+ message Goal {
+ ElevatorGoal elevator;
+ IntakeGoal intake;
+ SuctionGoal suction;
+ StiltsGoal stilts;
+ WristGoal wrist;
+ };
+
+ message Status {
+ // All subsystems know their location.
+ bool zeroed;
+
+ // If true, we have aborted. This is the or of all subsystem estops.
+ bool estopped;
+
+ // Whether suction_pressure indicates cargo is held
+ bool has_piece;
+
+ // Status of each subsystem.
+ .frc971.control_loops.AbsoluteProfiledJointStatus elevator;
+ .frc971.control_loops.AbsoluteProfiledJointStatus wrist;
+ .frc971.control_loops.AbsoluteProfiledJointStatus intake;
+ .frc971.control_loops.AbsoluteProfiledJointStatus stilts;
+ };
+
+ message Position {
+ // Input from pressure sensor in psi
+ // 0 = current atmospheric pressure, negative = suction.
+ double suction_pressure;
+
+ // Position of the elevator, 0 at lowest position, positive when up.
+ .frc971.PotAndAbsolutePosition elevator;
+
+ // Position of wrist, 0 when up, positive is rotating toward the front,
+ // over the top.
+ .frc971.PotAndAbsolutePosition wrist;
+
+ // Position of the intake. 0 when rollers are retracted, positive extended.
+ .frc971.PotAndAbsolutePosition intake_joint;
+
+ // Position of the stilts, 0 when retracted (defualt), positive lifts robot.
+ .frc971.PotAndAbsolutePosition stilts;
+ };
+
+ message Output {
+ // Voltage sent to motors moving elevator up/down. Positive is up.
+ double elevator_voltage;
+
+ // Voltage sent to wrist motors on elevator to rotate.
+ // Positive rotates over the top towards the front of the robot.
+ double wrist_voltage;
+
+ // Voltage sent to motors on intake joint. Positive extends rollers.
+ double intake_joint_voltage;
+
+ // Voltage sent to rollers on intake. Positive rolls inward.
+ double intake_roller_voltage;
+
+ // Voltage sent to motors to move stilts height. Positive moves robot upward.
+ double stilts_voltage;
+
+ // True opens solenoid (applies suction)
+ // Top/bottom are when wrist is toward the front of the robot
+ bool intake_suction_top;
+ bool intake_suction_bottom;
+
+ // Voltage sent to the vacuum pump motors.
+ double pump_voltage;
+ };
+
+ queue Goal goal;
+ queue Output output;
+ queue Status status;
+ queue Position position;
+};
+
+queue_group SuperstructureQueue superstructure_queue;
\ No newline at end of file
diff --git a/y2019/control_loops/superstructure/superstructure_main.cc b/y2019/control_loops/superstructure/superstructure_main.cc
new file mode 100644
index 0000000..f3dd1ad
--- /dev/null
+++ b/y2019/control_loops/superstructure/superstructure_main.cc
@@ -0,0 +1,11 @@
+#include "y2019/control_loops/superstructure/superstructure.h"
+
+#include "aos/init.h"
+
+int main() {
+ ::aos::Init();
+ ::y2019::control_loops::superstructure::Superstructure superstructure;
+ superstructure.Run();
+ ::aos::Cleanup();
+ return 0;
+}