index | reset

cyan@ - portfolio

Maths | Algs | Data structs

Intangibles

Apps

Things

Mechatronics

Misc

Basement water emailer: replace beeper circuit with ESP8266 ESP01 emailer circuit.
Spring-loaded Inverted Pendulum (SLIP) model walking
cqpl
nightlighter
visual odometry / mapping
Cognition: 3 decomposition
refresh aliasing. you cheat the fixed clock by looking ahead.
Cognition: 3 decomposition
Modeling: 3 decomposition
3 kinds of information
Matrices: 3 takes
rover 4: Stanford Pupper
rover 3: 'Stan' (49 secs, 2x)
  • Apply KF (sensor fusion) for 9DOF IMU (BNO055), PID + LQR/G control via DRV8825 on 2x NEMA17 stepper motors, ATMEGA2560 board
  • 2020-06-21 insight: Adafruit BNO055 is better than Adafruit 10DOF LSM303 IMU signal (buy better vs try + fail to compensate noise)
  • 2020-06-21 insight: the 'inverted-pendulum on cart' maybe is 2 antagonistic control problems:
    • Tilt control: keep the IMU sensor-fused/filtered signal on calibrated pitch to a calibrated target reading
    • Base position control: keep the base from moving too far in either direction
    • Antagonism example if tilt is slightly forwards:
      • tilt control generates control signal to move forward. high PID gains means more responsive
      • Without base position control on this current implementation for some mechanical (wheel slip) / controls (imperfect gains) issue if it fails to correct adequately for the tilt, then the nonzero tilt control signal will cause base to displace in some direction indefinitely
      • Situation where increasing gains for better tilt control meant faster unbounded base displacement -> an underdetermined system without solution
      • Stop-gap solution (really hacky!):
        • refgen component takes pid output (velocity) applies gain and integrating with time to 'estimate' base displacement
        • and outputing a linear refgen
        • refgen oscillates between 2 setpoints of 'goal' base displacement, with linear interpolation
        • refgen output of control position is used as part of pid error
    • NOT a segway (base position blowing up == the whole point)
  • 2020-06-21 next steps:
    • repeated falls while testing caused ATMEGA2560 shield short? remake
    • 3D printed wheels are way too slippery, try cnc cutting out of MDF + double sided tape
    • Frame is too narrow for rover4 to walk through, replace with longer plywood cuts
Lowrider v2 CNC machine

Coursera certificates


Piano
Prusa i3 mk3s 3D Printer
Van Camper
blend_diff_sized_samples (sDzHs1)
  @staticmethod
  def interpolate(t_i, datum, other_ts, other_datums):
    # assumes datums are sorted, and filter is in-place
    # logic same for up-sampling and down-sampling
    lower_datum = datum
    lower_idx = -1
    lower = filter(lambda x: other_ts[x] < t_i,
      list(xrange(len(other_datums))))
    if len(lower) > 0:
      lower_idx = lower[-1]
      lower_datum = other_datums[lower_idx]

    upper_datum = datum
    upper_idx = -1
    upper = filter(lambda x: other_ts[x] > t_i,
      list(xrange(len(other_datums))))
    if len(upper) > 0:
      upper_idx = upper[0]
      upper_datum = other_datums[upper_idx]

    t_l = other_ts[lower_idx] if lower_idx >= 0 else t_i
    t_u = other_ts[upper_idx] if upper_idx >= 0 else t_i

    # implicit linear transition model
    ratio = (t_i - t_l) / (t_u - t_l)
    return lower_datum + ratio * (upper_datum - lower_datum)

  @staticmethod
  def blend_diff_sized_samples(
    xs_times, xs_list, ys_times, ys_list,
    direction = "down", custom_size = None):
    assert(len(xs_times) == len(xs_list))
    assert(len(ys_times) == len(ys_list))
    assert(direction in ["up", "down", "custom"])

    if len(xs_list) == len(ys_list):
      return xs_times, xs_list, ys_times, ys_list

    min_times = xs_times if len(xs_list) < len(xs_list) else ys_times
    min_list = xs_list if len(xs_list) < len(ys_list) else ys_list
    max_times = ys_times if len(xs_list) < len(xs_list) else xs_times
    max_list = ys_list if len(xs_list) < len(ys_list) else xs_list
    min_is_x = len(xs_list) < len(ys_list)

    if direction == "down":
      # downsampled_list is from max_times / max_list
      downsampled_list = map(lambda i: Util.interpolate(
        min_times[i], min_list[i], max_times, max_list),
        list(xrange(len(min_list))))
      if min_is_x:
        return min_times, min_list, min_times, downsampled_list
      else:
        return min_times, downsampled_list, min_times, min_list
    else:
        # upsampled list is from min_times, min_list
        upsampled_list = map(lambda i: Util.interpolate(
          max_times[i], max_list[i], min_times, min_list),
          list(xrange(len(max_list))))
        if min_is_x:
          return max_times, upsampled_list, max_times, max_list
        else:
          return max_times, max_list, max_times, upsampled_list
  
  
