diff --git a/python/examples/smc_node.py b/python/examples/smc_node.py
index 7fc29a86c4eb2704004a979e7cd7ef544bdb6404..6d55386f0a0cb9f736d122bba4b4a55ccac84372 100644
--- a/python/examples/smc_node.py
+++ b/python/examples/smc_node.py
@@ -2,12 +2,15 @@
 import rclpy
 from rclpy.node import Node
 from geometry_msgs import msg 
+from nav_msgs.msg import Odometry
 from rclpy import wait_for_message
 import pinocchio as pin
 import argcomplete, argparse
 from functools import partial
 from ur_simple_control.managers import getMinimalArgParser, ControlLoopManager, RobotManager
 from ur_simple_control.clik.clik import getClikArgs, getClikController, controlLoopClik, moveL, compliantMoveL, controlLoopCompliantClik, invKinmQP
+from ur_simple_control.optimal_control.get_ocp_args import get_OCP_args
+from ur_simple_control.optimal_control.crocoddyl_mpc import CrocoIKMPC
 import threading
 
 
@@ -16,14 +19,15 @@ def get_args():
     parser.description = 'Run closed loop inverse kinematics \
     of various kinds. Make sure you know what the goal is before you run!'
     parser = getClikArgs(parser)
+    parser = get_OCP_args(parser)
     argcomplete.autocomplete(parser)
     args = parser.parse_args()
     return args
 
-class ROSCommHandler(Node):
+class ROSCommHandlerHeron(Node):
 
     def __init__(self, args, robot_manager : RobotManager, loop_manager : ControlLoopManager):
-        super().__init__('ROSCommHandler')
+        super().__init__('ROSCommHandlerHeron')
         
         self.publisher_vel_base = self.create_publisher(msg.Twist, '/cmd_vel', 5)
         print("created publisher")
@@ -35,20 +39,29 @@ class ROSCommHandler(Node):
             history = rclpy.qos.HistoryPolicy.KEEP_LAST,
             depth = 1,
         )
