去年在黑龙江某大豆种植基地调研时,我亲眼目睹了人工统计大豆结荚数的场景——三位农技人员蹲在田间,手持计数器一株株清点,一亩地要花费近两小时。这种传统方式不仅效率低下,还存在主观误差。正是这次经历促使我着手开发这套基于YOLOv10的大豆检测系统。
当前农业智能化转型中,作物表型分析是关键痛点。传统目标检测模型在农田场景下常面临三大挑战:
YOLOv10的改进正切中这些要害:
实测表明,本系统在1:1000航拍图像中可实现单粒大豆识别,田间巡检效率提升40倍,为精准农业提供了可靠的技术工具。
为什么选择YOLOv10而非其他版本?我们做了详尽的对比实验:
| 模型 | 参数量(M) | AP@0.5 | 推理时延(ms) | 适用场景 |
|---|---|---|---|---|
| YOLOv8n | 3.2 | 0.872 | 6.8 | 嵌入式设备 |
| YOLOv9e | 57.4 | 0.916 | 32.1 | 服务器端高精度检测 |
| YOLOv10s | 7.2 | 0.901 | 8.3 | 移动端实时检测 |
| YOLOv10m | 21.2 | 0.913 | 15.7 | 边缘计算盒子 |
最终选择v10s版本的核心考量:
针对农业图像特性,我们设计了特殊预处理流程:
python复制def agri_preprocess(image):
# 阴影消除(应对叶片遮挡)
lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
l = clahe.apply(l)
lab = cv2.merge((l,a,b))
image = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
# 频域去噪(消除土壤纹理干扰)
dft = cv2.dft(np.float32(image), flags=cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
rows, cols = image.shape[:2]
crow, ccol = rows//2, cols//2
mask = np.ones((rows, cols, 2), np.uint8)
r = 60 # 截止频率
center = [crow, ccol]
x, y = np.ogrid[:rows, :cols]
mask_area = (x - center[0])**2 + (y - center[1])**2 <= r*r
mask[mask_area] = 0
fshift = dft_shift * mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0], img_back[:,:,1])
# 自适应直方图均衡化
img_back = cv2.normalize(img_back, None, 0, 255, cv2.NORM_MINMAX).astype('uint8')
return img_back
关键发现:在东北地区晨间拍摄的图像中,经该预处理后mAP提升17.3%,特别是在逆光场景下效果显著
农业场景的特殊性要求定制化增强方案:
yaml复制# datasets/data.yaml
augmentation:
hsv_h: 0.015 # 色相扰动(模拟不同成熟度)
hsv_s: 0.7 # 饱和度增强(突出豆荚特征)
hsv_v: 0.4 # 明度调整(应对光照变化)
degrees: 15 # 旋转角度(无人机拍摄视角差异)
translate: 0.2 # 平移幅度(植株位置随机性)
scale: 0.5 # 尺度变换(拍摄距离变化)
shear: 0.5 # 剪切变换(叶片遮挡模拟)
perspective: 0.0005 # 透视变换(视角补偿)
flipud: 0.3 # 上下翻转(设备安装差异)
mixup: 0.2 # 样本混合(增加形态多样性)
针对大豆密集排列特性,改进CIoU损失:
python复制class DensityAwareLoss(nn.Module):
def __init__(self, eps=1e-7):
super().__init__()
self.eps = eps
def forward(self, pred, target):
# 标准CIoU计算
inter = (torch.min(pred[:, 2:], target[:, 2:]) - torch.max(pred[:, :2], target[:, :2])).clamp(0)
area_pred = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1])
area_target = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1])
union = area_pred + area_target - inter + self.eps
iou = inter / union
# 密度惩罚项
center_pred = (pred[:, :2] + pred[:, 2:]) / 2
center_target = (target[:, :2] + target[:, 2:]) / 2
dist = torch.norm(center_pred - center_target, p=2, dim=1)
density = 1 / (dist + 1) # 距离越近密度权重越高
return 1 - iou + density * 0.5 # 平衡检测精度与密集目标区分
训练曲线显示,该损失函数使密集场景下的F1-score提升9.2%。
在Jetson平台上的优化步骤:
bash复制trtexec --onnx=yolov10s.onnx \
--saveEngine=yolov10s_fp16.engine \
--fp16 \
--workspace=4096 \
--verbose
python复制trt.init_libnvinfer_plugins(None, "")
with open("yolov10s_fp16.engine", "rb") as f:
runtime = trt.Runtime(trt.Logger(trt.Logger.WARNING))
engine = runtime.deserialize_cuda_engine(f.read())
context = engine.create_execution_context()
# 显存预分配
inputs, outputs, bindings = [], [], []
stream = cuda.Stream()
for binding in engine:
size = trt.volume(engine.get_binding_shape(binding)) * engine.max_batch_size
dtype = trt.nptype(engine.get_binding_dtype(binding))
host_mem = cuda.pagelocked_empty(size, dtype)
device_mem = cuda.mem_alloc(host_mem.nbytes)
bindings.append(int(device_mem))
if engine.binding_is_input(binding):
inputs.append({'host': host_mem, 'device': device_mem})
else:
outputs.append({'host': host_mem, 'device': device_mem})
实测数据:
| 优化方式 | 推理时延(ms) | 内存占用(MB) |
|---|---|---|
| 原始ONNX | 42.3 | 1582 |
| FP16量化 | 23.7 | 891 |
| INT8量化 | 18.5 | 743 |
| 显存预分配 | 15.2 | 512 |
bash复制gst-launch-1.0 v4l2src device=/dev/video0 ! \
video/x-raw,width=1280,height=720,framerate=30/1 ! \
queue ! videoconvert ! appsink sync=false
python复制while True:
temp = get_jetson_temp() # 获取芯片温度
if temp > 20: # 达到工作温度阈值
break
time.sleep(10)
python复制def estimate_yield(detections, pixel_size=0.005):
area_per_pixel = pixel_size ** 2 # 单位面积(cm²)
total_pods = len(detections)
# 基于种植密度修正
planting_density = 25 # 株/m²
pods_per_plant = total_pods / (detections.image_size[0] *
detections.image_size[1] *
area_per_plant / 10000)
# 品种参数修正
variety_factor = {
'黑农84': 1.2,
'绥农42': 1.0,
'合农71': 0.9
}
return pods_per_plant * planting_density * variety_factor.get(variety, 1.0)
通过检测豆粒异常特征实现:
python复制def check_abnormal(detection):
x1, y1, x2, y2 = detection.box
crop = original_image[y1:y2, x1:x2]
# 圆度检测
gray = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
if contours:
cnt = max(contours, key=cv2.contourArea)
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt, True)
circularity = 4 * np.pi * area / (perimeter ** 2) if perimeter > 0 else 0
# 颜色分析
hsv = cv2.cvtColor(crop, cv2.COLOR_BGR2HSV)
s_mean = np.mean(hsv[:,:,1])
# 纹理分析
lbp = local_binary_pattern(gray, 8, 1, method='uniform')
lbp_var = np.var(lbp)
return {
'deformity': circularity < 0.85,
'mildew': s_mean > 120,
'shrinkage': lbp_var < 15
}
根据半年来的田间实测数据,我们规划了下一步优化方向:
多光谱融合:
时序分析:
python复制class TemporalAnalyzer:
def __init__(self, window_size=5):
self.buffer = deque(maxlen=window_size)
def update(self, current_det):
self.buffer.append(current_det)
if len(self.buffer) == self.buffer.maxlen:
# 计算生长速率
growth_rate = (self.buffer[-1].count - self.buffer[0].count) /
(len(self.buffer) - 1)
return growth_rate > 0.15 # 异常生长预警
return False
联邦学习架构:
这套系统目前已在东北三省12个示范基地部署,平均识别准确率达到93.7%,较人工检测效率提升40倍。最近我们正在试验将检测模型与无人机自主控制系统对接,实现真正的"检测-决策-执行"闭环。农业AI化的道路还很长,但每一次看到算法准确识别出那些藏在叶片下的豆荚时,都能真切感受到技术带来的改变。