Ported the polydrivetrain code over to C++.
diff --git a/frc971/control_loops/drivetrain/drivetrain.cc b/frc971/control_loops/drivetrain/drivetrain.cc
index 548113e..122e2d2 100644
--- a/frc971/control_loops/drivetrain/drivetrain.cc
+++ b/frc971/control_loops/drivetrain/drivetrain.cc
@@ -4,11 +4,15 @@
 #include <sched.h>
 #include <cmath>
 #include <memory>
+#include "Eigen/Dense"
 
 #include "aos/common/logging/logging.h"
 #include "aos/common/queue.h"
+#include "aos/controls/polytope.h"
+#include "aos/common/commonmath.h"
 #include "frc971/control_loops/state_feedback_loop.h"
 #include "frc971/control_loops/drivetrain/drivetrain_motor_plant.h"
+#include "frc971/control_loops/drivetrain/polydrivetrain_motor_plant.h"
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
 #include "frc971/queues/GyroAngle.q.h"
 #include "frc971/queues/Piston.q.h"
@@ -21,6 +25,53 @@
 // Width of the robot.
 const double width = 22.0 / 100.0 * 2.54;
 
+Eigen::Matrix<double, 2, 1> CoerceGoal(aos::controls::HPolytope<2> &region,
+                                       const Eigen::Matrix<double, 1, 2> &K,
+                                       double w,
+                                       const Eigen::Matrix<double, 2, 1> &R) {
+  if (region.IsInside(R)) {
+    return R;
+  }
+  Eigen::Matrix<double, 2, 1> parallel_vector;
+  Eigen::Matrix<double, 2, 1> perpendicular_vector;
+  perpendicular_vector = K.transpose().normalized();
+  parallel_vector << perpendicular_vector(1, 0), -perpendicular_vector(0, 0);
+
+  aos::controls::HPolytope<1> t_poly(
+      region.H() * parallel_vector,
+      region.k() - region.H() * perpendicular_vector * w);
+
+  Eigen::Matrix<double, 1, Eigen::Dynamic> vertices = t_poly.Vertices();
+  if (vertices.innerSize() > 0) {
+    double min_distance_sqr = 0;
+    Eigen::Matrix<double, 2, 1> closest_point;
+    for (int i = 0; i < vertices.innerSize(); i++) {
+      Eigen::Matrix<double, 2, 1> point;
+      point = parallel_vector * vertices(0, i) + perpendicular_vector * w;
+      const double length = (R - point).squaredNorm();
+      if (i == 0 || length < min_distance_sqr) {
+        closest_point = point;
+        min_distance_sqr = length;
+      }
+    }
+    return closest_point;
+  } else {
+    Eigen::Matrix<double, 2, Eigen::Dynamic> region_vertices =
+        region.Vertices();
+    double min_distance;
+    int closest_i = 0;
+    for (int i = 0; i < region_vertices.outerSize(); i++) {
+      const double length = ::std::abs(
+          (perpendicular_vector.transpose() * (region_vertices.col(i)))(0, 0));
+      if (i == 0 || length < min_distance) {
+        closest_i = i;
+        min_distance = length;
+      }
+    }
+    return region_vertices.col(closest_i);
+  }
+}
+
 class DrivetrainMotorsSS {
  public:
   DrivetrainMotorsSS ()
@@ -86,23 +137,155 @@
   bool _control_loop_driving;
 };
 
