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