acousticSensor.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import time
  2. import statistics
  3. import math
  4. import threading
  5. import random
  6. import traceback
  7. import queue
  8. from sensors.calibration import CalibrationStateMashine
  9. from sensors.connection import globalArduinoSlave
  10. import logHandler
  11. conn = globalArduinoSlave()
  12. class AcousticSensor:
  13. def __init__(self, conf):
  14. self.conf = conf
  15. self.queue = queue.Queue()
  16. self.calibration_state = CalibrationStateMashine()
  17. self.field_height = float(conf["field"]["y"])
  18. self.field_width = float(conf["field"]["x"])
  19. self.sensor_y_offset = float(conf["ac_sensor"]["y_offset"])
  20. self.left_sensor_x_offset = float(conf["ac_sensor"]["left_x_offset"])
  21. self.right_sensor_x_offset = float(conf["ac_sensor"]["right_x_offset"])
  22. self.calibration_y_offset_1 = float(conf["ac_sensor"]["calibration_y_offset_1"])
  23. self.calibration_y_offset_2 = float(conf["ac_sensor"]["calibration_y_offset_2"])
  24. self.calibration_x_offset = float(conf["ac_sensor"]["calibration_x_offset"])
  25. self.sensor_distance = self.field_width - self.left_sensor_x_offset + self.right_sensor_x_offset
  26. self.sonic_speed = float(conf["ac_sensor"]["sonicspeed"])
  27. self.overhead_left = float(conf["ac_sensor"]["overhead_left"])
  28. self.overhead_right = float(conf["ac_sensor"]["overhead_right"])
  29. self.check = 1
  30. self.log_handler = logHandler.get_log_handler()
  31. # temporary calibration variables
  32. self.time_vals = [[],[]]
  33. self.cal_values = {
  34. "front": [0, 0],
  35. "back": [0, 0]
  36. }
  37. self.n = 0
  38. def start(self):
  39. self.log_handler.log_and_print("start acoustic sensor")
  40. if not conn.isConnected():
  41. conn.open(port = self.conf["arduino"]["port"])
  42. conn.addRecvCallback(self._readCb)
  43. # generate dummy values until arduino is ready
  44. self.dummyActive = True
  45. dummyThread = threading.Thread(target=self._readCb_dummy)
  46. dummyThread.start()
  47. def start_calibration(self):
  48. self.calibration_state.reset_state()
  49. self.time_vals = [[],[]]
  50. self.calibration_state.next_state()
  51. def stop(self):
  52. self.log_handler.log_and_print("stop acoustic sensor")
  53. self.dummyActive = False
  54. conn.close()
  55. def _readCb_dummy(self):
  56. self.log_handler.log_and_print("acoustic sensor: generating test values")
  57. while self.dummyActive:
  58. if self.n % 4 < 1:
  59. dummyPosition = (0, self.n%1 * self.field_height)
  60. elif self.n % 4 < 2:
  61. dummyPosition = (self.n%1 * self.field_width, self.field_height)
  62. elif self.n % 4 < 3:
  63. dummyPosition = (self.field_width, self.field_height - self.n%1 * self.field_height)
  64. else:
  65. dummyPosition = (self.field_width - self.n%1 * self.field_width, 0)
  66. self.n += 0.01
  67. if self.calibration_state.get_state() in [self.calibration_state.ACCUMULATING_1, self.calibration_state.WAITING_POS_1]:
  68. dummyPosition = (self.calibration_x_offset + random.randint(-5,5), self.calibration_y_offset_1 + random.randint(-5,5))
  69. elif self.calibration_state.get_state() in [self.calibration_state.ACCUMULATING_2, self.calibration_state.WAITING_POS_2]:
  70. dummyPosition = (self.calibration_x_offset + random.randint(-5,5), self.calibration_y_offset_2 + random.randint(-5,5))
  71. # these dummy parameters should also appear after the calibration
  72. dummyValue = self.predict_values(dummyPosition, sonic_speed=0.35, overhead_left=30, overhead_right=100)
  73. self.calibrate(dummyValue)
  74. position = self.calculate_position(dummyValue)
  75. if position != None:
  76. self.pass_to_gui(position + dummyValue)
  77. time.sleep(0.012)
  78. self.log_handler.log_and_print("acoustic sensor: disabled test mode")
  79. def _readCb(self, raw):
  80. if self.dummyActive == True:
  81. self.dummyActive = False
  82. value = conn.getAcousticRTTs()
  83. # partially missing values will be ignored
  84. if value[0] >= 0 and value[1] >= 0:
  85. self.calibrate(value)
  86. position = self.calculate_position(value)
  87. if position != None:
  88. self.pass_to_gui(position + value)
  89. def calibrate(self, value):
  90. if self.calibration_state.get_state() == self.calibration_state.ACCUMULATING_1:
  91. self.time_vals[0].append(value[0])
  92. self.time_vals[1].append(value[1])
  93. self.calibration_state.progress = len(self.time_vals[0]) / 2
  94. if len(self.time_vals[0]) >= 100:
  95. self.cal_values["front"][0] = statistics.mean(self.time_vals[0])
  96. self.cal_values["front"][1] = statistics.mean(self.time_vals[1])
  97. self.time_vals = [[],[]]
  98. self.calibration_state.next_state() # signal gui to get next position
  99. elif self.calibration_state.get_state() == self.calibration_state.ACCUMULATING_2:
  100. self.time_vals[0].append(value[0])
  101. self.time_vals[1].append(value[1])
  102. self.calibration_state.progress = 50 + len(self.time_vals[0]) / 2
  103. if len(self.time_vals[0]) >= 100:
  104. self.cal_values["back"][0] = statistics.mean(self.time_vals[0])
  105. self.cal_values["back"][1] = statistics.mean(self.time_vals[1])
  106. # all values have been captured
  107. self.log_handler.log_and_print("calibration measurements:", self.cal_values)
  108. # calculate distances from config
  109. # /|\
  110. # d1 d2 d3 / | \ d4
  111. # _..'|'.._y_off + calYoff_2 / | \y_off + calYoff_2
  112. # /____|____\ /___|___\
  113. # x_off x_off
  114. distance_1 = math.sqrt((self.calibration_x_offset + self.left_sensor_x_offset)**2 + (self.sensor_y_offset + self.calibration_y_offset_1)**2 )
  115. distance_2 = math.sqrt((self.calibration_x_offset + self.left_sensor_x_offset)**2 + (self.sensor_y_offset + self.calibration_y_offset_2)**2 )
  116. distancedif = distance_2 - distance_1
  117. timedif = self.cal_values["back"][0] - self.cal_values["front"][0]
  118. # speed of sound in mm/us
  119. sonicspeed_1 = distancedif / timedif
  120. # same for the second set of values
  121. distance_3 = math.sqrt((self.right_sensor_x_offset + (self.field_width - self.calibration_x_offset))**2 + (self.sensor_y_offset + self.calibration_y_offset_1)**2 )
  122. distance_4 = math.sqrt((self.right_sensor_x_offset + (self.field_width - self.calibration_x_offset))**2 + (self.sensor_y_offset + self.calibration_y_offset_2)**2 )
  123. distancedif = distance_4 - distance_3
  124. timedif = self.cal_values["back"][1] - self.cal_values["front"][1]
  125. sonicspeed_2 = distancedif / timedif
  126. # processing time overhead in us
  127. overhead_1 = statistics.mean((self.cal_values["front"][0] - distance_1/sonicspeed_1, self.cal_values["back"][0] - distance_2/sonicspeed_1))
  128. overhead_2 = statistics.mean((self.cal_values["front"][1] - distance_3/sonicspeed_2, self.cal_values["back"][1] - distance_4/sonicspeed_2))
  129. # calculate calibration results
  130. self.sonic_speed = statistics.mean((sonicspeed_1,sonicspeed_2))
  131. self.overhead_left = overhead_1
  132. self.overhead_right = overhead_2
  133. self.log_handler.log_and_print("calibration results:")
  134. self.log_handler.log_and_print(" sonicspeed: {:8.6f} mm/us".format(self.sonic_speed))
  135. self.log_handler.log_and_print(" overhead_left: {:8.3f} us".format(self.overhead_left))
  136. self.log_handler.log_and_print(" overhead_right: {:8.3f} us".format(self.overhead_right))
  137. self.calibration_state.next_state()
  138. def read(self):
  139. value = conn.getAcousticRTTs()
  140. return value
  141. def calculate_position(self, values):
  142. val1, val2 = values
  143. val1 -= self.overhead_left
  144. val2 -= self.overhead_right
  145. distance_left = val1 * self.sonic_speed
  146. distance_right = val2 * self.sonic_speed
  147. # compute intersection of distance circles
  148. x = (self.sensor_distance**2 - distance_right**2 + distance_left**2) / (2*self.sensor_distance) + self.left_sensor_x_offset
  149. if distance_left**2 - x**2 >= 0:
  150. y = math.sqrt(distance_left**2 - x**2) - self.sensor_y_offset
  151. return (x, y)
  152. else:
  153. return None
  154. def predict_values(self, position, sonic_speed, overhead_left, overhead_right):
  155. x, y = position
  156. distance_left = math.sqrt((y + self.sensor_y_offset)**2 + x**2)
  157. distance_right = math.sqrt(self.sensor_distance**2 + distance_left**2 - x * 2 * self.sensor_distance - x * self.left_sensor_x_offset)
  158. return (distance_left / sonic_speed + overhead_left, distance_right / sonic_speed + overhead_right)
  159. def pass_to_gui(self, data):
  160. self.queue.put(("data", data))