Hardware Connection fix
This commit is contained in:
+13
-3
@@ -11,7 +11,7 @@ class ArduinoCommunication(Thread):
|
|||||||
def __init__(self, port=cfg.port, baudrate=cfg.baudrate, timeout=cfg.comm_timeout):
|
def __init__(self, port=cfg.port, baudrate=cfg.baudrate, timeout=cfg.comm_timeout):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.daemon = True # Thread schlie�t sich beim Programmende
|
self.daemon = True # Thread schlie�t sich beim Programmende
|
||||||
self.serial_conn = serial.Serial(port, baudrate, timeout=timeout)
|
self.serial_conn = serial.Serial(port, baudrate, timeout=timeout)
|
||||||
time.sleep(2) # Warten bis Arduino ready
|
time.sleep(2) # Warten bis Arduino ready
|
||||||
|
|
||||||
self.command_queue = Queue()
|
self.command_queue = Queue()
|
||||||
@@ -19,6 +19,9 @@ class ArduinoCommunication(Thread):
|
|||||||
self.running = Event()
|
self.running = Event()
|
||||||
self.running.set()
|
self.running.set()
|
||||||
|
|
||||||
|
def send_motion(self, radial_array):
|
||||||
|
self.write(radial_array)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while self.running.is_set():
|
while self.running.is_set():
|
||||||
# 1. Befehle senden
|
# 1. Befehle senden
|
||||||
@@ -70,5 +73,12 @@ class ArduinoCommunication(Thread):
|
|||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.running.clear()
|
self.running.clear()
|
||||||
self.join()
|
try:
|
||||||
self.serial_conn.close()
|
self.join(timeout=0.5)
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
if self.serial_conn and self.serial_conn.is_open:
|
||||||
|
self.serial_conn.close()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.stop()
|
||||||
+12
-2
@@ -25,8 +25,12 @@ HEADER_FMT = "<B B H I" # ID, Ver, Len, Timestamp
|
|||||||
TLV_FMT = "<B H"
|
TLV_FMT = "<B H"
|
||||||
|
|
||||||
class ESP32Communication(Thread):
|
class ESP32Communication(Thread):
|
||||||
def __init__(self, host=cfg.esp32_ip, port=cfg.esp32_port):
|
def __init__(self, host=None, port=None, ip=None):
|
||||||
super().__init__(daemon=True)
|
super().__init__(daemon=True)
|
||||||
|
if ip is not None:
|
||||||
|
host = ip
|
||||||
|
host = cfg.esp32_ip if host is None else host
|
||||||
|
port = cfg.esp32_port if port is None else port
|
||||||
self.addr = (host, port)
|
self.addr = (host, port)
|
||||||
|
|
||||||
# UDP Socket - Zero Lag
|
# UDP Socket - Zero Lag
|
||||||
@@ -126,4 +130,10 @@ class ESP32Communication(Thread):
|
|||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.running.clear()
|
self.running.clear()
|
||||||
self.sock.close()
|
try:
|
||||||
|
self.sock.close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.stop()
|
||||||
@@ -59,8 +59,11 @@ class HardwareBackend:
|
|||||||
return [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]
|
return [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]
|
||||||
|
|
||||||
def cleanup(self) -> None:
|
def cleanup(self) -> None:
|
||||||
if self.comm_channel and hasattr(self.comm_channel, 'close'):
|
if self.comm_channel:
|
||||||
self.comm_channel.close()
|
if hasattr(self.comm_channel, 'stop'):
|
||||||
|
self.comm_channel.stop()
|
||||||
|
elif hasattr(self.comm_channel, 'close'):
|
||||||
|
self.comm_channel.close()
|
||||||
|
|
||||||
|
|
||||||
class PyBulletBackend:
|
class PyBulletBackend:
|
||||||
@@ -123,9 +126,11 @@ class Robot:
|
|||||||
self.backend: RobotBackend = PyBulletBackend(sim_instance)
|
self.backend: RobotBackend = PyBulletBackend(sim_instance)
|
||||||
elif backend_type == BackendType.ESP32:
|
elif backend_type == BackendType.ESP32:
|
||||||
comm = ESP32Communication(ip=cfg.esp32_ip, port=cfg.esp32_port)
|
comm = ESP32Communication(ip=cfg.esp32_ip, port=cfg.esp32_port)
|
||||||
|
comm.start()
|
||||||
self.backend = HardwareBackend(comm)
|
self.backend = HardwareBackend(comm)
|
||||||
elif backend_type == BackendType.ARDUINO:
|
elif backend_type == BackendType.ARDUINO:
|
||||||
comm = ArduinoCommunication(port=cfg.port, baudrate=cfg.baudrate)
|
comm = ArduinoCommunication(port=cfg.port, baudrate=cfg.baudrate)
|
||||||
|
comm.start()
|
||||||
self.backend = HardwareBackend(comm)
|
self.backend = HardwareBackend(comm)
|
||||||
else:
|
else:
|
||||||
self.backend = backend_type
|
self.backend = backend_type
|
||||||
@@ -188,7 +193,11 @@ class Robot:
|
|||||||
self.current_state = STATE_REGISTRY[next_state_key]
|
self.current_state = STATE_REGISTRY[next_state_key]
|
||||||
self.current_state.enter(self)
|
self.current_state.enter(self)
|
||||||
|
|
||||||
def tick(self, action: Optional[np.ndarray] = None, physics_substeps: int = 4) -> None:
|
def tick(self, action: Optional[np.ndarray] = None) -> None:
|
||||||
|
"""
|
||||||
|
Unified control loop tick.
|
||||||
|
Processes commands through direct RL, residual RL, or State Machine kinematics.
|
||||||
|
"""
|
||||||
vx, vy, omega = self.vector_dirmov
|
vx, vy, omega = self.vector_dirmov
|
||||||
|
|
||||||
if self.mode == "direct":
|
if self.mode == "direct":
|
||||||
@@ -200,12 +209,12 @@ class Robot:
|
|||||||
if action is not None:
|
if action is not None:
|
||||||
self.apply_rl_action_delta(action)
|
self.apply_rl_action_delta(action)
|
||||||
|
|
||||||
else: # kinematics mode
|
else: # "kinematics" / standard State Machine execution
|
||||||
self.step_kinematic_gait(vx, vy, omega)
|
next_state_key = self.current_state.execute(self)
|
||||||
|
if next_state_key:
|
||||||
|
self.transition_to(next_state_key)
|
||||||
|
|
||||||
# Step PyBullet engine sub-steps to allow physics actuation
|
self.step_sim()
|
||||||
for _ in range(physics_substeps):
|
|
||||||
self.step_sim()
|
|
||||||
|
|
||||||
def step_kinematic_gait(self, vx: float, vy: float, omega: float) -> None:
|
def step_kinematic_gait(self, vx: float, vy: float, omega: float) -> None:
|
||||||
"""Procedural Tripod Gait solver."""
|
"""Procedural Tripod Gait solver."""
|
||||||
@@ -261,17 +270,9 @@ class Robot:
|
|||||||
self.set_joint_angles(target_rad)
|
self.set_joint_angles(target_rad)
|
||||||
|
|
||||||
def apply_rl_action(self, action: np.ndarray) -> None:
|
def apply_rl_action(self, action: np.ndarray) -> None:
|
||||||
"""
|
action = np.asarray(action, dtype=np.float32)
|
||||||
Applies direct RL joint action deltas to the current joint positions.
|
new_rad = dt.RadArray(data=action.reshape(self.current_rad.data.shape))
|
||||||
"""
|
self.set_joint_angles(new_rad)
|
||||||
action_flat = np.asarray(action, dtype=np.float32).flatten()
|
|
||||||
|
|
||||||
# Keep internal memory updated in (6, 3) format for Kinematic/IK math
|
|
||||||
self.current_rad = dt.RadArray(data=action_flat.reshape(6, 3))
|
|
||||||
|
|
||||||
# Send target positions to the PyBullet backend
|
|
||||||
if self.backend:
|
|
||||||
self.backend.send_angles(self.current_rad)
|
|
||||||
|
|
||||||
def apply_rl_action_delta(self, action: np.ndarray) -> None:
|
def apply_rl_action_delta(self, action: np.ndarray) -> None:
|
||||||
"""Applies action deltas on top of joint state for Residual RL."""
|
"""Applies action deltas on top of joint state for Residual RL."""
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ class RobotConfig:
|
|||||||
tick_rate_hz: float = 25.0 # Motion loop execution rate [ticks/sec]
|
tick_rate_hz: float = 25.0 # Motion loop execution rate [ticks/sec]
|
||||||
step_duration: float = 0.8 # Time to complete full gait stride [s]
|
step_duration: float = 0.8 # Time to complete full gait stride [s]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def standard_tickpersec(self) -> float:
|
||||||
|
return self.tick_rate_hz
|
||||||
|
|
||||||
|
@standard_tickpersec.setter
|
||||||
|
def standard_tickpersec(self, value: float) -> None:
|
||||||
|
self.tick_rate_hz = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tick_duration(self) -> float:
|
def tick_duration(self) -> float:
|
||||||
return 1.0 / self.tick_rate_hz
|
return 1.0 / self.tick_rate_hz
|
||||||
|
|||||||
Reference in New Issue
Block a user