引言在上一篇方案设计中我们分享了“智渔哨兵”仿生机械鱼的硬件选型与蓝图。本篇将正式进入代码实战阶段。为了降低开发门槛并提高系统集成度我们选用Python作为核心编程语言AI 大脑端Jetson/树莓派利用Ultralytics YOLOv8运行水下目标检测。主从通信端利用pyserial库将 AI 决策转化为控制指令通过串口下发给底层控制器STM32驱动柔性鱼尾摆动与胸鳍修正。本文将公开一套完整的、可直接运行的 Python 核心框架代码包含视频流目标检测与串口控制闭环。1. 环境准备与依赖安装在边缘端开发板如树莓派 5、Jetson Nano上首先安装必要的 Python 依赖库pip install ultralytics pyserial opencv-python2. 核心 Python 实现AI 识别与串口控制闭环以下是核心控制主程序fish_commander.py。代码实现了读取摄像头 - YOLO 目标检测 - 计算鱼群中心偏差 - 串口发送控制指令的完整闭环。import cv2 import serial import time import json from ultralytics import YOLO # # 1. 串口配置 (用于与底层STM32通信) # try: # 根据实际开发板修改串口号Linux下通常为 /dev/ttyUSB0 或 /dev/ttyTHS1 ser serial.Serial( port/dev/ttyUSB0, baudrate115200, timeout0.1 ) print(成功连接到底层 STM32 控制器) except Exception as e: print(f串口连接失败: {e}。将以『无硬件模式』运行。) ser None def send_control_command(action, intensity): 通过串口向STM32发送控制指令 (JSON格式便于拓展) :param action: FORWARD (前进), LEFT (左转), RIGHT (右转), STOP (停止) :param intensity: 动作强度 (0-100), 对应鱼尾摆幅或频率 if ser and ser.is_open: payload { action: action, intensity: int(intensity), timestamp: int(time.time()) } # 将字典转换为标准JSON字符串并添加换行符 cmd_str json.dumps(payload) \n ser.write(cmd_str.encode(utf-8)) print(f 已发送指令: {cmd_str.strip()}) # # 2. YOLO 初始化 (加载轻量化模型) # # 初次运行会自动下载 yolov8n.pt (可自行训练针对水下鱼类的权重模型) print(正在加载 YOLOv8 视觉追踪模型...) model YOLO(yolov8n.pt) # # 3. 视频流采集与控制闭环 # # 打开摄像头 (0 通常为内置或USB摄像头若使用CSI摄像头请配置GStreamer管道) cap cv2.VideoCapture(0) if not cap.isOpened(): print(无法打开视频流请检查摄像头连接) exit() # 获取画面尺寸用于计算偏差 frame_width int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) screen_center_x frame_width // 2 print(f视频流初始化成功分辨率宽度: {frame_width}, 画面中心点: {screen_center_x}) try: while True: ret, frame cap.read() if not ret: print(未能获取到当前帧) break # 运行 YOLOv8 目标检测 (仅检测置信度大于 0.4 的目标) results model(frame, conf0.4, verboseFalse) target_detected False largest_box_area 0 best_target_x screen_center_x # 解析检测结果 for result in results: boxes result.boxes for box in boxes: # 获取类别 ID我们假设训练的模型中鱼的类目或使用COCO的默认目标 class_id int(box.cls[0]) class_name model.names[class_id] # 这里以 COCO 模型的 person 或自定义的 fish 为例进行追踪测试 # 如果你训练了专门的鱼类模型请将条件改为你的目标类名 if class_name in [person, fish, animal]: target_detected True # 获取边界框坐标 [xmin, ymin, xmax, ymax] x1, y1, x2, y2 map(int, box.xyxy[0]) # 计算目标中心点与目标面积 target_x (x1 x2) // 2 target_area (x2 - x1) * (y2 - y1) # 策略优先追踪画面中最大的目标通常代表距离机械鱼最近 if target_area largest_box_area: largest_box_area target_area best_target_x target_x # 在画面上绘制识别框 cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, f{class_name}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) # # 4. 追随决策算法 (基于位置偏差产生控制量) # if target_detected: # 计算目标与画面中心的水平像素偏差 error_x best_target_x - screen_center_x dead_zone 50 # 容死区偏差在50像素以内视为正对目标 # 计算转向强度 (将偏差等比例映射到 30-100 的控制强度) mapped_intensity min(max(abs(error_x) / (frame_width / 2) * 100, 30), 100) if error_x dead_zone: # 目标在右边 - 机械鱼右转修正 send_control_command(RIGHT, mapped_intensity) elif error_x -dead_zone: # 目标在左边 - 机械鱼左转修正 send_control_command(LEFT, mapped_intensity) else: # 目标在正前方 - 保持高频鱼尾摆动全力前进 send_control_command(FORWARD, 80) else: # 视野内未发现目标 - 减速停滞或执行默认原地巡航寻敌逻辑 send_control_command(STOP, 0) # 实时显示画面 cv2.imshow(Smart Fish Vision, frame) # 按 q 键退出循环 if cv2.waitKey(1) 0xFF ord(q): break finally: # 释放资源 cap.release() cv2.destroyAllWindows() if ser and ser.is_open: ser.close() print(程序安全退出串口已关闭)3. 底层通信协议设计数据互通标准为了配合上述 Python 代码底层 STM32使用 C/C需要能够解析通过串口接收到的 JSON 数据。数据的控制逻辑设计如下动作FORWARD—— STM32 控制柔性鱼尾进行对称的高频摆动胸鳍保持水平稳定。动作LEFT/RIGHT—— STM32 控制鱼尾的摆动中心线向一侧偏置偏置摆动或者单侧胸鳍协同拨水实现快速水下转向。强度intensity(0-100)—— 直接映射为底层驱动舵机或无刷电机的 PWM 占空比数值越大鱼尾摆动频率越高游动越快。4. 代码迭代与进阶攻坚方向这份开源框架搭建起了“眼睛”到“身体”的桥梁。为进一步完善项目后续将在 Python 端加入以下两个进阶模块引入多目标追踪DeepSORT / ByteTRACK为什么要加当前的 YOLO 代码仅做“逐帧检测”。在实际鱼群密集的养殖网箱中不加跟踪会导致控制指令频繁在多个目标间切换引起机械鱼“左右疯狂摆动”。如何升级使用model.track(frame, persistTrue)代替model(frame)为每条鱼绑定固定 ID。加入多模态传感器数据融合如何扩展利用 Python 的threading多线程库在一个独立线程中实时通过串口或 I2C 读取 BNO055 航向角IMU以及水听器声学数据。在主控制回路中将视觉偏差与航向角偏差进行加权融合使得鱼体在浑浊水域也能闭环游动。结语欢迎在评论区交流探讨在水下折射率不同、光线昏暗的环境下你认为通过调整 YOLO 的置信度门限conf还是提前做一轮水下图像去雾预处理更能提升目标追踪的稳定性欢迎留下你的看法。