Build hybrid velocity loop for each drivetrain

This provides a way to get to the continuous time drivetrain model from
C++ without doing crazy codegen for all gears.

Change-Id: Iadd7388b74628a94a74ec19efb972fa01efe70c9
diff --git a/frc971/control_loops/python/polydrivetrain.py b/frc971/control_loops/python/polydrivetrain.py
index 29fdeef..48adbb9 100644
--- a/frc971/control_loops/python/polydrivetrain.py
+++ b/frc971/control_loops/python/polydrivetrain.py
@@ -120,7 +120,19 @@
     self.FF = self.B.I * (numpy.eye(2) - self.A)
 
     self.PlaceControllerPoles(drivetrain_params.controller_poles)
+    # Build a kalman filter for the velocity.  We don't care what the gains
+    # are, but the hybrid kalman filter that we want to write to disk to get
+    # access to A_continuous and B_continuous needs this for completeness.
+    self.Q_continuous = numpy.matrix([[(0.5 ** 2.0), 0.0], [0.0, (0.5 ** 2.0)]])
+    self.R_continuous = numpy.matrix([[(0.1 ** 2.0), 0.0], [0.0, (0.1 ** 2.0)]])
     self.PlaceObserverPoles(drivetrain_params.observer_poles)
+    _, _, self.Q, self.R = controls.kalmd(
+        A_continuous=self.A_continuous, B_continuous=self.B_continuous,
+        Q_continuous=self.Q_continuous, R_continuous=self.R_continuous,
+        dt=self.dt)
+
+    self.KalmanGain, self.P_steady_state = controls.kalman(
+        A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
 
     self.G_high = self._drivetrain.G_high
     self.G_low = self._drivetrain.G_low
@@ -139,18 +151,18 @@
   SHIFTING_UP = 'up'
   SHIFTING_DOWN = 'down'
 
-  def __init__(self, drivetrain_params):
+  def __init__(self, drivetrain_params, name='VelocityDrivetrain'):
     self.drivetrain_low_low = VelocityDrivetrainModel(
-        left_low=True, right_low=True, name='VelocityDrivetrainLowLow',
+        left_low=True, right_low=True, name=name + 'LowLow',
         drivetrain_params=drivetrain_params)
     self.drivetrain_low_high = VelocityDrivetrainModel(
-        left_low=True, right_low=False, name='VelocityDrivetrainLowHigh',
+        left_low=True, right_low=False, name=name + 'LowHigh',
         drivetrain_params=drivetrain_params)
     self.drivetrain_high_low = VelocityDrivetrainModel(
-        left_low=False, right_low=True, name = 'VelocityDrivetrainHighLow',
+        left_low=False, right_low=True, name = name + 'HighLow',
         drivetrain_params=drivetrain_params)
     self.drivetrain_high_high = VelocityDrivetrainModel(
-        left_low=False, right_low=False, name = 'VelocityDrivetrainHighHigh',
+        left_low=False, right_low=False, name = name + 'HighHigh',
         drivetrain_params=drivetrain_params)
 
     # X is [lvel, rvel]
@@ -406,9 +418,12 @@
                self.left_gear, self.left_shifter_position,
                self.right_gear, self.right_shifter_position)
 
-def WritePolyDrivetrain(drivetrain_files, motor_files, year_namespace,
-                        drivetrain_params, scalar_type='double'):
+def WritePolyDrivetrain(drivetrain_files, motor_files, hybrid_files,
+                        year_namespace, drivetrain_params,
+                        scalar_type='double'):
   vdrivetrain = VelocityDrivetrain(drivetrain_params)
+  hybrid_vdrivetrain = VelocityDrivetrain(drivetrain_params,
+                                          name="HybridVelocityDrivetrain")
   if isinstance(year_namespace, list):
     namespaces = year_namespace
   else:
@@ -423,6 +438,18 @@
 
   dog_loop_writer.Write(drivetrain_files[0], drivetrain_files[1])
 
+  hybrid_loop_writer = control_loop.ControlLoopWriter(
+      "HybridVelocityDrivetrain", [hybrid_vdrivetrain.drivetrain_low_low,
+                     hybrid_vdrivetrain.drivetrain_low_high,
+                     hybrid_vdrivetrain.drivetrain_high_low,
+                     hybrid_vdrivetrain.drivetrain_high_high],
+                     namespaces=namespaces,
+                     scalar_type=scalar_type,
+                     plant_type='StateFeedbackHybridPlant',
+                     observer_type='HybridKalman')
+
+  hybrid_loop_writer.Write(hybrid_files[0], hybrid_files[1])
+
   cim_writer = control_loop.ControlLoopWriter("CIM", [CIM()], scalar_type=scalar_type)
 
   cim_writer.Write(motor_files[0], motor_files[1])
