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