acousticSensor.py 8.5 KB

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