Skip to content

Commit 8e6125b

Browse files
authored
Merge pull request #1 from ijnek/ijnek-binary-mspack
LoLA msgpack specifications should be exactly 896 bytes
2 parents 5d19cd5 + 2a9c2c2 commit 8e6125b

1 file changed

Lines changed: 49 additions & 41 deletions

File tree

controllers/nao_lola_python/nao_lola_python.py

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class Nao (Robot):
1414
DOF = 25
1515
PHALANX_MAX = 8
1616
ACTUATOR_PKT_SIZE = 786
17+
MSGPACK_READ_LENGTH = 896
1718

1819
sensors = {
1920
"Stiffness": [ 1.0 ] * DOF,
@@ -26,9 +27,9 @@ class Nao (Robot):
2627
"Angles": [ 0.0, 0.0 ],
2728
"Sonar": [ 0.0, 0.0 ],
2829
"FSR": [ 0.0 ] * 8,
29-
"Status": [ 0.0 ] * DOF,
30+
"Status": [ 0 ] * DOF,
3031
"Touch": [ 0.0 ] * 14,
31-
"RobotConfig": [ "0000", "6.0.0", "0000", "6.0.0" ],
32+
"RobotConfig": [ "P0000000000000000000", "6.0.0", "P0000000000000000000", "6.0.0" ],
3233
}
3334

3435

@@ -43,7 +44,7 @@ def setHand(self, angle, right=True):
4344
array = self.rphalanx;
4445
if not right:
4546
array = self.lphalanx
46-
47+
4748
for i in range(self.PHALANX_MAX):
4849
clampedAngle = angle
4950
if clampedAngle > self.maxPhalanxMotorPosition[i]:
@@ -90,15 +91,15 @@ def findAndEnableDevices(self):
9091
self.lfootrbumper = self.getDevice('LFoot/Bumper/Right')
9192
self.rfootlbumper = self.getDevice('RFoot/Bumper/Left')
9293
self.rfootrbumper = self.getDevice('RFoot/Bumper/Right')
93-
94+
9495
self.leds["Chest"] = self.getDevice('ChestBoard/Led')
9596
self.leds["REar"] = self.getDevice('Ears/Led/Right')
9697
self.leds["LEar"] = self.getDevice('Ears/Led/Left')
9798
self.leds["LEye"] = self.getDevice('Face/Led/Left')
9899
self.leds["REye"] = self.getDevice('Face/Led/Right')
99100
self.leds["LFoot"] = self.getDevice('LFoot/Led')
100101
self.leds["RFoot"] = self.getDevice('RFoot/Led')
101-
102+
102103
# finger motors
103104
for i in range(self.PHALANX_MAX):
104105
self.lphalanx.append(self.getDevice("LPhalanx%d" % (i + 1)))
@@ -135,7 +136,7 @@ def findAndEnableDevices(self):
135136
self.lfootrbumper = self.getTouchSensor('LFoot/Bumper/Right')
136137
self.rfootlbumper = self.getTouchSensor('RFoot/Bumper/Left')
137138
self.rfootrbumper = self.getTouchSensor('RFoot/Bumper/Right')
138-
139+
139140
# there are 7 controlable LED groups in Webots
140141
self.leds["Chest"] = self.getLED('ChestBoard/Led')
141142
self.leds["REar"] = self.getLED('Ears/Led/Right')
@@ -144,7 +145,7 @@ def findAndEnableDevices(self):
144145
self.leds["REye"] = self.getLED('Face/Led/Right')
145146
self.leds["LFoot"] = self.getLED('LFoot/Led')
146147
self.leds["RFoot"] = self.getLED('RFoot/Led')
147-
148+
148149
# finger motors
149150
for i in range(self.PHALANX_MAX):
150151
self.lphalanx.append(self.getDevice("LPhalanx%d" % (i + 1)))
@@ -194,13 +195,13 @@ def findAndEnableDevices(self):
194195

195196

196197
def updateSensors(self):
197-
# increase tick, taking care it will not overflow
198+
# increase tick, taking care it will not overflow
198199
# the 16 bit unsigned image timestamp
199200
self.tick = (self.tick + 1) % 2**16
200201

201202
# update ticks (by abusing battery temperature field)
202203
if self.args.send_ticks:
203-
self.sensors["Battery"][3] = self.tick
204+
self.sensors["Battery"][3] = self.tick * 1.0 # convert to float
204205