+class PolyDrivetrain {
+ public:
+  PolyDrivetrain()
+      : U_Poly_((Eigen::Matrix<double, 4, 2>() << /*[[*/ 1, 0 /*]*/,
+                 /*[*/ -1, 0 /*]*/,
+                 /*[*/ 0, 1 /*]*/,
+                 /*[*/ 0, -1 /*]]*/).finished(),
+                (Eigen::Matrix<double, 4, 1>() << /*[[*/ 12 /*]*/,
+                 /*[*/ 12 /*]*/,
+                 /*[*/ 12 /*]*/,
+                 /*[*/ 12 /*]]*/).finished()),
+        loop_(new StateFeedbackLoop<2, 2, 2>(MakeVDrivetrainLoop())) {
+
+    ttrust_ = 1.0;
+
+    wheel_ = 0.0;
+    throttle_ = 0.0;
+    quickturn_ = false;
+    highgear_ = true;
+  }
+  void SetGoal(double wheel, double throttle, bool quickturn, bool highgear) {
+    const double kWheelNonLinearity = 0.1;
+    // Apply a sin function that's scaled to make it feel better.
+    const double angular_range = M_PI_2 * kWheelNonLinearity;
+    wheel_ = sin(angular_range * wheel) / sin(angular_range);
+    wheel_ = sin(angular_range * wheel_) / sin(angular_range);
+    throttle_ = throttle;
+    quickturn_ = quickturn;
+    highgear_ = highgear;
+    if (highgear_) {
+      loop_->set_controller_index(3);
+    } else {
+      loop_->set_controller_index(0);
+    }
+  }
+  double FilterVelocity(double throttle) {
+    const Eigen::Matrix<double, 2, 2> FF =
+        loop_->B().inverse() *
+        (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
+
+    constexpr int kHighGearController = 3;
+    const Eigen::Matrix<double, 2, 2> FF_high =
+        loop_->controller(kHighGearController).plant.B.inverse() *
+        (Eigen::Matrix<double, 2, 2>::Identity() -
+         loop_->controller(kHighGearController).plant.A);
+
+    ::Eigen::Matrix<double, 1, 2> FF_sum = FF.colwise().sum();
+    int min_FF_sum_index;
+    const double min_FF_sum = FF_sum.minCoeff(&min_FF_sum_index);
+    const double min_K_sum = loop_->K().col(min_FF_sum_index).sum();
+    const double high_min_FF_sum = FF_high.col(0).sum();
+
+    const double adjusted_ff_voltage = ::aos::Clip(
+        throttle * 12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0);
+    return ((adjusted_ff_voltage +
+             ttrust_ * min_K_sum * (loop_->X_hat(0, 0) + loop_->X_hat(1, 0)) /
+                 2.0) /
+            (ttrust_ * min_K_sum + min_FF_sum));
+  }
+
+  void Update() {
+    // FF * X = U (steady state)
+    const Eigen::Matrix<double, 2, 2> FF =
+        loop_->B().inverse() *
+        (Eigen::Matrix<double, 2, 2>::Identity() - loop_->A());
+
+    // Invert the plant to figure out how the velocity filter would have to work
+    // out in order to filter out the forwards negative inertia.
+    // This math assumes that the left and right power and velocity are equals,
+    // and that the plant is the same on the left and right.
+    const double fvel = FilterVelocity(throttle_);
+
+    const double sign_svel = wheel_ * ((fvel > 0.0) ? 1.0 : -1.0);
+    const double svel = ::std::abs(fvel) * wheel_;
+    const double left_velocity = fvel - svel;
+    const double right_velocity = fvel + svel;
+
+    // K * R = w
+    Eigen::Matrix<double,1,2> equality_k;
+    equality_k << 1 + sign_svel, -(1 - sign_svel);
+    const double equality_w = 0.0;
+
+    // Integrate velocity to get the position.
+    // This position is used to get integral control.
+    loop_->R << left_velocity, right_velocity;
+
+    // Construct a constraint on R by manipulating the constraint on U
+    ::aos::controls::HPolytope<2> R_poly = ::aos::controls::HPolytope<2>(
+        U_Poly_.H() * (loop_->K() + FF),
+        U_Poly_.k() + U_Poly_.H() * loop_->K() * loop_->X_hat);
+
+    // Limit R back inside the box.
+    const Eigen::Matrix<double, 2, 1> boxed_R =
+        CoerceGoal(R_poly, equality_k, equality_w, loop_->R);
+
+    const Eigen::Matrix<double, 2, 1> FF_volts = FF * boxed_R;
+    const Eigen::Matrix<double, 2, 1> U_ideal =
+        loop_->K() * (boxed_R - loop_->X_hat) + FF_volts;
+
+    for (int i = 0; i < 2; i++) {
+      loop_->U[i] = ::aos::Clip(U_ideal[i], -12, 12);
+    }
+
+    // TODO(austin): Feed back?
+    loop_->X_hat = loop_->A() * loop_->X_hat + loop_->B() * loop_->U;
+  }
+
+  void SendMotors(Drivetrain::Output *output) {
+    LOG(DEBUG, "left pwm: %f right pwm: %f wheel: %f throttle: %f\n",
+        loop_->U(0, 0), loop_->U(1, 0), wheel_, throttle_);
+    output->left_voltage = loop_->U(0, 0);
+    output->right_voltage = loop_->U(1, 0);
+    if (highgear_) {
+      shifters.MakeWithBuilder().set(false).Send();
+    } else {
+      shifters.MakeWithBuilder().set(true).Send();
+    }
+  }
+
+ private:
+  const ::aos::controls::HPolytope<2> U_Poly_;
+
+  ::std::unique_ptr<StateFeedbackLoop<2, 2, 2>> loop_;
+
+  double ttrust_;
+  double wheel_;
+  double throttle_;
+  bool quickturn_;
+  bool highgear_;
+};
+
+
 class DrivetrainMotorsOL {
  public:
   DrivetrainMotorsOL() {
     _old_wheel = 0.0;
-    _wheel = 0.0;
-    _throttle = 0.0;
-    _quickturn = false;
-    _highgear = true;
+    wheel_ = 0.0;
+    throttle_ = 0.0;
+    quickturn_ = false;
+    highgear_ = true;
     _neg_inertia_accumulator = 0.0;
     _left_pwm = 0.0;
     _right_pwm = 0.0;
   }
   void SetGoal(double wheel, double throttle, bool quickturn, bool highgear) {
-    _wheel = wheel;
-    _throttle = throttle;
-    _quickturn = quickturn;
-    _highgear = highgear;
+    wheel_ = wheel;
+    throttle_ = throttle;
+    quickturn_ = quickturn;
+    highgear_ = highgear;
     _left_pwm = 0.0;
     _right_pwm = 0.0;
   }
@@ -113,35 +296,35 @@
     float linear_power;
     double wheel;
 
-    double neg_inertia = _wheel - _old_wheel;
-    _old_wheel = _wheel;
+    double neg_inertia = wheel_ - _old_wheel;
+    _old_wheel = wheel_;
 
     double wheelNonLinearity;
-    if (_highgear) {
+    if (highgear_) {
       wheelNonLinearity = 0.1;  // used to be csvReader->TURN_NONLIN_HIGH
       // Apply a sin function that's scaled to make it feel better.
       const double angular_range = M_PI / 2.0 * wheelNonLinearity;
-      wheel = sin(angular_range * _wheel) / sin(angular_range);
+      wheel = sin(angular_range * wheel_) / sin(angular_range);
       wheel = sin(angular_range * wheel) / sin(angular_range);
     } else {
       wheelNonLinearity = 0.2;  // used to be csvReader->TURN_NONLIN_LOW
       // Apply a sin function that's scaled to make it feel better.
       const double angular_range = M_PI / 2.0 * wheelNonLinearity;
-      wheel = sin(angular_range * _wheel) / sin(angular_range);
+      wheel = sin(angular_range * wheel_) / sin(angular_range);
       wheel = sin(angular_range * wheel) / sin(angular_range);
       wheel = sin(angular_range * wheel) / sin(angular_range);
     }
 
     static const double kThrottleDeadband = 0.05;
-    if (::std::abs(_throttle) < kThrottleDeadband) {
-      _throttle = 0;
+    if (::std::abs(throttle_) < kThrottleDeadband) {
+      throttle_ = 0;
     } else {
-      _throttle = copysign((::std::abs(_throttle) - kThrottleDeadband) /
-                           (1.0 - kThrottleDeadband), _throttle);
+      throttle_ = copysign((::std::abs(throttle_) - kThrottleDeadband) /
+                           (1.0 - kThrottleDeadband), throttle_);
     }
 
     double neg_inertia_scalar;
-    if (_highgear) {
+    if (highgear_) {
       neg_inertia_scalar = 8.0;  // used to be csvReader->NEG_INTERTIA_HIGH
       sensitivity = 1.22; // used to be csvReader->SENSE_HIGH
     } else {
@@ -168,9 +351,9 @@
       _neg_inertia_accumulator = 0;
     }
 
-    linear_power = _throttle;
+    linear_power = throttle_;
 
-    if (_quickturn) {
+    if (quickturn_) {
       double qt_angular_power = wheel;
       if (::std::abs(linear_power) < 0.2) {
         if (qt_angular_power > 1) qt_angular_power = 1.0;
@@ -179,7 +362,7 @@
         qt_angular_power = 0.0;
       }
       overPower = 1.0;
-      if (_highgear) {
+      if (highgear_) {
         sensitivity = 1.0;
       } else {
         sensitivity = 1.0;
@@ -187,7 +370,7 @@
       angular_power = wheel;
     } else {
       overPower = 0.0;
-      angular_power = ::std::abs(_throttle) * wheel * sensitivity;
+      angular_power = ::std::abs(throttle_) * wheel * sensitivity;
     }
 
     _right_pwm = _left_pwm = linear_power;
@@ -211,12 +394,12 @@
 
   void SendMotors(Drivetrain::Output *output) {
     LOG(DEBUG, "left pwm: %f right pwm: %f wheel: %f throttle: %f\n",
-        _left_pwm, _right_pwm, _wheel, _throttle);
+        _left_pwm, _right_pwm, wheel_, throttle_);
     if (output) {
       output->left_voltage = _left_pwm * 12.0;
       output->right_voltage = _right_pwm * 12.0;
     }
-    if (_highgear) {
+    if (highgear_) {
       shifters.MakeWithBuilder().set(false).Send();
     } else {
       shifters.MakeWithBuilder().set(true).Send();
@@ -225,10 +408,10 @@
 
  private:
   double _old_wheel;
-  double _wheel;
-  double _throttle;
-  bool _quickturn;
-  bool _highgear;
+  double wheel_;
+  double throttle_;
+  bool quickturn_;
+  bool highgear_;
   double _neg_inertia_accumulator;
   double _left_pwm;
   double _right_pwm;
@@ -240,7 +423,7 @@
                                   Drivetrain::Status * /*status*/) {
   // TODO(aschuh): These should be members of the class.
   static DrivetrainMotorsSS dt_closedloop;
-  static DrivetrainMotorsOL dt_openloop;
+  static PolyDrivetrain dt_openloop;
 
   bool bad_pos = false;
   if (position == NULL) {
@@ -257,20 +440,19 @@
   double left_goal = goal->left_goal;
   double right_goal = goal->right_goal;
 
-  dt_closedloop.SetGoal(left_goal, goal->left_velocity_goal,
-                        right_goal, goal->right_velocity_goal);
+  dt_closedloop.SetGoal(left_goal, goal->left_velocity_goal, right_goal,
+                        goal->right_velocity_goal);
   if (!bad_pos) {
     const double left_encoder = position->left_encoder;
     const double right_encoder = position->right_encoder;
     if (gyro.FetchLatest()) {
-      dt_closedloop.SetPosition(left_encoder, right_encoder,
-          gyro->angle, control_loop_driving);
+      dt_closedloop.SetPosition(left_encoder, right_encoder, gyro->angle,
+                                control_loop_driving);
     } else {
       dt_closedloop.SetRawPosition(left_encoder, right_encoder);
     }
   }
   dt_closedloop.Update(position, output == NULL);
-  //dt_closedloop.PrintMotors();
   dt_openloop.SetGoal(wheel, throttle, quickturn, highgear);
   dt_openloop.Update();
   if (control_loop_driving) {
diff --git a/frc971/control_loops/drivetrain/drivetrain.gyp b/frc971/control_loops/drivetrain/drivetrain.gyp
index 8e98e4b..89a2321 100644
--- a/frc971/control_loops/drivetrain/drivetrain.gyp
+++ b/frc971/control_loops/drivetrain/drivetrain.gyp
@@ -23,15 +23,18 @@
       'sources': [
         'drivetrain.cc',
         'drivetrain_motor_plant.cc',
+        'polydrivetrain_motor_plant.cc',
       ],
       'dependencies': [
         'drivetrain_loop',
         '<(AOS)/common/common.gyp:controls',
         '<(DEPTH)/frc971/frc971.gyp:common',
+        '<(DEPTH)/aos/build/externals.gyp:libcdd',
         '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
         '<(DEPTH)/frc971/queues/queues.gyp:queues',
       ],
       'export_dependent_settings': [
+        '<(DEPTH)/aos/build/externals.gyp:libcdd',
         '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
         '<(AOS)/common/common.gyp:controls',
         'drivetrain_loop',
diff --git a/frc971/control_loops/drivetrain/drivetrain.h b/frc971/control_loops/drivetrain/drivetrain.h
index 7b07247..63ca76c 100644
--- a/frc971/control_loops/drivetrain/drivetrain.h
+++ b/frc971/control_loops/drivetrain/drivetrain.h
@@ -1,12 +1,20 @@
 #ifndef FRC971_CONTROL_LOOPS_DRIVETRAIN_H_
 #define FRC971_CONTROL_LOOPS_DRIVETRAIN_H_
 
+#include "Eigen/Dense"
+
+#include "aos/controls/polytope.h"
 #include "aos/common/control_loop/ControlLoop.h"
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
 
 namespace frc971 {
 namespace control_loops {
 
+Eigen::Matrix<double, 2, 1> CoerceGoal(aos::controls::HPolytope<2> &region,
+                                       const Eigen::Matrix<double, 1, 2> &K,
+                                       double w,
+                                       const Eigen::Matrix<double, 2, 1> &R);
+
 class DrivetrainLoop
     : public aos::control_loops::ControlLoop<control_loops::Drivetrain> {
  public:
diff --git a/frc971/control_loops/drivetrain/drivetrain_lib_test.cc b/frc971/control_loops/drivetrain/drivetrain_lib_test.cc
index d15de44..43b6443 100644
--- a/frc971/control_loops/drivetrain/drivetrain_lib_test.cc
+++ b/frc971/control_loops/drivetrain/drivetrain_lib_test.cc
@@ -5,6 +5,8 @@
 #include "gtest/gtest.h"
 #include "aos/common/queue.h"
 #include "aos/common/queue_testutils.h"
+#include "aos/controls/polytope.h"
+
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
 #include "frc971/control_loops/drivetrain/drivetrain.h"
 #include "frc971/control_loops/state_feedback_loop.h"
@@ -18,6 +20,17 @@
 namespace control_loops {
 namespace testing {
 
+class Environment : public ::testing::Environment {
+ public:
+  virtual ~Environment() {}
+  // how to set up the environment.
+  virtual void SetUp() {
+    aos::controls::HPolytope<0>::Init();
+  }
+};
+::testing::Environment* const holder_env =
+  ::testing::AddGlobalTestEnvironment(new Environment);
+
 
 // Class which simulates the drivetrain and sends out queue messages containing the
 // position.
@@ -182,6 +195,108 @@
   VerifyNearGoal();
 }
 
+::aos::controls::HPolytope<2> MakeBox(double x1_min, double x1_max,
+                                      double x2_min, double x2_max) {
+  Eigen::Matrix<double, 4, 2> box_H;
+  box_H << /*[[*/ 1.0, 0.0 /*]*/,
+            /*[*/-1.0, 0.0 /*]*/,
+            /*[*/ 0.0, 1.0 /*]*/,
+            /*[*/ 0.0,-1.0 /*]]*/;
+  Eigen::Matrix<double, 4, 1> box_k;
+  box_k << /*[[*/ x1_max /*]*/,
+            /*[*/-x1_min /*]*/,
+            /*[*/ x2_max /*]*/,
+            /*[*/-x2_min /*]]*/;
+  ::aos::controls::HPolytope<2> t_poly(box_H, box_k);
+  return t_poly;
+}
+
+class CoerceGoalTest : public ::testing::Test {
+ public:
+  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+};
+
+// WHOOOHH!
+TEST_F(CoerceGoalTest, Inside) {
+  ::aos::controls::HPolytope<2> box = MakeBox(1, 2, 1, 2);
+
+  Eigen::Matrix<double, 1, 2> K;
+  K << /*[[*/ 1, -1 /*]]*/;
+
+  Eigen::Matrix<double, 2, 1> R;
+  R << /*[[*/ 1.5, 1.5 /*]]*/;
+
+  Eigen::Matrix<double, 2, 1> output =
+      ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+  EXPECT_EQ(R(0, 0), output(0, 0));
+  EXPECT_EQ(R(1, 0), output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, Outside_Inside_Intersect) {
+  ::aos::controls::HPolytope<2> box = MakeBox(1, 2, 1, 2);
+
+  Eigen::Matrix<double, 1, 2> K;
+  K << 1, -1;
+
+  Eigen::Matrix<double, 2, 1> R;
+  R << 5, 5;
+
+  Eigen::Matrix<double, 2, 1> output =
+      ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+  EXPECT_EQ(2.0, output(0, 0));
+  EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, Outside_Inside_no_Intersect) {
+  ::aos::controls::HPolytope<2> box = MakeBox(3, 4, 1, 2);
+
+  Eigen::Matrix<double, 1, 2> K;
+  K << 1, -1;
+
+  Eigen::Matrix<double, 2, 1> R;
+  R << 5, 5;
+
+  Eigen::Matrix<double, 2, 1> output =
+      ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+  EXPECT_EQ(3.0, output(0, 0));
+  EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, Middle_Of_Edge) {
+  ::aos::controls::HPolytope<2> box = MakeBox(0, 4, 1, 2);
+
+  Eigen::Matrix<double, 1, 2> K;
+  K << -1, 1;
+
+  Eigen::Matrix<double, 2, 1> R;
+  R << 5, 5;
+
+  Eigen::Matrix<double, 2, 1> output =
+      ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+  EXPECT_EQ(2.0, output(0, 0));
+  EXPECT_EQ(2.0, output(1, 0));
+}
+
+TEST_F(CoerceGoalTest, PerpendicularLine) {
+  ::aos::controls::HPolytope<2> box = MakeBox(1, 2, 1, 2);
+
+  Eigen::Matrix<double, 1, 2> K;
+  K << 1, 1;
+
+  Eigen::Matrix<double, 2, 1> R;
+  R << 5, 5;
+
+  Eigen::Matrix<double, 2, 1> output =
+      ::frc971::control_loops::CoerceGoal(box, K, 0, R);
+
+  EXPECT_EQ(1.0, output(0, 0));
+  EXPECT_EQ(1.0, output(1, 0));
+}
+
 }  // namespace testing
 }  // namespace control_loops
 }  // namespace frc971
diff --git a/frc971/control_loops/drivetrain/polydrivetrain_motor_plant.cc b/frc971/control_loops/drivetrain/polydrivetrain_motor_plant.cc
new file mode 100644
index 0000000..31a029f
--- /dev/null
+++ b/frc971/control_loops/drivetrain/polydrivetrain_motor_plant.cc
@@ -0,0 +1,125 @@
+#include "frc971/control_loops/drivetrain/polydrivetrain_motor_plant.h"
+
+#include <vector>
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowLowPlantCoefficients() {
+  Eigen::Matrix<double, 2, 2> A;
+  A << 0.899177606502, 0.000686937184856, 0.000686937184856, 0.899177606502;
+  Eigen::Matrix<double, 2, 2> B;
+  B << 0.0186835844877, -0.000127297602107, -0.000127297602107, 0.0186835844877;
+  Eigen::Matrix<double, 2, 2> C;
+  C << 1.0, 0.0, 0.0, 1.0;
+  Eigen::Matrix<double, 2, 2> D;
+  D << 0.0, 0.0, 0.0, 0.0;
+  Eigen::Matrix<double, 2, 1> U_max;
+  U_max << 12.0, 12.0;
+  Eigen::Matrix<double, 2, 1> U_min;
+  U_min << -12.0, -12.0;
+  return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowHighPlantCoefficients() {
+  Eigen::Matrix<double, 2, 2> A;
+  A << 0.89917740051, 0.000149762601927, 0.000716637450627, 0.978035563679;
+  Eigen::Matrix<double, 2, 2> B;
+  B << 0.0186836226605, -6.07092175404e-05, -0.00013280141337, 0.0089037164526;
+  Eigen::Matrix<double, 2, 2> C;
+  C << 1.0, 0.0, 0.0, 1.0;
+  Eigen::Matrix<double, 2, 2> D;
+  D << 0.0, 0.0, 0.0, 0.0;
+  Eigen::Matrix<double, 2, 1> U_max;
+  U_max << 12.0, 12.0;
+  Eigen::Matrix<double, 2, 1> U_min;
+  U_min << -12.0, -12.0;
+  return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighLowPlantCoefficients() {
+  Eigen::Matrix<double, 2, 2> A;
+  A << 0.978035563679, 0.000716637450627, 0.000149762601927, 0.89917740051;
+  Eigen::Matrix<double, 2, 2> B;
+  B << 0.0089037164526, -0.00013280141337, -6.07092175404e-05, 0.0186836226605;
+  Eigen::Matrix<double, 2, 2> C;
+  C << 1.0, 0.0, 0.0, 1.0;
+  Eigen::Matrix<double, 2, 2> D;
+  D << 0.0, 0.0, 0.0, 0.0;
+  Eigen::Matrix<double, 2, 1> U_max;
+  U_max << 12.0, 12.0;
+  Eigen::Matrix<double, 2, 1> U_min;
+  U_min << -12.0, -12.0;
+  return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighHighPlantCoefficients() {
+  Eigen::Matrix<double, 2, 2> A;
+  A << 0.978035518136, 0.000156145735499, 0.000156145735499, 0.978035518136;
+  Eigen::Matrix<double, 2, 2> B;
+  B << 0.0089037349145, -6.32967463335e-05, -6.32967463335e-05, 0.0089037349145;
+  Eigen::Matrix<double, 2, 2> C;
+  C << 1.0, 0.0, 0.0, 1.0;
+  Eigen::Matrix<double, 2, 2> D;
+  D << 0.0, 0.0, 0.0, 0.0;
+  Eigen::Matrix<double, 2, 1> U_max;
+  U_max << 12.0, 12.0;
+  Eigen::Matrix<double, 2, 1> U_min;
+  U_min << -12.0, -12.0;
+  return StateFeedbackPlantCoefficients<2, 2, 2>(A, B, C, D, U_max, U_min);
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowLowController() {
+  Eigen::Matrix<double, 2, 2> L;
+  L << 0.879177606502, 0.000686937184856, 0.000686937184856, 0.879177606502;
+  Eigen::Matrix<double, 2, 2> K;
+  K << 32.0714744818, 0.255280724401, 0.255280724401, 32.0714744818;
+  return StateFeedbackController<2, 2, 2>(L, K, MakeVelocityDrivetrainLowLowPlantCoefficients());
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowHighController() {
+  Eigen::Matrix<double, 2, 2> L;
+  L << 0.879178111554, 0.000716636558747, 0.000716636558747, 0.958034852635;
+  Eigen::Matrix<double, 2, 2> K;
+  K << 32.0714744826, 0.255470827831, 0.558842435881, 76.1557821586;
+  return StateFeedbackController<2, 2, 2>(L, K, MakeVelocityDrivetrainLowHighPlantCoefficients());
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighLowController() {
+  Eigen::Matrix<double, 2, 2> L;
+  L << 0.958040379369, 0.000149803514919, 0.000149803514919, 0.87917258482;
+  Eigen::Matrix<double, 2, 2> K;
+  K << 76.1557821586, 0.558842435881, 0.255470827831, 32.0714744826;
+  return StateFeedbackController<2, 2, 2>(L, K, MakeVelocityDrivetrainHighLowPlantCoefficients());
+}
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighHighController() {
+  Eigen::Matrix<double, 2, 2> L;
+  L << 0.958035518136, 0.000156145735499, 0.000156145735499, 0.958035518136;
+  Eigen::Matrix<double, 2, 2> K;
+  K << 76.1557821586, 0.558929371597, 0.558929371597, 76.1557821586;
+  return StateFeedbackController<2, 2, 2>(L, K, MakeVelocityDrivetrainHighHighPlantCoefficients());
+}
+
+StateFeedbackPlant<2, 2, 2> MakeVDrivetrainPlant() {
+  ::std::vector<StateFeedbackPlantCoefficients<2, 2, 2> *> plants(4);
+  plants[0] = new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainLowLowPlantCoefficients());
+  plants[1] = new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainLowHighPlantCoefficients());
+  plants[2] = new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainHighLowPlantCoefficients());
+  plants[3] = new StateFeedbackPlantCoefficients<2, 2, 2>(MakeVelocityDrivetrainHighHighPlantCoefficients());
+  return StateFeedbackPlant<2, 2, 2>(plants);
+}
+
+StateFeedbackLoop<2, 2, 2> MakeVDrivetrainLoop() {
+  ::std::vector<StateFeedbackController<2, 2, 2> *> controllers(4);
+  controllers[0] = new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainLowLowController());
+  controllers[1] = new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainLowHighController());
+  controllers[2] = new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainHighLowController());
+  controllers[3] = new StateFeedbackController<2, 2, 2>(MakeVelocityDrivetrainHighHighController());
+  return StateFeedbackLoop<2, 2, 2>(controllers);
+}
+
+}  // namespace control_loops
+}  // namespace frc971
diff --git a/frc971/control_loops/drivetrain/polydrivetrain_motor_plant.h b/frc971/control_loops/drivetrain/polydrivetrain_motor_plant.h
new file mode 100644
index 0000000..99b113e
--- /dev/null
+++ b/frc971/control_loops/drivetrain/polydrivetrain_motor_plant.h
@@ -0,0 +1,32 @@
+#ifndef FRC971_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_MOTOR_PLANT_H_
+#define FRC971_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_MOTOR_PLANT_H_
+
+#include "frc971/control_loops/state_feedback_loop.h"
+
+namespace frc971 {
+namespace control_loops {
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowLowPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowLowController();
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainLowHighPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainLowHighController();
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighLowPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighLowController();
+
+StateFeedbackPlantCoefficients<2, 2, 2> MakeVelocityDrivetrainHighHighPlantCoefficients();
+
+StateFeedbackController<2, 2, 2> MakeVelocityDrivetrainHighHighController();
+
+StateFeedbackPlant<2, 2, 2> MakeVDrivetrainPlant();
+
+StateFeedbackLoop<2, 2, 2> MakeVDrivetrainLoop();
+
+}  // namespace control_loops
+}  // namespace frc971
+
+#endif  // FRC971_CONTROL_LOOPS_DRIVETRAIN_POLYDRIVETRAIN_MOTOR_PLANT_H_
diff --git a/frc971/control_loops/python/polydrivetrain.py b/frc971/control_loops/python/polydrivetrain.py
index 1c7aca2..9fa7841 100755
--- a/frc971/control_loops/python/polydrivetrain.py
+++ b/frc971/control_loops/python/polydrivetrain.py
@@ -4,6 +4,7 @@
 import sys
 import polytope
 import drivetrain
+import control_loop
 import controls
 from matplotlib import pylab
 
@@ -94,22 +95,30 @@
     return closest_point
 
 
-class VelocityDrivetrainModel(object):
-  def __init__(self, left_low=True, right_low=True):
+class VelocityDrivetrainModel(control_loop.ControlLoop):
+  def __init__(self, left_low=True, right_low=True, name="VelocityDrivetrainModel"):
+    super(VelocityDrivetrainModel, self).__init__(name)
     self._drivetrain = drivetrain.Drivetrain(left_low=left_low,
                                              right_low=right_low)
-    self.A = numpy.matrix(
-        [[self._drivetrain.A[1, 1], self._drivetrain.A[1, 3]],
-         [self._drivetrain.A[3, 1], self._drivetrain.A[3, 3]]])
+    self.dt = 0.01
+    self.A_continuous = numpy.matrix(
+        [[self._drivetrain.A_continuous[1, 1], self._drivetrain.A_continuous[1, 3]],
+         [self._drivetrain.A_continuous[3, 1], self._drivetrain.A_continuous[3, 3]]])
 
-    self.B = numpy.matrix(
-        [[self._drivetrain.B[1, 0], self._drivetrain.B[1, 1]],
-         [self._drivetrain.B[3, 0], self._drivetrain.B[3, 1]]])
+    self.B_continuous = numpy.matrix(
+        [[self._drivetrain.B_continuous[1, 0], self._drivetrain.B_continuous[1, 1]],
+         [self._drivetrain.B_continuous[3, 0], self._drivetrain.B_continuous[3, 1]]])
+    self.C = numpy.matrix(numpy.eye(2));
+    self.D = numpy.matrix(numpy.zeros((2, 2)));
+
+    self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
+                                               self.B_continuous, self.dt)
 
     # FF * X = U (steady state)
     self.FF = self.B.I * (numpy.eye(2) - self.A)
 
-    self.K = controls.dplace(self.A, self.B, [0.3, 0.3])
+    self.PlaceControllerPoles([0.3, 0.3])
+    self.PlaceObserverPoles([0.02, 0.02])
 
     self.G_high = self._drivetrain.G_high
     self.G_low = self._drivetrain.G_low
@@ -118,13 +127,16 @@
     self.Kv = self._drivetrain.Kv
     self.Kt = self._drivetrain.Kt
 
+    self.U_max = self._drivetrain.U_max
+    self.U_min = self._drivetrain.U_min
+
 
 class VelocityDrivetrain(object):
   def __init__(self):
-    self.drivetrain_low_low = VelocityDrivetrainModel(left_low=True, right_low=True)
-    self.drivetrain_low_high = VelocityDrivetrainModel(left_low=True, right_low=False)
-    self.drivetrain_high_low = VelocityDrivetrainModel(left_low=False, right_low=True)
-    self.drivetrain_high_high = VelocityDrivetrainModel(left_low=False, right_low=False)
+    self.drivetrain_low_low = VelocityDrivetrainModel(left_low=True, right_low=True, name='VelocityDrivetrainLowLow')
+    self.drivetrain_low_high = VelocityDrivetrainModel(left_low=True, right_low=False, name='VelocityDrivetrainLowHigh')
+    self.drivetrain_high_low = VelocityDrivetrainModel(left_low=False, right_low=True, name = 'VelocityDrivetrainHighLow')
+    self.drivetrain_high_high = VelocityDrivetrainModel(left_low=False, right_low=False, name = 'VelocityDrivetrainHighHigh')
 
     # X is [lvel, rvel]
     self.X = numpy.matrix(
@@ -215,13 +227,11 @@
     # Do this by finding the index of FF that has the lowest value, and computing
     # the sums using that index.
     FF_sum = self.CurrentDrivetrain().FF.sum(axis=1)
-    max_FF_sum_index = numpy.argmin(FF_sum)
-    max_FF_sum = FF_sum[max_FF_sum_index, 0]
-    max_K_sum = self.CurrentDrivetrain().K[max_FF_sum_index, :].sum()
-    max_A_sum = self.CurrentDrivetrain().A[max_FF_sum_index, :].sum()
-    max_B_sum = self.CurrentDrivetrain().B[max_FF_sum_index, :].sum()
+    min_FF_sum_index = numpy.argmin(FF_sum)
+    min_FF_sum = FF_sum[min_FF_sum_index, 0]
+    min_K_sum = self.CurrentDrivetrain().K[min_FF_sum_index, :].sum()
     # Compute the FF sum for high gear.
-    high_max_FF_sum = self.drivetrain_high_high.FF[0, :].sum()
+    high_min_FF_sum = self.drivetrain_high_high.FF[0, :].sum()
 
     # U = self.K[0, :].sum() * (R - x_avg) + self.FF[0, :].sum() * R
     # throttle * 12.0 = (self.K[0, :].sum() + self.FF[0, :].sum()) * R
@@ -233,12 +243,12 @@
     # U = (K + FF) * R - K * X
     # (K + FF) ^-1 * (U + K * X) = R
 
-    # Scale throttle by max_FF_sum / high_max_FF_sum.  This will make low gear
+    # Scale throttle by min_FF_sum / high_min_FF_sum.  This will make low gear
     # have the same velocity goal as high gear, and so that the robot will hold
     # the same speed for the same throttle for all gears.
-    adjusted_ff_voltage = numpy.clip(throttle * 12.0 * max_FF_sum / high_max_FF_sum, -12.0, 12.0)
-    return ((adjusted_ff_voltage + self.ttrust * max_K_sum * (self.X[0, 0] + self.X[1, 0]) / 2.0)
-            / (self.ttrust * max_K_sum + max_FF_sum))
+    adjusted_ff_voltage = numpy.clip(throttle * 12.0 * min_FF_sum / high_min_FF_sum, -12.0, 12.0)
+    return ((adjusted_ff_voltage + self.ttrust * min_K_sum * (self.X[0, 0] + self.X[1, 0]) / 2.0)
+            / (self.ttrust * min_K_sum + min_FF_sum))
 
   def Update(self, throttle, steering):
     # Shift into the gear which sends the most power to the floor.
@@ -303,6 +313,22 @@
 def main(argv):
   vdrivetrain = VelocityDrivetrain()
 
+  if len(argv) != 3:
+    print "Expected .h file name and .cc file name"
+  else:
+    loop_writer = control_loop.ControlLoopWriter(
+        "VDrivetrain", [vdrivetrain.drivetrain_low_low,
+                        vdrivetrain.drivetrain_low_high,
+                        vdrivetrain.drivetrain_high_low,
+                        vdrivetrain.drivetrain_high_high])
+
+    if argv[1][-3:] == '.cc':
+      loop_writer.Write(argv[2], argv[1])
+    else:
+      loop_writer.Write(argv[1], argv[2])
+    return
+
+
   vl_plot = []
   vr_plot = []
   ul_plot = []
diff --git a/frc971/control_loops/state_feedback_loop.h b/frc971/control_loops/state_feedback_loop.h
index ff5e9d8..4c35419 100644
--- a/frc971/control_loops/state_feedback_loop.h
+++ b/frc971/control_loops/state_feedback_loop.h
@@ -223,15 +223,18 @@
   Eigen::Matrix<double, number_of_outputs, 1> U_ff;
   Eigen::Matrix<double, number_of_outputs, 1> Y;
 
-  ::std::vector<StateFeedbackController<number_of_states, number_of_inputs,
-                                        number_of_outputs> *> controllers_;
-
   const StateFeedbackController<
       number_of_states, number_of_inputs, number_of_outputs>
           &controller() const {
     return *controllers_[controller_index_];
   }
 
+  const StateFeedbackController<
+      number_of_states, number_of_inputs, number_of_outputs>
+          &controller(int index) const {
+    return *controllers_[index];
+  }
+
   void Reset() {
     X_hat.setZero();
     R.setZero();
@@ -324,6 +327,9 @@
   void controller_index() const { return controller_index_; }
 
  protected:
+  ::std::vector<StateFeedbackController<number_of_states, number_of_inputs,
+                                        number_of_outputs> *> controllers_;
+
   // these are accessible from non-templated subclasses
   static const int kNumStates = number_of_states;
   static const int kNumOutputs = number_of_outputs;
diff --git a/frc971/control_loops/update_polydrivetrain.sh b/frc971/control_loops/update_polydrivetrain.sh
new file mode 100755
index 0000000..71e5d09
--- /dev/null
+++ b/frc971/control_loops/update_polydrivetrain.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+#
+# Updates the drivetrain controller.
+
+./python/polydrivetrain.py drivetrain/polydrivetrain_motor_plant.h \
+    drivetrain/polydrivetrain_motor_plant.cc