人口老龄化与劳动力短缺正严重制约水果种植业发展,智能采摘机器人成为破解“谁来种地、谁来采收”困局的关键。而机器人能否精准、无损地完成采摘,核心在于对果实采摘点的视觉定位能力——这要求算法不仅要检测果实,还需精确识别果梗等关键部位。本文深入解析一个专为水果采摘机器人设计的视觉感知数据集,包含937张图像、标注2,500个果实及3个关键特征点,并提供基于YOLO-Pose v11在ROS环境下的训练与部署代码,助力精准农业与机器人自动化。

| 数据集名称 | |
| 总图像量 | 937张 |
| 标注目标数 | 约2,500个 |
| 水果种类 | |
| 标注格式 | |
| 关键点定义 | |
| 任务类型 | |
| 配套资源 | |
| 核心应用 |

该数据集的最大特色在于关键点的任务导向设计:
通过检测这三点,可推导出果实的姿态、成熟度(通过大小)以及最优剪切角度,为机械臂规划提供充分视觉信息。
数据集包含苹果、橘子、草莓等形态、大小、颜色差异显著的水果,对模型的跨物种通用性和细粒度特征提取能力提出高要求。训练出的模型有望在不同果园场景中部署。
937张图像、2,500个实例的规模属于中小型专用数据集,但结合迁移学习足以训练出可用的采摘点检测模型。其配套的ROS推理模块表明该数据集天然面向机器人实际部署,而非仅学术研究。

以下提供基于Ultralytics YOLO-Pose v11的训练、评估与ROS接口核心代码,并演示如何调整OKS权重以匹配采摘点精度需求。

