from maix import camera, display, image, time, app
from maix.v1.machine import UART
import struct
# 初始化串口
uart = UART("/dev/ttyS0", 115200)
time.sleep_ms(100) # 等待串口就绪
# 摄像头初始化
cam = camera.Camera(320, 240, format=image.Format.FMT_RGB888)
disp = display.Display()
# 颜色阈值(需要根据实际环境校准!)
Threshold_BLACK = [[0, 31, -2, 21, -30, 2]] # 黑色巡线和标记
Threshold_RED = [[14, 71, 1, 85, 4, 45]] # 红色巡线和标记
# ROI区域设置(巡线专用)
ROIS = [
(0, 20, 320, 60, 0.2), # 上
(0, 90, 320, 60, 0.4), # 中
(0, 170, 320, 50, 0.9) # 下
]
weight_sum = sum(r[4] for r in ROIS) # 权重和
# 全局变量
line_rho_black = 0 # 黑色巡线偏移量
line_rho_red = 0 # 红色巡线偏移量
marker_x = 0 # 标记X坐标
marker_y = 0 # 标记Y坐标
marker_type = "" # 标记类型(Black/Red)
marker_detected = False # 标记是否被检测到
debug_mode = True # 调试模式开关
# 巡线函数(仅处理ROI区域的线条)
def track_line(img):
global line_rho_black, line_rho_red
line_rho_black = 0
line_rho_red = 0
black_blobs = []
red_blobs = []
centroid_black = 0
centroid_red = 0
for r in ROIS:
# 黑色巡线检测
blobs_black = img.find_blobs(Threshold_BLACK, roi=r[:4], merge=True, pixels_threshold=150)
if blobs_black:
black_blobs.extend(blobs_black)
centroid_black += sum(b.cx() for b in blobs_black) * r[4]
# 红色巡线检测
blobs_red = img.find_blobs(Threshold_RED, roi=r[:4], merge=True,
pixels_threshold=80, # 巡线通常更细
area_threshold=80)
if blobs_red:
red_blobs.extend(blobs_red)
centroid_red += sum(b.cx() for b in blobs_red) * r[4]
# 计算最终偏移量
if centroid_black > 0:
line_rho_black = int(centroid_black / weight_sum)
if centroid_red > 0:
line_rho_red = int(centroid_red / weight_sum)
# 调试信息
img.draw_string(10, 10, f'Black Line: {line_rho_black}', image.COLOR_WHITE)
img.draw_string(10, 30, f'Red Line: {line_rho_red}', image.COLOR_WHITE)
return black_blobs, red_blobs
# 标记绘制函数(彻底修复文字越界)
def mark_object(img, blob, color, label=""):
# 绘制矩形框
img.draw_rect(blob.x(), blob.y(), blob.w(), blob.h(), color=color, thickness=2)
# 绘制中心十字
img.draw_cross(blob.cx(), blob.cy(), color=color, size=5)
# 计算文字内容和尺寸
text = f"{label}({blob.cx()},{blob.cy()})"
text_width = len(text) * 8 # 估算文字宽度(每个字符约8像素)
text_height = 16 # 估算文字高度
# 严格计算边界(避免任何越界)
max_x = 320 - text_width - 10 # 右侧留10px边距
max_y = 240 - text_height # 下侧留0px边距(文字底部对齐屏幕底)
min_x = 10 # 左侧留10px边距
min_y = 10 # 上侧留10px边距
# 计算安全坐标
x_pos = max(min_x, min(blob.cx() + 10, max_x))
y_pos = max(min_y, min(blob.cy() - 15, max_y))
# 绘制坐标文字
img.draw_string(x_pos, y_pos, text, color=color)
# 标记检测函数(优化红色标记参数 + 换色)
def detect_markers(img):
global marker_x, marker_y, marker_detected, marker_type
marker_detected = False
best_marker = None
# 1. 检测黑色标记(全图范围)
black_markers = img.find_blobs(
Threshold_BLACK,
pixels_threshold=200, # 黑色标记通常更大
area_threshold=200,
merge=True,
roi=(0, 0, 320, 240) # 全图检测
)
# 2. 检测红色标记(降低阈值 + 换显示颜色为黄色)
red_markers = img.find_blobs(
Threshold_RED,
pixels_threshold=150, # 降低阈值,适配小红色标记
area_threshold=150, # 降低阈值
merge=True,
roi=(0, 0, 320, 240) # 全图检测
)
# 3. 合并候选标记并选最大
candidates = []
for blob in black_markers:
candidates.append( (blob, "Black", image.COLOR_BLACK) )
for blob in red_markers:
# 红色标记显示为黄色(区分巡线)
candidates.append( (blob, "Red", image.COLOR_YELLOW) )
if candidates:
# 选面积最大的标记
best_marker = max(candidates, key=lambda x: x[0].area())
marker_blob, marker_type, marker_color = best_marker
# 获取标记坐标
marker_x = marker_blob.cx()
marker_y = marker_blob.cy()
# 标记中心(调用优化后的绘制函数)
mark_object(img, marker_blob, marker_color, f"{marker_type}Marker")
marker_detected = True
print(f"检测到{marker_type}标记: ({marker_x}, {marker_y})")
else:
img.draw_string(10, 150, "未检测到标记", image.COLOR_YELLOW)
# 发送数据
def send_data():
# 限制数据范围
black_data = max(0, min(255, line_rho_black))
red_data = max(0, min(255, line_rho_red))
x_data = max(0, min(255, marker_x))
y_data = max(0, min(255, marker_y))
detected_data = 1 if marker_detected else 0
marker_type_data = 1 if marker_type == "Red" else 0 # 1=红色,0=黑色
# 数据格式: 帧头(FD), 黑线偏移, 红线偏移, X坐标, Y坐标, 检测标志, 标记类型, 帧尾(FE)
data = struct.pack(">BBBBBBBB", 0xFD, black_data, red_data, x_data, y_data,
detected_data, marker_type_data, 0xFE)
try:
uart.write(data)
except Exception as e:
print(f"串口发送错误: {e}")
# 主循环
print("=== 巡线与标记检测系统(红色标记优化版) ===")
print("提示:")
print("1. 红色标记将以黄色显示,区分巡线的红色")
print("2. 降低了红色标记的检测阈值,适配小尺寸标记")
print("3. 严格修复文字越界问题")
while not app.need_exit():
try:
t = time.time_ms()
img = cam.read()
# 巡线处理并获取色块(仅ROI区域)
black_line_blobs, red_line_blobs = track_line(img)
# 标记黑色巡线(白色标签)
for blob in black_line_blobs:
mark_object(img, blob, image.COLOR_WHITE, "BlackLine")
# 标记红色巡线(红色标签,与标记的黄色区分)
for blob in red_line_blobs:
mark_object(img, blob, image.COLOR_RED, "RedLine")
# 检测特殊标记(全图范围)
detect_markers(img)
# 显示统计信息
img.draw_string(10, 50, f'Black Line Blobs: {len(black_line_blobs)}', image.COLOR_WHITE)
img.draw_string(10, 70, f'Red Line Blobs: {len(red_line_blobs)}', image.COLOR_WHITE)
img.draw_string(10, 90,
f'Marker: {marker_type} {"Found" if marker_detected else "Not Found"}',
image.COLOR_WHITE)
# 计算帧率
fps = int(1000 / (time.time_ms() - t))
img.draw_string(10, 110, f'FPS: {fps}', image.COLOR_WHITE)
# 发送数据
send_data()
# 屏幕显示
disp.show(img)
# 打印状态信息
if debug_mode:
print(f"FPS: {fps}, 黑线偏移: {line_rho_black}, 红线偏移: {line_rho_red}")
print(f"标记坐标: X={marker_x}, Y={marker_y}, 类型: {marker_type}")
except Exception as e:
print(f"主循环错误: {e}")
time.sleep_ms(100) # 错误恢复延迟 在这个代码的基础上添加屏幕退出功能