Advertisement
Wolvenspud

robot.py

Mar 4th, 2019
137
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 36.36 KB | None | 0 0
  1.  
  2. from __future__ import print_function
  3. from __future__ import division
  4.  
  5. ###
  6. # Robot interface
  7.  
  8. def init(ip_address = None, visualise = True, width = 5, height = 2,
  9.          pos = -1, boxes = [["red"], [], ["green"], [], ["blue"]]):
  10.     '''Initialise (or re-initialise) the robot interface or simulation.
  11.    For the simulator, optional arguments are width (integer > 0),
  12.    height (integer > 0), boxes (list of lists of color names, or
  13.    string naming a particular initial arrangement, such as "random")
  14.    and visualise (True or False). The ip_address argument should only
  15.    be used when initialising the interface to the physical robot.'''
  16.     global _robot
  17.     if ip_address is not None:
  18.         _robot = _RPCRobot(ip_address)
  19.     else:
  20.         _robot = _SimRobot(visualise, width, height, pos, boxes)
  21.  
  22. def drive_right():
  23.     '''Drive robot one step to the right. Note that if the gripper
  24.    is not folded (open or closed), this may cause a collision.'''
  25.     if _robot is None:
  26.         raise RobotError("The robot has not been initialised.")
  27.     _robot.drive_right()
  28.  
  29. def drive_left():
  30.     '''Drive robot one step to the left. Note that if the gripper
  31.    is not folded (open or closed), this may cause a collision.'''
  32.     if _robot is None:
  33.         raise RobotError("The robot has not been initialised.")
  34.     _robot.drive_left()
  35.  
  36. def lift_up():
  37.     '''Move the lift one step up.'''
  38.     if _robot is None:
  39.         raise RobotError("The robot has not been initialised.")
  40.     _robot.lift_up()
  41.  
  42. def lift_down():
  43.     '''Move the lift one step down. Note that if the gripper is not
  44.    folded (open or closed), this may cause a collision.'''
  45.     if _robot is None:
  46.         raise RobotError("The robot has not been initialised.")
  47.     _robot.lift_down()
  48.  
  49. def gripper_to_open():
  50.     '''Move the gripper to the open position (regardless of
  51.    current position). If the gripper is currently folded, this
  52.    may cause a collision.'''
  53.     if _robot is None:
  54.         raise RobotError("The robot has not been initialised.")
  55.     _robot.gripper_to_open()
  56.  
  57. def gripper_to_closed():
  58.     '''Move the gripper to the closed position (regardless of
  59.    current position). If the gripper is currently folded, this
  60.    may cause a collision.'''
  61.     if _robot is None:
  62.         raise RobotError("The robot has not been initialised.")
  63.     _robot.gripper_to_closed()
  64.  
  65. def gripper_to_folded():
  66.     '''Move the gripper to the folded position (regardless of
  67.    current position). This may cause a collision.'''
  68.     if _robot is None:
  69.         raise RobotError("The robot has not been initialised.")
  70.     _robot.gripper_to_folded()
  71.  
  72. def sense_color():
  73.     '''Get a reading from the color sensor. Returns the name of
  74.    the color as a string, or an empty string ("") if no box is
  75.    detected.'''
  76.     if _robot is None:
  77.         raise RobotError("The robot has not been initialised.")
  78.     return _robot.sense_color()
  79.  
  80. def print_state():
  81.     _robot.print_state()
  82.  
  83. def stackBoxes(x):
  84.     if x > 0:
  85.         while x != 0:
  86.             _robot.lift_up()
  87.             _robot.drive_right()
  88.             _robot.drive_right()
  89.             _robot.gripper_to_open()
  90.             _robot.lift_down()
  91.             _robot.gripper_to_closed()
  92.        
  93.             x -= 1
  94.  
  95.    
  96. def SwapBoxes(a, b):
  97.     if a != b:
  98.         #_robot.drive_left() #assuming the robot starts at block one and it is occupied
  99.         if a > b:
  100.             a, b = b, a
  101.         dist_betw = b - a
  102.         dist_start = a #just to make naming more streamline LOL
  103.         #drives to and picks up 1st block
  104.         _robot.lift_up()
  105.         right(dist_start)
  106.         _robot.gripper_to_open()
  107.         _robot.lift_down()
  108.         _robot.gripper_to_closed()
  109.         _robot.lift_up()
  110.         #drives to 2nd block, stacks, and picks up
  111.         right(dist_betw)
  112.         _robot.gripper_to_open()
  113.         _robot.lift_down()
  114.         _robot.gripper_to_closed()
  115.         _robot.lift_up()
  116.         #drives back to place 2nd block in 1st location, then picks up 1st block only
  117.         left(dist_betw)
  118.         _robot.lift_down()
  119.         _robot.gripper_to_open()
  120.         _robot.lift_up()
  121.         _robot.gripper_to_closed()
  122.         #drives to place 1st block in 2nd location
  123.         right(dist_betw)
  124.         _robot.lift_down()
  125.         _robot.gripper_to_open()
  126.         _robot.lift_up()
  127.         #drive back to start, resets
  128.         left(b)
  129.         _robot.lift_down()
  130.         _robot.gripper_to_closed()
  131.        
  132. def left(x):
  133.     while x != 0:
  134.         _robot.drive_left()
  135.         _robot.drive_left()
  136.         x -= 1
  137.        
  138. def right(x):
  139.     while x != 0:
  140.         _robot.drive_right()
  141.         _robot.drive_right()
  142.         x -= 1
  143.  
  144. # left drive to initialise position when robot overlaps box at start        
  145. def SwapMiddleToRight():
  146.     _robot.drive_left()
  147.     SwapBoxes(2,3)
  148.  
  149. def SwapMiddleToLeft():
  150.     _robot.drive_left()
  151.     SwapBoxes(1,2)
  152.  
  153. def SwapLeftToRight():
  154.     _robot.drive_left()
  155.     SwapBoxes(1,3)
  156.        
  157. ###
  158. # Everything below is internal to the robot implementation.
  159.  
  160. import sys
  161. import time
  162. import math
  163.  
  164. _robot = None
  165.  
  166. class _RPCRobot:
  167.     '''Robot class interfacing with ev3 via RPYC.'''
  168.     DEFAULT_DRIVE_RIGHT = 575
  169.     DEFAULT_DRIVE_LEFT = 600
  170.     DEFAULT_LIFT_UP = 220
  171.     DEFAULT_LIFT_DOWN = 200
  172.  
  173.     def __init__(self, ip_address = "192.168.0.1"):
  174.         import rpyc
  175.  
  176.         self.rpcconn = rpyc.classic.connect(ip_address)
  177.         self.ev3 = self.rpcconn.modules.ev3dev.ev3
  178.         self.battery = self.ev3.PowerSupply()
  179.         self.drive = self.ev3.LargeMotor('outB')
  180.         self.lift = self.ev3.LargeMotor('outD')
  181.         self.gripper = self.ev3.MediumMotor('outC')
  182.         self.sensor = self.ev3.ColorSensor()
  183.         self.proxor = self.ev3.InfraredSensor()
  184.  
  185.     def print_state(self):
  186.         print("drive at " + str(self.drive.position))
  187.         print("lift at " + str(self.lift.position))
  188.         print("gripper at " + str(self.gripper.position))
  189.         print("sensor read: " + str(self.sensor.value()))
  190.         print("proxor read: " + str(self.proxor.value()))
  191.         print("battery: " + str(self.battery.measured_volts) + "V, "
  192.               + str(self.battery.measured_amps) + "A")
  193.  
  194.     # moving up doesn't require braking
  195.     def lift_up(self, distance=DEFAULT_LIFT_UP):
  196.         print("lift at " + str(self.lift.position)
  197.               + ", speed " + str(self.lift.speed))
  198.         self.lift.run_to_rel_pos(position_sp = -distance, duty_cycle_sp = -25)
  199.         time.sleep(0.5)
  200.         while abs(self.lift.speed) > 0:
  201.             print("lift at " + str(self.lift.position)
  202.                   + ", speed " + str(self.lift.speed))
  203.             time.sleep(0.25)
  204.         print("(end) lift at " + str(self.lift.position)
  205.               + ", speed " + str(self.lift.speed))
  206.  
  207.     # moving down requires braking, and even then has to be commanded ~10 short
  208.     def lift_down(self, distance=DEFAULT_LIFT_DOWN):
  209.         print("lift at " + str(self.lift.position)
  210.               + ", speed " + str(self.lift.speed))
  211.         self.lift.run_to_rel_pos(position_sp = distance,
  212.                                  duty_cycle_sp = 25,
  213.                                  stop_command='brake')
  214.         time.sleep(0.5)
  215.         while abs(self.lift.speed) > 0:
  216.             print("lift at " + str(self.lift.position)
  217.                   + ", speed " + str(self.lift.speed))
  218.             time.sleep(0.25)
  219.         print("(end) lift at " + str(self.lift.position)
  220.               + ", speed " + str(self.lift.speed))
  221.  
  222.     def drive_right(self, distance = DEFAULT_DRIVE_RIGHT):
  223.         print("drive at " + str(self.drive.position)
  224.               + ", speed " + str(self.drive.speed))
  225.         self.drive.run_to_rel_pos(position_sp = distance,
  226.                                   duty_cycle_sp = 50,
  227.                                   stop_command='hold')
  228.         time.sleep(0.5)
  229.         while abs(self.drive.speed) > 0:
  230.             print("drive at " + str(self.drive.position)
  231.                   + ", speed " + str(self.drive.speed))
  232.             time.sleep(0.25)
  233.         print("(end) drive at " + str(self.drive.position)
  234.               + ", speed " + str(self.drive.speed))
  235.  
  236.     def drive_left(self, distance = DEFAULT_DRIVE_LEFT):
  237.         print("drive at " + str(self.drive.position)
  238.               + ", speed " + str(self.drive.speed))
  239.         self.drive.run_to_rel_pos(position_sp = -distance,
  240.                                   duty_cycle_sp = -50,
  241.                                   stop_command='hold')
  242.         time.sleep(0.5)
  243.         while abs(self.drive.speed) > 0:
  244.             print("drive at " + str(self.drive.position)
  245.                   + ", speed " + str(self.drive.speed))
  246.             time.sleep(0.25)
  247.         print("(end) drive at " + str(self.drive.position)
  248.               + ", speed " + str(self.drive.speed))
  249.  
  250.     def gripper_to_closed(self):
  251.         print("gripper at " + str(self.gripper.position)
  252.               + ", speed " + str(self.gripper.speed))
  253.         if self.gripper.position > -1900:
  254.             dc = -50
  255.         else:
  256.             dc = 50
  257.         self.gripper.run_to_abs_pos(position_sp = -1900, duty_cycle_sp = dc)
  258.         time.sleep(0.5)
  259.         while abs(self.gripper.speed) > 0:
  260.             print("gripper at " + str(self.gripper.position)
  261.                   + ", speed " + str(self.gripper.speed))
  262.             time.sleep(0.25)
  263.         print("(end) gripper at " + str(self.gripper.position)
  264.               + ", speed " + str(self.gripper.speed))
  265.  
  266.     def gripper_to_open(self):
  267.         print("gripper at " + str(self.gripper.position)
  268.               + ", speed " + str(self.gripper.speed))
  269.         if self.gripper.position > -1500:
  270.             dc = -100
  271.         else:
  272.             dc = 100
  273.         self.gripper.run_to_abs_pos(position_sp = -1500,
  274.                                     duty_cycle_sp = dc,
  275.                                     stop_command='brake')
  276.         time.sleep(0.5)
  277.         while abs(self.gripper.speed) > 0:
  278.             print("gripper at " + str(self.gripper.position)
  279.                   + ", speed " + str(self.gripper.speed))
  280.             time.sleep(0.25)
  281.         print("(end) gripper at " + str(self.gripper.position)
  282.               + ", speed " + str(self.gripper.speed))
  283.  
  284.     def gripper_to_folded(self):
  285.         print("gripper at " + str(self.gripper.position)
  286.               + ", speed " + str(self.gripper.speed))
  287.         if self.gripper.position > 0:
  288.             dc = -100
  289.         else:
  290.             dc = 100
  291.         self.gripper.run_to_abs_pos(position_sp = 0,
  292.                                     duty_cycle_sp = dc,
  293.                                     stop_command='brake')
  294.         time.sleep(0.5)
  295.         while abs(self.gripper.speed) > 0:
  296.             print("gripper at " + str(self.gripper.position)
  297.                   + ", speed " + str(self.gripper.speed))
  298.             time.sleep(0.25)
  299.         print("(end) gripper at " + str(self.gripper.position)
  300.               + ", speed " + str(self.gripper.speed))
  301.  
  302.     def sense_color(self):
  303.         self.sensor.mode = 'COL-COLOR'
  304.         reading = self.sensor.value()
  305.         if reading == 1:
  306.             return "black"
  307.         elif reading == 2:
  308.             return "blue"
  309.         elif reading == 3:
  310.             return "green"
  311.         elif reading == 4:
  312.             return "yellow"
  313.         elif reading == 5:
  314.             return "red"
  315.         elif reading == 6:
  316.             return "white"
  317.         elif reading == 7:
  318.             return "brown"
  319.         else:
  320.             return ""
  321.  
  322.     def sense_distance(self):
  323.         self.sensor.mode = 'COL-REFLECT'
  324.         reading = self.sensor.value()
  325.         return reading
  326.  
  327.     def data_collector(self, count, direction):
  328.         self.drive.run_forever(duty_cycle_sp = direction * 50)
  329.         start_time = time.time()
  330.         data = []
  331.         while count > 0:
  332.             sample = (time.time() - start_time, self.drive.position, self.proxor.value(), self.sensor.value())
  333.             data.append(sample)
  334.             print(count, sample)
  335.             time.sleep(0.01)
  336.             count -= 1
  337.         self.drive.stop(stop_command='coast')
  338.         return data
  339.  
  340.     ## end class RPCRobot
  341.  
  342.  
  343. class RobotError (Exception):
  344.     def __init__(self, message):
  345.         super(Exception, self).__init__()
  346.         self.message = message
  347.  
  348.     def __str__(self):
  349.         return "Robot Error: " + self.message
  350.  
  351.  
  352. class _Visualiser:
  353.     CANVAS_WIDTH = 800
  354.     CANVAS_HEIGHT = 600
  355.     ROBOT_COLOR = "darkgray"
  356.     FOLDED_GRIPPER_ANGLE = 0.05 * math.pi
  357.     OPEN_GRIPPER_ANGLE = -0.5 * math.pi
  358.     CLOSED_GRIPPER_ANGLE = -0.7 * math.pi
  359.  
  360.     def __init__(self):
  361.         try:
  362.             import tkinter as tk
  363.         except ImportError:
  364.             try:
  365.                 import Tkinter as tk
  366.             except ImportError:
  367.                 self.root = None
  368.                 return
  369.         self.root = tk.Tk()
  370.         try:
  371.             self.root.wm_title("Robot Simulator")
  372.             frame = tk.Frame(self.root)
  373.             frame.pack()
  374.             self.canvas = tk.Canvas(frame,
  375.                                     width=self.CANVAS_WIDTH,
  376.                                     height=self.CANVAS_HEIGHT,
  377.                                     bg="white")
  378.             self.canvas.pack(side="top")
  379.             # self.close_button = tk.Button(frame, text="Close",
  380.             #                               command=self.root.destroy)
  381.             # self.close_button.pack(side="bottom")
  382.         except Exception as exc:
  383.             self.root.destroy()
  384.             raise exc
  385.         self.simulator = None
  386.  
  387.     def check_window(self):
  388.         if self.root is None:
  389.             return False
  390.         try:
  391.             _state = self.root.state()
  392.             return True
  393.         except:
  394.             print("The visualiser window appears to have been closed.\n" +
  395.                   "Reset the robot if you want to see it again.")
  396.             self.root = None
  397.         return False
  398.  
  399.     def reset(self, simulator):
  400.         if self.root is not None:
  401.             self.canvas.delete("robot")
  402.             self.canvas.delete("gripper")
  403.             self.canvas.delete("box")
  404.             self.canvas.delete("table")
  405.             self.canvas.delete("error")
  406.             self.sim = simulator
  407.             self.create_objects()
  408.             self.root.update()
  409.         else:
  410.             print("tk not available, visualisation disabled")
  411.             simulator.vis = False
  412.  
  413.     def create_objects(self):
  414.         xscale = self.CANVAS_WIDTH // (self.sim.width + 2)
  415.         yscale = self.CANVAS_HEIGHT // (self.sim.height + 2)
  416.         self.scale = min(xscale, yscale)
  417.         # draw table
  418.         self.canvas.create_line((1 * self.scale) - 2,
  419.                                 self.CANVAS_HEIGHT - (self.scale - 2),
  420.                                 ((self.sim.width + 1) * self.scale) + 2,
  421.                                 self.CANVAS_HEIGHT - (self.scale - 2),
  422.                                 width=2, fill="black", tag="table")
  423.         # draw boxes
  424.         print(self.sim.boxes)
  425.         print(self.sim.table)
  426.         self.box_id = [ 0 for item in self.sim.boxes ]
  427.         for (xpos,stack) in enumerate(self.sim.table):
  428.             for (ypos,box) in enumerate(stack):
  429.                 assert(box < len(self.sim.boxes))
  430.                 color = self.sim.boxes[box]
  431.                 bid = self.canvas.create_rectangle((xpos + 1) * self.scale,
  432.                                                    self.CANVAS_HEIGHT - (ypos + 1) * self.scale,
  433.                                                    (xpos + 2) * self.scale,
  434.                                                    self.CANVAS_HEIGHT - (ypos + 2) * self.scale,
  435.                                                    outline="black", fill=color,
  436.                                                    tag="box")
  437.                 self.box_id[box] = bid
  438.         self.draw_robot()
  439.  
  440.     def draw_robot(self):
  441.         assert(self.sim.lift_pos >= 0)
  442.         assert(self.sim.lift_pos < self.sim.height)
  443.         center_x = int((self.sim.drive_pos + 1.5) * self.scale)
  444.         half_width = int(0.7 * self.scale)
  445.         left_x = center_x - half_width
  446.         right_x = center_x + half_width
  447.         sensor_x = int((self.sim.drive_pos + 2.5) * self.scale)
  448.         s_size = int(0.25 * self.scale)
  449.         lift_y = self.CANVAS_HEIGHT - int((self.sim.lift_pos + 1.2) * self.scale)
  450.         sensor_y = self.CANVAS_HEIGHT - int((self.sim.lift_pos + 1.5) * self.scale)
  451.         if self.sim.drive_pos >= -1 and self.sim.drive_pos < self.sim.width:
  452.             self.canvas.create_line(left_x, lift_y, right_x, lift_y,
  453.                                     width=5, fill=self.ROBOT_COLOR, tag="robot")
  454.             self.canvas.create_oval(sensor_x - s_size, sensor_y - s_size,
  455.                                     sensor_x + s_size, sensor_y + s_size,
  456.                                     fill=self.ROBOT_COLOR, tag="robot")
  457.             if self.sim.gripper_state < 0:
  458.                 angle = self.FOLDED_GRIPPER_ANGLE
  459.             elif self.sim.gripper_state == 0:
  460.                 angle = self.OPEN_GRIPPER_ANGLE
  461.             else:
  462.                 angle = self.CLOSED_GRIPPER_ANGLE
  463.             self.draw_gripper(angle, center_x, lift_y)
  464.  
  465.     def draw_gripper(self, angle, center_x, center_y):
  466.         r = 1.0 * self.scale
  467.         half_width = int(0.7 * self.scale)
  468.         # offset from center of baseline
  469.         pz = math.sin(-angle) * r
  470.         sr = (1 - (0.8/r) * pz) * r
  471.         px = math.cos(angle) * sr
  472.         py = math.sin(angle) * sr
  473.         left_x = center_x - half_width
  474.         right_x = center_x + half_width
  475.         #print(angle / math.pi, r, pz, sr, px, py)
  476.         self.canvas.create_line(right_x, center_y,
  477.                                 right_x + px, center_y + py,
  478.                                 width=5, fill=self.ROBOT_COLOR,
  479.                                 tag="gripper")
  480.         self.canvas.create_line(left_x, center_y,
  481.                                 left_x - px, center_y + py,
  482.                                 width=5, fill=self.ROBOT_COLOR,
  483.                                 tag="gripper")
  484.  
  485.     def move_gripper(self, from_angle, to_angle, speed = 1):
  486.         angle_diff = (to_angle - from_angle)
  487.         if angle_diff < 0:
  488.             angle_step = -0.04 * speed
  489.         else:
  490.             angle_step = 0.04 * speed
  491.         n_steps = int(angle_diff / angle_step)
  492.         #print(angle_diff, angle_step, n_steps)
  493.         center_x = int((self.sim.drive_pos + 1.5) * self.scale)
  494.         lift_y = self.CANVAS_HEIGHT - int((self.sim.lift_pos + 1.2) * self.scale)
  495.         angle = from_angle
  496.         for i in range(n_steps - 1):
  497.             self.canvas.delete("gripper")
  498.             angle += angle_step
  499.             self.draw_gripper(angle, center_x, lift_y)
  500.             self.root.update()
  501.             time.sleep(0.05)
  502.         self.canvas.delete("gripper")
  503.         self.draw_gripper(to_angle, center_x, lift_y)
  504.         self.root.update()
  505.  
  506.     def gripper_folded_to_open(self):
  507.         if not self.check_window():
  508.             return
  509.         self.move_gripper(self.FOLDED_GRIPPER_ANGLE,
  510.                           self.OPEN_GRIPPER_ANGLE,
  511.                           speed = 2)
  512.  
  513.     def gripper_folded_to_closed(self):
  514.         if not self.check_window():
  515.             return
  516.         self.move_gripper(self.FOLDED_GRIPPER_ANGLE,
  517.                           self.CLOSED_GRIPPER_ANGLE,
  518.                           speed = 2)
  519.  
  520.     def gripper_open_to_folded(self):
  521.         if not self.check_window():
  522.             return
  523.         self.move_gripper(self.OPEN_GRIPPER_ANGLE,
  524.                           self.FOLDED_GRIPPER_ANGLE,
  525.                           speed = 2)
  526.  
  527.     def gripper_open_to_closed(self):
  528.         if not self.check_window():
  529.             return
  530.         self.move_gripper(self.OPEN_GRIPPER_ANGLE,
  531.                           self.CLOSED_GRIPPER_ANGLE)
  532.  
  533.     def gripper_closed_to_open(self):
  534.         if not self.check_window():
  535.             return
  536.         self.move_gripper(self.CLOSED_GRIPPER_ANGLE,
  537.                           self.OPEN_GRIPPER_ANGLE)
  538.  
  539.     def gripper_closed_to_folded(self):
  540.         if not self.check_window():
  541.             return
  542.         self.move_gripper(self.CLOSED_GRIPPER_ANGLE,
  543.                           self.FOLDED_GRIPPER_ANGLE,
  544.                           speed = 2)
  545.  
  546.     def move_robot_one_step(self, xdir, ydir):
  547.         if self.scale >= 30:
  548.             step_size = self.scale / 30
  549.             moved = 0
  550.             pos = 0
  551.             while moved < self.scale:
  552.                 pos += step_size
  553.                 offset = int(math.floor(pos - moved))
  554.                 if offset > 0:
  555.                     if moved + offset > self.scale:
  556.                         offset = self.scale - moved
  557.                     self.canvas.move("robot", xdir * offset, ydir * offset)
  558.                     self.canvas.move("gripper", xdir * offset, ydir * offset)
  559.                     if self.sim.holding is not None:
  560.                         for box in self.sim.holding:
  561.                             assert(box < len(self.sim.boxes))
  562.                             self.canvas.move(self.box_id[box], xdir * offset, ydir * offset)
  563.                     self.root.update()
  564.                     moved += offset
  565.                     time.sleep(0.01)
  566.         else:
  567.             time_step = 0.30 / self.scale
  568.             assert(time_step >= 0.01)
  569.             for i in range(self.scale):
  570.                 self.canvas.move("robot", xdir, ydir)
  571.                 self.canvas.move("gripper", xdir, ydir)
  572.                 if self.sim.holding is not None:
  573.                     for box in self.sim.holding:
  574.                         assert(box < len(self.sim.boxes))
  575.                         self.canvas.move(self.box_id[box], xdir, ydir)
  576.                 self.root.update()
  577.                 time.sleep(0.01)
  578.  
  579.     def move_robot_left(self):
  580.         if not self.check_window():
  581.             return
  582.         self.move_robot_one_step(-1, 0)
  583.  
  584.     def move_robot_right(self):
  585.         if not self.check_window():
  586.             return
  587.         self.move_robot_one_step(1, 0)
  588.  
  589.     def move_robot_up(self):
  590.         if not self.check_window():
  591.             return
  592.         self.move_robot_one_step(0, -1)
  593.  
  594.     def move_robot_down(self):
  595.         if not self.check_window():
  596.             return
  597.         self.move_robot_one_step(0, 1)
  598.  
  599.     def flag_collision(self, xpos, ypos):
  600.         if not self.check_window():
  601.             return
  602.         assert(xpos >= -1)
  603.         assert(xpos <= self.sim.width + 1)
  604.         assert(ypos >= -1)
  605.         assert(ypos <= self.sim.height + 1)
  606.         x1 = int((xpos + 1.1) * self.scale)
  607.         x2 = int((xpos + 1.9) * self.scale)
  608.         y1 = self.CANVAS_HEIGHT - int((ypos + 1.1) * self.scale)
  609.         y2 = self.CANVAS_HEIGHT - int((ypos + 1.9) * self.scale)
  610.         self.canvas.create_line(x1, y2, x2, y1, width=5, fill="white", tag="error")
  611.         self.canvas.create_line(x1, y1, x2, y2, width=5, fill="white", tag="error")
  612.         self.canvas.create_line(x1, y2, x2, y1, width=3, fill="red", tag="error")
  613.         self.canvas.create_line(x1, y1, x2, y2, width=3, fill="red", tag="error")
  614.         self.root.update()
  615.  
  616.     ## end class Visualiser
  617.  
  618.  
  619. def _random_box_setup(width, height):
  620.     import random
  621.     color_list = ["black", "blue", "green", "yellow", "red", "white", "brown"]
  622.     result = []
  623.     for i in range(0, width, 2):
  624.         stack = []
  625.         for j in range(random.randint(0, height - 1)):
  626.             col = color_list[random.randint(0, len(color_list) - 1)]
  627.             stack.append(col)
  628.         result.append(stack)
  629.         if i + 1 < width:
  630.             result.append([])
  631.     return result
  632.  
  633. class _SimRobot:
  634.     '''Simulated Robot class.'''
  635.     visualiser = None
  636.  
  637.     def __init__(self, visualise = True, width = 5, height = 2,
  638.                  pos = -1, boxes = [["red"], [], ["green"], [], ["blue"]]):
  639.         self.width = width # width of the table
  640.         self.height = height # levels of the lift
  641.         self.drive_pos = pos # where the gripper is; sensor is at +1
  642.         self.lift_pos = 0 # lift is down
  643.         self.gripper_state = -1 # < 0 = folded, 0 = open, > 0 = closed
  644.         self.holding = None
  645.         self.collided = False
  646.         self.collisions = []
  647.         if isinstance(boxes, str):
  648.             if boxes == "random":
  649.                 boxes = _random_box_setup(width, height)
  650.             elif boxes == "flat":
  651.                 clist = ["red", "green", "blue"]
  652.                 boxes = [ [ clist[ (i // 2) % 3 ] ] if i % 2 == 0 else []
  653.                           for i in range(width) ]
  654.             else:
  655.                 raise RobotError("Invalid keyword '" + boxes + "' for boxes")
  656.         assert(len(boxes) <= self.width)
  657.         self.boxes = []
  658.         self.table = [ [] for i in range(self.width) ]
  659.         pos = 0
  660.         for stack in boxes:
  661.             for color in stack:
  662.                 self.boxes.append(color)
  663.                 index = len(self.boxes) - 1
  664.                 self.table[pos].append(index)
  665.             pos += 1
  666.         self.vis = visualise
  667.         if self.vis:
  668.             if _SimRobot.visualiser is None:
  669.                 _SimRobot.visualiser = _Visualiser()
  670.             elif not _SimRobot.visualiser.check_window():
  671.                 _SimRobot.visualiser = _Visualiser()
  672.             self.visualiser.reset(self)
  673.  
  674.     def print_state(self, command = None):
  675.         if command is not None:
  676.             print("executing command " + command)
  677.         print("drive position: " + str(self.drive_pos)
  678.               + " (width = " + str(self.width) + ")")
  679.         print("lift position: " + str(self.lift_pos)
  680.               + " (height = " + str(self.height) + ")")
  681.         if self.gripper_state < 0:
  682.             print("gripper is folded")
  683.         elif self.gripper_state == 0:
  684.             print("gripper is open")
  685.         else:
  686.             print("gripper is closed")
  687.             if self.holding is not None:
  688.                 print("holding: " + str(self.holding))
  689.         print("boxes on shelf: " + str(self.table))
  690.         if self.collided:
  691.             print("collision flag is set "
  692.                   + ", ".join([str(pos) for pos in self.collisions]))
  693.         else:
  694.             print("collision flag is not set")
  695.  
  696.     def sense_color(self):
  697.         if self.collided:
  698.             raise RobotError("The robot has hit a box. You must reset it.")
  699.         spos = self.drive_pos + 1
  700.         if spos >= 0 and spos < self.width:
  701.             height = len(self.table[spos])
  702.             if self.lift_pos < height:
  703.                 box_num = self.table[spos][self.lift_pos]
  704.                 assert(box_num < len(self.boxes))
  705.                 return self.boxes[box_num]
  706.             else:
  707.                 return ""
  708.         else:
  709.             return ""
  710.  
  711.     # check if there is an obstacle at drive/lift pos, and raise
  712.     # a collision exception if so; dpos is the x-position to check
  713.     # (not necessarily the actual drive position); both positions
  714.     # are after the move currently being attempted
  715.     def check_for_collision(self, dpos, lpos, more = False):
  716.         if dpos >= 0 and dpos < self.width:
  717.             # height will be zero if table at dpos is clear
  718.             height = len(self.table[dpos])
  719.             if height > lpos:
  720.                 self.collided = True
  721.                 self.collisions.append((dpos, lpos))
  722.                 if self.vis:
  723.                     self.visualiser.flag_collision(dpos, lpos)
  724.             if not more:
  725.                 if self.collided:
  726.                     raise RobotError("The robot has hit one or more boxes at "
  727.                                      + ", ".join([str(pos)
  728.                                                   for pos in self.collisions])
  729.                                      + "!")
  730.  
  731.     def lift_up(self):
  732.         if self.collided:
  733.             raise RobotError("The robot has hit a box. You must reset it.")
  734.         if self.lift_pos + 1 < self.height:
  735.             self.lift_pos = self.lift_pos + 1
  736.         else:
  737.             raise RobotError("The lift is at level " + str(self.lift_pos)
  738.                              + " and can't go any higher!")
  739.         if self.vis:
  740.             self.visualiser.move_robot_up()
  741.         else:
  742.             self.print_state("lift_up")
  743.  
  744.     def lift_down(self):
  745.         if self.collided:
  746.             raise RobotError("The robot has hit a box. You must reset it.")
  747.         if self.lift_pos > 0:
  748.             self.lift_pos = self.lift_pos - 1
  749.             if self.vis:
  750.                 self.visualiser.move_robot_down()
  751.             else:
  752.                 self.print_state("lift_down")
  753.             # check for collisions
  754.             if self.holding is not None:
  755.                 self.check_for_collision(self.drive_pos, self.lift_pos)
  756.             elif self.gripper_state > 0: # closed
  757.                 self.check_for_collision(self.drive_pos, self.lift_pos)
  758.             if self.gripper_state >= 0: # not folded (closed or open)
  759.                 self.check_for_collision(self.drive_pos - 1, self.lift_pos,
  760.                                          more = True)
  761.                 self.check_for_collision(self.drive_pos + 1, self.lift_pos)
  762.         else:
  763.             raise RobotError("The lift is at level 0 and can't go any lower!")
  764.  
  765.     def drive_right(self):
  766.         if self.collided:
  767.             raise RobotError("The robot has hit a box. You must reset it.")
  768.         self.drive_pos = self.drive_pos + 1
  769.         if self.vis:
  770.             self.visualiser.move_robot_right()
  771.         else:
  772.             self.print_state("drive_right")
  773.         if self.gripper_state >= 0: # not folded
  774.             self.check_for_collision(self.drive_pos + 1, self.lift_pos,
  775.                                      more = True)
  776.             self.check_for_collision(self.drive_pos - 1, self.lift_pos)
  777.  
  778.     def drive_left(self):
  779.         if self.collided:
  780.             raise RobotError("The robot has hit a box. You must reset it.")
  781.         self.drive_pos = self.drive_pos - 1
  782.         if self.vis:
  783.             self.visualiser.move_robot_left()
  784.         else:
  785.             self.print_state("drive_left")
  786.         if self.gripper_state >= 0: # not folded
  787.             self.check_for_collision(self.drive_pos - 1, self.lift_pos,
  788.                                      more = True)
  789.             self.check_for_collision(self.drive_pos + 1, self.lift_pos)
  790.  
  791.     def gripper_to_closed(self):
  792.         if self.collided:
  793.             raise RobotError("The robot has hit a box. You must reset it.")
  794.         if self.gripper_state > 0: # already closed, do nothing
  795.             if not self.vis:
  796.                 self.print_state("gripper_to_closed")
  797.             return
  798.         assert(self.holding == None)
  799.         if self.gripper_state == 0: # open
  800.             self.gripper_state = 1 # closed
  801.             self.check_if_holding()
  802.             if self.vis:
  803.                 self.visualiser.gripper_open_to_closed()
  804.             else:
  805.                 self.print_state("gripper_to_closed")
  806.         elif self.gripper_state < 0: # folded
  807.             self.gripper_state = 1
  808.             self.check_if_holding()
  809.             if self.vis:
  810.                 self.visualiser.gripper_folded_to_closed()
  811.             else:
  812.                 self.print_state("gripper_to_closed")
  813.             self.check_for_collision(self.drive_pos - 1, self.lift_pos,
  814.                                      more = True)
  815.             self.check_for_collision(self.drive_pos + 1, self.lift_pos,
  816.                                      more = True)
  817.             self.check_for_collision(self.drive_pos - 2, self.lift_pos,
  818.                                      more = True)
  819.             self.check_for_collision(self.drive_pos + 2, self.lift_pos)
  820.  
  821.  
  822.     def check_if_holding(self):
  823.         if self.drive_pos >= 0 and self.drive_pos < self.width:
  824.             height = len(self.table[self.drive_pos])
  825.             if self.lift_pos <= (height - 1):
  826.                 self.holding = self.table[self.drive_pos][self.lift_pos:]
  827.                 self.table[self.drive_pos] = self.table[self.drive_pos][:self.lift_pos]
  828.  
  829.     def drop_if_holding(self):
  830.         if self.holding is not None:
  831.             # dropping what we're holding, check if it falls
  832.             if self.drive_pos < 0 or self.drive_pos >= self.width:
  833.                 # dropped off the table
  834.                 self.collided = True
  835.                 self.collisions.append((self.drive_pos, self.lift_pos - 1))
  836.                 if self.vis:
  837.                     assert(self.lift_pos > 0)
  838.                     self.visualiser.flag_collision(self.drive_pos, self.lift_pos - 1)
  839.                 raise RobotError("Dropped box(es) off the table!")
  840.             # else, check if too high
  841.             height = len(self.table[self.drive_pos])
  842.             assert(self.lift_pos >= height)
  843.             if self.lift_pos == height:
  844.                 self.table[self.drive_pos] = self.table[self.drive_pos] + self.holding
  845.                 self.holding = None
  846.             else:
  847.                 self.collided = True
  848.                 self.collisions.append((self.drive_pos, self.lift_pos - 1))
  849.                 if self.vis:
  850.                     assert(self.lift_pos > 0)
  851.                     self.visualiser.flag_collision(self.drive_pos, self.lift_pos - 1)
  852.                 raise RobotError("Dropped box(es) from too high!")
  853.  
  854.     def gripper_to_open(self):
  855.         if self.collided:
  856.             raise RobotError("The robot has hit a box. You must reset it.")
  857.         if self.gripper_state == 0: # already open, do nothing
  858.             if not self.vis:
  859.                 self.print_state("gripper_to_open")
  860.             return
  861.         if self.gripper_state > 0: # closed
  862.             self.gripper_state = 0 # open
  863.             self.drop_if_holding()
  864.             if self.vis:
  865.                 self.visualiser.gripper_closed_to_open()
  866.             else:
  867.                 self.print_state("gripper_to_open")
  868.         elif self.gripper_state < 0: # folded
  869.             self.gripper_state = 0
  870.             if self.vis:
  871.                 self.visualiser.gripper_folded_to_open()
  872.             else:
  873.                 self.print_state("gripper_to_open")
  874.             # check for collisions
  875.             self.check_for_collision(self.drive_pos - 1, self.lift_pos,
  876.                                      more = True)
  877.             self.check_for_collision(self.drive_pos + 1, self.lift_pos,
  878.                                      more = True)
  879.             self.check_for_collision(self.drive_pos - 2, self.lift_pos,
  880.                                      more = True)
  881.             self.check_for_collision(self.drive_pos + 2, self.lift_pos)
  882.  
  883.     def gripper_to_folded(self):
  884.         if self.collided:
  885.             raise RobotError("The robot has hit a box. You must reset it.")
  886.         if self.gripper_state < 0: # already folded, do nothing
  887.             if not self.vis:
  888.                 self.print_state("gripper_to_folded")
  889.             return
  890.         if self.gripper_state > 0: # closed
  891.             self.gripper_state = -1 # folded
  892.             self.drop_if_holding()
  893.             if self.vis:
  894.                 self.visualiser.gripper_closed_to_folded()
  895.             else:
  896.                 self.print_state("gripper_to_folded")
  897.             # check for collisions
  898.             self.check_for_collision(self.drive_pos - 1, self.lift_pos,
  899.                                      more = True)
  900.             self.check_for_collision(self.drive_pos + 1, self.lift_pos,
  901.                                      more = True)
  902.             self.check_for_collision(self.drive_pos - 2, self.lift_pos,
  903.                                      more = True)
  904.             self.check_for_collision(self.drive_pos + 2, self.lift_pos)
  905.         elif self.gripper_state == 0: # open
  906.             self.gripper_state = -1 # folded
  907.             if self.vis:
  908.                 self.visualiser.gripper_open_to_folded()
  909.             else:
  910.                 self.print_state("gripper_to_folded")
  911.             # check for collisions
  912.             self.check_for_collision(self.drive_pos - 1, self.lift_pos,
  913.                                      more = True)
  914.             self.check_for_collision(self.drive_pos + 1, self.lift_pos,
  915.                                      more = True)
  916.             self.check_for_collision(self.drive_pos - 2, self.lift_pos,
  917.                                      more = True)
  918.             self.check_for_collision(self.drive_pos + 2, self.lift_pos,
  919.                                      more = True)
  920.        
  921.  
  922.     # def __str__(self):
  923.     #     return ("X: " + str(self.drive_pos) + " [0," + str(self.width) +
  924.     #             "], Y: " + str(self.lift_pos) + " [0," + str(self.height) +
  925.     #             "], G: " + str(self.gripper_state) + ", H: " +
  926.     #             str(self.holding) + ", T: " + str(self.table))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement