Skip to content

Commit c2ae93d

Browse files
author
shakuevda
committed
refactor(control): introduce abstract AxisController and integrate into control loop
1 parent 22d0ab2 commit c2ae93d

File tree

4 files changed

+91
-78
lines changed

4 files changed

+91
-78
lines changed

src/stingray_core_control/stingray_core_control/control/__init__.py

Whitespace-only changes.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# stingray_core_control/control/axis_controller.py
2+
3+
from abc import ABC, abstractmethod
4+
5+
class AxisController(ABC):
6+
"""
7+
Абстрактный контроллер одной оси.
8+
Отвечает за вычисление управляющего воздействия.
9+
"""
10+
11+
@abstractmethod
12+
def compute(self, target: float, dt: float) -> float:
13+
pass
14+
15+
class AngularAxisController(AxisController):
16+
def __init__(self, controller, angle_fn, rate_fn, feedback_flag_fn):
17+
self.controller = controller
18+
self.angle = angle_fn
19+
self.rate = rate_fn
20+
self.feedback_flag = feedback_flag_fn
21+
22+
def compute(self, target, dt):
23+
return self.controller.update(
24+
target,
25+
self.angle(),
26+
self.rate(),
27+
dt,
28+
self.feedback_flag(),
29+
param_update=self.controller.__dict__,
30+
)
31+
32+
class LinearAxisController(AxisController):
33+
def __init__(self, controller, pos_fn, vel_fn, accel_fn, feedback_flag_fn):
34+
self.controller = controller
35+
self.pos = pos_fn
36+
self.vel = vel_fn
37+
self.accel = accel_fn
38+
self.feedback_flag = feedback_flag_fn
39+
40+
def compute(self, target, dt):
41+
return self.controller.update(
42+
target,
43+
self.pos(),
44+
self.vel(),
45+
self.accel(),
46+
dt,
47+
self.feedback_flag(),
48+
param_update=self.controller.__dict__,
49+
)

src/stingray_core_control/stingray_core_control/state/__init__.py

Whitespace-only changes.

src/stingray_core_control/stingray_core_control/stingray_core_control_node.py

Lines changed: 42 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
from .utils.save_params import save_params
2727
from .state.imu import ImuState
2828
from .state.control import ControlState
29+
from .control.axis_controller import (
30+
AxisController,
31+
AngularAxisController,
32+
LinearAxisController,
33+
)
2934

3035

3136
class StingrayCoreControlNode(Node):
@@ -117,9 +122,6 @@ def __init__(self):
117122
self.declare_parameter('axes', ["surge", "sway", "heave", "roll", "pitch", "yaw"])
118123
self.axes = list(self.get_parameter('axes').get_parameter_value().string_array_value)
119124

120-
self.imu = ImuState()
121-
self.control = ControlState.from_axes(self.axes)
122-
123125
self.param_keys = ["K_p", "K_1", "K_2", "K_i", "I_min", "I_max", "out_sat", "ap_K", "ap_T"]
124126

125127
# внутри ноды
@@ -141,6 +143,31 @@ def __init__(self):
141143

142144
self.get_logger().info(f"Controllers initialized: {self.controllers}")
143145

146+
self.imu = ImuState()
147+
self.control = ControlState.from_axes(self.axes)
148+
149+
# --- Axis controllers (angular / linear split) ---
150+
self.axis_ctrl: dict[str, AxisController] = {
151+
"yaw": AngularAxisController(
152+
controller=self.controllers["yaw"],
153+
angle_fn=lambda: self.imu.yaw,
154+
rate_fn=lambda: self.imu.rate_z,
155+
feedback_flag_fn=lambda: self.flag_setup_feedback_speed,
156+
),
157+
"pitch": AngularAxisController(
158+
controller=self.controllers["pitch"],
159+
angle_fn=lambda: self.imu.pitch,
160+
rate_fn=lambda: self.imu.rate_y,
161+
feedback_flag_fn=lambda: self.flag_setup_feedback_speed,
162+
),
163+
"heave": LinearAxisController(
164+
controller=self.controllers["heave"],
165+
pos_fn=lambda: self.depth,
166+
vel_fn=lambda: 0.0, # пока нет оценки вертикальной скорости
167+
accel_fn=lambda: 0.0,
168+
feedback_flag_fn=lambda: self.flag_setup_feedback_speed,
169+
),
170+
}
144171

145172
qos = QoSProfile(depth=10)
146173

