Ant is working with collision avoidance

This commit is contained in:
Pascal Serrarens 2024-12-22 13:51:05 +01:00
parent 134bb7c332
commit d23ff21aa9
8 changed files with 169 additions and 14 deletions

View File

@ -1,4 +1,5 @@
import numpy as np import numpy as np
from .SwingTwist import SwingTwist
def SendAngle8(buffer, ix_ref, angle): def SendAngle8(buffer, ix_ref, angle):
# Normalize angle # Normalize angle
@ -21,12 +22,22 @@ def SendFloat16(buffer, ix_ref, value):
] ]
ix_ref[0] += 2 ix_ref[0] += 2
def ReceiveFloat16(buffer, ix_ref) -> float:
ix = ix_ref[0]
binary = (buffer[ix] << 8) + buffer[ix+1]
value16 = np.uint16(binary).view(np.float16)
ix_ref[0] += 2
return float(value16)
def SendSpherical(buffer, ix_ref, vector): def SendSpherical(buffer, ix_ref, vector):
SendFloat16(buffer, ix_ref, vector.distance) SendFloat16(buffer, ix_ref, vector.distance)
SendAngle8(buffer, ix_ref, vector.direction.horizontal) SendAngle8(buffer, ix_ref, vector.direction.horizontal)
SendAngle8(buffer, ix_ref, vector.direction.vertical) SendAngle8(buffer, ix_ref, vector.direction.vertical)
def SendQuat32(buffer, ix_ref, q): def SendQuat32(buffer, ix_ref, q):
if isinstance(q, SwingTwist):
q = q.ToQuaternion()
ix = ix_ref[0] ix = ix_ref[0]
qx = (int)(q.x * 127 + 128) qx = (int)(q.x * 127 + 128)
qy = (int)(q.y * 127 + 128) qy = (int)(q.y * 127 + 128)

View File

@ -62,7 +62,15 @@ class NetworkIdMsg(IMessage):
self.network_id self.network_id
] ]
return NetworkIdMsg.length return NetworkIdMsg.length
class InvestigateMsg():
id = 0x81
length = 3
def __init__(self, buffer):
self.network_id = buffer[1]
self.thing_id = buffer[2]
class ThingMsg(IMessage): class ThingMsg(IMessage):
id = 0x80 id = 0x80
length = 5 length = 5
@ -151,7 +159,7 @@ class PoseMsg(IMessage):
id = 0x10 id = 0x10
length = 4 length = 4
def __init__(self, network_id, thing, poseType): def __init__(self, network_id, thing):
self.network_id = network_id self.network_id = network_id
self.thing = thing self.thing = thing
@ -176,3 +184,28 @@ class PoseMsg(IMessage):
if self.thing.pose_updated & Thing.AngularVelocity: if self.thing.pose_updated & Thing.AngularVelocity:
LowLevelMessages.SendSpherical(buffer, ix, self.thing.angularVelocity) LowLevelMessages.SendSpherical(buffer, ix, self.thing.angularVelocity)
return ix[0] return ix[0]
class BinaryMsg():
id = 0xB1
def __init__(self, buffer):
self.network_id = buffer[1]
self.thing_id = buffer[2]
self.thing: Thing = Thing.Get(self.network_id, self.thing_id)
self.data = buffer[3:]
def SendTo(participant, thing, data: bytearray):
length = 3
if thing.network_id is None or thing is None or data is None:
return False
participant.buffer[0:length] = [
id,
participant.network_id,
thing.id
]
full_length = length + len(data)
participant.buffer[length:full_length] = data
participant.SendBuffer(full_length)
return True

View File

