Projects/Linkbot Lock Popper

From LinkbotLabs
Jump to: navigation, search

3D Printed Parts

After you've tried it out we'd love it if you'd make it even better. Edit this project in Linkbot Labs so others can benefit from your genius!

3D Models
Description IGS Format STL Format
Pull Arm Lock Puller.IGS Lock Puller.STL
Dial Connector Lock Connection.IGS Lock Connection.STL
Stand Stand.IGS Stand.STL

Purchased Parts

These are attachments you can attach to a Linkbot-L to brute force solve a combination lock. These parts are all 3D printable and use twelve standard #6-32 x 3/8" long screws you can find at the hardware store, or you can get them from McMaster.

Source Code

Please note that you will need PyLinkbot version 2.1.0 or later to run the following source code. To upgrade your current installation of PyLinkbot, try running the command:

pip install PyLinkbot --upgrade

To run the source code, download the two files into "spartacus.py" and "main.py". Then, run the file "main.py".

spartacus.py

#!/usr/bin/env python3
from linkbot import Linkbot
import time
 
class Spartacus(Linkbot):
  def __init__(self, *args, **kwargs):
    self.__current_number = 0
    Linkbot.__init__(self, *args, **kwargs)
    self.combo_guess1 = 0
    self.combo_guess2 = 0
    self.combo_guess3 = 0
    self.trial_run = False
 
  def cwToNumber(self, number):
    """Turn the combo-lock dial clockwise to get to the specified dial number.
 
       If the dial is already at the number, a full rotation is performed."""
    if not self.trial_run:
        self.setMotorPower(1, -255)
    while number >= self.__norm(self.__current_number):
      number -= 40
    delta = -abs(self.__norm(self.__current_number) - number)
    self.__current_number += delta
    if abs(delta) <= 4:
      self.driveJointToNB(2, self.__current_number * -360.0 / 40.0)
      self.__moveWait()
    else:
      self.moveJointToNB(2, self.__current_number * -360.0 / 40.0)
      self.__moveWait()
 
  def ccwToNumber(self, number): 
    """Turn the combo-lock dial counter-clockwise to get to the specified dial number.
 
       If the dial is already at the number, a full rotation is performed."""
    if not self.trial_run:
        self.setMotorPower(1, -255)
    while number <= self.__norm(self.__current_number):
      number += 40
    delta = abs(self.__norm(self.__current_number) - number)
    self.__current_number += delta
    if abs(delta) <= 4:
      self.driveJointToNB(2, self.__current_number * -360.0 / 40.0)
      self.__moveWait()
    else:
      self.moveJointToNB(2, self.__current_number * -360.0 / 40.0)
      self.__moveWait()
 
  def resetToNumber(self, number):
    self.cwToNumber(number)
    self.cwToNumber(number)
    self.cwToNumber(number)
 
  def guess_combos(self, firstnum):
    print("Resetting to first num...")
    self.resetToNumber(firstnum)
    num2range = list(range(firstnum+2, firstnum + 2 + 40, 4))
    num2range = [x%40 for x in num2range]
    # CCW one full rotation to engage second ring
    print("Engaging second ring...")
    self.ccwToNumber(firstnum)
    for i in num2range:
      print("Moving to second number")
      self.ccwToNumber(i)
      num3range = list(range(i+2, i+2+40, 4))
      num3range = [x%40 for x in num3range]
      num3range = reversed(num3range)
      for j in num3range:
        print("Testing combo: {0}, {1}, {2}".format(firstnum, i, j))
        self.cwToNumber(j)
        if self.checkShackle():
          print("Combination found!! {0} - {1} - {2}".format(firstnum, i, j))
          return True 
      print("Moving back to second ring...")
      self.ccwToNumber(i)
    return False 
 
 
  def __norm(self, num):
    while num < 0:
      num += 40
    while num >= 40:
      num -= 40
    return num
 
  def __moveWait(self):
    [j1, j2, _] = self.getJointAngles()
    j2 = j2 * -40.0 / 360.0
    time.sleep(0.1)
    while abs( j2 - self.__current_number) > 0.5:
      [j1, j2, _] = self.getJointAngles()
      j2 = j2 * -40.0 / 360.0
      time.sleep(0.1)
 
  def checkShackle(self):
    """Check the shackle. If it opens, return True, otherwise False"""
    [j1, _, _] = self.getJointAngles()
    """
    for i in range(2):
      self.setMotorPower(1, -255)
      time.sleep(0.5)
      self.setMotorPower(1, 255)
      time.sleep(0.5)
    """
    if not self.trial_run:
        self.setMotorPower(1, 255)
    time.sleep(0.6)
    self.setMotorPower(1, 0)
    [j1b, _, _] = self.getJointAngles()
    self.setMotorPower(1, 0)
    if abs(j1 - j1b) > 4:
      return True
    else:
      return False

main.py

#!/usr/bin/env python3
from spartacus import Spartacus
 
def main():
    s = Spartacus()
    s.trial_run = True
    s.setJointSpeed(2, 180)
    s.setJointSpeed(1, 180)
    s.reset()
    s.moveJointTo(2, 0)
    myin = input("Device moved to zero. Please affix the lock now. Press enter to continue")
    s.stop()
    for i in range(0,40):
      if s.guess_combos(i):
        break
 
if __name__ == '__main__':
    main()