99"""
1010
1111import math
12+
1213import numpy as np
1314import shapely
1415from shapely import Polygon , Point , affinity , union
1516from shapely .ops import nearest_points
1617
17-
18- import math
19- import numpy as np
20- from typing import Tuple
18+ from biped_walking_controller .state_machine import WalkingStateMachineParams , Foot
2119
2220
2321def bezier_quintic (P : np .ndarray , s : np .ndarray ) -> np .ndarray :
@@ -185,15 +183,44 @@ def __call__(self, p_start: np.ndarray, p_end: np.ndarray, s: np.ndarray) -> np.
185183 return path
186184
187185
188- def compute_feet_path_and_poses (
186+ def compute_steps_sequence (
187+ rf_initial_pose : np .ndarray ,
188+ lf_initial_pose : np .ndarray ,
189+ n_steps : int ,
190+ l_stride : float ,
191+ ):
192+ steps_pose = np .zeros ((n_steps + 2 , 3 ))
193+ steps_pose [0 ] = rf_initial_pose
194+ steps_ids = [Foot .RIGHT ]
195+ for i in range (1 , n_steps + 1 ):
196+ sign = - 1.0 if i % 2 == 0 else 1.0
197+ steps_ids .append (Foot .RIGHT if i % 2 == 0 else Foot .LEFT )
198+ steps_pose [i ] = np .array ([i * l_stride , sign * math .fabs (lf_initial_pose [1 ]), 0.0 ])
199+
200+ # Add a last step to have both feet at the same level
201+ steps_pose [- 1 ] = steps_pose [- 2 ]
202+ steps_pose [- 1 ][1 ] = steps_pose [- 1 ][1 ] * - 1.0
203+
204+ return steps_pose , steps_ids
205+
206+
207+ def compute_time_vector (t_ss , t_ds , t_init , t_final , n_steps , dt ):
208+ total_time = t_init + n_steps * (t_ss + t_ds ) + (t_ss + t_final )
209+ N = int (total_time / dt )
210+ t = np .arange (N ) * dt
211+
212+ return t
213+
214+
215+ def compute_feet_trajectories (
189216 rf_initial_pose ,
190217 lf_initial_pose ,
191218 n_steps ,
219+ steps_pose ,
192220 t_ss ,
193221 t_ds ,
194222 t_init ,
195223 t_final ,
196- l_stride ,
197224 dt ,
198225 traj_generator = BezierCurveFootPathGenerator (foot_height = 0.1 ),
199226):
@@ -221,7 +248,7 @@ def compute_feet_path_and_poses(
221248 Double-support duration between steps.
222249 t_init : float
223250 Initial DS duration before stepping.
224- t_final : float
251+ t_end : float
225252 Final DS duration to center the CoM.
226253 l_stride : float
227254 Step length along +x for each new foothold.
@@ -258,30 +285,14 @@ def compute_feet_path_and_poses(
258285 # the same level and a double support step to place the CoM in the
259286 # middle of the feet
260287
261- total_time = t_init + n_steps * (t_ss + t_ds ) + (t_ss + t_final )
262- N = int (total_time / dt )
263- t = np .arange (N ) * dt
288+ t = compute_time_vector (t_ss , t_ds , t_init , t_final , n_steps , dt )
289+ N = len (t )
264290
265291 # Initialize path
266292 rf_path = np .ones ([N , 3 ]) * rf_initial_pose
267293 lf_path = np .ones ([N , 3 ]) * lf_initial_pose
268294 phases = np .ones (N )
269295
270- # Switch of the CoM to the right foot implies both feet stays at the same position
271- mask = t < t_init
272- rf_path [mask , :] = rf_initial_pose
273- lf_path [mask , :] = lf_initial_pose
274-
275- steps_pose = np .zeros ((n_steps + 2 , 3 ))
276- steps_pose [0 ] = rf_initial_pose
277- for i in range (1 , n_steps + 1 ):
278- sign = - 1.0 if i % 2 == 0 else 1.0
279- steps_pose [i ] = np .array ([i * l_stride , sign * math .fabs (lf_initial_pose [1 ]), 0.0 ])
280-
281- # Add a last step to have both feet at the same level
282- steps_pose [- 1 ] = steps_pose [- 2 ]
283- steps_pose [- 1 ][1 ] = steps_pose [- 1 ][1 ] * - 1.0
284-
285296 # Compute motion of left foot
286297 for k in range (0 , n_steps , 2 ):
287298 t_begin = t_init + k * (t_ss + t_ds )
@@ -301,7 +312,7 @@ def compute_feet_path_and_poses(
301312
302313 # # Add constant part till the next step
303314 t_begin = t_init + k * (t_ss + t_ds ) + t_ss
304- t_end = total_time
315+ t_end = t [ - 1 ]
305316 mask = (t >= t_begin ) & (t < t_end )
306317 lf_path [mask , 0 ] = steps_pose [k + 1 ][0 ]
307318
@@ -324,11 +335,11 @@ def compute_feet_path_and_poses(
324335
325336 # # Add constant part till the next step
326337 t_begin = t_init + k * (t_ss + t_ds ) + t_ss
327- t_end = total_time
338+ t_end = t [ - 1 ]
328339 mask = (t >= t_begin ) & (t < t_end )
329340 rf_path [mask , 0 ] = steps_pose [k + 1 ][0 ]
330341
331- return t , lf_path , rf_path , steps_pose , phases
342+ return t , lf_path , rf_path , phases
332343
333344
334345def clamp_to_polygon (pnt : np .ndarray , poly : Polygon ):
@@ -442,3 +453,37 @@ def get_active_polygon(t: float, steps_pose, t_ss: float, t_ds: float, foot_shap
442453 return compute_single_support_polygon (steps_pose [- 1 ], foot_shape )
443454 else :
444455 return compute_double_support_polygon (steps_pose [i ], steps_pose [i + 1 ], foot_shape )
456+
457+
458+ def compute_swing_foot_pose (
459+ t_state : float ,
460+ params : WalkingStateMachineParams ,
461+ step_start : np .ndarray ,
462+ step_target : np .ndarray ,
463+ touchdown_extension_vel : float ,
464+ path_generator ,
465+ ) -> np .ndarray :
466+ """
467+ Compute swing foot pose for current state time with late touchdown extension.
468+
469+ - For t_state in [0, t_ss]: nominal min-jerk swing between step_start and step_target.
470+ - For t_state > t_ss and no contact: keep moving the *commanded* foot down
471+ below the nominal ground height at constant velocity.
472+ The physics/contact solver will clamp penetration.
473+ - As soon as contact_force > force_threshold: freeze at step_target.
474+ """
475+ t_ss = params .t_ss
476+
477+ if t_state <= t_ss :
478+ # Normal swing phase
479+ s = np .clip (t_state / t_ss , 0.0 , 1.0 )
480+
481+ return path_generator (step_start , step_target , s )
482+
483+ # Late-touchdown extension phase
484+ # We assume step_target[2] is the nominal ground height.
485+ # After nominal end of swing, keep commanding the foot below that plane.
486+ dt = t_state - t_ss
487+ pos = step_target .copy ()
488+ pos [2 ] = step_target [2 ] - touchdown_extension_vel * dt
489+ return pos
0 commit comments