1+ """
2+ Inverse kinematics for an n-link arm using the Jacobian inverse method
3+
4+ Author: Daniel Ingram (daniel-s-ingram)
5+ """
6+ import matplotlib .pyplot as plt
7+ import numpy as np
8+
9+ from NLinkArm import NLinkArm
10+
11+ #Simulation parameters
12+ Kp = 2
13+ dt = 0.1
14+ N_LINKS = 10
15+ N_ITERATIONS = 10000
16+
17+ #States
18+ WAIT_FOR_NEW_GOAL = 1
19+ MOVING_TO_GOAL = 2
20+
21+ def n_link_arm_ik ():
22+ """
23+ Creates an arm using the NLinkArm class and uses its inverse kinematics
24+ to move it to the desired position.
25+ """
26+ link_lengths = [1 ]* N_LINKS
27+ joint_angles = np .array ([0 ]* N_LINKS )
28+ goal_pos = [N_LINKS , 0 ]
29+ arm = NLinkArm (link_lengths , joint_angles , goal_pos )
30+ state = WAIT_FOR_NEW_GOAL
31+ solution_found = False
32+ while True :
33+ old_goal = goal_pos
34+ goal_pos = arm .goal
35+ end_effector = arm .end_effector
36+ errors , distance = distance_to_goal (end_effector , goal_pos )
37+
38+ #State machine to allow changing of goal before current goal has been reached
39+ if state is WAIT_FOR_NEW_GOAL :
40+ if distance > 0.1 and not solution_found :
41+ joint_goal_angles , solution_found = inverse_kinematics (link_lengths , joint_angles , goal_pos )
42+ if not solution_found :
43+ print ("Solution could not be found." )
44+ state = WAIT_FOR_NEW_GOAL
45+ arm .goal = end_effector
46+ elif solution_found :
47+ state = MOVING_TO_GOAL
48+ elif state is MOVING_TO_GOAL :
49+ if distance > 0.1 and (old_goal is goal_pos ):
50+ joint_angles = joint_angles + Kp * ang_diff (joint_goal_angles , joint_angles )* dt
51+ else :
52+ state = WAIT_FOR_NEW_GOAL
53+ solution_found = False
54+
55+ arm .update_joints (joint_angles )
56+
57+
58+ def inverse_kinematics (link_lengths , joint_angles , goal_pos ):
59+ """
60+ Calculates the inverse kinematics using the Jacobian inverse method.
61+ """
62+ for iteration in range (N_ITERATIONS ):
63+ current_pos = forward_kinematics (link_lengths , joint_angles )
64+ errors , distance = distance_to_goal (current_pos , goal_pos )
65+ if distance < 0.1 :
66+ print ("Solution found in %d iterations." % iteration )
67+ return joint_angles , True
68+ J = jacobian_inverse (link_lengths , joint_angles )
69+ joint_angles = joint_angles + np .matmul (J , errors )
70+ return joint_angles , False
71+
72+ def forward_kinematics (link_lengths , joint_angles ):
73+ x = y = 0
74+ for i in range (1 , N_LINKS + 1 ):
75+ x += link_lengths [i - 1 ]* np .cos (np .sum (joint_angles [:i ]))
76+ y += link_lengths [i - 1 ]* np .sin (np .sum (joint_angles [:i ]))
77+ return np .array ([x , y ]).T
78+
79+ def jacobian_inverse (link_lengths , joint_angles ):
80+ J = np .zeros ((2 , N_LINKS ))
81+ for i in range (N_LINKS ):
82+ J [0 , i ] = 0
83+ J [1 , i ] = 0
84+ for j in range (i , N_LINKS ):
85+ J [0 , i ] -= link_lengths [j ]* np .sin (np .sum (joint_angles [:j ]))
86+ J [1 , i ] += link_lengths [j ]* np .cos (np .sum (joint_angles [:j ]))
87+
88+ return np .linalg .pinv (J )
89+
90+ def distance_to_goal (current_pos , goal_pos ):
91+ x_diff = goal_pos [0 ] - current_pos [0 ]
92+ y_diff = goal_pos [1 ] - current_pos [1 ]
93+ return np .array ([x_diff , y_diff ]).T , np .math .sqrt (x_diff ** 2 + y_diff ** 2 )
94+
95+ def ang_diff (theta1 , theta2 ):
96+ """
97+ Returns the difference between two angles in the range -pi to +pi
98+ """
99+ return (theta1 - theta2 + np .pi )% (2 * np .pi ) - np .pi
100+
101+ if __name__ == '__main__' :
102+ n_link_arm_ik ()
0 commit comments