acusticSensor.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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 AcusticSensor:
  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 = float(conf["ac_sensor"]["calibration_y_offset"])
  21. self.sensor_distance = self.field_width - self.left_sensor_x_offset + self.right_sensor_x_offset
  22. self.sonic_speed = float(conf["ac_sensor"]["sonicspeed"])
  23. self.overhead_left = float(conf["ac_sensor"]["overhead_left"])
  24. self.overhead_right = float(conf["ac_sensor"]["overhead_right"])
  25. self.log_handler = logHandler.get_log_handler()
  26. self.log_handler.log_and_print("start acustic sensor")
  27. # temporary calibration variables
  28. self.time_vals = [[],[]]
  29. self.cal_values = {
  30. "left": [0, 0],
  31. "right": [0, 0]
  32. }
  33. self.n = 0
  34. def start(self):
  35. if not conn.isConnected():
  36. conn.open(port = self.conf["arduino"]["port"])
  37. conn.addRecvCallback(self._readCb)
  38. # generate dummy values until arduino is ready
  39. self.dummyActive = True
  40. dummyThread = threading.Thread(target=self._readCb_dummy)
  41. dummyThread.start()
  42. def start_calibration(self):
  43. self.calibration_state.reset_state()
  44. self.time_vals = [[],[]]
  45. self.calibration_state.next_state()
  46. def stop(self):
  47. self.log_handler.log_and_print("stop acustic sensor")
  48. self.dummyActive = False
  49. conn.close()
  50. def _readCb_dummy(self):
  51. self.log_handler.log_and_print("acustic sensor: generating test values")
  52. while self.dummyActive:
  53. value = (900+random.randint(0,300),900+random.randint(0,300))
  54. value = ((math.sin(self.n)+1)*400+900, (math.cos(self.n)+1)*400+900)
  55. self.n += 0.02
  56. if self.calibration_state.get_state() == self.calibration_state.ACCUMULATING_1:
  57. value = (1541+random.randint(-50,50),2076+random.randint(-50,50))
  58. elif self.calibration_state.get_state() == self.calibration_state.ACCUMULATING_2:
  59. value = (2076+random.randint(-50,50),1541+random.randint(-50,50))
  60. self.calibrate(value)
  61. self.pass_to_gui(self.calculate_position(value) + value)
  62. time.sleep(0.01)
  63. self.log_handler.log_and_print("acustic sensor: disabled test mode")
  64. def _readCb(self, raw):
  65. if self.dummyActive == True:
  66. self.dummyActive = False
  67. value = conn.getAcusticRTTs()
  68. # partially missing values will be ignored
  69. if value[0] >= 0 and value[1] >= 0:
  70. self.calibrate(value)
  71. position = self.calculate_position(value)
  72. if position != None:
  73. self.pass_to_gui(position + value)
  74. def calibrate(self, value):
  75. if self.calibration_state.get_state() == self.calibration_state.ACCUMULATING_1:
  76. self.time_vals[0].append(value[0])
  77. self.time_vals[1].append(value[1])
  78. self.calibration_state.progress = len(self.time_vals[0]) / 2
  79. if len(self.time_vals[0]) >= 100:
  80. self.cal_values["left"][0] = statistics.mean(self.time_vals[0])
  81. self.cal_values["right"][1] = statistics.mean(self.time_vals[1])
  82. self.time_vals = [[],[]]
  83. self.calibration_state.next_state() # signal gui to get next position
  84. elif self.calibration_state.get_state() == self.calibration_state.ACCUMULATING_2:
  85. self.time_vals[0].append(value[0])
  86. self.time_vals[1].append(value[1])
  87. self.calibration_state.progress = 50 + len(self.time_vals[0]) / 2
  88. if len(self.time_vals[0]) >= 100:
  89. self.cal_values["left"][1] = statistics.mean(self.time_vals[0])
  90. self.cal_values["right"][0] = statistics.mean(self.time_vals[1])
  91. # all values have been captured
  92. self.log_handler.log_and_print("calibration measurements:", self.cal_values)
  93. # calculate distances from config
  94. # /| _.-|
  95. # d1 / | d2 _.-` |
  96. # / | y_off + calYoff _.-` | y_off + calibration_y_offset
  97. # /___| -____________|
  98. # x_off x_off + width
  99. distance_1 = math.sqrt(self.left_sensor_x_offset**2 + (self.sensor_y_offset + self.calibration_y_offset)**2 )
  100. distance_2 = math.sqrt((self.left_sensor_x_offset + self.field_width)**2 + (self.sensor_y_offset + self.calibration_y_offset)**2 )
  101. distancedif = distance_2 - distance_1
  102. timedif = self.cal_values["left"][1] - self.cal_values["left"][0]
  103. # speed of sound in mm/us
  104. sonicspeed_1 = distancedif / timedif
  105. # processing time overhead in us
  106. overhead_1 = statistics.mean((self.cal_values["left"][1] - distance_1/sonicspeed_1, self.cal_values["left"][0] - distance_2/sonicspeed_1))
  107. # same for the second set of values
  108. distance_1 = math.sqrt(self.right_sensor_x_offset**2 + (self.sensor_y_offset + self.calibration_y_offset)**2 )
  109. distance_2 = math.sqrt((self.right_sensor_x_offset + self.field_width)**2 + (self.sensor_y_offset + self.calibration_y_offset)**2 )
  110. distancedif = distance_2 - distance_1
  111. timedif = self.cal_values["right"][1] - self.cal_values["right"][0]
  112. sonicspeed_2 = distancedif / timedif
  113. overhead_2 = statistics.mean((self.cal_values["right"][0] - distance_1/sonicspeed_2, self.cal_values["right"][1] - distance_2/sonicspeed_2))
  114. # calculate calibration results
  115. self.sonic_speed = statistics.mean((sonicspeed_1,sonicspeed_2))
  116. self.overhead_left = overhead_1
  117. self.overhead_right = overhead_2
  118. self.log_handler.log_and_print("calibration results:")
  119. self.log_handler.log_and_print(" sonicspeed: {:8.6f} mm/us".format(self.sonic_speed))
  120. self.log_handler.log_and_print(" overhead_left: {:8.3f} us".format(self.overhead_left))
  121. self.log_handler.log_and_print(" overhead_right: {:8.3f} us".format(self.overhead_right))
  122. self.calibration_state.next_state()
  123. def read(self):
  124. value = conn.getAcusticRTTs()
  125. return value
  126. def calculate_position(self,values):
  127. try:
  128. val1, val2 = values
  129. val1 -= self.overhead_left
  130. val2 -= self.overhead_right
  131. distance_left = val1 * self.sonic_speed
  132. distance_right = val2 * self.sonic_speed
  133. # compute intersection of distance circles
  134. x = (self.sensor_distance**2 - distance_right**2 + distance_left**2) / (2*self.sensor_distance) + self.left_sensor_x_offset
  135. if distance_left**2 - x**2 >= 0:
  136. y = math.sqrt(distance_left**2 - x**2) - self.sensor_y_offset
  137. return (x, y)
  138. else:
  139. return None
  140. except Exception as e:
  141. print(values)
  142. traceback.print_exc()
  143. def pass_to_gui(self, data):
  144. self.ac_queue.put(("data", data))