Event Dispatch: event-driven, producer-consumer, concurrent, discrete-time orchestration (JQ3uqm)
Inspiration / Motivations
Details
Context
Disadvantages
  class EventDispatch(object):
    def __init__(self,
      blackboard = None,
      node_name = "",
      service_handler = None):

      self.dispatch_lock = Lock()
      self.lock_registry = {}
      self.thread_registry = {}

      self.dispatch_switch_lock = Lock()
      self.dispatch_switch = True
      wrap_instance_method(self, 'dispatch',
        call_when_switch_turned_on(
          self, "dispatch_switch",
          "dispatch_switch_lock"))
      # if any event sets the switch off
      # no other events are dispatched
      # until the switch is cleared

      if blackboard is None:
        rospy.logwarn(
          "EventDispatch::no blackboard => no service")
        return

      if node_name == "":
        rospy.logwarn(
          "EventDispatch::no node_name => no service")
        return

      # given a blackboard with Event definitions
      self.blackboard = blackboard

    def dispatch(self, event, *args, **kwargs):
      self.dispatch_lock.acquire()
      if event.get_id() in self.thread_registry:
        event.status = DISPATCH_STATUS.DISPATCH_FAILED
        self.dispatch_lock.release()
        return
      self.thread_registry[event.get_id()] = StoppableThread(
        target=lambda args = args, kwargs = kwargs:\
          event.dispatch(self, *args, **kwargs),
        # note that the event is dispatched with a reference
        # to the event dispatch, giving access / control
        # over other events
        callback=lambda event = event, args = args, kwargs = kwargs:\
          self.dispatch_finish(event, *args, **kwargs))
      self.thread_registry[event.get_id()].start()
      self.dispatch_lock.release()

    def dispatch_finish(self, event, *args, **kwargs):
      self.thread_registry.pop(event.get_id(), None)
      event.finish(self, *args, **kwargs)
      # ONLY the event defines what is dispatched next
      # this includes multiple subsequent concurrent events
  
  
  class Event(object):
    def __init__(self, event_id, *args, **kwargs):
      self.event_id = event_id
      self.status = DISPATCH_STATUS.UNKNOWN
      self.event_dispatch = None
      self.dispatching_event = None

      wrap_instance_method(self,
        "dispatch",
        watchdog_try_to_run_and_notify_failure(
          "dispatch",
          self.dispatch_done,
          self.catch_exception))

      wrap_instance_method(self,
        "finish",
        watchdog_try_to_run_and_notify_failure(
          "finish",
          self.finish_done,
          self.catch_exception))

      self.finish_switch_lock = Lock()
      self.finish_switch = True
      wrap_instance_method(self, 'finish',
        call_when_switch_turned_on(
          self, "finish_switch",
          "finish_switch_lock"))

    def get_id(self):
      return self.__class__.__name__ + "@" + str(self.event_id)

    # methods for the child to override
    def dispatch(self, event_dispatch, *args, **kwargs):
      if self.get_id() not in event_dispatch.lock_registry:
        event_dispatch.lock_registry[self.get_id()] = Lock()
      self.event_dispatch = event_dispatch

    def finish(self, event_dispatch, *args, **kwargs):
      raise NotImplementedError

    def dispatch_done(self, result_key, result):
      raise NotImplementedError

    def finish_done(self, result_key, result):
      raise NotImplementedError

    def catch_exception(self, result_key, result):
      raise NotImplementedError

    @staticmethod
    def deserialize(blackboard, req):
      raise NotImplementedError
  
  
Planar graph branch-and-bound parameter-free order-invariant algorithm to perceive 'sides' from lines
Algorithm
Outcomes
Future Work
"Automated Drywall Planning System and Method"
Zeus: Electric Go Kart!
color_png.py
Chalk Bag!
Key Wrangler Copy
Parameterized Plywood Photo Frame
Hub-motor powered electric unicycle!
Constellations
Neurobiology Research
Plywood Bookmark
rover Series 1.x
Plywood Sutro Tower Mark 2
Tom going away gift
Wedding hangers for Jun and Bruce
Land Ahead in the Webstore
Busman
Quadcopter project
catchup.py
Hangouts Arcade in the Chrome App Store (Discontinued)
Takee Outee No. 1
Deep thanks to all involved here:
Standouts