diff --git a/motors/seems_reasonable/BUILD b/motors/seems_reasonable/BUILD
index 4b2919e..ccd4c36 100644
--- a/motors/seems_reasonable/BUILD
+++ b/motors/seems_reasonable/BUILD
@@ -53,6 +53,8 @@
         "polydrivetrain_dog_motor_plant.cc",
         "polydrivetrain_cim_plant.h",
         "polydrivetrain_cim_plant.cc",
+        "hybrid_velocity_drivetrain.h",
+        "hybrid_velocity_drivetrain.cc",
     ],
     cmd = "$(location :polydrivetrain) $(OUTS)",
     restricted_to = mcu_cpus,
@@ -66,6 +68,7 @@
     srcs = [
         "drivetrain_dog_motor_plant.cc",
         "polydrivetrain_dog_motor_plant.cc",
+        # Note: skip the hybrid filter since we don't need it down here.
     ],
     hdrs = [
         "drivetrain_dog_motor_plant.h",
diff --git a/motors/seems_reasonable/polydrivetrain.py b/motors/seems_reasonable/polydrivetrain.py
index 4d3720e..a006c67 100644
--- a/motors/seems_reasonable/polydrivetrain.py
+++ b/motors/seems_reasonable/polydrivetrain.py
@@ -19,12 +19,14 @@
 def main(argv):
     if FLAGS.plot:
         polydrivetrain.PlotPolyDrivetrainMotions(drivetrain.kDrivetrain)
-    elif len(argv) != 5:
+    elif len(argv) != 7:
         glog.fatal('Expected .h file name and .cc file name')
     else:
         polydrivetrain.WritePolyDrivetrain(
             argv[1:3],
-            argv[3:5], ['motors', 'seems_reasonable'],
+            argv[3:5],
+            argv[5:7],
+            ['motors', 'seems_reasonable'],
             drivetrain.kDrivetrain,
             scalar_type='float')
 
diff --git a/y2012/control_loops/drivetrain/BUILD b/y2012/control_loops/drivetrain/BUILD
index 24fde84..a0aa4cc 100644
--- a/y2012/control_loops/drivetrain/BUILD
+++ b/y2012/control_loops/drivetrain/BUILD
@@ -24,6 +24,8 @@
         "polydrivetrain_dog_motor_plant.cc",
         "polydrivetrain_cim_plant.h",
         "polydrivetrain_cim_plant.cc",
+        "hybrid_velocity_drivetrain.h",
+        "hybrid_velocity_drivetrain.cc",
     ],
     cmd = "$(location //y2012/control_loops/python:polydrivetrain) $(OUTS)",
     tools = [
@@ -36,15 +38,18 @@
     name = "polydrivetrain_plants",
     srcs = [
         "drivetrain_dog_motor_plant.cc",
+        "hybrid_velocity_drivetrain.cc",
         "kalman_drivetrain_motor_plant.cc",
         "polydrivetrain_dog_motor_plant.cc",
     ],
     hdrs = [
         "drivetrain_dog_motor_plant.h",
+        "hybrid_velocity_drivetrain.h",
         "kalman_drivetrain_motor_plant.h",
         "polydrivetrain_dog_motor_plant.h",
     ],
     deps = [
+        "//frc971/control_loops:hybrid_state_feedback_loop",
         "//frc971/control_loops:state_feedback_loop",
     ],
 )
diff --git a/y2012/control_loops/python/polydrivetrain.py b/y2012/control_loops/python/polydrivetrain.py
index a6b3b18..efe4511 100755
--- a/y2012/control_loops/python/polydrivetrain.py
+++ b/y2012/control_loops/python/polydrivetrain.py
@@ -19,10 +19,10 @@
 def main(argv):
   if FLAGS.plot:
     polydrivetrain.PlotPolyDrivetrainMotions(drivetrain.kDrivetrain)
-  elif len(argv) != 5:
+  elif len(argv) != 7:
     glog.fatal('Expected .h file name and .cc file name')
   else:
-    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], 'y2012',
+    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], argv[5:7], 'y2012',
                                        drivetrain.kDrivetrain)
 
 if __name__ == '__main__':