pip install ultralytics rospy cv-bridge sensor-msgs # ROS环境需额外安装
假设数据标注为YOLO-Pose格式(class_id x_center y_center width height kp1_x kp1_y kp1_v kp2_x kp2_y kp2_v ...),关键点顺序为:底部、顶部、采摘点。
创建数据集配置文件 fruit_picking.yaml:
# ============================================================
# 场景:水果采摘点关键点检测 - YOLO-Pose配置
# 关键:定义3个关键点及其可见性,调整OKS权重
# 经验:采摘点(索引2)权重最高,以优化采摘精度
# ============================================================
path: ./fruit_picking_dataset # 数据集根目录
train: images/train
val: images/val
test: images/test # 可选
nc:1# 类别数 (此处假设为'fruit'一类,可细分)
names:['fruit']
# 关键点定义 (YOLO-Pose要求)
kpt_shape:[3,3]# [num_kpts, num_dims] -> 3个关键点,每个有x,y,visible
# OKS权重: 用于计算关键点相似度 (必须与kpt_shape对应)
# 底部:0.05, 顶部:0.05, 采摘点:0.1 (突出其重要性)
kpt_weights:[0.05,0.05,0.1]
# ============================================================
# 场景:水果采摘点检测 - YOLO-Pose v11训练
# 策略:使用COCO预训练的pose模型,冻结骨干网络前几层
# 调优:重点关注关键点损失,调整OKS权重已体现在配置中
# ============================================================
from ultralytics import YOLO
import wandb
wandb.init(project="fruit-picking-pose", name="yolov11-pose-finetune")
# 加载预训练的YOLO-Pose模型 (v11)
model = YOLO('yolo11n-pose.pt')# 或 yolo11s-pose.pt
# 训练参数
results = model.train(
data='fruit_picking.yaml',
epochs=120,
imgsz=640,
batch=12,
device=0,
workers=4,
optimizer='AdamW',
lr0=0.0005,# 较低学习率,保护预训练关键点特征
lrf=0.01,
momentum=0.937,
weight_decay=0.0005,
warmup_epochs=3,
# ----- 关键点损失权重 (在ultralytics中通过loss配置) -----
# 在YOLOv11中,关键点损失(pose loss)权重可通过配置文件调整,此处为示意
# 实际可在模型yaml中修改 loss_kpt 参数
box=7.5,# 框损失增益
cls=0.5,# 分类损失
dfl=1.5,# DFL损失
pose=12.0,# 关键点损失增益 (可调大,突出关键点学习)
kpt_obj=1.0,# 关键点目标损失
# ----- 数据增强 (对关键点敏感) -----
mosaic=1.0,
mixup=0.1,
copy_paste=0.0,# 复制粘贴可能破坏关键点语义,关闭或降低
hsv_h=0.015, hsv_s=0.7, hsv_v=0.4,
degrees=10,# 小角度旋转
scale=0.5,
fliplr=0.5,# 水平翻转 (需确认是否改变关键点顺序)
project='runs/train',
name='fruit_pose_exp',
exist_ok=True
)
print(f"最终Pose mAP@0.5: {results.results_dict['metrics/pose_mAP_0.5']:.4f}")
# ============================================================
# 场景:评估模型在验证集上的关键点精度
# 关注:采摘点(第3个关键点)的OKS和平均误差
# ============================================================
from ultralytics import YOLO
import numpy as np
model = YOLO('runs/train/fruit_pose_exp/weights/best.pt')
# 验证并获取详细指标
metrics = model.val(data='fruit_picking.yaml', split='val', plots=True)
# 解析关键点指标 (假设输出中包含)
# 手动计算采摘点平均欧氏距离误差 (需要真实和预测坐标)
# 此处为示意框架
defcalc_kpt_error(results, kpt_index=2):
"""计算指定关键点(索引)的归一化欧氏距离误差"""
total_error =0
count =0
for result in results:
if result.keypoints isnotNone:
# 获取预测关键点 (xy坐标)
pred_kpts = result.keypoints.xy[0].cpu().numpy()# [3, 2]
# 此处需从数据集中获取真实关键点,简化省略
# error = np.linalg.norm(pred_kpts[kpt_index] - gt_kpts[kpt_index])
# total_error += error
count +=1
avg_error = total_error / count if count else0
print(f"采摘点平均归一化误差: {avg_error:.4f}")
return avg_error
# 注意:实际需配合数据集真实标注计算,这里仅演示结构
#!/usr/bin/env python3
# ============================================================
# 场景:ROS节点,订阅摄像头图像,发布采摘点坐标
# 功能:加载训练好的YOLO-Pose模型,执行推理,提取采摘点
# 消息:发布采摘点的3D坐标 (相机坐标系) 用于机械臂规划
# ============================================================
import rospy
import cv2
import torch
import numpy as np
from sensor_msgs.msg import Image
from geometry_msgs.msg import PointStamped
from cv_bridge import CvBridge
from ultralytics import YOLO
classFruitPickingNode:
def__init__(self):
rospy.init_node('fruit_picking_pose', anonymous=True)
self.bridge = CvBridge()
# 加载模型 (使用训练好的result2.pt)
self.model = YOLO('path/to/result2.pt')# 替换为实际权重路径
# 发布采摘点 (相机坐标系)
self.pub_pick = rospy.Publisher('/fruit/picking_point', PointStamped, queue_size=10)
# 订阅图像话题 (根据实际相机调整)
self.sub_img = rospy.Subscriber('/camera/rgb/image_raw', Image, self.image_callback)
rospy.loginfo("YOLO-Pose 采摘节点已启动,等待图像...")
defimage_callback(self, msg):
try:
cv_img = self.bridge.imgmsg_to_cv2(msg,'bgr8')
except Exception as e:
rospy.logerr(f"图像转换失败: {e}")
return
# 执行推理 (指定关键点置信度阈值)
results = self.model(cv_img, conf=0.25, iou=0.45, kpt_conf=0.3)
# 处理检测结果
for result in results:
if result.keypoints isnotNone:
# 提取置信度最高的目标 (或根据需求选择)
kpts = result.keypoints.xy[0].cpu().numpy()# [3, 2]
confs = result.keypoints.conf[0].cpu().numpy()if result.keypoints.conf isnotNoneelse[1,1,1]
# 找到采摘点 (索引2)
pick_point = kpts[2]# (x, y) 像素坐标
pick_conf = confs[2]
if pick_conf >0.5:# 置信度过滤
# 像素坐标转相机坐标系 (需相机内参和深度信息)
# 此处仅发布像素坐标示例,实际应使用深度图或双目视差计算3D点
point_msg = PointStamped()
point_msg.header = msg.header
point_msg.point.x = pick_point[0]
point_msg.point.y = pick_point[1]
point_msg.point.z =0.0# 假设深度已知或由其他方式获取
self.pub_pick.publish(point_msg)
rospy.loginfo(f"发布采摘点: ({pick_point[0]:.1f}, {pick_point[1]:.1f})")
# (可选) 可视化并发布带关键点的图像
annotated_frame = results[0].plot()
annotated_msg = self.bridge.cv2_to_imgmsg(annotated_frame,'bgr8')
# pub_img.publish(annotated_msg) # 若定义了发布者
if __name__ =='__main__':
try:
node = FruitPickingNode()
rospy.spin()
except rospy.ROSInterruptException:
pass
kpt_weights中给予采摘点(索引2)更高权重(如0.1),模型在训练时会更侧重于优化该点的定位误差。pose(关键点总损失)和kpt_obj(关键点目标损失)的增益。建议从小数据集开始网格搜索最佳比例。PointStamped消息供moveit等规划模块使用。#农业机器人 #水果采摘 #YOLOPose #关键点检测 #ROS #智慧农业 #自动采收 #精准农业 #计算机视觉 #机器人控制