acousticSensor.py 8.5 KB

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