@@ -23,6 +23,24 @@ def _is_premption_exception(exc: ControlException) -> bool:
2323 return "Move command preempted!" in str (exc )
2424
2525
26+ _DEFAULT_JOINT_STIFFNESS = np .full (7 , 50.0 )
27+
28+
29+ def _default_joint_damping (stiffness : np .ndarray ) -> np .ndarray :
30+ return 2.0 * np .sqrt (stiffness )
31+
32+
33+ def _as_joint_gain (name : str , value ) -> np .ndarray :
34+ vector = np .asarray (value , dtype = float )
35+ if vector .shape != (7 ,):
36+ raise ValueError (f"{ name } must contain exactly 7 values" )
37+ if not np .all (np .isfinite (vector )):
38+ raise ValueError (f"{ name } must contain only finite values" )
39+ if np .any (vector < 0.0 ):
40+ raise ValueError (f"{ name } must contain only non-negative values" )
41+ return vector .copy ()
42+
43+
2644class CartesianImpedanceTracker :
2745 """A long-lived session for streaming Cartesian impedance tracking commands.
2846
@@ -277,16 +295,21 @@ def __init__(
277295 q = self ._robot .current_joint_positions
278296 self ._reference_handle .set (q )
279297
280- # Seed gains handle with initial values.
281- stiffness_init = (
282- np .asarray (stiffness ) if stiffness is not None else np .full (7 , 50.0 )
298+ # Seed gains handle with initial values. When damping is omitted, use
299+ # critical damping for unit inertia so zero stiffness implies zero damping.
300+ stiffness_init = _as_joint_gain (
301+ "stiffness" , _DEFAULT_JOINT_STIFFNESS if stiffness is None else stiffness
302+ )
303+ damping_init = (
304+ _as_joint_gain ("damping" , damping )
305+ if damping is not None
306+ else _default_joint_damping (stiffness_init )
283307 )
284- damping_init = np .asarray (damping ) if damping is not None else np .full (7 , 10.0 )
285308 self ._gains_handle .set (stiffness_init , damping_init )
286309
287310 kwargs = {
288- "stiffness" : stiffness ,
289- "damping" : damping ,
311+ "stiffness" : stiffness_init ,
312+ "damping" : damping_init ,
290313 "constant_torque_offset" : constant_torque_offset ,
291314 "compensate_coriolis" : compensate_coriolis ,
292315 "max_delta_tau" : max_delta_tau ,
@@ -351,18 +374,27 @@ def set_gains(
351374 ) -> None :
352375 """Update joint impedance gains. Smoothed in the RT loop via exponential interpolation.
353376
354- Only the provided gains are changed; omitted gains keep their current target values.
377+ When stiffness is changed and damping is omitted, damping is updated to
378+ the critical damping heuristic 2*sqrt(stiffness). Otherwise, omitted
379+ gains keep their current target values.
355380 """
356381 current = self ._gains_handle .get () if self ._gains_handle .has_gains else None
357- k = (
358- np .asarray (stiffness )
359- if stiffness is not None
360- else (current .stiffness if current else np .full (7 , 50.0 ))
382+ k = _as_joint_gain (
383+ "stiffness" ,
384+ (
385+ stiffness
386+ if stiffness is not None
387+ else (current .stiffness if current else _DEFAULT_JOINT_STIFFNESS )
388+ ),
361389 )
362390 d = (
363- np . asarray ( damping )
391+ _as_joint_gain ( "damping" , damping )
364392 if damping is not None
365- else (current .damping if current else np .full (7 , 10.0 ))
393+ else (
394+ _default_joint_damping (k )
395+ if stiffness is not None or current is None
396+ else _as_joint_gain ("damping" , current .damping )
397+ )
366398 )
367399 self ._gains_handle .set (k , d )
368400
0 commit comments