@@ -40,6 +40,10 @@ def __init__(self, Kp_rho, Kp_alpha, Kp_beta):
4040 self .Kp_rho = Kp_rho
4141 self .Kp_alpha = Kp_alpha
4242 self .Kp_beta = Kp_beta
43+ self .direction = 0 # 0: not initialized, 1: forward, -1: backward
44+
45+ def reset_direction (self ):
46+ self .direction = 0
4347
4448 def calc_control_command (self , x_diff , y_diff , theta , theta_goal ):
4549 """
@@ -76,26 +80,38 @@ def calc_control_command(self, x_diff, y_diff, theta, theta_goal):
7680 # [-pi, pi] to prevent unstable behavior e.g. difference going
7781 # from 0 rad to 2*pi rad with slight turn
7882
83+ # Ref: The velocity v always has a constant sign which depends on the initial value of α.
84+ if self .direction == 0 :
85+ alpha = angle_mod (np .arctan2 (y_diff , x_diff ) - theta )
86+ if alpha > np .pi / 2 or alpha < - np .pi / 2 :
87+ print (f"alpha: { alpha } , direction: -1" )
88+ self .direction = - 1
89+ else :
90+ print (f"alpha: { alpha } , direction: 1" )
91+ self .direction = 1
92+
7993 rho = np .hypot (x_diff , y_diff )
8094 v = self .Kp_rho * rho
8195
82- alpha = angle_mod (np .arctan2 (y_diff , x_diff ) - theta )
83- beta = angle_mod (theta_goal - theta - alpha )
84- if alpha > np .pi / 2 or alpha < - np .pi / 2 :
85- # recalculate alpha to make alpha in the range of [-pi/2, pi/2]
96+ if self .direction == 1 :
97+ alpha = angle_mod (np .arctan2 (y_diff , x_diff ) - theta )
98+ alpha = np .clip (alpha , - np .pi / 2 , np .pi / 2 )
99+ else :
100+ # backward direction should calculate alpha from the opposite direction
86101 alpha = angle_mod (np .arctan2 (- y_diff , - x_diff ) - theta )
87- beta = angle_mod (theta_goal - theta - alpha )
88- w = self .Kp_alpha * alpha - self .Kp_beta * beta
102+ alpha = np .clip (alpha , - np .pi / 2 , np .pi / 2 )
89103 v = - v
90- else :
91- w = self .Kp_alpha * alpha - self .Kp_beta * beta
104+
105+ beta = angle_mod (theta_goal - theta - alpha )
106+ w = self .Kp_alpha * alpha - self .Kp_beta * beta
92107
93108 return rho , v , w
94109
95110
96111# simulation parameters
97112controller = PathFinderController (9 , 15 , 3 )
98113dt = 0.01
114+ MAX_SIM_TIME = 5 # seconds, robot will stop moving when time exceeds this value
99115
100116# Robot specifications
101117MAX_LINEAR_SPEED = 15
@@ -105,17 +121,20 @@ def calc_control_command(self, x_diff, y_diff, theta, theta_goal):
105121
106122
107123def move_to_pose (x_start , y_start , theta_start , x_goal , y_goal , theta_goal ):
124+ controller .reset_direction ()
108125 x = x_start
109126 y = y_start
110127 theta = theta_start
111128
112129 x_diff = x_goal - x
113130 y_diff = y_goal - y
114131
115- x_traj , y_traj = [], []
132+ x_traj , y_traj , v_traj , w_traj = [x ], [y ], [ 0 ], [ 0 ]
116133
117134 rho = np .hypot (x_diff , y_diff )
118- while rho > 0.001 :
135+ t = 0
136+ while rho > 0.001 and t < MAX_SIM_TIME :
137+ t += dt
119138 x_traj .append (x )
120139 y_traj .append (y )
121140
@@ -130,6 +149,9 @@ def move_to_pose(x_start, y_start, theta_start, x_goal, y_goal, theta_goal):
130149 if abs (w ) > MAX_ANGULAR_SPEED :
131150 w = np .sign (w ) * MAX_ANGULAR_SPEED
132151
152+ v_traj .append (v )
153+ w_traj .append (w )
154+
133155 theta = theta + w * dt
134156 x = x + v * np .cos (theta ) * dt
135157 y = y + v * np .sin (theta ) * dt
@@ -154,6 +176,8 @@ def move_to_pose(x_start, y_start, theta_start, x_goal, y_goal, theta_goal):
154176 )
155177 plot_vehicle (x , y , theta , x_traj , y_traj )
156178
179+ return x_traj , y_traj , v_traj , w_traj
180+
157181
158182def plot_vehicle (x , y , theta , x_traj , y_traj ): # pragma: no cover
159183 # Corners of triangular vehicle when pointing to the right (0 radians)
0 commit comments