205206
# IMU
206207
a = self.accelerometer.getValues()
@@ -226,7 +227,7 @@ def updateSensors(self):
226227
# FSR
227228
# conversion is stolen from webots nao_demo_python controller
228229
fsv = [self.fsr[0].getValues(), self.fsr[1].getValues()]
229-
230+
230231
a = []
231232
a.append(fsv[0][2] / 3.4 + 1.5 * fsv[0][0] + 1.15 * fsv[0][1]) # Left Foot Front Left
232233
a.append(fsv[0][2] / 3.4 + 1.5 * fsv[0][0] - 1.15 * fsv[0][1]) # Left Foot Front Right
@@ -238,23 +239,23 @@ def updateSensors(self):
238239
a.append(fsv[1][2] / 3.4 - 1.5 * fsv[1][0] - 1.15 * fsv[1][1]) # Right Foot Rear Right
239240
a.append(fsv[1][2] / 3.4 - 1.5 * fsv[1][0] + 1.15 * fsv[1][1]) # Right Foot Rear Left
240241
for i in range(len(a)):
241-
self.sensors["FSR"][i] = max(0, a[i]/25.0) # fix scaling of values
242+
self.sensors["FSR"][i] = max(0.0, a[i]/25.0) # fix scaling of values
242243

243244
# set chest button if C key is pressed
244245
if self.key == ord('C'):
245246
if self.sensors["Touch"][0] < 1:
246247
print(AnsiCodes.CYAN_FOREGROUND + "Chest button pressed." + AnsiCodes.RESET)
247-
self.sensors["Touch"][0] = 1
248+
self.sensors["Touch"][0] = 1.0
248249
elif self.key == ord('U'):
249250
if self.sensors["Touch"][2] < 1:
250251
print(AnsiCodes.CYAN_FOREGROUND + "Head touched." + AnsiCodes.RESET)
251252
for i in range(1,4):
252-
self.sensors["Touch"][i] = 1
253+
self.sensors["Touch"][i] = 1.0
253254
elif self.key == ord('A'):
254255
if self.sensors["Touch"][1] < 1:
255256
print(AnsiCodes.CYAN_FOREGROUND + "Front head & chest button pressed." + AnsiCodes.RESET)
256-
self.sensors["Touch"][0] = 1
257-
self.sensors["Touch"][1] = 1
257+
self.sensors["Touch"][0] = 1.0
258+
self.sensors["Touch"][1] = 1.0
258259
else:
259260
if self.sensors["Touch"][0] > 0:
260261
print(AnsiCodes.CYAN_FOREGROUND + "Chest button released." + AnsiCodes.RESET)
@@ -263,7 +264,7 @@ def updateSensors(self):
263264
elif self.sensors["Touch"][1] > 0:
264265
print(AnsiCodes.CYAN_FOREGROUND + "Front head & chest button released." + AnsiCodes.RESET)
265266
for i in range(0,4):
266-
self.sensors["Touch"][i] = 0
267+
self.sensors["Touch"][i] = 0.0
267268

268269

269270

@@ -276,35 +277,35 @@ def led_array2RGB(self, a):
276277

277278
def updateActuators(self, actuators):
278279
# motors
279-
if "Position" in actuators:
280+
if b"Position" in actuators:
280281
for i in range(self.DOF-2):
281-
self.motors[i].setPosition(actuators["Position"][i])
282+
self.motors[i].setPosition(actuators[b"Position"][i])
282283
# set hands, since they have more than 1 actuator each in webots
283-
self.setHands(actuators["Position"][23], actuators["Position"][24])
284+
self.setHands(actuators[b"Position"][23], actuators[b"Position"][24])
284285
# set RHipYawPitch which does not exists in LoLa packet
285-
self.motors[25].setPosition(actuators["Position"][7])
286+
self.motors[25].setPosition(actuators[b"Position"][7])
286287

287288
# LEDs
288-
if "Chest" in actuators:
289-
self.leds["Chest"].set(self.led_array2RGB(actuators["Chest"]))
290-
if "LFoot" in actuators:
291-
self.leds["LFoot"].set(self.led_array2RGB(actuators["LFoot"]))
292-
if "RFoot" in actuators:
293-
self.leds["RFoot"].set(self.led_array2RGB(actuators["RFoot"]))
289+
if b"Chest" in actuators:
290+
self.leds["Chest"].set(self.led_array2RGB(actuators[b"Chest"]))
291+
if b"LFoot" in actuators:
292+
self.leds["LFoot"].set(self.led_array2RGB(actuators[b"LFoot"]))
293+
if b"RFoot" in actuators:
294+
self.leds["RFoot"].set(self.led_array2RGB(actuators[b"RFoot"]))
294295