@@ -211,17 +238,21 @@ def control_loop(self):
211238
dt = now - self.last_time if self.last_time is not None else 0.0
212239
self.last_time = now
213240
self.last_dt = dt
214-
# self.get_logger().info(f"self.flag_setup_feedback_speed={self.flag_setup_feedback_speed}")
241+
215242
# === 1. Определяем управляющие воздействия ===
216-
u_surge = self.compute_surge() if self.control.enabled["surge"] else self.control.impact["surge"]
217-
u_sway = self.compute_sway() if self.control.enabled["sway"] else self.control.impact["sway"]
218-
u_heave = self.compute_heave() if self.control.enabled["heave"] else self.control.impact["heave"]
219-
u_roll = self.compute_roll() if self.control.enabled["roll"] else self.control.impact["roll"]
220-
u_pitch = self.compute_pitch() if self.control.enabled["pitch"] else self.control.impact["pitch"]
221-
u_yaw = self.compute_yaw() if self.control.enabled["yaw"] else self.control.impact["yaw"]
243+
u: dict[str, float] = {}
244+
245+
for axis in self.axes:
246+
if axis in self.axis_ctrl and self.control.enabled[axis]:
247+
u[axis] = self.axis_ctrl[axis].compute(
248+
target=self.control.impact[axis],
249+
dt=self.last_dt,
250+
)
251+
else:
252+
u[axis] = self.control.impact[axis]
222253

223254
# === 2. Преобразуем в команды thrusters ===
224-
control_list = [u_surge, u_sway, u_heave, u_roll, u_pitch, u_yaw]
255+
control_list = [u["surge"], u["sway"], u["heave"], u["roll"], u["pitch"], u["yaw"]]
225256
thruster_cmds = self.mixer.mix_from_list(control_list)
226257

227258
# === 3. Публикуем UInt8MultiArray ===
@@ -304,73 +335,6 @@ def control_mode_flags_callback(self, msg: UInt8):
304335
self.control.enabled["pitch"] = bool(flags & (1 << 4))
305336
self.control.enabled["roll"] = bool(flags & (1 << 5))
306337

307-
# === Заглушки регуляторов ===
308-
def compute_surge(self):
309-
return 0.0
310-
311-
def compute_sway(self):
312-
return 0.0
313-
314-
def compute_heave(self):
315-
dt = self.last_dt
316-
return self.depth_ctrl.update(
317-
self.control.impact["heave"],
318-
self.depth,
319-
dt,
320-
self.flag_setup_feedback_speed,
321-
param_update={
322-
"K_p": self.controllers["heave"].K_p,
323-
"K_i": self.controllers["heave"].K_i,
324-
"K_1": self.controllers["heave"].K_1,
325-
"K_2": self.controllers["heave"].K_2,
326-
"ap_T": self.controllers["heave"].ap_T,
327-
"ap_K": self.controllers["heave"].ap_K,
328-
"out_sat": self.controllers["heave"].out_sat,
329-
})
330-
331-
332-
def compute_roll(self):
333-
return 0.0
334-
335-
336-
def compute_pitch(self):
337-
dt = self.last_dt
338-
return self.pitch_ctrl.update(
339-
self.control.impact["pitch"],
340-
self.imu.pitch,
341-
self.imu.rate_y,
342-
dt,
343-
self.flag_setup_feedback_speed,
344-
param_update={
345-
"K_p": self.controllers["pitch"].K_p,
346-
"K_i": self.controllers["pitch"].K_i,
347-
"K_1": self.controllers["pitch"].K_1,
348-
"K_2": self.controllers["pitch"].K_2,
349-
"ap_T": self.controllers["pitch"].ap_T,
350-
"ap_K": self.controllers["pitch"].ap_K,
351-
"out_sat": self.controllers["pitch"].out_sat,
352-
})
353-
354-
def compute_yaw(self):
355-
dt = self.last_dt
356-
return self.yaw_ctrl.update(
357-
self.control.impact["yaw"],
358-
self.imu.yaw,
359-
self.imu.rate_z,
360-
dt,
361-
self.flag_setup_feedback_speed,
362-
param_update={
363-
"K_p": self.controllers["yaw"].K_p,
364-
"K_i": self.controllers["yaw"].K_i,
365-
"K_1": self.controllers["yaw"].K_1,
366-
"K_2": self.controllers["yaw"].K_2,
367-
"ap_T": self.controllers["yaw"].ap_T,
368-
"ap_K": self.controllers["yaw"].ap_K,
369-
"out_sat": self.controllers["yaw"].out_sat,
370-
})
371-
372-
373-
374338
def _on_params_changed(self, params: list[Parameter]) -> SetParametersResult:
375339
try:
376340
thruster_params: list[Parameter] = []

0 commit comments

Comments
 (0)