Improve simple control algorithm and add drawing and mouse control
This commit is contained in:
parent
213c09d246
commit
27f114a6d4
|
@ -13,18 +13,27 @@ args = parser.parse_args()
|
||||||
bot = args.bot
|
bot = args.bot
|
||||||
meas = args.meas
|
meas = args.meas
|
||||||
|
|
||||||
|
w = 640
|
||||||
|
h = 480
|
||||||
|
|
||||||
pygame.init()
|
pygame.init()
|
||||||
pygame.display.set_mode((640, 480))
|
surf = pygame.display.set_mode((640, 480))
|
||||||
|
bg = pygame.Color(0,0,0)
|
||||||
|
botcol = pygame.Color(255, 0, 0)
|
||||||
|
setcol = pygame.Color(0, 255, 0)
|
||||||
|
|
||||||
meas_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
meas_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
try:
|
||||||
|
meas_socket.connect((meas, 42424)) # connect to robot
|
||||||
|
except socket.error:
|
||||||
|
print("could not connect to measurement socket")
|
||||||
rc_socket = socket.socket()
|
rc_socket = socket.socket()
|
||||||
try:
|
try:
|
||||||
rc_socket.connect((bot, 1234)) # connect to robot
|
rc_socket.connect((bot, 1234)) # connect to robot
|
||||||
except socket.error:
|
except socket.error:
|
||||||
print("could not connect to bot socket")
|
print("could not connect to bot socket")
|
||||||
|
|
||||||
meas_socket.sendto(f"{args.id}\n".encode(), (meas, 42424))
|
meas_socket.sendall(f"{args.id}\n".encode())
|
||||||
|
|
||||||
class Bot(object):
|
class Bot(object):
|
||||||
FAR, CLOSE, ANGLE, REACHED = range(4)
|
FAR, CLOSE, ANGLE, REACHED = range(4)
|
||||||
|
@ -41,6 +50,10 @@ class Bot(object):
|
||||||
self.pps = True
|
self.pps = True
|
||||||
self.pas = True
|
self.pas = True
|
||||||
|
|
||||||
|
print()
|
||||||
|
print(f"GOING TO {x},{y}")
|
||||||
|
print()
|
||||||
|
|
||||||
self.state = Bot.FAR
|
self.state = Bot.FAR
|
||||||
|
|
||||||
def rotate(self, angle):
|
def rotate(self, angle):
|
||||||
|
@ -56,26 +69,58 @@ class Bot(object):
|
||||||
dp = ((self.tx - x)**2 + (self.ty - y)**2)**0.5
|
dp = ((self.tx - x)**2 + (self.ty - y)**2)**0.5
|
||||||
da = atan2(self.ty - y, self.tx - x) - angle
|
da = atan2(self.ty - y, self.tx - x) - angle
|
||||||
|
|
||||||
if da > pi:
|
|
||||||
da -= 2*pi
|
|
||||||
elif da < -pi:
|
|
||||||
da += 2*pi
|
|
||||||
|
|
||||||
# Position state: Determine if the angle to the target flips over +-pi/2, i.e. going past the target now
|
# Position state: Determine if the angle to the target flips over +-pi/2, i.e. going past the target now
|
||||||
def pst(angle):
|
def pst(angle):
|
||||||
return abs(angle) > pi
|
while angle > pi:
|
||||||
|
angle -= 2*pi
|
||||||
|
while angle <-pi:
|
||||||
|
angle += 2*pi
|
||||||
|
return abs(angle) > pi/2
|
||||||
# Angle state: When 'close' to target angle (absolute value <= pi/2) use the sign, else 0. When the angle flips over 0, the difference is +- 2
|
# Angle state: When 'close' to target angle (absolute value <= pi/2) use the sign, else 0. When the angle flips over 0, the difference is +- 2
|
||||||
def ast(angle):
|
def ast(angle):
|
||||||
|
while angle > pi:
|
||||||
|
angle -= 2*pi
|
||||||
|
while angle <-pi:
|
||||||
|
angle += 2*pi
|
||||||
return 0 if abs(angle) >= pi/2 else 1 if angle >= 0 else -1
|
return 0 if abs(angle) >= pi/2 else 1 if angle >= 0 else -1
|
||||||
# Move with speed and change towards angle
|
# Move with speed and change towards angle
|
||||||
def control(speed, angle):
|
def control(speed, angle, backwards = True):
|
||||||
|
ca = cos(angle)
|
||||||
speed = speed * cos(angle)
|
speed = speed * cos(angle)
|
||||||
|
|
||||||
|
|
||||||
|
# Move angle to [0..2*pi]
|
||||||
|
while angle < 0:
|
||||||
|
angle += 2*pi
|
||||||
|
while angle > 2*pi:
|
||||||
|
angle -= 2*pi
|
||||||
|
|
||||||
|
if backwards:
|
||||||
|
# If we can go backwards, angle should be pi or 0
|
||||||
|
if angle >= pi/2 and angle < 3*pi/2:
|
||||||
|
# pi/2 .. 3/2 pi should move towards pi
|
||||||
|
angle -= pi
|
||||||
|
print(f"Go for pi, {angle}")
|
||||||
|
else:
|
||||||
|
# Everything else should be 0. Make sure angles in 3/2 pi .. 2 pi don't lead to huge outputs.
|
||||||
|
if angle >= 3*pi/2:
|
||||||
|
angle -= 2*pi
|
||||||
|
print(f"Go for 0, {angle}")
|
||||||
|
else:
|
||||||
|
# Have to go forward, just try to get angle to 0
|
||||||
|
# Correct angle to be [-pi..pi]
|
||||||
|
while angle > pi:
|
||||||
|
angle -= 2*pi
|
||||||
|
print(f"Always go for 0, {angle}")
|
||||||
ul, ur = speed, speed
|
ul, ur = speed, speed
|
||||||
|
if speed == 0 and abs(angle) < 0.5:
|
||||||
|
angle = 0.5 if angle > 0 else -0.5 if angle < 0 else 0
|
||||||
ul -= angle * 0.5
|
ul -= angle * 0.5
|
||||||
ur += angle * 0.5
|
ur += angle * 0.5
|
||||||
vd = max(1, abs(ul)/umax, abs(ur)/umax)
|
vd = max(1, abs(ul)/umax, abs(ur)/umax)
|
||||||
ul /= vd
|
ul /= vd
|
||||||
ur /= vd
|
ur /= vd
|
||||||
|
print(f"Movement: {ul},{ur}")
|
||||||
self.sock.send(f'({ul},{ur})\n'.encode())
|
self.sock.send(f'({ul},{ur})\n'.encode())
|
||||||
self.pps = pst(angle)
|
self.pps = pst(angle)
|
||||||
self.pas = ast(angle)
|
self.pas = ast(angle)
|
||||||
|
@ -83,20 +128,25 @@ class Bot(object):
|
||||||
if self.state == Bot.FAR:
|
if self.state == Bot.FAR:
|
||||||
if dp <= 0.3:
|
if dp <= 0.3:
|
||||||
# Close to the target
|
# Close to the target
|
||||||
self.state = Bot.NEAR
|
self.state = Bot.CLOSE
|
||||||
|
self.pps = pst(da)
|
||||||
else:
|
else:
|
||||||
# When far, just move towards the target
|
# When far, just move towards the target
|
||||||
|
print(f"FAR {umax}, {da}")
|
||||||
control(umax, da)
|
control(umax, da)
|
||||||
if self.state == Bot.NEAR:
|
if self.state == Bot.CLOSE:
|
||||||
if pst(da) != self.pps:
|
if pst(da) != self.pps and dp <= 0.1:
|
||||||
# The angle flips over +- pi/2, go for the angle setpoint
|
# The angle flips over +- pi/2, go for the angle setpoint
|
||||||
self.state = Bot.ANGLE
|
self.state = Bot.ANGLE
|
||||||
|
self.pas = 0
|
||||||
else:
|
else:
|
||||||
control(umax * dp / 0.3, da)
|
print(f"CLOSE {umax*dp/0.3} {da} {pst(da)} {self.pps}")
|
||||||
|
control(0.3+(umax*dp/0.3), da)
|
||||||
if self.state == Bot.ANGLE:
|
if self.state == Bot.ANGLE:
|
||||||
if self.ta == None:
|
if self.ta == None:
|
||||||
# No angle setpoint, we're done
|
# No angle setpoint, we're done
|
||||||
self.state = Bot.REACHED
|
self.state = Bot.REACHED
|
||||||
|
bot.stop()
|
||||||
else:
|
else:
|
||||||
# Use angle setpoint for angle difference
|
# Use angle setpoint for angle difference
|
||||||
da = self.ta - angle
|
da = self.ta - angle
|
||||||
|
@ -106,18 +156,25 @@ class Bot(object):
|
||||||
if abs(ast(da) - self.pas) == 2:
|
if abs(ast(da) - self.pas) == 2:
|
||||||
# Done
|
# Done
|
||||||
self.state = Bot.REACHED
|
self.state = Bot.REACHED
|
||||||
|
bot.stop()
|
||||||
else:
|
else:
|
||||||
# Don't move, just rotate
|
# Don't move, just rotate
|
||||||
control(0, da)
|
print(f"ANGL {0} {da} {ast(da)} {self.pas}")
|
||||||
|
control(0, da, False)
|
||||||
|
|
||||||
|
meas_file = meas_socket.makefile('rw', encoding='utf8', newline='\n')
|
||||||
|
|
||||||
bot = Bot(rc_socket)
|
bot = Bot(rc_socket)
|
||||||
bot.move(0, 0, 0)
|
bot.move(0, 0, 0)
|
||||||
|
|
||||||
running = True
|
running = True
|
||||||
ml = 0.5
|
ml = 0.5
|
||||||
|
|
||||||
|
scale = 220 / 0.5
|
||||||
|
|
||||||
while running:
|
while running:
|
||||||
received = json.loads(str(meas_socket.recv(1024), "utf-8").strip())
|
received = json.loads(meas_file.readline().strip())
|
||||||
print("Received: {}".format(received))
|
received = json.loads(meas_file.readline().strip())
|
||||||
|
|
||||||
# Extract position, angle
|
# Extract position, angle
|
||||||
x = received['x']
|
x = received['x']
|
||||||
|
@ -138,6 +195,22 @@ while running:
|
||||||
if event.key == pygame.K_ESCAPE:
|
if event.key == pygame.K_ESCAPE:
|
||||||
bot.stop()
|
bot.stop()
|
||||||
running = False
|
running = False
|
||||||
|
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||||
|
x,y = pygame.mouse.get_pos()
|
||||||
|
bot.move((x-w/2)/scale, (y-h/2)/scale)
|
||||||
|
|
||||||
|
surf.fill(bg)
|
||||||
|
pygame.draw.circle(surf, botcol, (w/2 + scale * x, h/2 + scale * y), 10, 2)
|
||||||
|
pygame.draw.line(surf, botcol, (w/2 + scale * x, h/2 + scale * y), (w/2 + scale * x + 15 * cos(a), h/2 + scale * y + 15 * sin(a)))
|
||||||
|
|
||||||
|
if bot.tx != None and bot.ty != None:
|
||||||
|
pygame.draw.circle(surf, setcol, (w/2 + scale * bot.tx, h/2 + scale * bot.ty), 10, 2)
|
||||||
|
if bot.ta != None:
|
||||||
|
pygame.draw.line(surf, setcol,
|
||||||
|
(w/2 + scale * bot.tx, h/2 + scale * bot.ty),
|
||||||
|
(w/2 + scale * bot.tx + 15 * cos(bot.ta), h/2 + scale * bot.ty + 15 * sin(bot.ta)))
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
if running:
|
if running:
|
||||||
bot.step(x, y, a)
|
bot.step(x, y, a)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user