1
Former-commit-id: 1b34dc488f5306fcb5d20746c217255bb9ead3c5
This commit is contained in:
2
ch340.py
2
ch340.py
@ -117,7 +117,7 @@ else:
|
|||||||
self._speed = 43
|
self._speed = 43
|
||||||
self._speed = 43/34
|
self._speed = 43/34
|
||||||
# x = x*30
|
# x = x*30
|
||||||
print(int((x-int(x))*100),int(x),x)
|
# print(int((x-int(x))*100),int(x),x)
|
||||||
self.pump_ser.write(f"q1h{int(x)}d".encode('ascii'))
|
self.pump_ser.write(f"q1h{int(x)}d".encode('ascii'))
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
self.pump_ser.write(f"q2h{int((x-int(x))*100)}d".encode('ascii'))
|
self.pump_ser.write(f"q2h{int((x-int(x))*100)}d".encode('ascii'))
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:fc7d1088d4f1edb2c939f12a2d5cd136142744c6ddd23835f91710a0383d295f
|
|
||||||
size 21382656
|
|
1
ch340_gui.exe.REMOVED.git-id
Normal file
1
ch340_gui.exe.REMOVED.git-id
Normal file
@ -0,0 +1 @@
|
|||||||
|
f3beccf0b1e66d017cf3a9c35c1f0a50476f25b6
|
132
main.py
132
main.py
@ -6,6 +6,7 @@ import ch340
|
|||||||
import atexit
|
import atexit
|
||||||
import utils
|
import utils
|
||||||
from utils import State, History
|
from utils import State, History
|
||||||
|
from scipy.signal import find_peaks
|
||||||
|
|
||||||
|
|
||||||
class MAT:
|
class MAT:
|
||||||
@ -39,8 +40,8 @@ class MAT:
|
|||||||
self.control_logger.info('完成抽取')
|
self.control_logger.info('完成抽取')
|
||||||
|
|
||||||
def ch340_init(self):
|
def ch340_init(self):
|
||||||
self.ch340.push(speed=1,t=1)
|
# self.ch340.push(speed=1,t=1)
|
||||||
self.ch340.pull(speed=1.2,vol=1)
|
self.ch340.pull(speed=1.2,vol=1.8)
|
||||||
self.control_logger.info('电极位置已初始化')
|
self.control_logger.info('电极位置已初始化')
|
||||||
|
|
||||||
def ch340_push(self, speed=0.1):
|
def ch340_push(self, speed=0.1):
|
||||||
@ -93,12 +94,17 @@ class MAT:
|
|||||||
if self.state.mode == State.Mode.CRAZY:
|
if self.state.mode == State.Mode.CRAZY:
|
||||||
self._display_status(im, ret, rate, val)
|
self._display_status(im, ret, rate, val)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
# === 状态进入逻辑 ===
|
# === 状态进入逻辑 ===
|
||||||
if now - self._start_time<10:
|
if now - self._start_time<10:
|
||||||
self._display_status(im, ret, rate, val)
|
self._display_status(im, ret, rate, val)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
if not self.edited and self.history.base is not None and rate>self.history.base * 2:
|
||||||
|
self.edited = True
|
||||||
|
self.speeds[0] /= 2
|
||||||
|
|
||||||
# 1. middle: predictor返回middle立即进入slow状态
|
# 1. middle: predictor返回middle立即进入slow状态
|
||||||
if ret == "middle":
|
if ret == "middle":
|
||||||
if self.state.is_fast_mode():
|
if self.state.is_fast_mode():
|
||||||
@ -141,8 +147,8 @@ class MAT:
|
|||||||
|
|
||||||
if self.state.mode == State.Mode.ABOUT and self.state.about_check:
|
if self.state.mode == State.Mode.ABOUT and self.state.about_check:
|
||||||
h = self.history.get_recent_records(self.about_time/3,now)
|
h = self.history.get_recent_records(self.about_time/3,now)
|
||||||
ratio = self.history.get_state_ratio("about", h)
|
ratio = self.history.get_state_ratio("transport", h)
|
||||||
self.history.about_history.append(ratio<0.3)
|
self.history.about_history.append(ratio == 1)
|
||||||
while len(self.history.about_history) > 5:
|
while len(self.history.about_history) > 5:
|
||||||
self.history.about_history.pop(0)
|
self.history.about_history.pop(0)
|
||||||
if len(self.history.about_history) == 5:
|
if len(self.history.about_history) == 5:
|
||||||
@ -150,51 +156,96 @@ class MAT:
|
|||||||
if rate > 0.8:
|
if rate > 0.8:
|
||||||
self.state.exit_about()
|
self.state.exit_about()
|
||||||
self.control_logger.info(f"about比例{ratio:.2%}<80%,退出about检查,返回middle模式")
|
self.control_logger.info(f"about比例{ratio:.2%}<80%,退出about检查,返回middle模式")
|
||||||
|
|
||||||
|
self.end_check()
|
||||||
|
# print(f"end_history: {self.history.end_history}",peaks)
|
||||||
|
# if len(self.history.end_history) >= 10:
|
||||||
|
# colored_ratio=sum(self.history.end_history)/len(self.history.end_history)
|
||||||
|
# if colored_ratio > 0.7:
|
||||||
|
# self.endpoint_logger.info(f"colored比例{colored_ratio:.2%}>=70%,确认滴定终点")
|
||||||
|
# self.endpoint_logger.info(f"最终体积: {self.colored_volume:.2f} ml")
|
||||||
|
# self.running = False
|
||||||
|
# self.ch340.stop()
|
||||||
|
# if self.colored_im is not None:
|
||||||
|
# cv2.imwrite(f"colored_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg", self.colored_im)
|
||||||
|
# return "colored"
|
||||||
|
# while len(self.history.end_history) > 10:
|
||||||
|
# self.history.end_history.pop(0)
|
||||||
self.state.about_check = False
|
self.state.about_check = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# end检查: 进入end之后的end_bounce_time,如果end比例<80%,则重置;否则终止实验
|
# end检查: 进入end之后的end_bounce_time,如果end比例<80%,则重置;否则终止实验
|
||||||
if self.state.should_check_end_result(now):
|
# if self.state.should_check_end_result(now):
|
||||||
colored_ratio = self.history.get_state_ratio("colored", self.history.get_recent_records(self.state.end_bounce_time, now))
|
# colored_ratio = self.history.get_state_ratio("colored", self.history.get_recent_records(self.state.end_bounce_time, now))
|
||||||
|
|
||||||
if colored_ratio < 0.8:
|
# if colored_ratio < 0.8:
|
||||||
# end比例<80%,从history中找到第二个end并继续check逻辑
|
# # end比例<80%,从history中找到第二个end并继续check逻辑
|
||||||
self.endpoint_logger.warning(f"colored比例{colored_ratio:.2%}<80%,寻找下一个colored点")
|
# self.endpoint_logger.warning(f"colored比例{colored_ratio:.2%}<80%,寻找下一个colored点")
|
||||||
|
|
||||||
# 寻找历史中倒数第二个colored状态
|
# # 寻找历史中倒数第二个colored状态
|
||||||
colored_times = self.history.get_states_by_type("colored")
|
# colored_times = self.history.get_states_by_type("colored")
|
||||||
if len(colored_times) >= 2:
|
# if len(colored_times) >= 2:
|
||||||
# 使用倒数第二个colored时间重新开始检查
|
# # 使用倒数第二个colored时间重新开始检查
|
||||||
second_last_colored_time = colored_times[1]
|
# second_last_colored_time = colored_times[1]
|
||||||
self.state.end_detected_time = second_last_colored_time
|
# self.state.end_detected_time = second_last_colored_time
|
||||||
|
|
||||||
# 更新colored记录为对应的体积
|
# # 更新colored记录为对应的体积
|
||||||
record = self.history.find_record_by_timestamp(second_last_colored_time)
|
# record = self.history.find_record_by_timestamp(second_last_colored_time)
|
||||||
if record:
|
# if record:
|
||||||
self.colored_volume = record.volume
|
# self.colored_volume = record.volume
|
||||||
self.colored_time = record.timestamp
|
# self.colored_time = record.timestamp
|
||||||
self.colored_im = record.image.copy()
|
# self.colored_im = record.image.copy()
|
||||||
|
|
||||||
self.endpoint_logger.info(f"重置到第二个colored点: {self.colored_volume:.2f} ml")
|
# self.endpoint_logger.info(f"重置到第二个colored点: {self.colored_volume:.2f} ml")
|
||||||
else:
|
# else:
|
||||||
# 没有足够的colored点,重置end检查
|
# # 没有足够的colored点,重置end检查
|
||||||
self.state.reset_end_check()
|
# self.state.reset_end_check()
|
||||||
self.colored_volume = None
|
# self.colored_volume = None
|
||||||
self.colored_time = None
|
# self.colored_time = None
|
||||||
self.endpoint_logger.info("没有足够的colored点,重置end检查")
|
# self.endpoint_logger.info("没有足够的colored点,重置end检查")
|
||||||
else: # end比例>=80%,确认终点,终止实验
|
# else: # end比例>=80%,确认终点,终止实验
|
||||||
self.endpoint_logger.info(f"colored比例{colored_ratio:.2%}>=80%,确认滴定终点")
|
# self.endpoint_logger.info(f"colored比例{colored_ratio:.2%}>=80%,确认滴定终点")
|
||||||
self.endpoint_logger.info(f"最终体积: {self.colored_volume:.2f} ml")
|
# self.endpoint_logger.info(f"最终体积: {self.colored_volume:.2f} ml")
|
||||||
self.running = False
|
# self.running = False
|
||||||
self.ch340.stop()
|
# self.ch340.stop()
|
||||||
if self.colored_im is not None:
|
# if self.colored_im is not None:
|
||||||
cv2.imwrite(f"colored_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg", self.colored_im)
|
# cv2.imwrite(f"colored_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg", self.colored_im)
|
||||||
return "colored"
|
# return "colored"
|
||||||
|
|
||||||
# 显示状态信息
|
# 显示状态信息
|
||||||
self._display_status(im, ret, rate, val)
|
self._display_status(im, ret, rate, val)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def end_check(self,dep=0):
|
||||||
|
if dep > 5:
|
||||||
|
# self.endpoint_logger.info(f"colored比例{.2%}>=70%,确认滴定终点")
|
||||||
|
self.endpoint_logger.info(f"最终体积: {self.colored_volume:.2f} ml")
|
||||||
|
self.running = False
|
||||||
|
self.ch340.stop()
|
||||||
|
if self.colored_im is not None:
|
||||||
|
cv2.imwrite(f"colored_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg", self.colored_im)
|
||||||
|
return "colored"
|
||||||
|
suc,im = self.cap.read()
|
||||||
|
hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
|
||||||
|
s = hsv[:, :, 0]
|
||||||
|
s = s[s > 0]
|
||||||
|
hist = cv2.calcHist([s], [0], None, [256], [0, 256])
|
||||||
|
hist = hist.flatten() # 转换为一维数组
|
||||||
|
|
||||||
|
# 峰值检测 - 找到直方图中的峰值
|
||||||
|
peaks, properties = find_peaks(hist,
|
||||||
|
height=np.max(hist) * 0.2, # 峰值高度至少是最大值的10%
|
||||||
|
distance=10, # 峰值之间的最小距离
|
||||||
|
prominence=np.max(hist) * 0.01) # 峰值的突出度
|
||||||
|
if np.any(peaks>130):
|
||||||
|
self.history.end_history.append(True)
|
||||||
|
time.sleep(2)
|
||||||
|
self.control_logger.info(f"检测到colored状态,end_check at {dep}")
|
||||||
|
self.end_check(dep+1)
|
||||||
|
else:
|
||||||
|
self.history.end_history.append(False)
|
||||||
|
|
||||||
def _display_status(self, im, detection_result, rate, volume):
|
def _display_status(self, im, detection_result, rate, volume):
|
||||||
"""显示状态信息到图像上"""
|
"""显示状态信息到图像上"""
|
||||||
mode_color = {
|
mode_color = {
|
||||||
@ -223,7 +274,7 @@ class MAT:
|
|||||||
rate = val/tot
|
rate = val/tot
|
||||||
if self.history.base is not None:
|
if self.history.base is not None:
|
||||||
base = self.history.base
|
base = self.history.base
|
||||||
thr = (min(0.05,base*5), min(0.4,base*9), 0.75)
|
thr = (min(0.05,base*5), min(0.4,base*13), 0.5)
|
||||||
if rate < thr[0]:
|
if rate < thr[0]:
|
||||||
return "transport",rate
|
return "transport",rate
|
||||||
elif rate <thr[1]:
|
elif rate <thr[1]:
|
||||||
@ -246,8 +297,9 @@ class MAT:
|
|||||||
def run(self, quick_speed=0.2, slow_speed=0.05, end_speed=0.02, mid_time=0.5, about_time=1, cap_dir="Videos"):
|
def run(self, quick_speed=0.2, slow_speed=0.05, end_speed=0.02, mid_time=0.5, about_time=1, cap_dir="Videos"):
|
||||||
self.running = True
|
self.running = True
|
||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
self.speeds = (quick_speed, slow_speed, end_speed, 1.0)
|
self.speeds = [quick_speed, slow_speed, end_speed, 1.0]
|
||||||
self.need_check = False
|
self.need_check = False
|
||||||
|
self.edited = False
|
||||||
|
|
||||||
if cap_dir is not None:
|
if cap_dir is not None:
|
||||||
vid_name = f"{cap_dir}/{datetime.now().strftime('%Y%m%d_%H%M%S')}.mkv"
|
vid_name = f"{cap_dir}/{datetime.now().strftime('%Y%m%d_%H%M%S')}.mkv"
|
||||||
@ -363,7 +415,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
mat.run(
|
mat.run(
|
||||||
slow_speed = 0.05,
|
slow_speed = 0.05,
|
||||||
quick_speed = 0.15,
|
quick_speed = 0.45,
|
||||||
about_time=3,
|
about_time=3,
|
||||||
# cap_dir=None
|
# cap_dir=None
|
||||||
)
|
)
|
||||||
|
11
test.py
11
test.py
@ -3,7 +3,7 @@ import numpy as np
|
|||||||
from matplotlib import pyplot as plt
|
from matplotlib import pyplot as plt
|
||||||
from scipy.signal import find_peaks
|
from scipy.signal import find_peaks
|
||||||
|
|
||||||
cap = cv2.VideoCapture(1) # 使用摄像头0,通常更稳定
|
cap = cv2.VideoCapture(2) # 使用摄像头0,通常更稳定
|
||||||
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # 降低分辨率提高处理速度
|
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # 降低分辨率提高处理速度
|
||||||
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
|
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ while True:
|
|||||||
|
|
||||||
# 直接提取饱和度通道,避免完整HSV转换
|
# 直接提取饱和度通道,避免完整HSV转换
|
||||||
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
|
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
|
||||||
s = hsv[:, :, 1]
|
s = hsv[:, :, 0]
|
||||||
s = s[s > 0] # 只保留非零饱和度值,减少噪声
|
s = s[s > 0] # 只保留非零饱和度值,减少噪声
|
||||||
# 使用更高效的直方图计算
|
# 使用更高效的直方图计算
|
||||||
hist = cv2.calcHist([s], [0], None, [256], [0, 256])
|
hist = cv2.calcHist([s], [0], None, [256], [0, 256])
|
||||||
@ -33,9 +33,9 @@ while True:
|
|||||||
|
|
||||||
# 峰值检测 - 找到直方图中的峰值
|
# 峰值检测 - 找到直方图中的峰值
|
||||||
peaks, properties = find_peaks(hist,
|
peaks, properties = find_peaks(hist,
|
||||||
# height=np.max(hist) * 0.1, # 峰值高度至少是最大值的10%
|
height=np.max(hist) * 0.5, # 峰值高度至少是最大值的10%
|
||||||
distance=5, # 峰值之间的最小距离
|
distance=10, # 峰值之间的最小距离
|
||||||
prominence=np.max(hist) * 0.05) # 峰值的突出度
|
prominence=np.max(hist) * 0.2) # 峰值的突出度
|
||||||
|
|
||||||
# 清除旧数据并绘制新直方图
|
# 清除旧数据并绘制新直方图
|
||||||
ax.clear()
|
ax.clear()
|
||||||
@ -43,6 +43,7 @@ while True:
|
|||||||
|
|
||||||
# 标注峰值
|
# 标注峰值
|
||||||
if len(peaks) > 0:
|
if len(peaks) > 0:
|
||||||
|
print(peaks)
|
||||||
ax.text(0.5, 1.05, f'Found {len(peaks)} peaks')
|
ax.text(0.5, 1.05, f'Found {len(peaks)} peaks')
|
||||||
ax.plot(peaks, hist[peaks], 'ro', markersize=8, label=f'Peaks ({len(peaks)})')
|
ax.plot(peaks, hist[peaks], 'ro', markersize=8, label=f'Peaks ({len(peaks)})')
|
||||||
# 在峰值处添加文字标注
|
# 在峰值处添加文字标注
|
||||||
|
11
test2.py
Normal file
11
test2.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
cap = cv2.VideoCapture(2) # 使用摄像头0,通常更稳定
|
||||||
|
im = cap.read()[1]
|
||||||
|
hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
|
||||||
|
h, s, v = cv2.split(hsv)
|
||||||
|
pink_mask = (((h >= 300/360) | (h <= 20/360)) & (s >= 0.15) & (v >= 0.2))
|
||||||
|
cv2.imshow("Pink Mask", im * pink_mask[:,:,np.newaxis]) # 显示掩码
|
||||||
|
cv2.waitKey(0) # 等待按键
|
||||||
|
cap.release()
|
1
utils.py
1
utils.py
@ -43,6 +43,7 @@ class History:
|
|||||||
self.base = None
|
self.base = None
|
||||||
self._base_time = base_time
|
self._base_time = base_time
|
||||||
self._base_cnt = 0
|
self._base_cnt = 0
|
||||||
|
self.end_history:list[bool] = []
|
||||||
self.display = display
|
self.display = display
|
||||||
self.fig: Any = None
|
self.fig: Any = None
|
||||||
self.ax: Any = None
|
self.ax: Any = None
|
||||||
|
Reference in New Issue
Block a user