295296
# webots model has only one LED per eye, so just use the first one
296-
if "LEye" in actuators:
297-
v = actuators["LEye"]
297+
if b"LEye" in actuators:
298+
v = actuators[b"LEye"]
298299
self.leds["LEye"].set(self.led_array2RGB([v[0], v[8], v[16]]))
299-
if "REye" in actuators:
300-
v = actuators["REye"]
300+
if b"REye" in actuators:
301+
v = actuators[b"REye"]
301302
self.leds["REye"].set(self.led_array2RGB([v[0], v[8], v[16]]))
302303

303304
# webots model has only one LED per ear, so just use the first one
304-
if "LEar" in actuators:
305-
self.leds["LEar"].set(self.led_array2RGB([0, 0, actuators["LEar"][0]]))
306-
if "REar" in actuators:
307-
self.leds["REar"].set(self.led_array2RGB([0, 0, actuators["REar"][0]]))
305+
if b"LEar" in actuators:
306+
self.leds["LEar"].set(self.led_array2RGB([0, 0, actuators[b"LEar"][0]]))
307+
if b"REar" in actuators:
308+
self.leds["REar"].set(self.led_array2RGB([0, 0, actuators[b"REar"][0]]))
308309

309310

310311

@@ -338,10 +339,10 @@ def parse_args(self):
338339
dest="lola_addr", default="", metavar="ADDR",
339340
help="Path for LoLa UNIX socket or address on which TCP server will listen on (\"<host>:<port>\" or \"<port>\")")
340341
op.add_argument("--tcam-listen=", action="store",
341-
dest="tcam_addr", default="", metavar="ADDR",
342+
dest="tcam_addr", default="", metavar="ADDR",
342343
help="Listen address for top camera image server (\"<host>:<port>\" or \"<port>\")")
343344
op.add_argument("--bcam-listen=", action="store",
344-
dest="bcam_addr", default="", metavar="ADDR",
345+
dest="bcam_addr", default="", metavar="ADDR",
345346
help="Listen address for bottom camera image server (\"<host>:<port>\" or \"<port>\")")
346347
op.add_argument("--framerate=", action="store",
347348
dest="fps", default=30, type=int,
@@ -374,7 +375,7 @@ def parse_args(self):
374375
args.tcam_addr = ("localhost", self.TCP_BASE_PORT + 1)
375376
else:
376377
args.tcam_addr = self.parse_tcp_addr(args.tcam_addr)
377-
378+
378379
# set bottom cam listen address
379380
if not args.bcam_addr:
380381
a = args.tcam_addr
@@ -454,9 +455,12 @@ def run(self):
454455

455456
if conn:
456457
self.updateSensors()
457-
try:
458+
try:
458459
# send sensor data to LoLa client
459-
conn.send(umsgpack.packb(self.sensors))
460+
packed = umsgpack.packb(self.sensors, force_float_precision="single")
461+
if len(packed) != self.MSGPACK_READ_LENGTH:
462+
print(AnsiCodes.RED_FOREGROUND + "Msgpack packet size doesn't match LoLA specifications." + AnsiCodes.RESET)
463+
conn.send(packed)
460464
data = conn.recv(self.ACTUATOR_PKT_SIZE*3)
461465
if data:
462466
self.updateActuators(umsgpack.unpackb(data))
@@ -476,7 +480,10 @@ def run(self):
476480
else:
477481
try:
478482
(conn, addr) = sock.accept()
479-
conn.send(umsgpack.packb(self.sensors))
483+
packed = umsgpack.packb(self.sensors, force_float_precision="single")
484+
if len(packed) != self.MSGPACK_READ_LENGTH:
485+
print(AnsiCodes.RED_FOREGROUND + "Msgpack packet size doesn't match LoLA specifications." + AnsiCodes.RESET)
486+
conn.send(packed)
480487
print(AnsiCodes.GREEN_FOREGROUND + "LoLa client connected." + AnsiCodes.RESET)
481488
except:
482489
conn = None
@@ -490,7 +497,8 @@ def run(self):
490497
self.topImageServer.stop()
491498
self.bottomImageServer.stop()
492499

493-
500+
# Enable Msgpack Old Specification Compatibility Mode, which is used in LoLA
501+
umsgpack.compatibility = True
494502

495503
# create the Robot instance and run main loop
496504
robot = Nao()

0 commit comments

Comments
 (0)