-        self.subscription = self.create_subscription(msg.PoseWithCovarianceStamped, 
+# MASSIVE TODO: you want to be subsribed to a proper localization thing,
+# specifically to output from robot_localization
+        self.subscription_amcl = self.create_subscription(msg.PoseWithCovarianceStamped, 
                                             '/amcl_pose', self.pose_callback, qos_prof)
+        self.subscription_odom = self.create_subscription(Odometry, 
+                                            '/odom', self.odom_callback, qos_prof)
         #wait_for_message.wait_for_message(msg.PoseWithCovarianceStamped, self, '/amcl_pose')
         #print("subscribed to /amcl_pose")
-        self.subscription  # prevent unused variable warning
+        self.subscription_amcl  # prevent unused variable warning
+        self.subscription_odom  # prevent unused variable warning
         self.robot_manager = robot_manager
         self.loop_manager = loop_manager
         self.args = args
 
+    # ONE TRILLION PERCENT YOU NEED TO INTEGRATE/USE ODOM IN-BETWEEN THESE
     def pose_callback(self, mesg):
         self.robot_manager.q[0] = mesg.pose.pose.position.x
         self.robot_manager.q[1] = mesg.pose.pose.position.y
         # TODO: check that z can be used as cos(theta) and w as sin(theta)
         # (they could be defined some other way or theta could be offset of something)
+    # ONE TRILLION PERCENT THIS ISN'T CORRECT
+    # TODO: generate a rotation matrix from this quarternion with pinocchio,
+    # and give this is the value of the x or the y axis 
         self.robot_manager.q[2] = mesg.pose.pose.orientation.z
         self.robot_manager.q[3] = mesg.pose.pose.orientation.w
         
@@ -56,6 +69,17 @@ class ROSCommHandler(Node):
         print("received new amcl")
         #self.publisher_vel_base.publish(vel_cmd)
 
+    # HIGHLY LIKELY THAT THIS IS NOT THE THE VELOCITY OF THE BASE FRAME
+    def odom_callback(self, mesg : Odometry):
+        self.robot_manager.v_q[0] = mesg.twist.twist.linear.x
+        self.robot_manager.v_q[1] = mesg.twist.twist.linear.y
+        # TODO: check that z can be used as cos(theta) and w as sin(theta)
+        # (they could be defined some other way or theta could be offset of something)
+        self.robot_manager.v_q[2] = mesg.twist.twist.angular.z
+        
+        #vel_cmd = msg.Twist()
+        print("received new amcl")
+        #self.publisher_vel_base.publish(vel_cmd)
 
 
 
@@ -65,8 +89,9 @@ if __name__ == '__main__':
     robot = RobotManager(args)
     goal = robot.defineGoalPointCLI()
     #goal = pin.SE3.Identity()
-    loop_manager = compliantMoveL(args, robot, goal, run=False)
-    ros_comm_handler = ROSCommHandler(args, robot, loop_manager)
+    #loop_manager = compliantMoveL(args, robot, goal, run=False)
+    loop_manager = CrocoIKMPC(args, robot, goal, run=False)
+    ros_comm_handler = ROSCommHandlerHeron(args, robot, loop_manager)
     
     thread = threading.Thread(target=rclpy.spin, args=(ros_comm_handler,), daemon=True)
     thread.start()
diff --git a/python/ur_simple_control/__pycache__/managers.cpython-310.pyc b/python/ur_simple_control/__pycache__/managers.cpython-310.pyc
index 9450f01b9ff3639106a03cef0cc1522b1ebe61c2..3b476c272538d9a1f12c522c5ae7d32bfa054cde 100644
Binary files a/python/ur_simple_control/__pycache__/managers.cpython-310.pyc and b/python/ur_simple_control/__pycache__/managers.cpython-310.pyc differ
diff --git a/python/ur_simple_control/managers.py b/python/ur_simple_control/managers.py
index df733acfd49f789012812efcd66287fff25aeee7..9a943bc19503f8e94bbba1ce9b3d060f2e0a2498 100644
--- a/python/ur_simple_control/managers.py
+++ b/python/ur_simple_control/managers.py
@@ -13,7 +13,7 @@ from ur_simple_control.util.grippers.robotiq.robotiq_gripper import RobotiqGripp
 from ur_simple_control.util.grippers.on_robot.twofg import TWOFG
 import copy
 import signal
-from ur_simple_control.util.get_model import get_model, heron_approximation, heron_approximationDD, getGripperlessUR5e
+from ur_simple_control.util.get_model import get_model, heron_approximation, heron_approximationDD, getGripperlessUR5e, mir_approximation
 from collections import deque
 from ur_simple_control.visualize.visualize import plotFromDict, realTimePlotter, manipulatorVisualizer
 from ur_simple_control.util.logging_utils import LogManager
@@ -86,7 +86,7 @@ def getMinimalArgParser():
     #################################################
     parser.add_argument('--robot', type=str, \
             help="which robot you're running or simulating", default="ur5e", \
-            choices=['ur5e', 'heron', 'heronros', 'gripperlessur5e'])
+            choices=['ur5e', 'heron', 'heronros', 'gripperlessur5e', 'mirros'])
     parser.add_argument('--simulation', action=argparse.BooleanOptionalAction, \
             help="whether you are running the UR simulator", default=False)
     parser.add_argument('--robot-ip', type=str, 
@@ -424,6 +424,9 @@ class RobotManager:
         if self.robot_name == "heronros":
             self.model, self.collision_model, self.visual_model, self.data = \
                  heron_approximation()
+        if self.robot_name == "mirros":
+            self.model, self.collision_model, self.visual_model, self.data = \
+                 mir_approximation()
             #self.publisher_vel_base = create_publisher(msg.Twist, '/cmd_vel', 5)
             #self.publisher_vel_base = publisher_vel_base
         if self.robot_name == "gripperlessur5e":
@@ -494,6 +497,10 @@ class RobotManager:
         # so it can't be all zeros
         if self.robot_name == "heron":
             self.q[2] = 1.0
+        if self.robot_name == "heronros":
+            self.q[2] = 1.0
+        if self.robot_name == "mirros":
+            self.q[2] = 1.0
         # v_q is the generalization of qd for every type of joint.
         # for revolute joints it's qd, but for ex. the planar joint it's the body velocity.
         self.v_q = np.zeros(self.model.nv)
@@ -836,13 +843,35 @@ class RobotManager:
                 self.v_q = qd
                 self.q = pin.integrate(self.model, self.q, qd * self.dt)
 
-        # TODO: implement real thing
         if self.robot_name == "heron":
             # y-direction is not possible on heron
             qd[1] = 0
             self.q = pin.integrate(self.model, self.q, qd * self.dt)
+
         if self.robot_name == "heronros":
             # y-direction is not possible on heron
+            # TODO: REMOVE OBVIOUSLY
+#############################################33
+#            qd[:] = 0.0
+#############################################33
+
+            qd[1] = 0
+            cmd_msg = msg.Twist()
+            cmd_msg.linear.x = qd[0]
+            cmd_msg.angular.z = qd[2]
+            #print("about to publish", cmd_msg)
+            self.publisher_vel_base.publish(cmd_msg)
+            # good to keep because updating is slow otherwise
+            # it's not correct, but it's more correct than not updating
+            #self.q = pin.integrate(self.model, self.q, qd * self.dt)
+
+        if self.robot_name == "mirros":
+            # y-direction is not possible on heron
+            # TODO: REMOVE OBVIOUSLY
+#############################################33
+            #qd[:] = 0.0
+#############################################33
+
             qd[1] = 0
             cmd_msg = msg.Twist()
             cmd_msg.linear.x = qd[0]
diff --git a/python/ur_simple_control/optimal_control/crocoddyl_mpc.py b/python/ur_simple_control/optimal_control/crocoddyl_mpc.py
index 644615345770826ac408fc3975adc19eebec6fdc..9f5debcc60a06eaaaf2da42a971e6768a59ef75d 100644
--- a/python/ur_simple_control/optimal_control/crocoddyl_mpc.py
+++ b/python/ur_simple_control/optimal_control/crocoddyl_mpc.py
@@ -60,7 +60,7 @@ def CrocoIKMPCControlLoop(args, robot : RobotManager, solver, i, past_data):
     
     return breakFlag, save_past_dict, log_item
 
-def CrocoIKMPC(args, robot, goal):
+def CrocoIKMPC(args, robot, goal, run=True):
     """
     IKMPC
     -----
@@ -92,7 +92,10 @@ def CrocoIKMPC(args, robot, goal):
         }
     save_past_dict = {}
     loop_manager = ControlLoopManager(robot, controlLoop, args, save_past_dict, log_item)
-    loop_manager.run()
+    if run:
+        loop_manager.run()
+    else:
+        return loop_manager
 
 def CrocoEndEffectorPathFollowingMPCControlLoop(args, robot : RobotManager, solver : crocoddyl.SolverBoxFDDP, path_planner : ProcessManager, i, past_data):
     """
diff --git a/python/ur_simple_control/optimal_control/crocoddyl_optimal_control.py b/python/ur_simple_control/optimal_control/crocoddyl_optimal_control.py
index 09157b30ead2d179ff9301f477cbc1fb5dec347a..2da5229203ef2f6a7fda2a966ba14d5418724cbd 100644
--- a/python/ur_simple_control/optimal_control/crocoddyl_optimal_control.py
+++ b/python/ur_simple_control/optimal_control/crocoddyl_optimal_control.py
@@ -104,7 +104,7 @@ def createCrocoIKOCP(args, robot : RobotManager, x0, goal : pin.SE3):
     # and this should be 2 * model.nv the way things are defined here.
 
 
-    if robot.robot_name == "heron":
+    if robot.robot_name == "heron" or robot.robot_name == "heronros" or robot.robot_name == "mirros":
         xlb = xlb[1:]
         xub = xub[1:]
 
@@ -293,7 +293,7 @@ def createCrocoEEPathFollowingOCP(args, robot : RobotManager, x0):
     # from one to point to the other.
     # point activation input and the residual need to be of the same length obviously,
     # and this should be 2 * model.nv the way things are defined here.
-    if robot.robot_name == "heron":
+    if robot.robot_name == "heron" or robot.robot_name == "heronros" or robot.robot_name == "mirros":
         xlb = xlb[1:]
         xub = xub[1:]
 
@@ -447,7 +447,7 @@ def createBaseAndEEPathFollowingOCP(args, robot : RobotManager, x0):
     # and this should be 2 * model.nv the way things are defined here.
 
 
-    if robot.robot_name == "heron":
+    if robot.robot_name == "heron" or robot.robot_name == "heronros" or robot.robot_name == "mirros":
         xlb = xlb[1:]
         xub = xub[1:]
 
diff --git a/python/ur_simple_control/util/__pycache__/get_model.cpython-310.pyc b/python/ur_simple_control/util/__pycache__/get_model.cpython-310.pyc
index f3163af30f2c40fddc884a5ec95aabd5f6cbf222..7d915c240c73fce908dbaf592187296265279892 100644
Binary files a/python/ur_simple_control/util/__pycache__/get_model.cpython-310.pyc and b/python/ur_simple_control/util/__pycache__/get_model.cpython-310.pyc differ
diff --git a/python/ur_simple_control/util/get_model.py b/python/ur_simple_control/util/get_model.py
index 71fcdc3f7dba9fb6cfec27070b67852c3b068c22..f687b61f674f7f8eace4fed4d9408900a88ad4b8 100644
--- a/python/ur_simple_control/util/get_model.py
+++ b/python/ur_simple_control/util/get_model.py
@@ -235,6 +235,64 @@ def heron_approximation():
 
     return model, visual_model.copy(), visual_model, data
 
+
+def mir_approximation():
+    # mobile base as planar joint (there's probably a better
+    # option but whatever right now)
+    model_mobile_base = pin.Model()
+    model_mobile_base.name = "mobile_base"
+    geom_model_mobile_base = pin.GeometryModel()
+    joint_name = "mobile_base_planar_joint"
+    parent_id = 0
+    # TEST
+    joint_placement = pin.SE3.Identity()
+    #joint_placement.rotation = pin.rpy.rpyToMatrix(0, -np.pi/2, 0) 
+    #joint_placement.translation[2] = 0.2
+    # TODO TODO TODO TODO TODO TODO TODO TODO
+    # TODO: heron is actually a differential drive,
+    # meaning that it is not a planar joint.
+    # we could put in a prismatic + revolute joint
+    # as the base (both joints being at same position),
+    # and that should work for our purposes.
+    # this makes sense for initial testing
+    # because mobile yumi's base is a planar joint
+    MOBILE_BASE_JOINT_ID = model_mobile_base.addJoint(parent_id, pin.JointModelPlanar(), 
+            joint_placement.copy(), joint_name)
+    # we should immediately set velocity limits.
+    # there are no position limit by default and that is what we want.
+    # TODO: put in heron's values
+    # TODO: make these parameters the same as in mpc_params in the planner
+    model_mobile_base.velocityLimit[0] = 2
+    model_mobile_base.velocityLimit[1] = 0
+    model_mobile_base.velocityLimit[2] = 2
+    # TODO: i have literally no idea what reasonable numbers are here
+    model_mobile_base.effortLimit[0] = 200
+    model_mobile_base.effortLimit[1] = 0
+    model_mobile_base.effortLimit[2] = 200
+    #print("OBJECT_JOINT_ID",OBJECT_JOINT_ID)
+    #body_inertia = pin.Inertia.FromBox(args.box_mass, box_dimensions[0], 
+    #        box_dimensions[1], box_dimensions[2])
+
+    # pretty much random numbers
+    # TODO: find heron (mir) numbers
+    body_inertia = pin.Inertia.FromBox(30, 0.5, 0.3, 0.4)
+    # maybe change placement to sth else depending on where its grasped
+    model_mobile_base.appendBodyToJoint(MOBILE_BASE_JOINT_ID, body_inertia, pin.SE3.Identity()) 
+    box_shape = fcl.Box(0.5, 0.3, 0.4) 
+    body_placement = pin.SE3.Identity()
+    geometry_mobile_base = pin.GeometryObject("box_shape", MOBILE_BASE_JOINT_ID, box_shape, body_placement.copy())
+
+    geometry_mobile_base.meshColor = np.array([1., 0.1, 0.1, 1.])
+    geom_model_mobile_base.addGeometryObject(geometry_mobile_base)
+
+    # have to add the frame manually
+    # it's tool0 because that's used everywhere
+    model_mobile_base.addFrame(pin.Frame('tool0',MOBILE_BASE_JOINT_ID,0,joint_placement.copy(),pin.FrameType.JOINT) )
+
+    data = model_mobile_base.createData()
+
+    return model_mobile_base, geom_model_mobile_base.copy(), geom_model_mobile_base.copy(), data
+
 # TODO:
 # try building the mobile base as a rotational joint,
 # on top of which is a prismatic joint.