LISHUZUOXUN_yangjiang/Exercise3/running_muwb_with_rssi.py

444 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# coding=gb2312
import json
import os.path
import sys
from Database.manager_database import *
from Speaker.speak_base import SpeakServer, beep
from UWB.positioning_standalone_v2 import *
FINISH_LINE_SIZE1 = 0
FINISH_LINE_SIZE2 = 1
RUNNING_COUNTING = "cnt"
RUNNING_STATUS = "stat"
RUNNING_MES = "mes"
STATUS_ACROSS = "across"
STATUS_EXIT = "exit"
RUNNING_LAST_STATUS_TIME = "lst"
RUNNING_COST = "cost"
RUNNING_DONE = 'done'
PATH = sys.path[0]
ANCHOR_RSSI_ADJUSTMENT_PATH = f"{PATH}/Exercise3/anchor_adjustment.json"
def score_compute(age, cost, score_data):
my_age_index = 0
for age_index in range(len(score_data["age"])):
candidate_age = score_data["age"][age_index]
if candidate_age[0] <= age <= candidate_age[1]:
my_age_index = age_index
break
my_score_index = len(score_data["values"]) - 1
for value_index in range(len(score_data["values"]) - 1, 0 - 1, -1):
value = score_data["values"][value_index][my_age_index]
if cost <= value[0] * 60 + value[1]:
my_score_index = value_index
else:
break
if cost > score_data["values"][-1][my_age_index][0] * 60 + score_data["values"][-1][my_age_index][1]:
my_base_score = 0
else:
my_base_score = score_data["score"][my_score_index]
if my_base_score == 100:
value = score_data["values"][my_score_index][my_age_index]
base_score = value[0] * 60 + value[1]
my_score = my_base_score + (base_score - cost) // 5 * 1
else:
my_score = my_base_score
return my_score
class Running:
def __init__(self, positioning: Positioning, round_num=3, min_round_time=30) -> None:
self.min_round_time = float(min_round_time)
self.round_num = int(round_num)
self.positioning = positioning
self.positioning_tag = None
self.finish_line = {}
# 起跑时间
self.start_time = 0
# 结束时间
self.stop_time = 0
# 跑步记录
self.running_record = {}
# 跑步流水记录
self.running_record_list = {}
# 跑步圈时记录
self.round_record = {}
# 记录手环
self.test_tag = []
# 手环对应人员信息
self.person_mes = {}
# 是否起跑状态
self.__start_running = threading.Event()
self.__start_running.clear()
# 说话
self.speak_driver = SpeakServer()
self.speak_driver.start()
# 成绩文件
file = open(f"{PATH}/Exercise3/running_score.json")
self.score_data = json.load(file)
# 基站校准数据
anchor_list = list(positioning.multi_uwb.uwb_driver_table.keys())
default_effective_rssi = -84
if os.path.exists(ANCHOR_RSSI_ADJUSTMENT_PATH):
with open(ANCHOR_RSSI_ADJUSTMENT_PATH, "r", encoding="utf-8_sig") as file:
self.anchor_rssi_adjustment = json.load(file)
if len(self.anchor_rssi_adjustment) < len(anchor_list):
self.anchor_rssi_adjustment = {
anchor: default_effective_rssi
for anchor in anchor_list
}
with open(ANCHOR_RSSI_ADJUSTMENT_PATH, "w", encoding="utf-8_sig") as file:
json.dump(self.anchor_rssi_adjustment, file)
else:
self.anchor_rssi_adjustment = {
anchor: default_effective_rssi
for anchor in anchor_list
}
with open(ANCHOR_RSSI_ADJUSTMENT_PATH, "w", encoding="utf-8_sig") as file:
json.dump(self.anchor_rssi_adjustment, file)
# 跑步记录数据
self.final_data = {}
# 是否播报
self.is_play = False
def set_play(self, is_play):
self.is_play = is_play
def update_and_play(self, tag, new_record):
if self.is_play:
this_record = self.running_record[tag]
if this_record[RUNNING_COUNTING] != new_record[RUNNING_COUNTING]:
person_name = self.person_mes[tag][NAME]
person_id = self.person_mes[tag][ID]
if new_record[RUNNING_DONE]:
self.speak_driver.add_speak(f"{person_name}完成考试!")
else:
self.speak_driver.add_speak(f"{person_name}通过第{new_record[RUNNING_COUNTING]}圈!")
self.running_record[tag] = new_record
def set_config(self, round_num, min_round_time):
self.min_round_time = float(min_round_time)
self.round_num = int(round_num)
def add_tag(self, tag):
self.test_tag.append(tag)
def del_tag(self, tag):
self.test_tag.remove(tag)
del self.person_mes[tag]
def add_tag_mes(self, tag, mes):
self.person_mes[tag] = mes
def reset(self):
self.test_tag.clear()
self.person_mes.clear()
self.start_time = 0
def start(self):
# 清除缓存
self.__start_running.clear()
self.round_record.clear()
self.positioning.clear_information()
self.positioning.resume()
# 初始化记录
self.running_record = {
tag: {
RUNNING_COUNTING: -1,
RUNNING_STATUS: STATUS_EXIT,
RUNNING_MES: "未起始",
RUNNING_COST: 0,
RUNNING_DONE: False,
}
for tag in self.test_tag
}
# 起跑播报
self.speak_driver.add_speak("准备开始考试,请就位,倒计时:")
counting = 5
for i in range(counting):
start_time = time.time()
self.speak_driver.add_speak(counting - i)
while time.time() - start_time < 1:
time.sleep(0.05)
self.speak_driver.wait_4_speak()
self.start_time = time.time()
self.positioning.set_valid_time(self.start_time)
beep(duration=700, freq=640)
self.__start_running.set()
# 启动计算线程
threading.Thread(target=self._thread_running, daemon=True).start()
threading.Thread(target=self._thread_processing, daemon=True).start()
def kill(self):
# 记录手环
self.test_tag.clear()
self.running_record_list.clear()
self.__start_running.clear()
def stop(self):
# # 起跑时间
self.stop_time = time.time()
# self.start_time = 0
self.kill()
# 计算最终成绩
self.final_data.clear()
raw_score = self.get_score()
for score in raw_score:
band_id = score["band_id"]
person_mes = self.person_mes[band_id]
tag_mes = self.running_record[band_id]
score.update(person_mes)
score.update(tag_mes)
person_id = score["id"]
round_time = self.round_record.get(person_id)
round_time_record = round_time if round_time else []
score.update({"round_time": round_time_record})
score.update({
"fix": False, "final_result": round_time_record
})
self.final_data[person_id] = score
# 获得当前所有成绩
def get_all_score(self):
return list(self.final_data.values())
# 成绩修复
def fix_score(self, person_id):
if not self.final_data[person_id][RUNNING_DONE]:
detected_round = len(self.final_data[person_id]["round_time"])
lacked_round = self.round_num - detected_round + 1
result = [
self.final_data[person_id]["round_time"][i]
for i in range(detected_round)
]
# 如果没有成绩,按最终完成时间去计算
if len(result) <= 1:
total_time = self.stop_time - self.start_time
result = [total_time / (self.round_num + 1) for i in range(self.round_num + 1)]
# 如果有成绩
else:
for _ in range(lacked_round):
max_result = max(result)
index = result.index(max_result)
result = result[0:index:] + [max_result / 2, max_result / 2] + result[index + 1::]
self.final_data[person_id]["final_result"] = {
i: result[i] for i in range(len(result))
}
self.final_data[person_id]["fix"] = True
self.final_data[person_id]["total_time"] = sum(result)
# 修复撤销
def fix_withdraw(self, person_id):
self.final_data[person_id]["final_result"] = self.final_data[person_id]["round_time"]
self.final_data[person_id]["fix"] = False
def _thread_processing(self):
while self.__start_running.is_set():
data = self.positioning.get_last_information()
try:
# 过滤无效操作
if data:
record_time, mes = data
# print(self.start_time - record_time, len(self.positioning.tag_information))
# if record_time < self.start_time - 3:
# continue
tag = mes[TAG]
if tag not in self.test_tag:
continue
distance = mes[DIST]
rssi = mes[RSSI]
anchor = mes[ANCHOR_ID]
self.running_record_list.setdefault(tag, [])
self.running_record_list[tag].append(
{RECORD: record_time, DIST: distance, RSSI: rssi, ANCHOR_ID: anchor}
)
except Exception as e:
traceback.format_exc()
print(f"获取数据时发生错误:{e.args}")
def _thread_running(self):
# 提前开始检测时间
bias = 3
# 判定周期时间
judge_window_time = 6
detect_time_range = judge_window_time / 2
# 有效进入距离
effective_distance = 300
# effective_rssi = -84
# 开始判定距离
judge_distance = 700
# 离开判定时间
leave_detect_time = 10
while self.__start_running.is_set():
this_time = time.time()
for tag, record in self.running_record_list.copy().items():
if tag not in self.test_tag or self.running_record[tag][RUNNING_DONE]:
continue
# 获取测试人员id
person_id = self.person_mes[tag]["id"]
sorted_record = sorted(record, key=lambda x: x[RECORD])
this_tag = {
RUNNING_COUNTING: -1,
RUNNING_STATUS: STATUS_EXIT,
RUNNING_MES: "离开",
RUNNING_COST: 0,
RUNNING_DONE: False
}
# Cache
# 上次进入检测区域的时间
last_enter_time = self.start_time
# 上次离开检测区域的时间
last_leave_time = -1
# 最后一条记录的时间
record_time = self.start_time
# print("===============================================================================================")
for one_record in sorted_record:
# print(one_record)
try:
# 消除无效数据
if one_record[RECORD] < self.start_time - bias or this_time - one_record[RECORD] < detect_time_range:
continue
record_time = one_record[RECORD]
distance = one_record[DIST]
rssi = one_record[RSSI]
anchor = one_record[ANCHOR_ID]
# 更新时间窗口中的数据,未来时间一段时间内的数据。
frontward_window = list(
filter(
lambda x: x[DIST] is not None and -detect_time_range <= record_time - x[RECORD] < 0,
sorted_record
)
)
# 离开判定1
if (
# 距离上次进入冲线条件的时间已经有一段时间
record_time - last_leave_time > leave_detect_time
and this_tag[RUNNING_STATUS] == STATUS_ACROSS
):
this_tag[RUNNING_STATUS] = STATUS_EXIT
this_tag[RUNNING_MES] = "离开"
# print("离开判定")
# 获取有效信号值
effective_rssi = self.anchor_rssi_adjustment[anchor]
# 冲线判定
if (
(
# CASE1如果未来距离没有数据
len(frontward_window) == 0
and (
# 并且检测到距离小于判定距离
(distance is not None and distance < judge_distance)
# 或者检测到的信号强度强于检测强度
or rssi >= effective_rssi
)
) or (
# CASE2如果未来还有数据
len(frontward_window) > 0
and distance is not None
and distance <= effective_distance
# 未来距离都比现在大
and distance - min([fw[DIST] for fw in frontward_window]) <= 20
)
):
# print("冲线判定")
if (
# 并且当前时间距离上次已经大于单圈时间,并且已经离开了
record_time - last_leave_time > self.min_round_time
and this_tag[RUNNING_STATUS] == STATUS_EXIT
):
# print("圈数判定")
this_tag[RUNNING_STATUS] = STATUS_ACROSS
this_tag[RUNNING_MES] = "冲线"
this_tag[RUNNING_COUNTING] += 1
# 圈数时间记录
self.round_record.setdefault(person_id, {})
this_round_cost = 0 if record_time - last_enter_time < 0 else record_time - last_enter_time
self.round_record[person_id][this_tag[RUNNING_COUNTING]] = this_round_cost
last_enter_time += this_round_cost
# 结束判定
if this_tag[RUNNING_COUNTING] == self.round_num:
this_tag[RUNNING_DONE] = True
this_tag[RUNNING_COST] = record_time - self.start_time
break
last_leave_time = record_time
# 如果还处于冲线状态,并且距离上次检测时间不超过离开判定周期,更新冲线时间
if (
this_tag[RUNNING_STATUS] == STATUS_ACROSS
# and record_time - last_leave_time < detect_time_range
):
last_leave_time = record_time
except Exception as e:
print(traceback.format_exc())
print(f"计算时发生错误:{e.args}")
# 离开判定2
if (
# 最后离开的时间已经超过了离开判定时间
this_time - last_leave_time > leave_detect_time
# 最后的一条数据距离现在已经超过了离开判定时间
and this_time - record_time > leave_detect_time
and this_tag[RUNNING_STATUS] == STATUS_ACROSS
):
this_tag[RUNNING_STATUS] = STATUS_EXIT
this_tag[RUNNING_MES] = "离开"
if this_time - last_leave_time > self.min_round_time and this_tag[RUNNING_STATUS] == STATUS_EXIT:
this_tag[RUNNING_MES] = "回程"
# print(this_tag)
# 更新记录
self.update_and_play(tag=tag, new_record=this_tag)
# 休息一下
time.sleep(0.5)
def get_person_round_time(self, person_id):
if person_id not in self.final_data.keys():
return {}
return self.final_data[person_id]["final_result"]
def get_valid_score(self):
result = []
for person_id, data in self.final_data.items():
if data["done"] or data["fix"]:
data["score"] = score_compute(data["age"], data["total_time"], self.score_data)
result.append(data)
return result
# 获取成绩接口
def get_score(self):
score = []
this_cost_time = time.time() - self.start_time
# print(self.running_record)
for tag, record in self.running_record.items():
if record[RUNNING_COUNTING] < 0:
finish_status = "未起始"
else:
if record[RUNNING_COUNTING] == 0:
finish_status = f"已开始({record[RUNNING_MES]})"
else:
finish_status = f"考试中({record[RUNNING_MES]})"
if record[RUNNING_COUNTING] == self.round_num:
finish_status = "已完成考试"
age = self.person_mes[tag]["age"]
one = {
"band_id": tag,
"score": score_compute(age, record[RUNNING_COST], self.score_data)
if record[RUNNING_COUNTING] == self.round_num else 0,
"total_time": record[RUNNING_COST]
if record[RUNNING_DONE] or self.start_time == 0 else this_cost_time,
"round": record[RUNNING_COUNTING] if record[RUNNING_COUNTING] > 0 else 0,
"finish": finish_status,
"percentage": record[RUNNING_COUNTING] / self.round_num * 100
if record[RUNNING_COUNTING] > 0 else 0
}
score.append(one)
# print(score)
return score