diff --git a/y2014/control_loops/drivetrain/BUILD b/y2014/control_loops/drivetrain/BUILD
index 062fa5b..bed5cd9 100644
--- a/y2014/control_loops/drivetrain/BUILD
+++ b/y2014/control_loops/drivetrain/BUILD
@@ -24,6 +24,8 @@
         "polydrivetrain_dog_motor_plant.cc",
         "polydrivetrain_cim_plant.h",
         "polydrivetrain_cim_plant.cc",
+        "hybrid_velocity_drivetrain.h",
+        "hybrid_velocity_drivetrain.cc",
     ],
     cmd = "$(location //y2014/control_loops/python:polydrivetrain) $(OUTS)",
     tools = [
@@ -36,15 +38,18 @@
     name = "polydrivetrain_plants",
     srcs = [
         "drivetrain_dog_motor_plant.cc",
+        "hybrid_velocity_drivetrain.cc",
         "kalman_drivetrain_motor_plant.cc",
         "polydrivetrain_dog_motor_plant.cc",
     ],
     hdrs = [
         "drivetrain_dog_motor_plant.h",
+        "hybrid_velocity_drivetrain.h",
         "kalman_drivetrain_motor_plant.h",
         "polydrivetrain_dog_motor_plant.h",
     ],
     deps = [
+        "//frc971/control_loops:hybrid_state_feedback_loop",
         "//frc971/control_loops:state_feedback_loop",
     ],
 )
diff --git a/y2014/control_loops/python/polydrivetrain.py b/y2014/control_loops/python/polydrivetrain.py
index b2c8d18..98910a1 100755
--- a/y2014/control_loops/python/polydrivetrain.py
+++ b/y2014/control_loops/python/polydrivetrain.py
@@ -19,10 +19,10 @@
 def main(argv):
   if FLAGS.plot:
     polydrivetrain.PlotPolyDrivetrainMotions(drivetrain.kDrivetrain)
-  elif len(argv) != 5:
+  elif len(argv) != 7:
     glog.fatal('Expected .h file name and .cc file name')
   else:
-    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], 'y2014',
+    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], argv[5:7], 'y2014',
                                        drivetrain.kDrivetrain)
 
 if __name__ == '__main__':
diff --git a/y2014_bot3/control_loops/drivetrain/BUILD b/y2014_bot3/control_loops/drivetrain/BUILD
index 38061fa..53605f3 100644
--- a/y2014_bot3/control_loops/drivetrain/BUILD
+++ b/y2014_bot3/control_loops/drivetrain/BUILD
@@ -24,6 +24,8 @@
         "polydrivetrain_dog_motor_plant.cc",
         "polydrivetrain_cim_plant.h",
         "polydrivetrain_cim_plant.cc",
+        "hybrid_velocity_drivetrain.h",
+        "hybrid_velocity_drivetrain.cc",
     ],
     cmd = "$(location //y2014_bot3/control_loops/python:polydrivetrain) $(OUTS)",
     tools = [
@@ -36,15 +38,18 @@
     name = "polydrivetrain_plants",
     srcs = [
         "drivetrain_dog_motor_plant.cc",
+        "hybrid_velocity_drivetrain.cc",
         "kalman_drivetrain_motor_plant.cc",
         "polydrivetrain_dog_motor_plant.cc",
     ],
     hdrs = [
         "drivetrain_dog_motor_plant.h",
+        "hybrid_velocity_drivetrain.h",
         "kalman_drivetrain_motor_plant.h",
         "polydrivetrain_dog_motor_plant.h",
     ],
     deps = [
+        "//frc971/control_loops:hybrid_state_feedback_loop",
         "//frc971/control_loops:state_feedback_loop",
     ],
 )
diff --git a/y2014_bot3/control_loops/python/polydrivetrain.py b/y2014_bot3/control_loops/python/polydrivetrain.py
index 97fa70a..d98e003 100755
--- a/y2014_bot3/control_loops/python/polydrivetrain.py
+++ b/y2014_bot3/control_loops/python/polydrivetrain.py
@@ -19,11 +19,11 @@
 def main(argv):
   if FLAGS.plot:
     polydrivetrain.PlotPolyDrivetrainMotions(drivetrain.kDrivetrain)
-  elif len(argv) != 5:
+  elif len(argv) != 7:
     glog.fatal('Expected .h file name and .cc file name')
   else:
-    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], 'y2014_bot3',
-                                       drivetrain.kDrivetrain)
+    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], argv[5:7],
+                                       'y2014_bot3', drivetrain.kDrivetrain)
 
 if __name__ == '__main__':
   argv = FLAGS(sys.argv)
diff --git a/y2016/control_loops/drivetrain/BUILD b/y2016/control_loops/drivetrain/BUILD
index 39f2b56..629c957 100644
--- a/y2016/control_loops/drivetrain/BUILD
+++ b/y2016/control_loops/drivetrain/BUILD
@@ -1,5 +1,3 @@
-package(default_visibility = ["//visibility:public"])
-
 load("//aos/build:queues.bzl", "queue_library")
 
 genrule(
@@ -14,7 +12,6 @@
     tools = [
         "//y2016/control_loops/python:drivetrain",
     ],
-    visibility = ["//visibility:private"],
 )
 
 genrule(
@@ -24,27 +21,32 @@
         "polydrivetrain_dog_motor_plant.cc",
         "polydrivetrain_cim_plant.h",
         "polydrivetrain_cim_plant.cc",
+        "hybrid_velocity_drivetrain.h",
+        "hybrid_velocity_drivetrain.cc",
     ],
     cmd = "$(location //y2016/control_loops/python:polydrivetrain) $(OUTS)",
     tools = [
         "//y2016/control_loops/python:polydrivetrain",
     ],
-    visibility = ["//visibility:private"],
 )
 
 cc_library(
     name = "polydrivetrain_plants",
     srcs = [
         "drivetrain_dog_motor_plant.cc",
+        "hybrid_velocity_drivetrain.cc",
         "kalman_drivetrain_motor_plant.cc",
         "polydrivetrain_dog_motor_plant.cc",
     ],
     hdrs = [
         "drivetrain_dog_motor_plant.h",
+        "hybrid_velocity_drivetrain.h",
         "kalman_drivetrain_motor_plant.h",
         "polydrivetrain_dog_motor_plant.h",
     ],
+    visibility = ["//visibility:public"],
     deps = [
+        "//frc971/control_loops:hybrid_state_feedback_loop",
         "//frc971/control_loops:state_feedback_loop",
     ],
 )
@@ -57,6 +59,7 @@
     hdrs = [
         "drivetrain_base.h",
     ],
+    visibility = ["//visibility:public"],
     deps = [
         ":polydrivetrain_plants",
         "//frc971:shifter_hall_effect",
@@ -70,6 +73,7 @@
     srcs = [
         "drivetrain_main.cc",
     ],
+    visibility = ["//visibility:public"],
     deps = [
         ":drivetrain_base",
         "//aos:init",
diff --git a/y2016/control_loops/python/polydrivetrain.py b/y2016/control_loops/python/polydrivetrain.py
index 87d33e6..cdc2881 100755
--- a/y2016/control_loops/python/polydrivetrain.py
+++ b/y2016/control_loops/python/polydrivetrain.py
@@ -19,10 +19,10 @@
 def main(argv):
   if FLAGS.plot:
     polydrivetrain.PlotPolyDrivetrainMotions(drivetrain.kDrivetrain)
-  elif len(argv) != 5:
+  elif len(argv) != 7:
     glog.fatal('Expected .h file name and .cc file name')
   else:
-    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], 'y2016',
+    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], argv[5:7], 'y2016',
                                        drivetrain.kDrivetrain)
 
 if __name__ == '__main__':
diff --git a/y2017/control_loops/drivetrain/BUILD b/y2017/control_loops/drivetrain/BUILD
index 3dcec74..2eb2a0f 100644
--- a/y2017/control_loops/drivetrain/BUILD
+++ b/y2017/control_loops/drivetrain/BUILD
@@ -24,6 +24,8 @@
         "polydrivetrain_dog_motor_plant.cc",
         "polydrivetrain_cim_plant.h",
         "polydrivetrain_cim_plant.cc",
