class Main(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(Main, self).__init__()
        self.setupUi(self)
        # IntnesityData : Array to store data to be displayed on Interface
        self.intensityData = []
        for i in range(4):
            self.intensityData.append(np.full((4, 4, 3), 0))
        # Two variables to avoid mutliple starting and stopping of thread
        self.start = 0
        self.stop = 0
        # initialise Interface to blue
        for i in range(4):
            for j in range(4):
                for k in range(4):
                    self.intensityData[i][j][k][2] = 255
        # Thread which updates the plot based on received data from Micro Controller
        self.thr = ThreadHandler(self.processData)

        print("Intitialisation")
        self.init()

    # init() Contains other initialisations
    def init(self):
        print("Adding ViewBoxes")
        # displays are the viewboxes (one for each patch of tactile sensors)
        self.display1 = self.patch1.addViewBox()
        self.display2 = self.patch2.addViewBox()
        self.display3 = self.patch3.addViewBox()
        self.display4 = self.patch4.addViewBox()

        # Image items to be displayed on the viewboxes
        self.currImage1 = pg.ImageItem(self.intensityData[0])
        self.display1.addItem(self.currImage1)
        self.currImage2 = pg.ImageItem(self.intensityData[1])
        self.display2.addItem(self.currImage2)
        self.currImage3 = pg.ImageItem(self.intensityData[2])
        self.display3.addItem(self.currImage3)
        self.currImage4 = pg.ImageItem(self.intensityData[3])
        self.display4.addItem(self.currImage4)

        # Functions of Start and Stop buttons
        self.startButton.clicked.connect(self.doStart)
        self.stopButton.clicked.connect(self.doStop)

    def doStart(self):
        # starting the thread to update the Interface
        global recvThread, ser
        if self.start == 0:
            ser.flushInput()
            self.start = 1
            self.thr.start()
            recvThread.start()

    def doStop(self):
        # stop the thread which updates the Interface
        global recvThread
        if self.stop == 0:
            print("Stopped")
            self.stop = 1
            self.thr.pause()
            recvThread.pause()
            self.thr.kill()
            recvThread.kill()

    # The function to update the Interface in real time. This function is ran in a thread.
    def processData(self):
        global update, dataQueue
        while True:
            #print(update)
            if update == 1:
                #print("First update")
                for pos in range(64):
                    patchNum = pos // 16
                    col = (pos % 16) // 4
                    row = (pos % 16) % 4
                    if patchNum == 0:
                        self.intensityData[0][row][col][0] = max(
                            0,
                            2 * int(dataQueue[patchNum][row][col] / 256) - 255)
                        self.intensityData[0][row][col][2] = max(
                            0,
                            255 - 2 * int(dataQueue[patchNum][row][col] / 256))
                        self.intensityData[0][row][col][
                            1] = 255 - self.intensityData[0][row][col][
                                2] - self.intensityData[0][row][col][0]
                        self.currImage1.setImage(self.intensityData[0],
                                                 levels=(0, 255))
                    elif patchNum == 1:
                        self.intensityData[1][row][col][0] = max(
                            0,
                            2 * int(dataQueue[patchNum][row][col] / 256) - 255)
                        self.intensityData[1][row][col][2] = max(
                            0,
                            255 - 2 * int(dataQueue[patchNum][row][col] / 256))
                        self.intensityData[1][row][col][
                            1] = 255 - self.intensityData[1][row][col][
                                2] - self.intensityData[1][row][col][0]
                        self.currImage2.setImage(self.intensityData[1],
                                                 levels=(0, 255))
                    elif patchNum == 2:
                        self.intensityData[2][row][col][0] = max(
                            0,
                            2 * int(dataQueue[patchNum][row][col] / 256) - 255)
                        self.intensityData[2][row][col][2] = max(
                            0,
                            255 - 2 * int(dataQueue[patchNum][row][col] / 256))
                        self.intensityData[2][row][col][
                            1] = 255 - self.intensityData[2][row][col][
                                2] - self.intensityData[2][row][col][0]
                        self.currImage3.setImage(self.intensityData[2],
                                                 levels=(0, 255))
                    elif patchNum == 3:
                        self.intensityData[3][row][col][0] = max(
                            0,
                            2 * int(dataQueue[patchNum][row][col] / 256) - 255)
                        self.intensityData[3][row][col][2] = max(
                            0,
                            255 - 2 * int(dataQueue[patchNum][row][col] / 256))
                        self.intensityData[3][row][col][
                            1] = 255 - self.intensityData[3][row][col][
                                2] - self.intensityData[3][row][col][0]
                        self.currImage4.setImage(self.intensityData[3],
                                                 levels=(0, 255))
                update = 0
Esempio n. 2
0
class FormMain(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        # Constructor
        super(FormMain, self).__init__()
        self.setupUi(self)

        # Handlers
        self.ur10 = None
        self.iLimb = None

        # Set available ports
        self.port_ur10.setText("10.1.1.6")
        self.port_iLimb.addItems(list_serial_ports())
        self.port_tactile.addItems(list_serial_ports())

        # For tactile values
        self.ser = None
        self.dataQueue = deque([], maxlen=1000)
        self.receiveThread = ThreadHandler(_worker=self.receive)
        self.MIN = np.array([0, 0])
        self.MAX = np.array([4096, 4096])
        self.mvaFilt = [
            MovingAverage(_windowSize=1000),
            MovingAverage(_windowSize=1000)
        ]

        # Connect buttons to callback functions
        self.initialize.clicked.connect(self.init_handlers)
        self.configure.clicked.connect(self.configure_handlers)
        self.init_main.clicked.connect(self.start_main)
        self.stop_main.clicked.connect(self.break_main)
        self.toHome.clicked.connect(self.move_to_home)
        self.toBase.clicked.connect(lambda: self.move_to_base(t=5))
        self.moveUp.clicked.connect(lambda: self.move_vertical(_dir=1))
        self.moveDown.clicked.connect(lambda: self.move_vertical(_dir=-1))
        self.cw.clicked.connect(self.rotate_hand_CW)
        self.ccw.clicked.connect(self.rotate_hand_CCW)
        self.toPose.clicked.connect(self.move_iLimb_to_pose)
        self.pinch.clicked.connect(lambda: self.close_hand())
        self.moveAway.clicked.connect(lambda: self.move_away())
        self.startSensors.clicked.connect(
            lambda: [self.sensors_timer.start(0),
                     self.receiveThread.start()])
        self.stopSensors.clicked.connect(
            lambda: [self.sensors_timer.stop(),
                     self.receiveThread.pause()])
        self.calibrate.clicked.connect(self.calibrate_sensors)
        self.visualize.clicked.connect(
            lambda: [self.save_points(),
                     render3D('run.pcd', stage=1)])
        self.convexHull.clicked.connect(
            lambda: [self.save_points(),
                     render3D('run.pcd', stage=2)])
        self.detectShape.clicked.connect(self.recognize_shape)
        self.clear.clicked.connect(self.reset_stored_values)

        # Initialize PoV graphs
        views = [
            self.view0, self.view1, self.view2, self.view3, self.view4,
            self.view5
        ]
        self.povBoxes = []
        self.povPlots = []
        for view in views:
            view.ci.layout.setContentsMargins(0, 0, 0, 0)
            view.ci.layout.setSpacing(0)
            self.povBoxes.append(view.addPlot())
            self.povPlots.append(pg.ScatterPlotItem())
            self.povBoxes[-1].addItem(self.povPlots[-1])

        # Initialize tactile sensor graphs
        self.timestep = [0]
        self.sensorData = [[], []]
        self.sensorBoxes = []
        self.sensorPlots = []
        for view in [self.sensor_index, self.sensor_thumb]:
            view.ci.layout.setContentsMargins(0, 0, 0, 0)
            view.ci.layout.setSpacing(0)
            self.sensorBoxes.append(view.addPlot())
            self.sensorPlots.append(
                pg.PlotCurveItem(pen=pg.mkPen('b', width=1)))
            self.sensorBoxes[-1].addItem(self.sensorPlots[-1])
            self.sensorBoxes[-1].setXRange(min=0, max=20, padding=0.1)
            # self.sensorBoxes[-1].setYRange(min=0, max=1, padding=0.1)

        self.pov_timer = QtCore.QTimer()
        self.pov_timer.timeout.connect(self.update_pov)
        self.sensors_timer = QtCore.QTimer()
        self.sensors_timer.timeout.connect(self.update_sensor_readings)

        self.main_thread = Thread(target=self._palpation_routine)
        self.main_thread.daemon = True

        # Redirect console output to textBrowser
        sys.stdout = port(self.textBrowser)

        # Create TensorFlow session and load pretrained model
        self.load_session()

    """ Function to initialize handler objects """

    def init_handlers(self):
        # Create handlers
        print('Initializing handlers ...')
        self.ur10 = UR10Controller(self.port_ur10.text())
        print('UR10 done.')
        self.iLimb = iLimbController(self.port_iLimb.currentText())
        self.iLimb.connect()
        print('iLimb done.')
        self.ser = serial.Serial(self.port_tactile.currentText(), 117964800)
        self.ser.flushInput()
        self.ser.flushOutput()
        print('TactileBoard done')

    """ Functions to set all handlers to default configuration """

    def move_to_home(self):
        print('Setting UR10 to default position ...')
        UR10pose = URPoseManager()
        UR10pose.load('shape_recog_home.urpose')
        UR10pose.moveUR(self.ur10, 'home_j', 5)
        time.sleep(5.2)

    def move_iLimb_to_pose(self):
        print('Setting iLimb to default pose ...')
        self.iLimb.setPose('openHand')
        time.sleep(3)
        self.iLimb.control(['thumbRotator'], ['position'], [700])
        time.sleep(3)

    def calibrate_sensors(self):
        self.receiveThread.start()
        print('Calibrating tactile sensors ...')
        # Clear data queue
        self.dataQueue.clear()
        # Wait till queue has sufficient readings
        while len(self.dataQueue) < 500:
            pass
        # Calculate lower and upper bounds
        samples = np.asarray(copy(self.dataQueue))
        self.MIN = np.mean(samples, axis=0)
        self.MAX = self.MIN + 500
        self.dataQueue.clear()
        # Set Y-range
        for box in self.sensorBoxes:
            box.setYRange(min=0, max=1, padding=0.1)
        print("Done")

    def configure_handlers(self):
        self.move_to_home()
        self.move_iLimb_to_pose()
        self.calibrate_sensors()
        print('Done.')

    """ Function to create and load pretrained model """

    def load_session(self):
        self.model = vCNN()
        self.session = tf.Session(graph=self.model.graph)
        with self.session.as_default():
            with self.session.graph.as_default():
                saver = tf.train.Saver(max_to_keep=3)
                saver.restore(
                    self.session,
                    tf.train.latest_checkpoint('../shape_recognition/save'))

    """ Function to clear all collected values """

    def reset_stored_values(self):
        STATE.NUM_POINTS = 0
        STATE.CONTACT_POINTS = []
        STATE.CONTROL_POS = [0 for _ in range(5)]
        STATE.FINGER_POS = {'index': [], 'thumb': []}
        STATE.XYZR = []
        STATE.UNIT_VECTOR = []

    """ Function to close fingers until all fingers touch surface """

    def close_hand(self, fingers=['index', 'thumb']):
        touched = [False] * len(fingers)
        touched_once = False
        fingerArray = [[x, MAPPING[x], THRESHOLD[x]] for x in fingers]
        while not all(touched):
            time.sleep(0.005)
            q = self.get_sensor_data()
            for _ in range(len(q)):
                tactileSample = q.popleft()
                touched = self.iLimb.doFeedbackPinchTouch(
                    tactileSample, fingerArray, 1)
                # update control_pos for fingers that have touched a surface
                for i in range(len(fingerArray)):
                    if touched[i]:
                        touched_once = True
                        STATE.CONTROL_POS[fingerArray[i]
                                          [1]] = self.iLimb.controlPos
                        #----------------------------------------------------------
                        # Collect information
                        STATE.FINGER_POS[fingerArray[i][0]].append(
                            self.iLimb.controlPos)
                        #----------------------------------------------------------

                # Self-touching condition
                # Can be modified later
                if self.iLimb.controlPos > 200 and not touched_once:
                    return False
                elif self.iLimb.controlPos > 200 and touched_once:
                    for i in range(len(fingerArray)):
                        if not touched[i]:
                            #----------------------------------------------------------
                            # Collect information
                            STATE.FINGER_POS[fingerArray[i][0]].append(-1)
                            #----------------------------------------------------------
                    return True

                if all(touched):
                    return True
                else:
                    # update fingerArray
                    fingerArray = [
                        fingerArray[i] for i in range(len(touched))
                        if not touched[i]
                    ]

    """ Function to calculate coordinates of points of contact """

    def compute_coordinates(self):
        self.ur10.read_joints_and_xyzR()
        xyzR = copy(self.ur10.xyzR)
        joints = copy(self.ur10.joints)
        sim = ur10_simulator()
        sim.set_joints(joints)
        _ = sim.joints2pose()
        _, rm = sim.get_Translation_and_Rotation_Matrix()
        # Calculate the direction in which the end effector is pointing
        # aVlue corresponding to z-direction is ignored
        direction = rm[:2, 2]  # x and y direction vector only
        direction /= np.linalg.norm(direction)

        # Calculate unit vector direction
        dir_ang = np.arctan(abs(direction[1] / direction[0]))
        if direction[0] < 0:
            if direction[1] < 0:
                dir_ang += np.pi
            else:
                dir_ang = np.pi - dir_ang
        else:
            if direction[1] < 0:
                dir_ang = 2 * np.pi - dir_ang

        # Find point of contact for index finger
        idx_control = STATE.CONTROL_POS[MAPPING['index']]
        if idx_control > 0:
            theta = 30 + 60 / 500 * idx_control
            if idx_control < 210:
                # Normal circular motion
                rel_theta = 30
            else:
                rel_theta = 30 + 60 / 290 * (idx_control - 210)
            # rel_theta = 30 + 60/500 * idx_control
            axis = IDX_0 * np.cos(np.deg2rad(theta)) + IDX_1 * np.cos(
                np.deg2rad(theta + rel_theta))
            perp = IDX_0 * np.sin(np.deg2rad(theta)) + IDX_1 * np.sin(
                np.deg2rad(theta + rel_theta))
            axis += IDX_TO_BASE

            pt_1 = [
                axis * np.cos(dir_ang) - perp * np.sin(dir_ang) + xyzR[0],
                axis * np.sin(dir_ang) + perp * np.cos(dir_ang) + xyzR[1],
                xyzR[2]
            ]
            STATE.NUM_POINTS += 1
            STATE.CONTACT_POINTS.append(pt_1)

        # Find point of contact for thumb
        thb_control = STATE.CONTROL_POS[MAPPING['thumb']]
        if thb_control > 0:
            theta = 90 * (1 - thb_control / 500)
            axis = THB * np.cos(np.deg2rad(theta)) + THB_TO_BASE
            perp = THB * np.sin(np.deg2rad(theta))

            pt_2 = [
                axis * np.cos(dir_ang) - perp * np.sin(dir_ang) + xyzR[0],
                axis * np.sin(dir_ang) + perp * np.cos(dir_ang) + xyzR[1],
                xyzR[2]
            ]

            STATE.NUM_POINTS += 1
            STATE.CONTACT_POINTS.append(pt_2)

        #--------------------------------------------------
        # Collect information
        STATE.XYZR.append(xyzR)
        STATE.UNIT_VECTOR.append(direction)
        #--------------------------------------------------

    """ Functions to rotate hand for next reading """

    def rotate_hand_CCW(self):
        self.ur10.read_joints()
        joints = copy(self.ur10.joints)

        if STATE.ROTATION_POS < 180 // STATE.ROTATION_ANGLE - 1:
            STATE.ROTATION_POS += 1
            joints[4] += STATE.ROTATION_ANGLE * -1
            xyzR = self.ur10.move_joints_with_grasp_constraints(
                joints, dist_pivot=220, grasp_pivot=60, constant_axis='z')
            self.ur10.movej(xyzR, 3)
            time.sleep(3.2)

    def rotate_hand_CW(self):
        self.ur10.read_joints()
        joints = copy(self.ur10.joints)

        if STATE.ROTATION_POS > 0:
            STATE.ROTATION_POS -= 1
            joints[4] += STATE.ROTATION_ANGLE * 1
            xyzR = self.ur10.move_joints_with_grasp_constraints(
                joints, dist_pivot=220, grasp_pivot=60, constant_axis='z')
            self.ur10.movej(xyzR, 3)
            time.sleep(3.2)

    def rotate_hand(self):
        # Boundary checks
        if STATE.ROTATION_POS == 0 and STATE.ROTATION_DIR == -1:
            STATE.ROTATION_DIR = 1
        if STATE.ROTATION_POS == 180 // STATE.ROTATION_ANGLE - 1 and STATE.ROTATION_DIR == 1:
            STATE.ROTATION_DIR = -1
        # Rotate the hand according to direction
        if STATE.ROTATION_DIR == 1:
            self.rotate_hand_CCW()
        else:
            self.rotate_hand_CW()

    """ Function to move hand in vertical direction """

    def move_vertical(self, _dir=1):
        # move one step up while palpating
        self.ur10.read_joints_and_xyzR()
        x, y, z, rx, ry, rz = copy(self.ur10.xyzR)
        new_joint_pos = np.array([x, y, z + 10 * _dir, rx, ry, rz])
        self.ur10.movej(new_joint_pos, 0.5)
        time.sleep(0.7)
        STATE.HEIGHT += 10 * _dir

    """ Function to move hand away from the object """

    def move_away(self, fingers=['thumb', 'index']):
        self.iLimb.control(fingers, ['position'] * len(fingers),
                           [0] * len(fingers))
        time.sleep(1)

    """ Function to move UR10 to base """

    def move_to_base(self, t=1):
        self.ur10.read_joints_and_xyzR()
        x, y, z, rx, ry, rz = copy(self.ur10.xyzR)
        new_joint_pos = np.array([x, y, -200, rx, ry, rz])
        self.ur10.movej(new_joint_pos, t)
        time.sleep(t + .2)
        STATE.HEIGHT = 0
        STATE.ESTIMATED_HEIGHT = 200

    """ Function to pause of main loop """

    def break_main(self):
        STATE.STOP = True

    """ Function to resume/start standard palpation """

    def start_main(self):
        if STATE.STARTED:
            STATE.STOP = False
        else:
            self.main_thread.start()
            STATE.STARTED = True
            self.pov_timer.start(0)

    """ Main routine """

    def _palpation_routine(self):
        print("Starting standard palpation routine ...")
        for i in range(180 // STATE.ROTATION_ANGLE):
            print("Rotation pos : %d" % (i + 1))
            while STATE.HEIGHT < STATE.ESTIMATED_HEIGHT:
                # Pause condition
                print("Height : %d" % STATE.HEIGHT)
                while STATE.STOP is True:
                    time.sleep(0.05)
                touched = self.close_hand(['thumb', 'index'])
                time.sleep(0.1)
                if touched:
                    self.compute_coordinates()
                else:
                    STATE.ESTIMATED_HEIGHT = STATE.HEIGHT
                self.iLimb.resetControl()
                time.sleep(0.5)
                self.move_away()
                self.move_vertical()
            self.move_to_base()
            self.rotate_hand()
        self.recognize_shape()

    """ Function to detect shape """

    def recognize_shape(self):
        self.save_points()
        detect_shape(self.session, self.model, 'run.pcd')

    """ Function to save point cloud """

    def save_points(self):
        # Convert collected points to a PCD file
        pts = np.asarray(STATE.CONTACT_POINTS)
        finger_pos = np.asarray(
            [STATE.FINGER_POS['index'], STATE.FINGER_POS['thumb']])
        np.savetxt('controlpos.txt', finger_pos)
        np.savetxt('xyzr.txt', np.asarray(STATE.XYZR))
        np.savetxt('uv.txt', np.asarray(STATE.UNIT_VECTOR))
        save_point_cloud(pts, 'run.pcd')

    """ Function to update the 6 PoV images """

    def update_pov(self):
        idx = STATE.FINGER_POS['index']
        if len(idx) > 0:
            thb = STATE.FINGER_POS['thumb']
            xyzr = STATE.XYZR
            uv = STATE.UNIT_VECTOR
            # Get lists of coordinates
            x1, y1, z1, x2, y2, z2 = get_coords(idx, thb, xyzr, uv)
            x = x1 + x2
            y = y1 + y2
            z = z1 + z2
            # Store projections
            pts = np.empty((6, 2, len(x)))
            pts[0][0] = x
            pts[0][1] = y
            pts[1][0] = x
            pts[1][1] = z
            pts[2][0] = y
            pts[2][1] = z
            pts[3][0] = 2 * np.mean(x) - x
            pts[3][1] = y
            pts[4][0] = 2 * np.mean(x) - x
            pts[4][1] = z
            pts[5][0] = 2 * np.mean(y) - y
            pts[5][1] = z

            for i, plot in enumerate(self.povPlots):
                plot.clear()
                plot.addPoints(pts[i][0], pts[i][1])

    """ Function to update the tactile sensor readings """

    def update_sensor_readings(self):
        if len(self.dataQueue) > 0:
            data = self.dataQueue[-1]  # take the latest reading
            val_1 = (data[0] - self.MIN[0]) / (
                self.MAX[0] - self.MIN[0])  # reading of first sensor (index)
            val_2 = (data[1] - self.MIN[1]) / (
                self.MAX[1] - self.MIN[1])  # reading of second sensor (thumb)
            val_1, val_2 = np.clip([val_1, val_2], 0, 1)
            t = self.timestep[-1] + 0.005
            self.sensorData[0].append(val_1)
            self.sensorData[1].append(val_2)
            self.timestep.append(t)

            for i, plot in enumerate(self.sensorPlots):
                plot.setData(self.timestep[1:], self.sensorData[i])

            if t > 20:
                self.timestep = [0]
                self.sensorData = [[], []]

    """ Function to populate the data queue with raw values """

    def receive(self):
        recvLength = 6
        waiting = self.ser.inWaiting()
        if waiting >= recvLength:
            rawQueue = [x for x in self.ser.read(recvLength)]
            reading_0 = rawQueue[1] * 256 + rawQueue[2]
            reading_1 = rawQueue[3] * 256 + rawQueue[4]
            self.dataQueue.append([
                self.mvaFilt[0].getSample(reading_0),
                self.mvaFilt[1].getSample(reading_1)
            ])

    """ Function to procure data from data queue """

    def get_sensor_data(self):
        # Select last (atmost) 50 readings
        data = np.asarray(copy(self.dataQueue))[-20:]
        self.dataQueue.clear()
        # Normalize data
        data = (data - self.MIN) / (self.MAX - self.MIN)
        data = np.clip(data, 0, 1)
        # Convert data to standard format (list of [patchno][col][row] values)
        processed_data = deque([x.reshape(2, 1, 1) for x in data])
        return processed_data