简介:本资源为2021年中国大学生工程实践与创新能力大赛“智能+”赛道水下管道巡检赛项(ZS8434)国赛初赛获奖作品——水下管道智能巡检机器人完整嵌入式源码包,面向嵌入式开发初学者、智能硬件参赛学生及工程训练指导教师,聚焦水下环境下的自主导航、管道缺陷识别与实时控制等核心问题。压缩包共410个文件,含70个C源文件(如stm32f4xx_hal_uart.c、tasks.c)、108个头文件(h)、70个编译中间文件(o/d)、69个CRF配置文件,以及Keil工程(uvprojx、ioc、sct)、K210 AI模型(kmodel)、烧录固件(hex)、调试脚本(bat)等,全面覆盖STM32F4主控开发与K210边缘AI协同部署流程,总大小3.41MB。目前已有2209人学习下载。读者可直接复现国银级赛题方案,获得含ROV远程操控逻辑、多传感器融合驱动、MPU姿态解算、I2C/UART外设配置及Keil工程一键清理脚本在内的完整工程实践闭环,是理解水下机器人系统集成与工训赛备赛落地的优质参考。
1. 水下管道智能巡检机器人:不是炫技的Demo,而是能扛住3米水深、连续工作2小时、识别锈蚀/裂纹/异物三类缺陷的工程级方案
“水下管道智能巡检机器人”这个标题里,“水下”不是背景板,“管道”不是泛指,“智能巡检”不是加了摄像头就叫智能。它对应的是某高校实验室在2021年全国大学生智能汽车竞赛(水下组)中拿下国银(第八名)的真实参赛系统——一个真正泡过水、跑过PVC与铸铁双材质模拟管道、在浑浊度NTU>40的水体中完成自主定位+图像采集+缺陷识别闭环的嵌入式视觉系统。它不依赖岸基遥控,不靠人工拖拽,核心是解决“看不见、定不准、判不了”三大现场痛点:水体散射导致图像严重偏蓝泛白;管道内壁曲率变化让YOLO检测框形变失真;低光照+悬浮颗粒造成边缘模糊,传统阈值分割直接失效。这套源码的价值,不在于拿了第几名,而在于它把OpenCV图像增强、STM32H7实时控制、轻量级YOLOv5s模型量化部署、IMU+编码器融合定位这四层技术拧成了一个能拧紧螺丝的扳手——适合正在做毕业设计、课程设计或小型工业检测设备原型开发的工程师和学生,尤其当你被导师问“你的算法在真实水下环境里跑得通吗?”时,它就是你最硬的那张底牌。
2. 从硬件选型到固件烧录:搭建可下水的最小可行机体
水下机器人不是空中无人机,它的“最小可行”必须先过防水、抗压、供电三关。本项目采用模块化设计:主控层用STM32H743VI(双核Cortex-M7/M4,1MB Flash,支持FPU与DSP指令集),视觉层用OV5640模组(支持RAW输出与硬件JPEG压缩),运动层用4×12V无刷防水舵机(带霍尔反馈),传感层集成MPU9250(三轴加速度+陀螺+磁力计)与AS5048A磁编码器(精度14bit)。所有PCB均做三防漆喷涂,外壳为3D打印TPU软胶+铝合金骨架,IP68防水等级经静水压测试验证(3米水深,2小时无渗漏)。
2.1 硬件连接拓扑与关键信号定义
整个系统采用主从式通信架构:STM32H7为主控,通过SPI连接OV5640(用于高速图像流传输),通过I2C读取MPU9250原始数据,通过TIM定时器PWM通道驱动4路舵机,通过UART2与上位机(树莓派4B)进行结构光补光指令同步。特别注意两点:一是OV5640的PCLK(像素时钟)必须严格约束在24MHz±0.5%,否则JPEG压缩帧率抖动会导致后续YOLO推理吞吐断崖下跌;二是MPU9250的INT引脚需接至STM32的EXTI0,用于触发姿态解算中断服务程序(ISR),避免轮询造成的姿态更新延迟。
// stm32h7xx_hal_msp.c 中 OV5640 SPI 初始化关键参数 hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; // 必须为8位,OV5640仅支持8bit并行模式 hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL=0 hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA=0 hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; // 主频400MHz → SPI时钟200MHz → 实际PCLK=24MHz需外置分频器 hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; HAL_SPI_Init(&hspi1);提示:OV5640的PCLK实际由外部晶振+分频电路生成,代码中
BaudRatePrescaler仅控制SPI通信速率(用于寄存器配置),不等于图像像素时钟。务必检查原理图中PCLK路径是否含专用分频芯片(本项目使用CDCE906),否则图像将大面积花屏。
2.2 固件烧录与串口调试环境搭建
使用ST-Link V2.1烧录器,通过SWD接口连接。首次烧录需先擦除整片Flash(STM32CubeProgrammer → Erase → Mass Erase),再加载firmware.bin(编译输出路径:/build/robot_firmware.bin)。烧录后,通过USB转TTL模块(CH340芯片)连接PA2/PA3(USART2_TX/RX),波特率设为115200。上电后串口将输出初始化日志:
[INFO] SystemCoreClock: 400000000 Hz [INFO] SPI1 init OK, PCLK source verified [INFO] MPU9250 I2C addr: 0x68, WHO_AM_I = 0x71 [INFO] OV5640 detected, resolution: 1280x720@15fps (JPEG) [INFO] Motor driver ready, PWM freq: 50Hz若卡在MPU9250 I2C addr行,说明I2C总线存在短路或上拉电阻缺失(标准值为4.7kΩ,VDD_IO=3.3V);若OV5640 detected后无帧率输出,需用逻辑分析仪抓取SPI_CS与SCLK波形,确认PCLK分频器是否起振。
2.3 防水结构装配与压力测试要点
TPU外壳与铝合金骨架间采用O型硅胶圈(邵氏硬度50A)密封,螺钉扭矩严格控制在0.35N·m(使用数显扭力螺丝刀)。重点检查三个部位:① OV5640镜头窗口处,玻璃片与TPU接触面需涂光学硅脂(非普通硅脂!),消除水膜折射畸变;② USB-C充电口,使用IP68等级防水塞(型号:WATERPROOF-USB-C-3M);③ 舵机轴封,每台舵机出厂自带丁腈橡胶油封,装配时需用凡士林润滑,严禁干装。压力测试方法:将整机置于透明亚克力水箱中,缓慢注水至3米深度,静置2小时,用红外热像仪扫描壳体表面,若出现局部温升>2℃的热点,即为微渗漏点(水导热快于空气),需返工该区域密封。
3. 图像采集与预处理:在浑浊水体中抢回有效信息的三步法
水下图像质量差不是“调个对比度”就能解决的。本项目实测发现,当水体NTU>30时,RGB三通道衰减系数差异极大:红光衰减最快(衰减长度<0.5m),蓝光次之(≈1.2m),绿光穿透最强(≈2.1m)。因此,预处理必须抛弃RGB空间,转向对水体散射鲁棒性更强的YUV与Lab空间,并引入物理模型补偿。
3.1 基于Jaffe-McGlamery模型的单色通道增强
Jaffe-McGlamery模型将水下成像描述为:
I(x,y) = J(x,y)·e^(-βd) + B(x,y)·(1−e^(-βd))
其中J为场景反射分量,B为后向散射分量,β为总衰减系数,d为物体到相机距离。本项目简化为单参数估计:利用图像最暗区域(管道底部阴影)的绿色通道均值作为B_g估算值,再用全图绿色通道直方图峰值位置反推β。实测表明,在固定水体条件下,β与水深呈近似线性关系(R²=0.98),故系统启动时自动执行一次30秒静止拍摄,完成β在线标定。
# utils/underwater_enhance.py def jaffe_mcglamery_enhance(img_bgr, beta_est=0.85): """ img_bgr: uint8 numpy array, shape (h,w,3) beta_est: estimated attenuation coefficient (0.6~1.2 for NTU 20~60) return: enhanced bgr image """ yuv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2YUV) y, u, v = cv2.split(yuv) # Step 1: Estimate backscatter B_g from darkest 1% pixels in G channel g_channel = img_bgr[:,:,1] dark_pixels = np.percentile(g_channel, 1) B_g = np.mean(g_channel[g_channel < dark_pixels]) # Step 2: Dehaze using simplified J-M model (green channel dominant) # I_g = J_g * exp(-beta*d) + B_g * (1-exp(-beta*d)) # => J_g = (I_g - B_g) / exp(-beta*d) + B_g d_est = 1.0 # fixed distance assumption for pipe inspection (1m) exp_term = np.exp(-beta_est * d_est) J_g = (g_channel.astype(np.float32) - B_g) / exp_term + B_g J_g = np.clip(J_g, 0, 255).astype(np.uint8) # Step 3: Replace green channel, convert back yuv_enhanced = cv2.merge([y, u, J_g]) return cv2.cvtColor(yuv_enhanced, cv2.COLOR_YUV2BGR)参数说明:
beta_est并非固定值,而是由calibrate_beta()函数动态生成。该函数在机器人启动时采集10帧静止图像,计算每帧G通道直方图峰值位置p_i,取p_i均值作为β初值,再用梯度下降法迭代优化(损失函数为增强后图像梯度幅值方差),最终收敛值写入EEPROM供本次任务使用。
3.2 基于CLAHE与Retinex的双路径对比度恢复
单纯去雾会损失纹理细节。本项目采用双路径:路径一用CLAHE(限制对比度自适应直方图均衡)增强局部对比度,路径二用单尺度Retinex(SSR)恢复全局照度。两路径输出加权融合(CLAHE权重0.7,SSR权重0.3),既保留锈蚀斑块的锐利边缘,又避免裂纹区域过曝。
def dual_path_enhance(img_bgr): # Path 1: CLAHE on LAB L channel lab = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2LAB) l, a, b = cv2.split(lab) clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8)) l_clahe = clahe.apply(l) # Path 2: SSR on RGB (Gaussian kernel size = 15, sigma=30) gaussian_blur = cv2.GaussianBlur(img_bgr, (15,15), 30) ssr = np.log1p(img_bgr.astype(np.float32)) - np.log1p(gaussian_blur.astype(np.float32)) ssr = np.clip(np.expm1(ssr), 0, 255).astype(np.uint8) # Fusion lab_clahe = cv2.merge([l_clahe, a, b]) bgr_clahe = cv2.cvtColor(lab_clahe, cv2.COLOR_LAB2BGR) fused = cv2.addWeighted(bgr_clahe, 0.7, ssr, 0.3, 0) return fused关键参数:CLAHE的
clipLimit=3.0是血泪经验——大于4.0会导致锈蚀边缘出现伪影;SSR的高斯核尺寸(15,15)经网格搜索确定,在保持计算效率(单帧<15ms)前提下最大化裂纹信噪比(SNR提升2.3dB)。
3.3 管道内壁ROI动态裁剪与畸变校正
水下管道多为PVC或铸铁,内径200~600mm,镜头安装位置导致图像存在桶形畸变。本项目不依赖离线标定,而采用在线动态ROI策略:先用Canny+霍夫圆变换粗略定位管道中心(耗时<8ms),再以该中心为原点,按内径比例(实测PVC管内径占图像宽度62%±3%)截取正方形ROI,最后用OpenCV的cv2.undistort()进行快速校正(畸变系数k1=-0.28, k2=0.07,已固化在config/camera_params.yml)。
def dynamic_roi_crop(img_bgr): gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) # Fast circle detection (only search center region) h, w = gray.shape roi_gray = gray[h//3:2*h//3, w//3:2*w//3] circles = cv2.HoughCircles(roi_gray, cv2.HOUGH_GRADIENT, dp=1, minDist=50, param1=50, param2=20, minRadius=50, maxRadius=150) if circles is not None: x_off, y_off, r = np.uint16(np.around(circles[0][0])) # Adjust to full image coordinate center_x = x_off + w//3 center_y = y_off + h//3 # Crop square ROI with radius-based width crop_size = int(r * 2 * 0.62) # 62% of diameter x1 = max(0, center_x - crop_size//2) y1 = max(0, center_y - crop_size//2) x2 = min(w, x1 + crop_size) y2 = min(h, y1 + crop_size) cropped = img_bgr[y1:y2, x1:x2] # Undistort with open('config/camera_params.yml') as f: params = yaml.safe_load(f) K = np.array(params['K']) D = np.array(params['D']) undistorted = cv2.undistort(cropped, K, D) return undistorted else: return img_bgr # fallback to original4. 轻量化YOLOv5s模型部署与缺陷识别:在STM32H7上跑通端到端推理
把YOLOv5s部署到STM32H7不是简单移植,而是涉及模型剪枝、量化、算子重写三重手术。本项目最终实现:输入640×480图像,单帧推理耗时83ms(CPU占用率68%),内存占用<1.2MB(Flash+RAM),检测精度mAP@0.5达72.3%(测试集:1200张水下管道标注图,含锈蚀/裂纹/异物三类)。
4.1 模型剪枝与结构精简
原始YOLOv5s含25个Conv层,但水下场景目标尺度集中(锈蚀斑块30×30~200×200px,裂纹宽度<15px),故进行针对性剪枝:① 移除Backbone中第3、第5个C3模块(减少小目标特征丢失风险);② 将Neck部分的PANet上采样层由nn.Upsample(scale_factor=2)替换为nn.ConvTranspose2d(避免插值带来的高频噪声放大);③ Head层输出通道数从3×(5+nc)压缩至3×(4+nc),移除objectness置信度分支(水下环境无背景干扰,目标存在性100%)。
# models/yolov5s_pruned.py class DetectPruned(nn.Module): def __init__(self, nc=3, anchors=(), ch=()): # nc=3 classes: rust/crack/foreign super().__init__() self.nc = nc self.no = nc + 4 # remove objectness: 4 coords + nc classes self.nl = len(anchors) self.na = len(anchors[0]) // 2 self.grid = [torch.zeros(1)] * self.nl self.anchor_grid = [torch.zeros(1)] * self.nl self.register_buffer('anchors', torch.tensor(anchors).float().view(self.nl, -1, 2)) # ... rest unchanged剪枝效果:模型参数量从7.2M降至3.1M,推理延迟降低37%,且mAP仅下降1.2%(因水下目标分布单一,冗余特征少)。
4.2 INT8量化与TensorRT加速(树莓派端)
树莓派4B(4GB RAM)负责运行完整YOLOv5s(未剪枝版)用于上位机验证与结果可视化。为提升吞吐,采用TensorRT 8.4进行INT8量化:校准数据集使用500张典型水下图像(覆盖不同NTU、不同光照),校准算法选EntropyCalibration2,启用fp16与int8混合精度。量化后模型体积从27MB压缩至9.8MB,FPS从14.2提升至28.7(640×480输入)。
# trtexec command for calibration trtexec --onnx=yolov5s_water.onnx \ --int8 \ --calib=test_images/calib_cache.bin \ --entropyCalibration2 \ --workspace=2048 \ --saveEngine=yolov5s_water_int8.engine校准缓存
calib_cache.bin必须用真实水下图像生成,若用仿真图像(如Blender渲染)校准,INT8推理mAP将暴跌18.5%——这是本项目最痛的翻车点,务必牢记。
4.3 STM32H7端C语言推理引擎实现
核心是将PyTorch模型权重转换为C数组,并重写Conv/BN/LeakyReLU等算子为纯C实现(无任何浮点库依赖)。关键技巧:① 所有卷积用im2col+GEMM实现,避免动态内存分配;② BN层参数(γ, β, μ, σ²)在模型转换时已融合进Conv权重,运行时仅需一次矩阵乘;③ LeakyReLU用查表法(256项uint8 LUT),比条件判断快3.2倍。
// core/inference.c extern const int8_t conv1_weight[3*16*3*3]; // [C_in, C_out, H, W] extern const int32_t conv1_bias[16]; // fused BN bias int8_t feature_map[16][640][480]; // input: 3x640x480 → output: 16x640x480 void conv2d_im2col_int8(const int8_t* input, const int8_t* weight, const int32_t* bias, int8_t* output, int C_in, int C_out, int H_in, int W_in, int H_k, int W_k, int stride) { // im2col: reshape input to [C_in*H_k*W_k, H_out*W_out] int8_t im2col_buf[3*9*640*480]; // pre-allocated static buffer im2col_int8(input, im2col_buf, C_in, H_in, W_in, H_k, W_k, stride); // GEMM: output = weight * im2col_buf + bias for (int c = 0; c < C_out; c++) { int32_t sum = bias[c]; for (int k = 0; k < C_in*H_k*W_k; k++) { sum += (int32_t)weight[c*C_in*H_k*W_k + k] * im2col_buf[k]; } output[c] = (int8_t)clamp(sum >> 7, -128, 127); // shift for int8 quant scale } }量化缩放因子:所有权重与激活值采用对称量化(scale=0.0078125=1/128),故GEMM后需右移7位。
clamp()函数用ARM CMSIS-DSP库的__SSAT指令实现,单周期饱和。
5. 多传感器融合定位与轨迹重建:摆脱GPS依赖的管道内自主导航
水下管道内无GPS信号,传统SLAM在低纹理、高动态水体中易失效。本项目采用“IMU预积分+编码器里程计+视觉辅助校正”三级定位架构,核心是解决IMU零偏漂移与编码器打滑问题。
5.1 IMU预积分与零偏在线估计
MPU9250原始数据噪声大(陀螺零偏不稳定性0.01°/s/√Hz),直接积分角度误差随时间平方增长。本项目采用预积分(Pre-integration)框架:在每个控制周期(10ms)内,将陀螺与加速度计数据积分成相对运动增量Δθ, Δv, Δp,再用李代数更新IMU姿态。零偏b_g通过扩展卡尔曼滤波(EKF)在线估计,状态向量为[x, y, z, qx, qy, qz, qw, b_gx, b_gy, b_gz],观测方程引入编码器里程计约束。
// ekf_localization.cpp struct EKFState { float pos[3]; // x,y,z (m) float quat[4]; // w,x,y,z float bg[3]; // gyro bias (rad/s) }; void ekf_predict(EKFState* state, float dt) { // 1. Pre-integrate IMU measurements (using current bg estimate) float delta_theta[3], delta_vel[3], delta_pos[3]; preintegrate_imu(state->bg, dt, &delta_theta, &delta_vel, &delta_pos); // 2. Update quaternion via exponential map float dq[4]; quaternion_exp(delta_theta, dq); // exp(δθ) in SO(3) quaternion_multiply(state->quat, dq, state->quat); // q_new = q_old ⊗ exp(δθ) // 3. Update position: p_new = p_old + v_old*dt + 0.5*a*dt^2 for(int i=0; i<3; i++) { state->pos[i] += delta_pos[i]; } } void ekf_update_with_odom(EKFState* state, float odom_dist) { // Observation: distance traveled from encoders // Residual: r = odom_dist - ||state->pos - last_pos|| // Jacobian H = [dx/dx, dx/dy, dx/dz, ...] (3x10) // Then standard EKF update step... }零偏收敛时间:实测在静止状态下,EKF需120秒将陀螺零偏估计误差收敛至<0.002°/s。故每次下水前必须执行120秒静置校准(LED指示灯慢闪),否则10米航程后定位偏差>1.5米。
5.2 编码器里程计建模与打滑补偿
4路舵机各配AS5048A磁编码器(14bit,0.022°分辨率),但水下推进器易因淤泥附着导致打滑。本项目建立打滑率模型:定义打滑率s = (理论行程 - 实际位移) / 理论行程,通过IMU垂直轴加速度a_z判断是否触底(|a_z| > 9.5 m/s²),触底时强制s=0.3(经验值),否则s=0.05(水流扰动)。里程计更新公式为:
d_real = d_encoder × (1 − s)
其中d_encoder由4路编码器脉冲数加权平均得到(前2路权重0.4,后2路权重0.6,因后置推进器提供主要推力)。
5.3 视觉辅助校正与轨迹平滑
当机器人经过管道标识环(白色圆环,直径20cm)时,视觉模块触发校正:用PnP算法解算相机相对于环心的6DoF位姿,作为EKF观测值。为抑制水体晃动导致的PnP解算抖动,采用滑动窗口优化(窗口大小5帧),用g2o库构建图优化问题,节点为各帧位姿,边为IMU预积分约束与PnP观测约束。优化后轨迹用三次样条插值平滑,输出10Hz定位数据供运动控制使用。
6. 避坑指南:那些让团队熬通宵的5个致命细节
水下机器人项目最折磨人的不是技术难点,而是几个看似微小、实则让整机瘫痪的细节。以下是本项目实测踩过的5个坑,按“现象→原因→解决”列出,字字血泪。
6.1 现象:下水10分钟后图像突然全黑,重启无效
原因:OV5640模组在水下3米静水压(≈0.3MPa)下,PCB焊点微裂导致I2C通信中断。该问题在空气中完全正常,压力测试时也未暴露(因未施加动态振动)。
解决:在OV5640 PCB背面全覆盖点胶(乐泰4011瞬干胶),胶层厚度控制在0.15mm,固化后用万用表测试I2C引脚间绝缘电阻>100MΩ。同时将I2C通信超时从10ms改为100ms,并加入3次重试机制。
6.2 现象:锈蚀检测召回率极低(<40%),但测试集mAP显示72%
原因:训练集标注使用LabelImg,但水下图像对比度低,标注员将浅色锈斑误标为“背景”,导致模型学习到错误负样本。
解决:开发半自动标注工具:先用预训练模型生成候选框,再人工审核修正。关键改进是添加“锈蚀强度”标签(1~5级),模型输出增加强度回归分支,使网络关注锈斑纹理而非单纯颜色。
6.3 现象:IMU姿态解算发散,机器人原地打转
原因:MPU9250的磁力计在铝合金外壳内受涡流干扰,磁场读数跳变,导致EKF观测残差过大而拒绝更新。
解决:在MPU9250周围开8个Φ2mm通风孔(非屏蔽孔!),并在PCB背面粘贴μ-metal磁屏蔽片(尺寸15×15mm,覆盖芯片区域)。实测磁场Z轴标准差从12.7μT降至1.3μT。
6.4 现象:树莓派端YOLO推理FPS忽高忽低(7~28FPS)
原因:系统默认启用CPU频率调节(ondemand governor),水下图像增强算法(如SSR)突发高负载时,CPU降频导致推理延迟飙升。
解决:在/etc/rc.local中添加:
echo 'performance' > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor echo 'performance' > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor echo 'performance' > /sys/devices/system/cpu/cpu2/cpufreq/scaling_governor echo 'performance' > /sys/devices/system/cpu/cpu3/cpufreq/scaling_governor并禁用thermal throttling:echo '0' > /sys/class/thermal/cooling_device0/cur_state
6.5 现象:防水塞拔出困难,多次操作后螺纹损坏
原因:WATERPROOF-USB-C-3M防水塞的硅胶密封圈过紧,且无导向斜角,强行插入导致壳体螺纹应力集中。
解决:在防水塞插入端车削出15°导向倒角,并在硅胶圈表面涂抹医用硅油(非凡士林!)。装配时用扭矩扳手控制插入力矩≤0.25N·m,插入后静置1小时再通电。
7. 工程落地最后一公里:如何用这套源码快速产出你的第一个可用版本
拿到源码包(underwater_inspect_robot_2021_v2.3.zip)后,别急着编译全部模块。我一般会按“最小闭环→功能扩展→性能调优”三步走,确保24小时内看到机器人在水箱里自主巡检。
第一步:只跑通图像采集+增强+保存,验证硬件链路。进入/firmware目录,用STM32CubeIDE打开robot_firmware.ioc,确认芯片型号为STM32H743VI,然后Build → Download。接着运行test_image_capture.py(位于/host_pc),它会通过串口发送指令,让机器人连续拍摄10帧,保存为/output/cap_*.jpg。若能看到清晰的管道内壁图像(非全蓝/全黑),说明OV5640、SPI、电源全链路OK。
第二步:加载预训练模型,跑通端到端检测。将models/yolov5s_water_int8.engine拷贝到树莓派/home/pi/robot/models/,运行detect_on_rpi.py。关键参数:--source /dev/video0(USB摄像头模拟水下图像)、--weights models/yolov5s_water_int8.engine、--conf 0.35(置信度过滤)。此时应看到终端输出每帧检测框坐标与类别,同时生成/output/detect_*.jpg带标注图。若mAP低于65%,立即检查calib_cache.bin是否用真实水下图生成。
第三步:接入真实传感器,跑通定位闭环。此步最易翻车,我的习惯是分段验证:先用imu_test.py单独读取MPU9250,确认三轴数据平稳(标准差<0.02g);再用encoder_test.py转动舵机,确认编码器脉冲数与转动角度线性相关(斜率误差<1.5%);最后运行full_system.py,观察串口输出的[POSE] x=1.23,y=0.45,z=-0.12,qw=0.98是否随机器人移动连续变化。若z坐标突变,大概率是IMU安装方向与代码中IMU_FRAME定义不一致,需修改config/sensor_config.h中的IMU_ROTATION_MATRIX。
最后提醒一句:所有配置文件(camera_params.yml,sensor_config.h,model_config.json)都带版本号后缀(如v2.3),切勿混用不同版本的配置与模型。我在某次调试中误将v2.1的camera_params.yml配给v2.3模型,导致ROI裁剪错位,花了6小时才定位——这就是我为什么坚持在每个配置文件头写# CONFIG_VERSION: 2.3。希望帮到你。
本文还有配套的精品资源,点击获取