+        "hybrid_velocity_drivetrain.h",
+        "hybrid_velocity_drivetrain.cc",
     ],
     cmd = "$(location //y2017/control_loops/python:polydrivetrain) $(OUTS)",
     tools = [
@@ -36,15 +38,18 @@
     name = "polydrivetrain_plants",
     srcs = [
         "drivetrain_dog_motor_plant.cc",
+        "hybrid_velocity_drivetrain.cc",
         "kalman_drivetrain_motor_plant.cc",
         "polydrivetrain_dog_motor_plant.cc",
     ],
     hdrs = [
         "drivetrain_dog_motor_plant.h",
+        "hybrid_velocity_drivetrain.h",
         "kalman_drivetrain_motor_plant.h",
         "polydrivetrain_dog_motor_plant.h",
     ],
     deps = [
+        "//frc971/control_loops:hybrid_state_feedback_loop",
         "//frc971/control_loops:state_feedback_loop",
     ],
 )
diff --git a/y2017/control_loops/python/polydrivetrain.py b/y2017/control_loops/python/polydrivetrain.py
index 701308e..7ddd630 100755
--- a/y2017/control_loops/python/polydrivetrain.py
+++ b/y2017/control_loops/python/polydrivetrain.py
@@ -19,10 +19,10 @@
 def main(argv):
   if FLAGS.plot:
     polydrivetrain.PlotPolyDrivetrainMotions(drivetrain.kDrivetrain)
-  elif len(argv) != 5:
+  elif len(argv) != 7:
     glog.fatal('Expected .h file name and .cc file name')
   else:
-    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], 'y2017',
+    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], argv[5:7], 'y2017',
                                        drivetrain.kDrivetrain)
 
 if __name__ == '__main__':
diff --git a/y2017_bot3/control_loops/drivetrain/BUILD b/y2017_bot3/control_loops/drivetrain/BUILD
index cef561e..679e4bf 100644
--- a/y2017_bot3/control_loops/drivetrain/BUILD
+++ b/y2017_bot3/control_loops/drivetrain/BUILD
@@ -24,6 +24,8 @@
         "polydrivetrain_dog_motor_plant.cc",
         "polydrivetrain_cim_plant.h",
         "polydrivetrain_cim_plant.cc",
+        "hybrid_velocity_drivetrain.h",
+        "hybrid_velocity_drivetrain.cc",
     ],
     cmd = "$(location //y2017_bot3/control_loops/python:polydrivetrain) $(OUTS)",
     tools = [
@@ -36,15 +38,18 @@
     name = "polydrivetrain_plants",
     srcs = [
         "drivetrain_dog_motor_plant.cc",
+        "hybrid_velocity_drivetrain.cc",
         "kalman_drivetrain_motor_plant.cc",
         "polydrivetrain_dog_motor_plant.cc",
     ],
     hdrs = [
         "drivetrain_dog_motor_plant.h",
+        "hybrid_velocity_drivetrain.h",
         "kalman_drivetrain_motor_plant.h",
         "polydrivetrain_dog_motor_plant.h",
     ],
     deps = [
+        "//frc971/control_loops:hybrid_state_feedback_loop",
         "//frc971/control_loops:state_feedback_loop",
     ],
 )
diff --git a/y2017_bot3/control_loops/python/polydrivetrain.py b/y2017_bot3/control_loops/python/polydrivetrain.py
index 92aa6e4..955f78f 100755
--- a/y2017_bot3/control_loops/python/polydrivetrain.py
+++ b/y2017_bot3/control_loops/python/polydrivetrain.py
@@ -19,11 +19,11 @@
 def main(argv):
   if FLAGS.plot:
     polydrivetrain.PlotPolyDrivetrainMotions(drivetrain.kDrivetrain)
-  elif len(argv) != 5:
+  elif len(argv) != 7:
     glog.fatal('Expected .h file name and .cc file name')
   else:
-    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], 'y2017_bot3',
-                                       drivetrain.kDrivetrain)
+    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], argv[5:7],
+                                       'y2017_bot3', drivetrain.kDrivetrain)
 
 if __name__ == '__main__':
   argv = FLAGS(sys.argv)
diff --git a/y2018/control_loops/drivetrain/BUILD b/y2018/control_loops/drivetrain/BUILD
index 48bfeb0..1824196 100644
--- a/y2018/control_loops/drivetrain/BUILD
+++ b/y2018/control_loops/drivetrain/BUILD
@@ -21,6 +21,8 @@
         "polydrivetrain_dog_motor_plant.cc",
         "polydrivetrain_cim_plant.h",
         "polydrivetrain_cim_plant.cc",
