3333 both -> medium (head-on)
3434
3535All motion values are ROS parameters: the launch seeds them from
36- `kaia set clean.*`, and they are live -- `ros2 param set /wall_clean arc_omega
37- 0.03 ` retunes the running robot without a relaunch.
36+ `kaia set clean.*`, and they are live -- `ros2 param set /wall_clean arc_radius
37+ 2.0 ` retunes the running robot without a relaunch.
3838
3939Interfaces:
4040 subscribes bumper_left/contact ros_gz_interfaces/Contacts
@@ -79,7 +79,7 @@ class WallClean(Node):
7979 def __init__ (self ) -> None :
8080 super ().__init__ ('wall_clean' )
8181 self .v_cruise = self .declare_parameter ('v_cruise' , 0.15 ).value
82- self .arc_omega = self .declare_parameter ('arc_omega ' , 0.1 ).value
82+ self .arc_radius = self .declare_parameter ('arc_radius ' , 1.5 ).value
8383 self .v_back = self .declare_parameter ('v_back' , 0.10 ).value
8484 self .backoff_s = self .declare_parameter ('backoff_s' , 0.5 ).value
8585 self .turn_speed = self .declare_parameter ('turn_speed' , 0.7 ).value
@@ -113,7 +113,7 @@ def __init__(self) -> None:
113113 self .state = CRUISE
114114 self .until = self .get_clock ().now ()
115115 self .create_timer (1.0 / hz , self ._tick )
116- # live tuning: `ros2 param set /wall_clean arc_omega 0.03 `
116+ # live tuning: `ros2 param set /wall_clean arc_radius 2.0 `
117117 self .add_on_set_parameters_callback (self ._on_params )
118118 self .get_logger ().info (
119119 'wall_clean: reactive right-wall cleaning -- Ctrl-C to stop' )
@@ -123,8 +123,8 @@ def _on_params(self, params) -> SetParametersResult:
123123 for p in params :
124124 if p .name == 'v_cruise' :
125125 self .v_cruise = p .value
126- elif p .name == 'arc_omega ' :
127- self .arc_omega = p .value
126+ elif p .name == 'arc_radius ' :
127+ self .arc_radius = p .value
128128 elif p .name == 'v_back' :
129129 self .v_back = p .value
130130 elif p .name == 'backoff_s' :
@@ -158,12 +158,18 @@ def _drive(self, lin, ang) -> None:
158158 msg .angular .z = float (ang )
159159 self .pub .publish (msg )
160160
161+ def _cruise_omega (self ) -> float :
162+ # Arc rate from the chosen radius: omega = v / r. Driving it from
163+ # arc_radius (rather than a fixed omega) keeps the arc SHAPE constant
164+ # at any cruise speed -- same path whether cruising fast or slow.
165+ return self .v_cruise / max (self .arc_radius , 1e-3 )
166+
161167 def _tick (self ) -> None :
162168 now = self .get_clock ().now ()
163169 if self .state == CRUISE :
164170 # forward + gentle RIGHT arc, drifting toward the wall on the right;
165171 # the very first leg (teleop-aimed approach) drives dead straight
166- arc = 0.0 if self ._first_leg else - self .arc_omega
172+ arc = 0.0 if self ._first_leg else - self ._cruise_omega ()
167173 self ._drive (self .v_cruise , arc )
168174 left = self ._pressed (self ._bump_left_t , now )
169175 right = self ._pressed (self ._bump_right_t , now )
@@ -178,7 +184,7 @@ def _tick(self) -> None:
178184 # Back OUT along the entry arc, reversed. Reversing a differential-
179185 # drive path exactly means negating BOTH linear and angular velocity,
180186 # so at the backoff speed the retrace rate is -entry_arc scaled by
181- # v_back/v_cruise (same path curvature v_cruise/arc_omega , opposite
187+ # v_back/v_cruise (same path curvature, radius arc_radius , opposite
182188 # travel direction). A straight first leg (entry_arc 0) backs straight.
183189 retrace = - self ._entry_arc * self .v_back / max (self .v_cruise , 1e-3 )
184190 self ._drive (- self .v_back , retrace )
0 commit comments