Example #1
0
    def inverse_kinematics(self, goal, tol_limit=0.05, max_iterations=100):
        '''
		Preforms Inverse Kinematics on the arm using the known lengths and last positions.
		This uses the FABRIK method.
		See the more information section for information on the FABRIK method.


		Parameters
		----------
		goal : arraylike or Point
			desired location in the format of [x,y,z]
		tol_limit : scalar (optional)
			tolerance between the desired location and actual one after iteration
		max_iterations : scalar (optional)
			maximum itterations that the function is allowed to run


		Returns
		-------
		q1 : scalar
			rotation of the base about the z axis
		q2 : scalar
			rotation of first link relative to the base
		q3 : scalar
			rotation of the second link relative to the first link
		q4 : scalar
			rotation of the third link relative to the second link
		x_joints : array_like
			x points for all the joint locations
		y_joints : array_like
			y points for all the joint locations
		z_joints : array_like
			z points for all the joint locations


		More Information
		----------------
		Overall algorithim:
			solve for unit vectors going backwards from the desired point to the original point
			of the joints, then forwards from the origin, see youtube video
			https://www.youtube.com/watch?v=UNoX65PRehA&t=817s

		'''
        if isinstance(goal, list):
            if len(goal) != 3:
                raise IndexError(
                    "goal unclear. Need x,y,z coordinates in Point or list form."
                )
            goal = Point(goal[0], goal[1], goal[2])

        # Find base rotation
        # Initial angle of where end effector is
        initial_base_roation = np.arctan2(self.y_joints[-1], self.x_joints[-1])

        # Desired angle
        # arctan2(y,x)
        self.q1 = np.arctan2(goal.y, goal.x)

        # Base rotation
        base_rotation = self.q1 - initial_base_roation

        # Base rotation matrix about z
        z_rot = np.array([[np.cos(base_rotation), -np.sin(base_rotation), 0.0],
                          [np.sin(base_rotation),
                           np.cos(base_rotation), 0.0], [0.0, 0.0, 1.0]])

        # Rotate the location of each joint by the base rotation
        # This will force the FABRIK algorithim to only solve
        # in two dimensions, else each joint will move as if it has
        # a 3 DOF range of motion

        point4 = Point(
            np.dot(z_rot,
                   [self.x_joints[3], self.y_joints[3], self.z_joints[3]]))
        point3 = Point(
            np.dot(z_rot,
                   [self.x_joints[2], self.y_joints[2], self.z_joints[2]]))
        point2 = Point(
            np.dot(z_rot,
                   [self.x_joints[1], self.y_joints[1], self.z_joints[1]]))
        point1 = Point(
            np.dot(z_rot,
                   [self.x_joints[0], self.y_joints[0], self.z_joints[0]]))

        # store starting point of the first joint
        starting_point1 = point1

        iterations = 0

        # Make sure the desired x,y,z point is reachable
        if Vector.magnitude(goal) > self.total_arm_length:
            print ' desired point is likely out of reach'

        for _ in range(1, max_iterations + 1):

            # backwards
            point3 = Vector.project_along_vector(goal, point3,
                                                 self.third_link_length)
            point2 = Vector.project_along_vector(point3, point2,
                                                 self.second_link_length)
            point1 = Vector.project_along_vector(point2, point1,
                                                 self.first_link_length)

            # forwards
            point2 = Vector.project_along_vector(point1, point2,
                                                 self.first_link_length)
            point3 = Vector.project_along_vector(point2, point3,
                                                 self.second_link_length)
            point4 = Vector.project_along_vector(point3, goal,
                                                 self.third_link_length)

            # Solve for tolerance between iterated point and desired x,y,z,
            tol = point4 - goal

            # Make tolerance relative to x,y,z
            tol = Vector.magnitude(tol)

            iterations = iterations + 1

            # Check if tolerance is within the specefied limit
            if tol < tol_limit:
                break

        # Re-organize points into a big matrix for plotting elsewhere
        self.p_joints = np.transpose([
            starting_point1.as_array(),
            point2.as_array(),
            point3.as_array(),
            point4.as_array()
        ])

        self.x_joints = self.p_joints[0]
        self.y_joints = self.p_joints[1]
        self.z_joints = self.p_joints[2]

        # Return the joint angles by finding the angles with the dot produt
        vector21 = Vector(point2 - point1)
        vector32 = Vector(point3 - point2)
        vector43 = Vector(point4 - point3)

        # returns -pi to pi
        self.q2 = np.arctan2(
            vector21.end.z, Vector.magnitude([vector21.end.x, vector21.end.y]))

        # Negative sign because of dh notation, a rotation away from the previous link
        # and towards the x-y plane is a negative moment about the relative z axis.
        # the relative z axis of each link is out of the page if looking at the arm
        # in 2D
        # the x axis in dh convention is typically along the link direction.

        self.q3 = -1.0 * vector21.angle(vector32)
        self.q4 = -1.0 * vector32.angle(vector43)

        return [
            self.q1, self.q2, self.q3, self.q4, self.x_joints, self.y_joints,
            self.z_joints
        ]