@ -52,6 +52,8 @@ class Participant:
pass pass
def ProcessNetworkIdMsg(self, data: bytearray): def ProcessNetworkIdMsg(self, data: bytearray):
pass pass
def ProcessInvestigateMsg(self, data: bytearray):
pass
def ReceiveData(self, data): def ReceiveData(self, data):
msgId = data[0] msgId = data[0]
@ -62,4 +64,9 @@ class Participant:
case Messages.NetworkIdMsg.id: case Messages.NetworkIdMsg.id:
msg = Messages.NetworkIdMsg(data) msg = Messages.NetworkIdMsg(data)
self.ProcessNetworkIdMsg(msg) self.ProcessNetworkIdMsg(msg)
case Messages.InvestigateMsg.id:
msg = Messages.InvestigateMsg(data)
self.ProcessInvestigateMsg(msg)
case Messages.BinaryMsg.id:
msg = Messages.BinaryMsg(data)
msg.thing.ProcessBinary(msg.data)

View File

@ -1,8 +1,44 @@
import math
Deg2Rad = (math.pi * 2) / 360
class Quaternion: class Quaternion:
def __init__(self): def __init__(self):
self.x = 0 self.x = 0
self.y = 0 self.y = 0
self.z = 0 self.z = 0
self.w = 1 self.w = 1
@staticmethod
def Euler(x, y, z):
yaw = x * Deg2Rad
pitch = y * Deg2Rad
roll = z * Deg2Rad
roll_over_2 = roll * 0.5
sin_roll_over_2 = math.sin(roll_over_2)
cos_roll_over_2 = math.cos(roll_over_2)
pitch_over_2 = pitch * 0.5
sin_pitch_over_2 = math.sin(pitch_over_2)
cos_pitch_over_2 = math.cos(pitch_over_2)
yaw_over_2 = yaw * 0.5
sin_yaw_over_2 = math.sin(yaw_over_2)
cos_yaw_over_2 = math.cos(yaw_over_2)
result = Quaternion()
result.w = (cos_yaw_over_2 * cos_pitch_over_2 * cos_roll_over_2 +
sin_yaw_over_2 * sin_pitch_over_2 * sin_roll_over_2)
result.x = (sin_yaw_over_2 * cos_pitch_over_2 * cos_roll_over_2 +
cos_yaw_over_2 * sin_pitch_over_2 * sin_roll_over_2)
result.y = (cos_yaw_over_2 * sin_pitch_over_2 * cos_roll_over_2 -
sin_yaw_over_2 * cos_pitch_over_2 * sin_roll_over_2)
result.z = (cos_yaw_over_2 * cos_pitch_over_2 * sin_roll_over_2 -
sin_yaw_over_2 * sin_pitch_over_2 * cos_roll_over_2)
return result
Quaternion.identity = Quaternion() Quaternion.identity = Quaternion()

View File

@ -11,18 +11,42 @@ class SiteServer(Participant):
def Update(self, currentTime): def Update(self, currentTime):
ready_to_read, _, _ = select.select([self.udpSocket], [], [], 0.1) # Timeout of 0.1 seconds ready_to_read, _, _ = select.select([self.udpSocket], [], [], 0.1) # Timeout of 0.1 seconds
if ready_to_read: while ready_to_read:
data, addr = self.udpSocket.recvfrom(1024) data, addr = self.udpSocket.recvfrom(1024)
self.ReceiveData(data) self.ReceiveData(data)
ready_to_read, _, _ = select.select([self.udpSocket], [], [], 0.1) # Timeout of 0.1 seconds
return super().Update(currentTime) return super().Update(currentTime)
def ProcessNetworkIdMsg(self, thing_msg): def SendThingInfo(self, thing, recurse = False):
self.network_id = thing_msg.network_id if thing is None:
thing = next(iter(Thing.allThings)) return
thing_msg = Messages.ThingMsg(self.network_id, thing) thing_msg = Messages.ThingMsg(self.network_id, thing)
thing_msg.SendTo(self) thing_msg.SendTo(self)
name_msg = Messages.NameMsg(self.network_id, thing) name_msg = Messages.NameMsg(self.network_id, thing)
name_msg.SendTo(self) name_msg.SendTo(self)
model_msg = Messages.ModelUrlMsg(self.network_id, thing) model_msg = Messages.ModelUrlMsg(self.network_id, thing)
model_msg.SendTo(self) model_msg.SendTo(self)
pose_msg = Messages.PoseMsg(self.network_id, thing)
pose_msg.SendTo(self)
if recurse:
for child in thing.children:
self.SendThingInfo(child, True)
def ProcessNetworkIdMsg(self, thing_msg):
self.network_id = thing_msg.network_id
# HACK: send the root things first
for thing in Thing.allThings:
if thing is not None and thing.parent_id == 0:
self.SendThingInfo(thing)
# then sent the rest
for thing in Thing.allThings:
if thing is not None and thing.parent_id != 0:
self.SendThingInfo(thing)
def ProcessInvestigateMsg(self, msg: Messages.InvestigateMsg):
thing = Thing.Get(msg.network_id, msg.thing_id)
if thing is not None:
self.SendThingInfo(thing)

