22Inverse kinematics for an n-link arm using the Jacobian inverse method
33
44Author: Daniel Ingram (daniel-s-ingram)
5+ Atsushi Sakai (@Atsushi_twi)
56"""
67import matplotlib .pyplot as plt
78import numpy as np
89
910from NLinkArm import NLinkArm
1011
11- #Simulation parameters
12+ # Simulation parameters
1213Kp = 2
1314dt = 0.1
1415N_LINKS = 10
1516N_ITERATIONS = 10000
1617
17- #States
18+ # States
1819WAIT_FOR_NEW_GOAL = 1
1920MOVING_TO_GOAL = 2
2021
21- def n_link_arm_ik ():
22+ show_animation = True
23+
24+
25+ def main ():
2226 """
2327 Creates an arm using the NLinkArm class and uses its inverse kinematics
2428 to move it to the desired position.
2529 """
26- link_lengths = [1 ]* N_LINKS
27- joint_angles = np .array ([0 ]* N_LINKS )
30+ link_lengths = [1 ] * N_LINKS
31+ joint_angles = np .array ([0 ] * N_LINKS )
2832 goal_pos = [N_LINKS , 0 ]
29- arm = NLinkArm (link_lengths , joint_angles , goal_pos )
33+ arm = NLinkArm (link_lengths , joint_angles , goal_pos , show_animation )
3034 state = WAIT_FOR_NEW_GOAL
3135 solution_found = False
3236 while True :
@@ -35,10 +39,11 @@ def n_link_arm_ik():
3539 end_effector = arm .end_effector
3640 errors , distance = distance_to_goal (end_effector , goal_pos )
3741
38- #State machine to allow changing of goal before current goal has been reached
42+ # State machine to allow changing of goal before current goal has been reached
3943 if state is WAIT_FOR_NEW_GOAL :
4044 if distance > 0.1 and not solution_found :
41- joint_goal_angles , solution_found = inverse_kinematics (link_lengths , joint_angles , goal_pos )
45+ joint_goal_angles , solution_found = inverse_kinematics (
46+ link_lengths , joint_angles , goal_pos )
4247 if not solution_found :
4348 print ("Solution could not be found." )
4449 state = WAIT_FOR_NEW_GOAL
@@ -47,13 +52,14 @@ def n_link_arm_ik():
4752 state = MOVING_TO_GOAL
4853 elif state is MOVING_TO_GOAL :
4954 if distance > 0.1 and (old_goal is goal_pos ):
50- joint_angles = joint_angles + Kp * ang_diff (joint_goal_angles , joint_angles )* dt
55+ joint_angles = joint_angles + Kp * \
56+ ang_diff (joint_goal_angles , joint_angles ) * dt
5157 else :
5258 state = WAIT_FOR_NEW_GOAL
5359 solution_found = False
5460
5561 arm .update_joints (joint_angles )
56-
62+
5763
5864def inverse_kinematics (link_lengths , joint_angles , goal_pos ):
5965 """
@@ -69,34 +75,90 @@ def inverse_kinematics(link_lengths, joint_angles, goal_pos):
6975 joint_angles = joint_angles + np .matmul (J , errors )
7076 return joint_angles , False
7177
78+
79+ def get_random_goal ():
80+ from random import random
81+ SAREA = 15.0
82+ return [SAREA * random () - SAREA / 2.0 ,
83+ SAREA * random () - SAREA / 2.0 ]
84+
85+
86+ def animation ():
87+ link_lengths = [1 ] * N_LINKS
88+ joint_angles = np .array ([0 ] * N_LINKS )
89+ goal_pos = get_random_goal ()
90+ arm = NLinkArm (link_lengths , joint_angles , goal_pos , show_animation )
91+ state = WAIT_FOR_NEW_GOAL
92+ solution_found = False
93+
94+ i_goal = 0
95+ while True :
96+ old_goal = goal_pos
97+ goal_pos = arm .goal
98+ end_effector = arm .end_effector
99+ errors , distance = distance_to_goal (end_effector , goal_pos )
100+
101+ # State machine to allow changing of goal before current goal has been reached
102+ if state is WAIT_FOR_NEW_GOAL :
103+
104+ if distance > 0.1 and not solution_found :
105+ joint_goal_angles , solution_found = inverse_kinematics (
106+ link_lengths , joint_angles , goal_pos )
107+ if not solution_found :
108+ print ("Solution could not be found." )
109+ state = WAIT_FOR_NEW_GOAL
110+ arm .goal = get_random_goal ()
111+ elif solution_found :
112+ state = MOVING_TO_GOAL
113+ elif state is MOVING_TO_GOAL :
114+ if distance > 0.1 and (old_goal is goal_pos ):
115+ joint_angles = joint_angles + Kp * \
116+ ang_diff (joint_goal_angles , joint_angles ) * dt
117+ else :
118+ state = WAIT_FOR_NEW_GOAL
119+ solution_found = False
120+ arm .goal = get_random_goal ()
121+ i_goal += 1
122+
123+ if i_goal >= 5 :
124+ break
125+
126+ arm .update_joints (joint_angles )
127+
128+
72129def forward_kinematics (link_lengths , joint_angles ):
73130 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 ]))
131+ for i in range (1 , N_LINKS + 1 ):
132+ x += link_lengths [i - 1 ] * np .cos (np .sum (joint_angles [:i ]))
133+ y += link_lengths [i - 1 ] * np .sin (np .sum (joint_angles [:i ]))
77134 return np .array ([x , y ]).T
78135
136+
79137def jacobian_inverse (link_lengths , joint_angles ):
80138 J = np .zeros ((2 , N_LINKS ))
81139 for i in range (N_LINKS ):
82140 J [0 , i ] = 0
83141 J [1 , i ] = 0
84142 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 ]))
143+ J [0 , i ] -= link_lengths [j ] * np .sin (np .sum (joint_angles [:j ]))
144+ J [1 , i ] += link_lengths [j ] * np .cos (np .sum (joint_angles [:j ]))
87145
88146 return np .linalg .pinv (J )
89147
148+
90149def distance_to_goal (current_pos , goal_pos ):
91150 x_diff = goal_pos [0 ] - current_pos [0 ]
92151 y_diff = goal_pos [1 ] - current_pos [1 ]
93152 return np .array ([x_diff , y_diff ]).T , np .math .sqrt (x_diff ** 2 + y_diff ** 2 )
94153
154+
95155def ang_diff (theta1 , theta2 ):
96156 """
97157 Returns the difference between two angles in the range -pi to +pi
98158 """
99- return (theta1 - theta2 + np .pi )% (2 * np .pi ) - np .pi
159+ return (theta1 - theta2 + np .pi ) % (2 * np .pi ) - np .pi
160+
100161
101162if __name__ == '__main__' :
102- n_link_arm_ik ()
163+ main ()
164+ # animation()
0 commit comments