I am trying to use the _thread module to constantly fetch data from a GY271 compass module while moving a robot, but I get this error message:
Code: Select all
Unhandled exception in thread started by -37.72
Traceback (most recent call last):
File "robot.py", line 46, in __update_info
File "gy271.py", line 77, in direction
TypeError: can't convert __enter__ to float
Code: Select all
direction_radians = math.atan2(y, x)
My code for reference:
Code: Select all
import math
import uasyncio
import json
class GY271:
"""Class to control the GY271 compass module."""
scales = {
0.88: [0, 0.73],
1.3: [1, 0.92],
1.9: [2, 1.22],
2.5: [3, 1.52],
4.0: [4, 2.27],
4.7: [5, 2.56],
5.6: [6, 3.03],
8.1: [7, 4.35]
}
def __init__(self, i2c_object, declination = (0, 0), radians = False, calibration_file = 'calibration.json', gauss = 1.3):
"""Initialize the module."""
# i2c address definitions
self.address = 0x1E
self.config_address_a = 0x00
self.config_address_b = 0x01
self.mode_address = 0x02
self.data_address = 0x03
self.status_address = 0x09
self.declination_degrees, self.declination_minutes = declination
self.declination = (self.declination_degrees + self.declination_minutes / 60) * math.pi / 180
self.scale_register_value, self.scale = self.scales[gauss]
self.radians = radians
self.i2c_object = i2c_object
self.calibration_file = calibration_file
with open(self.calibration_file) as f:
self.calibration_values = json.load(f)
if self.address not in self.i2c_object.scan():
raise RuntimeError("Failed to get response from device.")
self.i2c_object.writeto_mem(self.address, self.config_address_a, bytearray([0x70])) # 8 Average, 15 Hz, normal measurement
self.i2c_object.writeto_mem(self.address, self.config_address_b, bytearray([self.scale_register_value << 5])) # Scale
self.i2c_object.writeto_mem(self.address, self.mode_address, bytearray([0x00])) # Continuous measurement
@property
def raw_values(self):
"""Get x, y, and z values from the module. Returns the ordered triple (x, y, z)."""
data = bytearray(6)
self.i2c_object.readfrom_mem_into(self.address, self.data_address, data)
x = self.__convert_values(data, 0) + self.calibration_values['xb']
y = self.__convert_values(data, 4) + self.calibration_values['yb']
z = self.__convert_values(data, 2)
return (x, y, z)
def __convert_values(self, data, offset):
"""Convert data values from bytes to integers. Returns the value rounded to 4 decimals."""
value = self.__twos_complement(data[offset] << 8 | data[offset + 1], 16)
if value == -4096:
return None
return round(value * self.scale, 4)
def __twos_complement(self, value, length):
"""Convert twos complement to integer value. Returns the integer value."""
if (value & (1 << (length - 1))):
value = value - (1 << length)
return value
@property
def direction(self):
"""Get the current direction. Returns direction in degrees, unless 'radians = True' is specified."""
(x, y, z) = self.raw_values
direction_radians = math.atan2(y, x)
direction_radians += self.declination
if direction_radians < 0:
direction_radians += 2 * math.pi
elif direction_radians > 2 * math.pi:
direction_radians += 2 * math.pi
direction_degrees = math.degrees(direction_radians)
if self.radians:
return direction_radians
return direction_degrees
def calibrate(self, mode, verbose = False):
"""Calibrate the sensor."""
if mode == "start":
self.x_min = 0
self.x_max = 0
self.y_min = 0
self.y_max = 0
self.calibration_values = {"xb": 0, "yb": 0}
self.cal_done = False
async def cal_routine():
while not self.cal_done:
x, y, z = self.raw_values
self.x_min = min(x, self.x_min)
self.x_max = max(x, self.x_max)
self.y_min = min(y, self.y_min)
self.y_max = max(y, self.y_max)
if verbose:
print("x: " + str(y))
print("y: " + str(y))
print("xmin: " + str(self.x_min))
print("ymin: " + str(self.y_min))
print("xmax: " + str(self.x_max))
print("ymax: " + str(self.y_max))
await uasyncio.sleep_ms(67)
uasyncio.run(cal_routine())
elif mode == "finish":
self.cal_done = True
xb = 0.5 * (self.x_max - self.x_min) - self.x_max
yb = 0.5 * (self.y_max - self.y_min) - self.y_max
calibration = {"xb": xb, "yb": yb}
if verbose:
print("xoffset: " + str(xb))
print("yoffset: " + str(yb))
print("xmin: " + str(self.x_min))
print("ymin: " + str(self.y_min))
print("xmax: " + str(self.x_max))
print("ymax: " + str(self.y_max))
print("calibration dictionary: " + str(calibration))
with open(self.calibration_file, 'w') as f:
json.dump(calibration, f)
elif mode == "abort":
self.cal_done = True
else:
raise RuntimeError("Invalid calibration mode. Valid modes are 'start', 'finish', and 'abort'.")
def angle_sum(self, angle_1, angle_2):
if self.radians:
sum_angle = (angle_1 + angle_2) % 2 * math.pi
return sum_angle
else:
sum_angle = (angle_1 + angle_2) % 360
return sum_angle
Code: Select all
import math
import _thread as thread
class Searching:
"""A class to manage the robot's movement, location tracking, and DFS systems."""
def __init__(self, motor_left, motor_right, compass, wheel_diameter, default_speed = 130, offset_left = 0, offset_right = 0):
"""Initialize the system."""
self.motor_left = motor_left
self.motor_right = motor_right
self.compass = compass
compass.radians = True
self.direction = self.compass.direction
self.offset_left = offset_left
self.offset_right = offset_right
self.default_speed = default_speed
self.step_size = wheel_diameter * math.pi / 80
self.locations = [(0, 0)]
self.waypoints = [(0, 0)]
self.enable_location = True
self.rotating = False
self.lock = thread.allocate_lock()
thread.start_new_thread(self.__update_info, ())
def forward(self, steps, speed = None):
"""Move the robot forward for a given number of steps at a given speed."""
if self.offset_left > self.offset_right:
self.motor_right.step(steps, frequency = (speed or self.default_speed) + self.offset_right)
self.motor_left.step_infinite(1, frequency = (speed or self.default_speed) + self.offset_left)
self.motor_right.finished_stepping += self.motor_left.stop
elif self.offset_right > self.offset_left:
self.motor_left.step(steps, frequency = (speed or self.default_speed) + self.offset_right)
self.motor_right.step_infinite(1, frequency = (speed or self.default_speed) + self.offset_left)
self.motor_left.finished_stepping += self.motor_right.stop()
elif self.offset_left == self.offset_right:
self.motor_left.step(steps, frequency = (speed or self.default_speed) + self.offset_right)
self.motor_right.step(steps, frequency = (speed or self.default_speed) + self.offset_right)
def __update_info(self):
"""Constantly update information."""
while True:
self.direction = self.compass.direction
if self.offset_left > self.offset_right:
if self.motor_right.steps_since_reset >= 80 and self.enable_location:
distance = self.motor_right.steps_since_reset * self.step_size
location = self.locations[-1]
direction = self.compass.direction
x = location[0] + distance * math.sin(direction)
y = location[1] + distance * math.cos(direction)
self.locations.append((x, y))
self.motor_right.steps_since_reset = 0
self.motor_left.steps_since_reset = 0
elif self.offset_right > self.offset_left:
if self.motor_left.steps_since_reset >= 80 and self.enable_location:
distance = self.motor_left.steps_since_reset * self.step_size
location = self.locations[-1]
direction = self.compass.direction
x = location[0] + distance * math.sin(direction)
y = location[1] + distance * math.cos(direction)
self.locations.append((x, y))
self.motor_right.steps_since_reset = 0
self.motor_left.steps_since_reset = 0
elif self.offset_left == self.offset_right:
if self.motor_right.steps_since_reset >= 80 and self.motor_left.steps_since_reset >= 80 and self.enable_location:
distance = self.motor_right.steps_since_reset * self.step_size
location = self.locations[-1]
direction = self.compass.direction
x = location[0] + distance * math.sin(direction)
y = location[1] + distance * math.cos(direction)
self.locations.append((x, y))
self.motor_right.steps_since_reset = 0
self.motor_left.steps_since_reset = 0
if self.rotating:
self.__angle_test(self.rotation_angle)
def __angle_test(self, angle):
"""Test if the current angle is the specified angle."""
if math.isclose(self.compass.angle_sum(self.initial_direction, angle), self.direction, rel_tol = 0.0872665):
self.rotating = False
self.__rotate_stop()
def rotate(self, angle, speed = None):
"""Rotate the robot a certain number of degrees at a certain speed."""
self.rotation_angle = math.radians(angle)
rotation_speed = speed or self.default_speed
self.enable_location = False
self.rotating = True
rotation = int(angle / abs(angle))
self.initial_direction = self.direction
self.motor_left_steps = self.motor_left.steps_since_reset
self.motor_right_steps = self.motor_right.steps_since_reset
self.motor_left.step_infinite(rotation, frequency = rotation_speed + self.offset_left)
self.motor_right.step_infinite(-1 * rotation, frequency = rotation_speed + self.offset_left)
def __rotate_stop(self):
self.motor_left.stop()
self.motor_right.stop()
self.motor_left.steps_since_reset = self.motor_left_steps
self.motor_right.steps_since_reset = self.motor_right_steps
self.enable_location = True