2025-01-15 01:12:27 +01:00
|
|
|
import numpy as np
|
2025-01-13 16:53:46 +01:00
|
|
|
class Controller:
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def __call__(self, gtm, *args, **kwargs):
|
|
|
|
return None
|
|
|
|
|
2025-01-14 18:23:55 +01:00
|
|
|
|
2025-01-13 17:52:51 +01:00
|
|
|
# We control position, but the output of the controller is a set speed.
|
|
|
|
# Therefore, we have proportional, differential and second differential terms.
|
2025-01-13 16:53:46 +01:00
|
|
|
class PIDController(Controller):
|
2025-01-14 18:23:55 +01:00
|
|
|
err_x, err_y, err_z = 0., 0., 0.
|
|
|
|
derr_x, derr_y, derr_z = 0., 0., 0.
|
|
|
|
d2err_x, d2err_y, d2err_z = 0., 0., 0.
|
|
|
|
|
2025-01-13 17:52:51 +01:00
|
|
|
def __init__(self, p, i, d):
|
2025-01-14 17:10:10 +01:00
|
|
|
super().__init__()
|
2025-01-14 18:23:55 +01:00
|
|
|
self.p, self.i, self.d = p, i, d
|
2025-01-13 16:53:46 +01:00
|
|
|
|
|
|
|
def __call__(self, gtm, *args, **kwargs):
|
2025-01-13 17:52:51 +01:00
|
|
|
err_x_new, err_y_new, err_z_new = gtm.delta_steps
|
2025-01-14 17:10:10 +01:00
|
|
|
derr_x_new, derr_y_new, derr_z_new = err_x_new - self.err_x, err_y_new - self.err_y, err_z_new - self.err_z
|
|
|
|
d2err_x_new, d2err_y_new, d2err_z_new = derr_x_new - self.derr_x, derr_y_new - self.derr_y, derr_z_new - self.derr_z
|
2025-01-13 17:52:51 +01:00
|
|
|
|
2025-01-14 17:10:10 +01:00
|
|
|
self.err_x, self.err_y, self.err_z = err_x_new, err_y_new, err_z_new
|
|
|
|
self.derr_x, self.derr_y, self.derr_z = derr_x_new, derr_y_new, derr_z_new
|
2025-01-14 18:23:55 +01:00
|
|
|
self.d2err_x, self.d2err_y, self.d2err_z = d2err_x_new, d2err_y_new, d2err_z_new
|
2025-01-13 18:11:28 +01:00
|
|
|
|
2025-01-14 17:10:10 +01:00
|
|
|
speed_cmd_x = self.p * self.derr_x + self.i * self.err_x + self.d * self.d2err_x
|
|
|
|
speed_cmd_y = self.p * self.derr_y + self.i * self.err_y + self.d * self.d2err_y
|
|
|
|
speed_cmd_z = self.p * self.derr_z + self.i * self.err_z + self.d * self.d2err_z
|
2025-01-13 17:52:51 +01:00
|
|
|
|
|
|
|
# It is the responsability of the coefficients to make sure this results in the desired feedrate
|
2025-01-14 18:23:55 +01:00
|
|
|
gtm.x_throttle = gtm.target_feedrate / abs(speed_cmd_x)
|
2025-01-13 17:52:51 +01:00
|
|
|
gtm.x_direction = 1 if (speed_cmd_x > 0) else 0
|
|
|
|
|
2025-01-14 18:23:55 +01:00
|
|
|
gtm.y_throttle = gtm.target_feedrate / abs(speed_cmd_y)
|
2025-01-13 17:52:51 +01:00
|
|
|
gtm.y_direction = 1 if (speed_cmd_y > 0) else 0
|
2025-01-13 16:53:46 +01:00
|
|
|
|
2025-01-14 18:23:55 +01:00
|
|
|
gtm.z_throttle = gtm.target_feedrate / abs(speed_cmd_z)
|
2025-01-13 17:52:51 +01:00
|
|
|
gtm.z_direction = 1 if (speed_cmd_z > 0) else 0
|
2025-01-13 16:53:46 +01:00
|
|
|
|
2025-01-14 17:10:10 +01:00
|
|
|
if gtm.x_throttle > 65536:
|
2025-01-13 17:52:51 +01:00
|
|
|
gtm.x_throttle = 0
|
2025-01-14 17:10:10 +01:00
|
|
|
|
|
|
|
if gtm.y_throttle > 65536:
|
2025-01-13 17:52:51 +01:00
|
|
|
gtm.y_throttle = 0
|
2025-01-14 17:10:10 +01:00
|
|
|
|
|
|
|
if gtm.z_throttle > 65536:
|
2025-01-13 17:52:51 +01:00
|
|
|
gtm.z_throttle = 0
|
2025-01-14 17:10:10 +01:00
|
|
|
|
2025-01-14 18:23:55 +01:00
|
|
|
|
2025-01-14 19:00:04 +01:00
|
|
|
class DummyController(Controller):
|
2025-02-05 13:07:49 +01:00
|
|
|
last_delta = np.nan
|
|
|
|
|
2025-01-14 18:23:55 +01:00
|
|
|
def __init__(self):
|
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
def __call__(self, gtm, *args, **kwargs):
|
|
|
|
|
2025-01-15 01:12:27 +01:00
|
|
|
feed = gtm.feedrate
|
|
|
|
dx, dy, dz = gtm.delta_units
|
2025-01-15 09:53:30 +01:00
|
|
|
feed_x = feed * dx/(dx**2 + dy**2 + dz**2)**0.5
|
|
|
|
feed_y = feed * dy/(dx**2 + dy**2 + dz**2)**0.5
|
|
|
|
feed_z = feed * dz/(dz**2 + dy**2 + dz**2)**0.5
|
|
|
|
|
|
|
|
print("feed (m/min) : ", feed)
|
|
|
|
print("current : ", gtm.current_units)
|
|
|
|
print("target : ", gtm.target_units)
|
|
|
|
print("deltas: ", dx, dy, dz)
|
2025-01-14 19:00:04 +01:00
|
|
|
|
2025-01-14 18:23:55 +01:00
|
|
|
gtm.x_direction = 1 if gtm.delta_steps[0] > 0 else 0
|
|
|
|
gtm.y_direction = 1 if gtm.delta_steps[1] > 0 else 0
|
|
|
|
gtm.z_direction = 1 if gtm.delta_steps[2] > 0 else 0
|
|
|
|
|
2025-01-15 01:12:27 +01:00
|
|
|
feed_x,feed_y,feed_z = abs(feed_x), abs(feed_y), abs(feed_z)
|
2025-01-15 09:53:30 +01:00
|
|
|
print("Feeds (m/min) : ", feed_x, feed_y, feed_z)
|
|
|
|
print("dirs : ", gtm.x_direction, gtm.y_direction, gtm.z_direction)
|
2025-01-15 01:12:27 +01:00
|
|
|
|
|
|
|
gtm.x_throttle = 0 if feed_x == 0. else np.floor(gtm.FAST_XY_FEEDRATE/feed_x)
|
|
|
|
gtm.y_throttle = 0 if feed_y == 0. else np.floor(gtm.FAST_XY_FEEDRATE/feed_y)
|
|
|
|
gtm.z_throttle = 0 if feed_z == 0. else np.floor(gtm.FAST_Z_FEEDRATE/feed_z)
|
|
|
|
|
|
|
|
gtm.x_throttle = 0 if gtm.x_throttle >= 65536 else gtm.x_throttle
|
|
|
|
gtm.y_throttle = 0 if gtm.y_throttle >= 65536 else gtm.y_throttle
|
|
|
|
gtm.z_throttle = 0 if gtm.z_throttle >= 65536 else gtm.z_throttle
|
2025-01-14 19:00:04 +01:00
|
|
|
|
2025-01-15 01:12:27 +01:00
|
|
|
print("Throttles : ", gtm.x_throttle, gtm.y_throttle, gtm.z_throttle)
|
2025-01-14 18:23:55 +01:00
|
|
|
|
2025-01-15 01:12:27 +01:00
|
|
|
# If we are close enough, we're good
|
2025-01-15 09:53:30 +01:00
|
|
|
if(gtm.x_throttle == 0 and gtm.y_throttle == 0 and gtm.z_throttle == 0):
|
2025-02-05 13:07:49 +01:00
|
|
|
last_delta = np.nan
|
|
|
|
return True
|
|
|
|
|
|
|
|
delta = (dx**2 + dy**2 + dz**2)**.5
|
|
|
|
if(delta > self.last_delta and self.last_delta < 2.0):
|
|
|
|
self.last_delta = np.nan
|
2025-01-15 09:53:30 +01:00
|
|
|
return True
|
|
|
|
|
2025-02-05 13:07:49 +01:00
|
|
|
self.last_delta = delta
|
|
|
|
|
2025-01-15 09:53:30 +01:00
|
|
|
print("Expected move (mm/call)", gtm.ctrl_step*feed*1000./60)
|
|
|
|
print("Delta (mm) ", (dx**2 + dy**2 + dz**2)**.5)
|
2025-02-05 13:07:49 +01:00
|
|
|
return False
|