diff --git a/arduino_alvik.py b/arduino_alvik.py index 01c6fa6..07329a8 100644 --- a/arduino_alvik.py +++ b/arduino_alvik.py @@ -21,53 +21,53 @@ class ArduinoAlvik: _update_thread_id = None def __new__(cls): - if not hasattr(cls, 'instance'): - cls.instance = super(ArduinoAlvik, cls).__new__(cls) - return cls.instance + if not hasattr(cls, '_instance'): + cls._instance = super(ArduinoAlvik, cls).__new__(cls) + return cls._instance def __init__(self): - self.packeter = ucPack(200) - self.left_wheel = _ArduinoAlvikWheel(self.packeter, ord('L')) - self.right_wheel = _ArduinoAlvikWheel(self.packeter, ord('R')) - self.led_state = [None] - self.left_led = _ArduinoAlvikRgbLed(self.packeter, 'left', self.led_state, + self._packeter = ucPack(200) + self.left_wheel = _ArduinoAlvikWheel(self._packeter, ord('L')) + self.right_wheel = _ArduinoAlvikWheel(self._packeter, ord('R')) + self._led_state = [None] + self.left_led = _ArduinoAlvikRgbLed(self._packeter, 'left', self._led_state, rgb_mask=[0b00000100, 0b00001000, 0b00010000]) - self.right_led = _ArduinoAlvikRgbLed(self.packeter, 'right', self.led_state, + self.right_led = _ArduinoAlvikRgbLed(self._packeter, 'right', self._led_state, rgb_mask=[0b00100000, 0b01000000, 0b10000000]) - self.battery_perc = None - self.touch_bits = None - self.behaviour = None - self.red = None - self.green = None - self.blue = None + self._battery_perc = None + self._touch_byte = None + self._behaviour = None + self._red = None + self._green = None + self._blue = None self._white_cal = None self._black_cal = None - self.left_line = None - self.center_line = None - self.right_line = None - self.roll = None - self.pitch = None - self.yaw = None - self.x = None - self.y = None - self.theta = None - self.ax = None - self.ay = None - self.az = None - self.gx = None - self.gy = None - self.gz = None - self.left_tof = None - self.center_left_tof = None - self.center_tof = None - self.center_right_tof = None - self.right_tof = None - self.top_tof = None - self.bottom_tof = None - self.linear_velocity = None - self.angular_velocity = None - self.last_ack = '' - self.version = [None, None, None] + self._left_line = None + self._center_line = None + self._right_line = None + self._roll = None + self._pitch = None + self._yaw = None + self._x = None + self._y = None + self._theta = None + self._ax = None + self._ay = None + self._az = None + self._gx = None + self._gy = None + self._gz = None + self._left_tof = None + self._center_left_tof = None + self._center_tof = None + self._center_right_tof = None + self._right_tof = None + self._top_tof = None + self._bottom_tof = None + self._linear_velocity = None + self._angular_velocity = None + self._last_ack = '' + self._version = [None, None, None] @staticmethod def is_on() -> bool: @@ -191,7 +191,7 @@ def _wait_for_ack(self) -> None: Waits until receives 0x00 ack from robot :return: """ - while self.last_ack != 0x00: + while self._last_ack != 0x00: sleep_ms(20) @staticmethod @@ -231,12 +231,12 @@ def is_target_reached(self) -> bool: It also responds with an ack received message :return: """ - if self.last_ack != ord('M') and self.last_ack != ord('R'): + if self._last_ack != ord('M') and self._last_ack != ord('R'): sleep_ms(50) return False else: - self.packeter.packetC1B(ord('X'), ord('K')) - uart.write(self.packeter.msg[0:self.packeter.msg_size]) + self._packeter.packetC1B(ord('X'), ord('K')) + uart.write(self._packeter.msg[0:self._packeter.msg_size]) sleep_ms(200) return True @@ -246,8 +246,8 @@ def set_behaviour(self, behaviour: int): :param behaviour: behaviour code :return: """ - self.packeter.packetC1B(ord('B'), behaviour & 0xFF) - uart.write(self.packeter.msg[0:self.packeter.msg_size]) + self._packeter.packetC1B(ord('B'), behaviour & 0xFF) + uart.write(self._packeter.msg[0:self._packeter.msg_size]) def rotate(self, angle: float, unit: str = 'deg', blocking: bool = True): """ @@ -259,8 +259,8 @@ def rotate(self, angle: float, unit: str = 'deg', blocking: bool = True): """ angle = convert_angle(angle, unit, 'deg') sleep_ms(200) - self.packeter.packetC1F(ord('R'), angle) - uart.write(self.packeter.msg[0:self.packeter.msg_size]) + self._packeter.packetC1F(ord('R'), angle) + uart.write(self._packeter.msg[0:self._packeter.msg_size]) if blocking: self._wait_for_target() @@ -274,8 +274,8 @@ def move(self, distance: float, unit: str = 'cm', blocking: bool = True): """ distance = convert_distance(distance, unit, 'mm') sleep_ms(200) - self.packeter.packetC1F(ord('G'), distance) - uart.write(self.packeter.msg[0:self.packeter.msg_size]) + self._packeter.packetC1F(ord('G'), distance) + uart.write(self._packeter.msg[0:self._packeter.msg_size]) if blocking: self._wait_for_target() @@ -293,8 +293,8 @@ def stop(self): # stop the update thrad self._stop_update_thread() - # delete instance - del self.__class__.instance + # delete _instance + del self.__class__._instance gc.collect() @staticmethod @@ -333,8 +333,8 @@ def set_wheels_speed(self, left_speed: float, right_speed: float, unit: str = 'r left_speed = convert_rotational_speed(left_speed, unit, 'rpm') right_speed = convert_rotational_speed(right_speed, unit, 'rpm') - self.packeter.packetC2F(ord('J'), left_speed, right_speed) - uart.write(self.packeter.msg[0:self.packeter.msg_size]) + self._packeter.packetC2F(ord('J'), left_speed, right_speed) + uart.write(self._packeter.msg[0:self._packeter.msg_size]) def set_wheels_position(self, left_angle: float, right_angle: float, unit: str = 'deg'): """ @@ -344,9 +344,9 @@ def set_wheels_position(self, left_angle: float, right_angle: float, unit: str = :param unit: the speed unit of measurement (default: 'rpm') :return: """ - self.packeter.packetC2F(ord('A'), convert_angle(left_angle, unit, 'deg'), + self._packeter.packetC2F(ord('A'), convert_angle(left_angle, unit, 'deg'), convert_angle(right_angle, unit, 'deg')) - uart.write(self.packeter.msg[0:self.packeter.msg_size]) + uart.write(self._packeter.msg[0:self._packeter.msg_size]) def get_wheels_position(self, unit: str = 'deg') -> (float, float): """ @@ -363,28 +363,28 @@ def get_orientation(self) -> (float, float, float): :return: roll, pitch, yaw """ - return self.roll, self.pitch, self.yaw + return self._roll, self._pitch, self._yaw def get_accelerations(self) -> (float, float, float): """ Returns the 3-axial acceleration of the IMU :return: ax, ay, az """ - return self.ax, self.ay, self.az + return self._ax, self._ay, self._az def get_gyros(self) -> (float, float, float): """ Returns the 3-axial angular acceleration of the IMU :return: gx, gy, gz """ - return self.gx, self.gy, self.gz + return self._gx, self._gy, self._gz def get_imu(self) -> (float, float, float, float, float, float): """ Returns all the IMUs readouts :return: ax, ay, az, gx, gy, gz """ - return self.ax, self.ay, self.az, self.gx, self.gy, self.gz + return self._ax, self._ay, self._az, self._gx, self._gy, self._gz def get_line_sensors(self) -> (int, int, int): """ @@ -392,7 +392,7 @@ def get_line_sensors(self) -> (int, int, int): :return: left_line, center_line, right_line """ - return self.left_line, self.center_line, self.right_line + return self._left_line, self._center_line, self._right_line def drive(self, linear_velocity: float, angular_velocity: float, linear_unit: str = 'cm/s', angular_unit: str = 'deg/s'): @@ -409,8 +409,8 @@ def drive(self, linear_velocity: float, angular_velocity: float, linear_unit: st angular_velocity = (angular_velocity/100)*ROBOT_MAX_DEG_S else: angular_velocity = convert_rotational_speed(angular_velocity, angular_unit, 'deg/s') - self.packeter.packetC2F(ord('V'), linear_velocity, angular_velocity) - uart.write(self.packeter.msg[0:self.packeter.msg_size]) + self._packeter.packetC2F(ord('V'), linear_velocity, angular_velocity) + uart.write(self._packeter.msg[0:self._packeter.msg_size]) def brake(self): """ @@ -427,11 +427,11 @@ def get_drive_speed(self, linear_unit: str = 'cm/s', angular_unit: str = 'deg/s' :return: linear_velocity, angular_velocity """ if angular_unit == '%': - angular_velocity = (self.angular_velocity/ROBOT_MAX_DEG_S)*100 + angular_velocity = (self._angular_velocity/ROBOT_MAX_DEG_S)*100 else: - angular_velocity = convert_rotational_speed(self.angular_velocity, 'deg/s', angular_unit) + angular_velocity = convert_rotational_speed(self._angular_velocity, 'deg/s', angular_unit) - return convert_speed(self.linear_velocity, 'mm/s', linear_unit), angular_velocity + return convert_speed(self._linear_velocity, 'mm/s', linear_unit), angular_velocity def reset_pose(self, x: float, y: float, theta: float, distance_unit: str = 'cm', angle_unit: str = 'deg'): """ @@ -446,8 +446,8 @@ def reset_pose(self, x: float, y: float, theta: float, distance_unit: str = 'cm' x = convert_distance(x, distance_unit, 'mm') y = convert_distance(y, distance_unit, 'mm') theta = convert_angle(theta, angle_unit, 'deg') - self.packeter.packetC3F(ord('Z'), x, y, theta) - uart.write(self.packeter.msg[0:self.packeter.msg_size]) + self._packeter.packetC3F(ord('Z'), x, y, theta) + uart.write(self._packeter.msg[0:self._packeter.msg_size]) sleep_ms(1000) def get_pose(self, distance_unit: str = 'cm', angle_unit: str = 'deg') -> (float, float, float): @@ -457,9 +457,9 @@ def get_pose(self, distance_unit: str = 'cm', angle_unit: str = 'deg') -> (float :param angle_unit: unit of theta output :return: x, y, theta """ - return (convert_distance(self.x, 'mm', distance_unit), - convert_distance(self.y, 'mm', distance_unit), - convert_angle(self.theta, 'deg', angle_unit)) + return (convert_distance(self._x, 'mm', distance_unit), + convert_distance(self._y, 'mm', distance_unit), + convert_angle(self._theta, 'deg', angle_unit)) def set_servo_positions(self, a_position: int, b_position: int): """ @@ -468,19 +468,19 @@ def set_servo_positions(self, a_position: int, b_position: int): :param b_position: position of B servomotor (0-180) :return: """ - self.packeter.packetC2B(ord('S'), a_position & 0xFF, b_position & 0xFF) - uart.write(self.packeter.msg[0:self.packeter.msg_size]) + self._packeter.packetC2B(ord('S'), a_position & 0xFF, b_position & 0xFF) + uart.write(self._packeter.msg[0:self._packeter.msg_size]) def get_ack(self): """ Returns last acknowledgement :return: """ - return self.last_ack + return self._last_ack # def send_ack(self): - # self.packeter.packetC1B(ord('X'), ACK_) - # uart.write(self.packeter.msg[0:self.packeter.msg_size]) + # self._packeter.packetC1B(ord('X'), ACK_) + # uart.write(self._packeter.msg[0:self._packeter.msg_size]) def _set_leds(self, led_state: int): """ @@ -489,9 +489,9 @@ def _set_leds(self, led_state: int): 5->right_red 6->right_green 7->right_blue :return: """ - self.led_state[0] = led_state & 0xFF - self.packeter.packetC1B(ord('L'), self.led_state[0]) - uart.write(self.packeter.msg[0:self.packeter.msg_size]) + self._led_state[0] = led_state & 0xFF + self._packeter.packetC1B(ord('L'), self._led_state[0]) + uart.write(self._packeter.msg[0:self._packeter.msg_size]) def set_builtin_led(self, value: bool): """ @@ -499,10 +499,10 @@ def set_builtin_led(self, value: bool): :param value: :return: """ - if self.led_state[0] is None: + if self._led_state[0] is None: self._set_leds(0x00) - self.led_state[0] = self.led_state[0] | 0b00000001 if value else self.led_state[0] & 0b11111110 - self._set_leds(self.led_state[0]) + self._led_state[0] = self._led_state[0] | 0b00000001 if value else self._led_state[0] & 0b11111110 + self._set_leds(self._led_state[0]) def set_illuminator(self, value: bool): """ @@ -510,10 +510,10 @@ def set_illuminator(self, value: bool): :param value: :return: """ - if self.led_state[0] is None: + if self._led_state[0] is None: self._set_leds(0x00) - self.led_state[0] = self.led_state[0] | 0b00000010 if value else self.led_state[0] & 0b11111101 - self._set_leds(self.led_state[0]) + self._led_state[0] = self._led_state[0] | 0b00000010 if value else self._led_state[0] & 0b11111101 + self._set_leds(self._led_state[0]) def _update(self, delay_=1): """ @@ -547,8 +547,8 @@ def _read_message(self) -> bool: """ while uart.any(): b = uart.read(1)[0] - self.packeter.buffer.push(b) - if b == self.packeter.end_index and self.packeter.checkPayload(): + self._packeter.buffer.push(b) + if b == self._packeter.end_index and self._packeter.checkPayload(): return True return False @@ -557,53 +557,53 @@ def _parse_message(self) -> int: Parse a received message :return: -1 if parse error 0 if ok """ - code = self.packeter.payloadTop() + code = self._packeter.payloadTop() if code == ord('j'): # joint speed - _, self.left_wheel._speed, self.right_wheel._speed = self.packeter.unpacketC2F() + _, self.left_wheel._speed, self.right_wheel._speed = self._packeter.unpacketC2F() elif code == ord('l'): # line sensor - _, self.left_line, self.center_line, self.right_line = self.packeter.unpacketC3I() + _, self._left_line, self._center_line, self._right_line = self._packeter.unpacketC3I() elif code == ord('c'): # color sensor - _, self.red, self.green, self.blue = self.packeter.unpacketC3I() + _, self._red, self._green, self._blue = self._packeter.unpacketC3I() elif code == ord('i'): # imu - _, self.ax, self.ay, self.az, self.gx, self.gy, self.gz = self.packeter.unpacketC6F() + _, self._ax, self._ay, self._az, self._gx, self._gy, self._gz = self._packeter.unpacketC6F() elif code == ord('p'): # battery percentage - _, self.battery_perc = self.packeter.unpacketC1F() + _, self._battery_perc = self._packeter.unpacketC1F() elif code == ord('d'): # distance sensor - _, self.left_tof, self.center_tof, self.right_tof = self.packeter.unpacketC3I() + _, self._left_tof, self._center_tof, self._right_tof = self._packeter.unpacketC3I() elif code == ord('t'): # touch input - _, self.touch_bits = self.packeter.unpacketC1B() + _, self._touch_byte = self._packeter.unpacketC1B() elif code == ord('b'): # behaviour - _, self.behaviour = self.packeter.unpacketC1B() + _, self._behaviour = self._packeter.unpacketC1B() elif code == ord('f'): # tof matrix - (_, self.left_tof, self.center_left_tof, self.center_tof, - self.center_right_tof, self.right_tof, self.top_tof, self.bottom_tof) = self.packeter.unpacketC7I() + (_, self._left_tof, self._center_left_tof, self._center_tof, + self._center_right_tof, self._right_tof, self._top_tof, self._bottom_tof) = self._packeter.unpacketC7I() elif code == ord('q'): # imu position - _, self.roll, self.pitch, self.yaw = self.packeter.unpacketC3F() + _, self._roll, self._pitch, self._yaw = self._packeter.unpacketC3F() elif code == ord('w'): # wheels position - _, self.left_wheel._position, self.right_wheel._position = self.packeter.unpacketC2F() + _, self.left_wheel._position, self.right_wheel._position = self._packeter.unpacketC2F() elif code == ord('v'): # robot velocity - _, self.linear_velocity, self.angular_velocity = self.packeter.unpacketC2F() + _, self._linear_velocity, self._angular_velocity = self._packeter.unpacketC2F() elif code == ord('x'): # robot ack - _, self.last_ack = self.packeter.unpacketC1B() + _, self._last_ack = self._packeter.unpacketC1B() elif code == ord('z'): # robot ack - _, self.x, self.y, self.theta = self.packeter.unpacketC3F() + _, self._x, self._y, self._theta = self._packeter.unpacketC3F() elif code == 0x7E: # firmware version - _, *self.version = self.packeter.unpacketC3B() + _, *self._version = self._packeter.unpacketC3B() else: return -1 @@ -614,72 +614,73 @@ def get_battery_charge(self) -> int: Returns the battery SOC :return: """ - if self.battery_perc > 100: + if self._battery_perc > 100: return 100 - return round(self.battery_perc) + return round(self._battery_perc) - def _get_touch(self) -> int: + @property + def _touch_bits(self) -> int: """ Returns the touch sensor's state :return: touch_bits """ - return self.touch_bits + return (self._touch_byte & 0xFF) if self._touch_byte is not None else 0x00 def get_touch_any(self) -> bool: """ Returns true if any button is pressed :return: """ - return bool(self.touch_bits & 0b00000001) + return bool(self._touch_bits & 0b00000001) def get_touch_ok(self) -> bool: """ Returns true if ok button is pressed :return: """ - return bool(self.touch_bits & 0b00000010) + return bool(self._touch_bits & 0b00000010) def get_touch_cancel(self) -> bool: """ Returns true if cancel button is pressed :return: """ - return bool(self.touch_bits & 0b00000100) + return bool(self._touch_bits & 0b00000100) def get_touch_center(self) -> bool: """ Returns true if center button is pressed :return: """ - return bool(self.touch_bits & 0b00001000) + return bool(self._touch_bits & 0b00001000) def get_touch_up(self) -> bool: """ Returns true if up button is pressed :return: """ - return bool(self.touch_bits & 0b00010000) + return bool(self._touch_bits & 0b00010000) def get_touch_left(self) -> bool: """ Returns true if left button is pressed :return: """ - return bool(self.touch_bits & 0b00100000) + return bool(self._touch_bits & 0b00100000) def get_touch_down(self) -> bool: """ Returns true if down button is pressed :return: """ - return bool(self.touch_bits & 0b01000000) + return bool(self._touch_bits & 0b01000000) def get_touch_right(self) -> bool: """ Returns true if right button is pressed :return: """ - return bool(self.touch_bits & 0b10000000) + return bool(self._touch_bits & 0b10000000) @staticmethod def _limit(value: float, lower: float, upper: float) -> float: @@ -768,7 +769,7 @@ def get_color_raw(self) -> (int, int, int): :return: red, green, blue """ - return self.red, self.green, self.blue + return self._red, self._green, self._blue def _normalize_color(self, r: float, g: float, b: float) -> (float, float, float): """ @@ -836,6 +837,9 @@ def get_color(self, color_format: str = 'rgb') -> (float, float, float): """ assert color_format in ['rgb', 'hsv'] + if None in list(self.get_color_raw()): + return None, None, None + if color_format == 'rgb': return self._normalize_color(*self.get_color_raw()) elif color_format == 'hsv': @@ -858,6 +862,9 @@ def hsv2label(h, s, v) -> str: :return: """ + if None in [h, s, v]: + return 'UNDEFINED' + if s < 0.1: if v < 0.05: label = 'BLACK' @@ -899,11 +906,15 @@ def get_distance(self, unit: str = 'cm') -> (float, float, float, float, float): :param unit: distance output unit :return: left_tof, center_left_tof, center_tof, center_right_tof, right_tof """ - return (convert_distance(self.left_tof, 'mm', unit), - convert_distance(self.center_left_tof, 'mm', unit), - convert_distance(self.center_tof, 'mm', unit), - convert_distance(self.center_right_tof, 'mm', unit), - convert_distance(self.right_tof, 'mm', unit)) + + if None in [self._left_tof, self._center_left_tof, self._center_tof, self._center_right_tof, self._right_tof]: + return None, None, None, None, None + + return (convert_distance(self._left_tof, 'mm', unit), + convert_distance(self._center_left_tof, 'mm', unit), + convert_distance(self._center_tof, 'mm', unit), + convert_distance(self._center_right_tof, 'mm', unit), + convert_distance(self._right_tof, 'mm', unit)) def get_distance_top(self, unit: str = 'cm') -> float: """ @@ -911,7 +922,7 @@ def get_distance_top(self, unit: str = 'cm') -> float: :param unit: :return: """ - return convert_distance(self.top_tof, 'mm', unit) + return convert_distance(self._top_tof, 'mm', unit) def get_distance_bottom(self, unit: str = 'cm') -> float: """ @@ -919,14 +930,14 @@ def get_distance_bottom(self, unit: str = 'cm') -> float: :param unit: :return: """ - return convert_distance(self.bottom_tof, 'mm', unit) + return convert_distance(self._bottom_tof, 'mm', unit) def get_version(self) -> str: """ Returns the firmware version of the Alvik :return: """ - return f'{self.version[0]}.{self.version[1]}.{self.version[2]}' + return f'{self._version[0]}.{self._version[1]}.{self._version[2]}' def print_status(self): """