Skip to content

Commit a67e376

Browse files
committed
improvements:
getters have guards on None values for alvik params typing reformat better types and type-hints
1 parent 94cf84a commit a67e376

File tree

2 files changed

+60
-52
lines changed

2 files changed

+60
-52
lines changed

arduino_alvik.py

+56-52
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def __init__(self):
2929
self._packeter = ucPack(200)
3030
self.left_wheel = _ArduinoAlvikWheel(self._packeter, ord('L'))
3131
self.right_wheel = _ArduinoAlvikWheel(self._packeter, ord('R'))
32-
self._led_state = [None]
32+
self._led_state = list((None,))
3333
self.left_led = _ArduinoAlvikRgbLed(self._packeter, 'left', self._led_state,
3434
rgb_mask=[0b00000100, 0b00001000, 0b00010000])
3535
self.right_led = _ArduinoAlvikRgbLed(self._packeter, 'right', self._led_state,
@@ -84,12 +84,13 @@ def _progress_bar(percentage: float) -> None:
8484
:param percentage:
8585
:return:
8686
"""
87-
sys.stdout.write('\r')
87+
sys.stdout.write(bytes('\r'.encode('utf-8')))
8888
if percentage > 97:
8989
marks_str = ' \U0001F50B'
9090
else:
9191
marks_str = ' \U0001FAAB'
92-
sys.stdout.write(marks_str + f" {percentage}% \t")
92+
word = marks_str + f" {percentage}% \t"
93+
sys.stdout.write(bytes((word.encode('utf-8'))))
9394

9495
def _idle(self, delay_=1, check_on_thread=False) -> None:
9596
"""
@@ -105,20 +106,20 @@ def _idle(self, delay_=1, check_on_thread=False) -> None:
105106
while not self.is_on():
106107
if check_on_thread and not self.__class__._update_thread_running:
107108
break
108-
ESP32_SDA = Pin(A4, Pin.OUT)
109-
ESP32_SCL = Pin(A5, Pin.OUT)
110-
ESP32_SCL.value(1)
111-
ESP32_SDA.value(1)
109+
_ESP32_SDA = Pin(A4, Pin.OUT)
110+
_ESP32_SCL = Pin(A5, Pin.OUT)
111+
_ESP32_SCL.value(1)
112+
_ESP32_SDA.value(1)
112113
sleep_ms(100)
113-
ESP32_SCL.value(0)
114-
ESP32_SDA.value(0)
114+
_ESP32_SCL.value(0)
115+
_ESP32_SDA.value(0)
115116

116117
cmd = bytearray(1)
117118
cmd[0] = 0x06
118119
i2c = I2C(0, scl=ESP32_SCL, sda=ESP32_SDA)
119120
i2c.writeto(0x36, cmd)
120121
soc_raw = struct.unpack('h', i2c.readfrom(0x36, 2))[0]
121-
soc_perc = soc_raw*0.00390625
122+
soc_perc = soc_raw * 0.00390625
122123
self._progress_bar(round(soc_perc))
123124
sleep_ms(delay_)
124125
if soc_perc > 97:
@@ -140,30 +141,32 @@ def _idle(self, delay_=1, check_on_thread=False) -> None:
140141
LEDG.value(1)
141142
NANO_CHK.value(0)
142143

143-
def _snake_robot(self, duration: int = 1000):
144+
@staticmethod
145+
def _snake_robot(duration: int = 1000):
144146
"""
145147
Snake robot animation
146-
:param percentage:
148+
:param duration:
147149
:return:
148150
"""
149-
i = 0
150151

151152
robot = '\U0001F916'
152153
snake = '\U0001F40D'
153154

154155
cycles = int(duration / 200)
155156

157+
frame = ''
156158
for i in range(0, cycles):
157-
sys.stdout.write('\r')
159+
sys.stdout.write(bytes('\r'.encode('utf-8')))
158160
pre = ' ' * i
159161
between = ' ' * (i % 2 + 1)
160162
post = ' ' * 5
161163
frame = pre + snake + between + robot + post
162-
sys.stdout.write(frame)
164+
sys.stdout.write(bytes(frame.encode('utf-8')))
163165
sleep_ms(200)
164166

165-
sys.stdout.write('\r')
166-
sys.stdout.write('')
167+
sys.stdout.write(bytes('\r'.encode('utf-8')))
168+
clear_frame = ' ' * len(frame)
169+
sys.stdout.write(bytes(clear_frame.encode('utf-8')))
167170

168171
def begin(self) -> int:
169172
"""
@@ -309,7 +312,7 @@ def _reset_hw():
309312
RESET_STM32.value(1)
310313
sleep_ms(100)
311314

312-
def get_wheels_speed(self, unit: str = 'rpm') -> (float, float):
315+
def get_wheels_speed(self, unit: str = 'rpm') -> (float | None, float | None):
313316
"""
314317
Returns the speed of the wheels
315318
:param unit: the speed unit of measurement (default: 'rpm')
@@ -327,8 +330,8 @@ def set_wheels_speed(self, left_speed: float, right_speed: float, unit: str = 'r
327330
"""
328331

329332
if unit == '%':
330-
left_speed = (left_speed/100)*MOTOR_MAX_RPM
331-
right_speed = (right_speed/100)*MOTOR_MAX_RPM
333+
left_speed = (left_speed / 100) * MOTOR_MAX_RPM
334+
right_speed = (right_speed / 100) * MOTOR_MAX_RPM
332335
else:
333336
left_speed = convert_rotational_speed(left_speed, unit, 'rpm')
334337
right_speed = convert_rotational_speed(right_speed, unit, 'rpm')
@@ -345,10 +348,10 @@ def set_wheels_position(self, left_angle: float, right_angle: float, unit: str =
345348
:return:
346349
"""
347350
self._packeter.packetC2F(ord('A'), convert_angle(left_angle, unit, 'deg'),
348-
convert_angle(right_angle, unit, 'deg'))
351+
convert_angle(right_angle, unit, 'deg'))
349352
uart.write(self._packeter.msg[0:self._packeter.msg_size])
350353

351-
def get_wheels_position(self, unit: str = 'deg') -> (float, float):
354+
def get_wheels_position(self, unit: str = 'deg') -> (float | None, float | None):
352355
"""
353356
Returns the angle of the wheels
354357
:param unit: the angle unit of measurement (default: 'deg')
@@ -357,36 +360,36 @@ def get_wheels_position(self, unit: str = 'deg') -> (float, float):
357360
return (convert_angle(self.left_wheel.get_position(), 'deg', unit),
358361
convert_angle(self.right_wheel.get_position(), 'deg', unit))
359362

360-
def get_orientation(self) -> (float, float, float):
363+
def get_orientation(self) -> (float | None, float | None, float | None):
361364
"""
362365
Returns the orientation of the IMU
363366
:return: roll, pitch, yaw
364367
"""
365368

366369
return self._roll, self._pitch, self._yaw
367370

368-
def get_accelerations(self) -> (float, float, float):
371+
def get_accelerations(self) -> (float | None, float | None, float | None):
369372
"""
370373
Returns the 3-axial acceleration of the IMU
371374
:return: ax, ay, az
372375
"""
373376
return self._ax, self._ay, self._az
374377

375-
def get_gyros(self) -> (float, float, float):
378+
def get_gyros(self) -> (float | None, float | None, float | None):
376379
"""
377380
Returns the 3-axial angular acceleration of the IMU
378381
:return: gx, gy, gz
379382
"""
380383
return self._gx, self._gy, self._gz
381384

382-
def get_imu(self) -> (float, float, float, float, float, float):
385+
def get_imu(self) -> (float | None, float | None, float | None, float | None, float | None, float | None):
383386
"""
384387
Returns all the IMUs readouts
385388
:return: ax, ay, az, gx, gy, gz
386389
"""
387390
return self._ax, self._ay, self._az, self._gx, self._gy, self._gz
388391

389-
def get_line_sensors(self) -> (int, int, int):
392+
def get_line_sensors(self) -> (int | None, int | None, int | None):
390393
"""
391394
Returns the line sensors readout
392395
:return: left_line, center_line, right_line
@@ -406,7 +409,7 @@ def drive(self, linear_velocity: float, angular_velocity: float, linear_unit: st
406409
"""
407410
linear_velocity = convert_speed(linear_velocity, linear_unit, 'mm/s')
408411
if angular_unit == '%':
409-
angular_velocity = (angular_velocity/100)*ROBOT_MAX_DEG_S
412+
angular_velocity = (angular_velocity / 100) * ROBOT_MAX_DEG_S
410413
else:
411414
angular_velocity = convert_rotational_speed(angular_velocity, angular_unit, 'deg/s')
412415
self._packeter.packetC2F(ord('V'), linear_velocity, angular_velocity)
@@ -419,15 +422,16 @@ def brake(self):
419422
"""
420423
self.drive(0, 0)
421424

422-
def get_drive_speed(self, linear_unit: str = 'cm/s', angular_unit: str = 'deg/s') -> (float, float):
425+
def get_drive_speed(self, linear_unit: str = 'cm/s', angular_unit: str = 'deg/s') -> (float | None, float | None):
423426
"""
424427
Returns linear and angular velocity of the robot
425428
:param linear_unit: output linear velocity unit of meas
426429
:param angular_unit: output angular velocity unit of meas
427430
:return: linear_velocity, angular_velocity
428431
"""
429432
if angular_unit == '%':
430-
angular_velocity = (self._angular_velocity/ROBOT_MAX_DEG_S)*100
433+
angular_velocity = (self._angular_velocity / ROBOT_MAX_DEG_S) * 100 \
434+
if self._angular_velocity is not None else None
431435
else:
432436
angular_velocity = convert_rotational_speed(self._angular_velocity, 'deg/s', angular_unit)
433437

@@ -450,7 +454,8 @@ def reset_pose(self, x: float, y: float, theta: float, distance_unit: str = 'cm'
450454
uart.write(self._packeter.msg[0:self._packeter.msg_size])
451455
sleep_ms(1000)
452456

453-
def get_pose(self, distance_unit: str = 'cm', angle_unit: str = 'deg') -> (float, float, float):
457+
def get_pose(self, distance_unit: str = 'cm', angle_unit: str = 'deg') \
458+
-> (float | None, float | None, float | None):
454459
"""
455460
Returns the current pose of the robot
456461
:param distance_unit: unit of x and y outputs
@@ -471,7 +476,7 @@ def set_servo_positions(self, a_position: int, b_position: int):
471476
self._packeter.packetC2B(ord('S'), a_position & 0xFF, b_position & 0xFF)
472477
uart.write(self._packeter.msg[0:self._packeter.msg_size])
473478

474-
def get_ack(self):
479+
def get_ack(self) -> str:
475480
"""
476481
Returns last acknowledgement
477482
:return:
@@ -609,11 +614,13 @@ def _parse_message(self) -> int:
609614

610615
return 0
611616

612-
def get_battery_charge(self) -> int:
617+
def get_battery_charge(self) -> int | None:
613618
"""
614619
Returns the battery SOC
615620
:return:
616621
"""
622+
if self._battery_perc is None:
623+
return None
617624
if self._battery_perc > 100:
618625
return 100
619626
return round(self._battery_perc)
@@ -729,9 +736,9 @@ def color_calibration(self, background: str = 'white') -> None:
729736
blue_avg += blue
730737
sleep_ms(10)
731738

732-
red_avg = int(red_avg/100)
733-
green_avg = int(green_avg/100)
734-
blue_avg = int(blue_avg/100)
739+
red_avg = int(red_avg / 100)
740+
green_avg = int(green_avg / 100)
741+
blue_avg = int(blue_avg / 100)
735742

736743
if background == 'white':
737744
self._white_cal = [red_avg, green_avg, blue_avg]
@@ -763,7 +770,7 @@ def color_calibration(self, background: str = 'white') -> None:
763770
for line in lines:
764771
file.write(line)
765772

766-
def get_color_raw(self) -> (int, int, int):
773+
def get_color_raw(self) -> (int | None, int | None, int | None):
767774
"""
768775
Returns the color sensor's raw readout
769776
:return: red, green, blue
@@ -783,9 +790,9 @@ def _normalize_color(self, r: float, g: float, b: float) -> (float, float, float
783790
g = self._limit(g, self._black_cal[1], self._white_cal[1])
784791
b = self._limit(b, self._black_cal[2], self._white_cal[2])
785792

786-
r = (r - self._black_cal[0])/(self._white_cal[0] - self._black_cal[0])
787-
g = (g - self._black_cal[1])/(self._white_cal[1] - self._black_cal[1])
788-
b = (b - self._black_cal[2])/(self._white_cal[2] - self._black_cal[2])
793+
r = (r - self._black_cal[0]) / (self._white_cal[0] - self._black_cal[0])
794+
g = (g - self._black_cal[1]) / (self._white_cal[1] - self._black_cal[1])
795+
b = (b - self._black_cal[2]) / (self._white_cal[2] - self._black_cal[2])
789796

790797
return r, g, b
791798

@@ -829,7 +836,7 @@ def rgb2hsv(r: float, g: float, b: float) -> (float, float, float):
829836

830837
return h, s, v
831838

832-
def get_color(self, color_format: str = 'rgb') -> (float, float, float):
839+
def get_color(self, color_format: str = 'rgb') -> (float | None, float | None, float | None):
833840
"""
834841
Returns the normalized color readout of the color sensor
835842
:param color_format: rgb or hsv only
@@ -888,7 +895,7 @@ def hsv2label(h, s, v) -> str:
888895
label = 'BLUE'
889896
elif 260 <= h < 280:
890897
label = 'VIOLET'
891-
else: # h<20 or h>=280 is more problematic
898+
else: # h<20 or h>=280 is more problematic
892899
if v < 0.5 and s < 0.45:
893900
label = 'BROWN'
894901
else:
@@ -900,31 +907,28 @@ def hsv2label(h, s, v) -> str:
900907
label = 'BLACK'
901908
return label
902909

903-
def get_distance(self, unit: str = 'cm') -> (float, float, float, float, float):
910+
def get_distance(self, unit: str = 'cm') -> (float | None, float | None, float | None, float | None, float | None):
904911
"""
905912
Returns the distance readout of the TOF sensor
906913
:param unit: distance output unit
907914
:return: left_tof, center_left_tof, center_tof, center_right_tof, right_tof
908915
"""
909916

910-
if None in [self._left_tof, self._center_left_tof, self._center_tof, self._center_right_tof, self._right_tof]:
911-
return None, None, None, None, None
912-
913917
return (convert_distance(self._left_tof, 'mm', unit),
914918
convert_distance(self._center_left_tof, 'mm', unit),
915919
convert_distance(self._center_tof, 'mm', unit),
916920
convert_distance(self._center_right_tof, 'mm', unit),
917921
convert_distance(self._right_tof, 'mm', unit))
918922

919-
def get_distance_top(self, unit: str = 'cm') -> float:
923+
def get_distance_top(self, unit: str = 'cm') -> float | None:
920924
"""
921925
Returns the obstacle top distance readout
922926
:param unit:
923927
:return:
924928
"""
925929
return convert_distance(self._top_tof, 'mm', unit)
926930

927-
def get_distance_bottom(self, unit: str = 'cm') -> float:
931+
def get_distance_bottom(self, unit: str = 'cm') -> float | None:
928932
"""
929933
Returns the obstacle bottom distance readout
930934
:param unit:
@@ -998,26 +1002,26 @@ def set_speed(self, velocity: float, unit: str = 'rpm'):
9981002
"""
9991003

10001004
if unit == '%':
1001-
velocity = (velocity/100)*MOTOR_MAX_RPM
1005+
velocity = (velocity / 100) * MOTOR_MAX_RPM
10021006
else:
10031007
velocity = convert_rotational_speed(velocity, unit, 'rpm')
10041008

10051009
self._packeter.packetC2B1F(ord('W'), self._label & 0xFF, ord('V'), velocity)
10061010
uart.write(self._packeter.msg[0:self._packeter.msg_size])
10071011

1008-
def get_speed(self, unit: str = 'rpm') -> float:
1012+
def get_speed(self, unit: str = 'rpm') -> float | None:
10091013
"""
10101014
Returns the current RPM speed of the wheel
10111015
:param unit: the unit of the output speed
10121016
:return:
10131017
"""
10141018
if unit == '%':
1015-
speed = (self._speed/MOTOR_MAX_RPM)*100
1019+
speed = (self._speed / MOTOR_MAX_RPM) * 100 if self._speed is not None else None
10161020
else:
10171021
speed = convert_rotational_speed(self._speed, 'rpm', unit)
10181022
return speed
10191023

1020-
def get_position(self, unit: str = 'deg') -> float:
1024+
def get_position(self, unit: str = 'deg') -> float | None:
10211025
"""
10221026
Returns the wheel position (angle with respect to the reference)
10231027
:param unit: the unit of the output position

conversions.py

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ def wrapper(*args, **kwargs):
99
return func(*args, **kwargs)
1010
except KeyError:
1111
raise ConversionError(f'Cannot {func.__name__} from {args[1]} to {args[2]}')
12+
except TypeError:
13+
return None
14+
except Exception as e:
15+
raise ConversionError(f'Unexpected error: {e}')
1216
return wrapper
1317

1418

0 commit comments

Comments
 (0)