+        "hybrid_velocity_drivetrain.h",
+        "hybrid_velocity_drivetrain.cc",
     ],
     cmd = "$(location //y2018/control_loops/python:polydrivetrain) $(OUTS)",
     tools = [
@@ -32,16 +34,19 @@
     name = "polydrivetrain_plants",
     srcs = [
         "drivetrain_dog_motor_plant.cc",
+        "hybrid_velocity_drivetrain.cc",
         "kalman_drivetrain_motor_plant.cc",
         "polydrivetrain_dog_motor_plant.cc",
     ],
     hdrs = [
         "drivetrain_dog_motor_plant.h",
+        "hybrid_velocity_drivetrain.h",
         "kalman_drivetrain_motor_plant.h",
         "polydrivetrain_dog_motor_plant.h",
     ],
     visibility = ["//visibility:public"],
     deps = [
+        "//frc971/control_loops:hybrid_state_feedback_loop",
         "//frc971/control_loops:state_feedback_loop",
     ],
 )
diff --git a/y2018/control_loops/python/polydrivetrain.py b/y2018/control_loops/python/polydrivetrain.py
index e08d3b6..1652b99 100644
--- a/y2018/control_loops/python/polydrivetrain.py
+++ b/y2018/control_loops/python/polydrivetrain.py
@@ -19,10 +19,10 @@
 def main(argv):
   if FLAGS.plot:
     polydrivetrain.PlotPolyDrivetrainMotions(drivetrain.kDrivetrain)
-  elif len(argv) != 5:
+  elif len(argv) != 7:
     glog.fatal('Expected .h file name and .cc file name')
   else:
-    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], 'y2018',
+    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], argv[5:7], 'y2018',
                                        drivetrain.kDrivetrain)
 
 if __name__ == '__main__':
diff --git a/y2018_bot3/control_loops/drivetrain/BUILD b/y2018_bot3/control_loops/drivetrain/BUILD
index a0d5683..d3fabe1 100644
--- a/y2018_bot3/control_loops/drivetrain/BUILD
+++ b/y2018_bot3/control_loops/drivetrain/BUILD
@@ -21,6 +21,8 @@
         "polydrivetrain_dog_motor_plant.cc",
         "polydrivetrain_cim_plant.h",
         "polydrivetrain_cim_plant.cc",
+        "hybrid_velocity_drivetrain.h",
+        "hybrid_velocity_drivetrain.cc",
     ],
     cmd = "$(location //y2018_bot3/control_loops/python:polydrivetrain) $(OUTS)",
     tools = [
@@ -32,15 +34,18 @@
     name = "polydrivetrain_plants",
     srcs = [
         "drivetrain_dog_motor_plant.cc",
+        "hybrid_velocity_drivetrain.cc",
         "kalman_drivetrain_motor_plant.cc",
         "polydrivetrain_dog_motor_plant.cc",
     ],
     hdrs = [
         "drivetrain_dog_motor_plant.h",
+        "hybrid_velocity_drivetrain.h",
         "kalman_drivetrain_motor_plant.h",
         "polydrivetrain_dog_motor_plant.h",
     ],
     deps = [
+        "//frc971/control_loops:hybrid_state_feedback_loop",
         "//frc971/control_loops:state_feedback_loop",
     ],
 )
@@ -65,6 +70,7 @@
     srcs = [
         "drivetrain_main.cc",
     ],
+    visibility = ["//visibility:public"],
     deps = [
         ":drivetrain_base",
         "//aos:init",
diff --git a/y2018_bot3/control_loops/python/polydrivetrain.py b/y2018_bot3/control_loops/python/polydrivetrain.py
index d7799ae..6037138 100644
--- a/y2018_bot3/control_loops/python/polydrivetrain.py
+++ b/y2018_bot3/control_loops/python/polydrivetrain.py
@@ -7,6 +7,8 @@
 import gflags
 import glog
 
+__author__ = 'Austin Schuh (austin.linux@gmail.com)'
+
 FLAGS = gflags.FLAGS
 
 try:
@@ -17,11 +19,11 @@
 def main(argv):
   if FLAGS.plot:
     polydrivetrain.PlotPolyDrivetrainMotions(drivetrain.kDrivetrain)
-  elif len(argv) != 5:
+  elif len(argv) != 7:
     glog.fatal('Expected .h file name and .cc file name')
   else:
-    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], 'y2018_bot3',
-                                       drivetrain.kDrivetrain)
+    polydrivetrain.WritePolyDrivetrain(argv[1:3], argv[3:5], argv[5:7],
+                                       'y2018_bot3', drivetrain.kDrivetrain)
 
 if __name__ == '__main__':
   argv = FLAGS(sys.argv)