18
SwingTwist.py Normal file
View File

@ -0,0 +1,18 @@
from .Direction import Direction
from .Quaternion import Quaternion
class SwingTwist:
def __init__(self, swing: Direction, twist: float):
if swing.vertical > 90 or swing.vertical < -90:
swing.horizontal += 180
swing.vertical = 180 - swing.vertical
twist += 180
self.swing = swing
self.twist = twist
def ToQuaternion(self) -> Quaternion:
q = Quaternion.Euler(-self.swing.vertical,
self.swing.horizontal,
self.twist)
return q

View File

@ -2,23 +2,28 @@ from .Spherical import Spherical
from .Quaternion import Quaternion from .Quaternion import Quaternion
class Thing: class Thing:
allThings = set() class Type:
Undetermined = 0x00
Switch = 0x01
DistanceSensor = 0x02
DirectionalSensor = 0x03
Animator = 0x40
Position = 0x01 Position = 0x01
Orientation = 0x02 Orientation = 0x02
LinearVelocity = 0x04 LinearVelocity = 0x04
AngularVelocity = 0x08 AngularVelocity = 0x08
def __init__(self, parent=None): def __init__(self, type=Type.Undetermined, parent=None, name=None):
self.networkId = 0 self.networkId = 0
self.id = 0 self.id = 0
self.type = 0 self.type = type
if parent is None: if parent is None:
self.parent_id = 0 self.parent_id = 0
else: else:
self.parent_id = parent.id self.parent_id = parent.id
self.name = None self.name = name
self.model_url = None self.model_url = None
self.position = Spherical.zero self.position = Spherical.zero
@ -33,11 +38,25 @@ class Thing:
def update(self, currentTime): def update(self, currentTime):
pass pass
def ProcessBinary(self, data):
pass
allThings = set({ None })
@staticmethod @staticmethod
def Add(thing): def Add(thing):
thing.id = len(Thing.allThings) thing.id = len(Thing.allThings)
Thing.allThings.add(thing) Thing.allThings.add(thing)
@staticmethod
def Get(networkId, thingId):
for thing in Thing.allThings:
if thing is not None:
if thing.networkId == networkId and thing.id == thingId:
return thing
return None
def UpdateAll(currentTime): def UpdateAll(currentTime):
for thing in Thing.allThings: for thing in Thing.allThings:
thing.update(currentTime) if thing is not None:
thing.update(currentTime)

View File

@ -1,7 +1,14 @@
__all__ = ['Direction', 'Spherical', 'Thing', 'Participant', 'Messages', 'SiteServer'] __all__ = ['Direction',
'Spherical',
'Thing',
'Participant',
'Messages',
'SiteServer',
'SwingTwist']
from .Direction import Direction from .Direction import Direction
from .Participant import Participant from .Participant import Participant
from .Thing import Thing from .Thing import Thing
from .Spherical import Spherical from .Spherical import Spherical
from .SwingTwist import SwingTwist
from .SiteServer import SiteServer from .SiteServer import SiteServer