19f0388848471.png

项目来源:第三届3D视觉创新应用竞赛 项目名称:利用深度相机实现对校园流浪动物的管理 团队/作者:魏丙正,杭州电子科技大学 相机型号:Astra Pro 支持设备:STM32、树莓派4B、xiaoche 四轮移动机器人 源代码链接:https://github.com/3DCVdeveloper/RobotV_Campus-stray-animals

1. 项目一句话

这不是一台“会拍照的小车”,而是一套面向校园流浪猫狗管理的移动式3D视觉系统:它让机器人在巡逻中发现动物、判断猫狗类别、结合深度信息估计空间位置,并为后续的追踪、投喂、建档和健康管理提供数据入口。

可以把它理解为一个会移动的“校园动物管理节点”:

识别目标 → 感知距离 → 规划路线 → 靠近观察 → 建立档案 → 持续追踪

2. 为什么校园需要这样的机器人?

校园流浪动物通常活动范围大、出现时间不固定、个体特征相似,传统人工巡查很难做到连续记录。项目背景中提到的问题主要集中在三个方面:

19f06c4831fb3.png

项目的核心价值并不是“驱赶动物”,而是把不可预测的流浪动物活动变成可记录、可识别、可干预的数据化管理过程。对校园管理者来说,它提供的是巡逻、发现、识别、追踪、投喂和健康档案的闭环能力;对动物本身来说,它也为疫苗接种、救助和绝育等工作提供更稳定的基础信息。

3. 系统总体架构:一台小车,三层智能

项目搭建了一台四轮移动机器人,硬件上采用“运动控制 + 信息处理 + 3D感知”的分层结构。

19f06c7117713.png

在工作时,雷达负责提供环境与障碍物信息,树莓派进行信息处理后把运动意图传递给STM32;Astra Pro深度相机则负责采集动物的外观特征和空间信息,用于识别、建档、避障和追踪。

4. 软件算法:先看懂数据怎么流,再看懂算法怎么配合

这个项目不是把几个算法简单堆在一起,而是形成了一条完整的数据链路:传感器采集环境,树莓派进行视觉和导航计算,STM32执行运动控制,最后把识别结果沉淀为动物档案。

为了便于理解,可以先把系统抽象成下面这条主线:

19f06ca53e54a.png

这条链路里,每个算法都有非常清晰的位置:

模块 输入 输出 解决的问题
FastSLAM 雷达观测、里程计信息 机器人位姿、环境地图 我在哪里,周围环境长什么样
A* 地图、起点、目标点 全局路径 应该从哪条路线接近目标
DWA 当前速度、障碍物、局部目标 线速度、角速度 眼前怎么走才安全
YOLOv7 RGB图像 类别、置信度、检测框 画面里有没有猫狗
深度融合 检测框、深度图 目标距离、空间位置 猫狗离机器人有多远
MedianFlow 初始目标框、连续视频帧 连续目标轨迹 目标移动后如何持续跟踪

4.1 感知层:RGB-D相机与雷达分别负责什么?

Astra Pro提供RGB图像和深度图。RGB图像适合做“看清楚是什么”,深度图适合做“判断离我多远”。雷达则更适合做环境结构感知,例如墙体、路沿、树木、行人等障碍物。

一个简化后的传感器数据结构可以这样理解:

sensor_frame:
  rgb_image: "640x480 BGR/RGB image"
  depth_map: "640x480 depth value in millimeters"
  lidar_scan:
    angle_min: -135
    angle_max: 135
    ranges: [1.20, 1.18, 1.25, "..."]
  timestamp: "2026-06-26T10:00:00"

其中,RGB图像进入YOLOv7做目标检测;深度图和检测框结合,计算目标距离;雷达数据进入建图与避障模块。三类信息分工明确,最后在树莓派侧融合。

4.2 定位建图:FastSLAM回答“机器人在哪里”

FastSLAM用于移动机器人定位与地图构建。它可以理解为“带着不确定性的地图绘制”:机器人一边移动,一边根据雷达观测和运动估计更新自己的位姿,同时维护校园环境地图。

理解性伪代码如下:

particles = init_particles(robot_start_pose)

while robot_is_running:
    odom = read_wheel_odometry()
    scan = read_lidar_scan()

    # 1. 根据运动模型预测机器人可能到达的位置
    particles = motion_update(particles, odom)

    # 2. 用雷达观测修正每个粒子的可信度
    particles = measurement_update(particles, scan)

    # 3. 重采样,保留更可信的位置假设
    particles = resample(particles)

    # 4. 输出当前最可信位姿与地图
    pose, map_grid = estimate_pose_and_map(particles)

校园环境的难点在于道路、草地、食堂门口、教学楼走廊等区域结构差异大,机器人不能只依赖固定路线。FastSLAM让小车在巡逻过程中持续更新“我在哪”和“周围有什么”,为后续路径规划提供基础。

**4.3 路径规划:A*负责大路线,DWA负责小动作**

A算法负责全局路径规划。它在地图上综合考虑起点、终点和路径代价,寻找一条整体较优路线。例如机器人从食堂门口移动到草坪投喂点时,A会给出一条大方向正确的路线。

def astar_plan(grid_map, start, goal):
    open_set = PriorityQueue()
    open_set.put(start, priority=0)

    while not open_set.empty():
        current = open_set.get()
        if current == goal:
            return rebuild_path(current)

        for next_cell in neighbors(current):
            cost = path_cost(current, next_cell) + heuristic(next_cell, goal)
            open_set.put(next_cell, priority=cost)

DWA动态窗口法负责局部避障。它不会重新规划整条路线,而是在机器人当前速度能力范围内,枚举多组可行速度,选择“更接近目标、离障碍物更远、运动更平滑”的那一组。

def dwa_select_velocity(current_pose, current_velocity, local_goal, obstacles):
    candidates = sample_velocity_window(current_velocity)
    best_score = -float("inf")
    best_cmd = (0.0, 0.0)

    for v, w in candidates:
        trajectory = simulate_motion(current_pose, v, w)
        score = (
            goal_score(trajectory, local_goal)
            + clearance_score(trajectory, obstacles)
            + smoothness_score(v, w)
        )
        if score > best_score:
            best_score = score
            best_cmd = (v, w)

    return best_cmd

两者的关系可以概括为:A*告诉机器人“往哪个方向走”,DWA告诉机器人“下一秒怎么安全地走”。

4.4 控制执行:树莓派给决策,STM32管动作

树莓派更适合运行视觉识别、路径规划、数据处理等计算任务;STM32更适合做稳定的底层控制,例如电机速度、转向、PWM输出、串口通信等。

树莓派发送给STM32的运动指令可以抽象成下面这种结构:

{
  "cmd": "move",
  "linear_velocity": 0.25,
  "angular_velocity": -0.12,
  "target_mode": "follow_animal",
  "stop_distance_m": 1.5
}

当深度相机判断动物距离过近,或者雷达检测到前方障碍物时,上层逻辑可以把速度降为0,STM32立即执行停车或转向。

5. 识别关键:YOLOv7不只是“分类”,而是给机器人一个可行动的目标

19f06cc81cf0d.png

项目目标识别部分采用YOLOv7。YOLO系列属于典型的一阶段目标检测算法,它把“目标在哪里”和“目标是什么”放在一次网络推理中完成,因此速度快,适合实时系统。

对于移动机器人来说,实时性不是锦上添花,而是基本要求。动物会移动,机器人也在移动,如果识别速度太慢,检测框对应的位置就会滞后,后续追踪和导航都会被影响。

本地代码文件中保留了完整的YOLOv7工程,包括:

• detect.py:图像、视频、摄像头目标检测入口

• video.py:面向双路画面/深度视觉场景的检测显示逻辑

• yolov7.pt、traced_model.pt:模型权重文件

• data/coco.yaml:COCO类别配置,其中包含cat、dog等动物类别

• runs/detect/:多次检测实验输出的视频与图片结果

5.1 训练数据:从“图片”变成“检测样本”

项目方案中提到训练集来自Kaggle猫狗识别数据集:下载约2000张图片进行标注,并划分出训练集与验证集。标注工具使用labelImg,将动物位置转换为目标检测所需的边界框数据。

目标检测数据和普通分类数据的差别在于:它不仅需要知道“这张图里有猫”,还要知道“猫在图中的哪个位置”。一个典型的YOLO标注行可以这样理解:

class_id center_x center_y width height
0        0.512    0.438    0.260 0.330

其中坐标是归一化后的比例值,不是原始像素值。假设0代表cat,1代表dog,那么数据配置可以抽象为:

train: datasets/cat_dog/images/train
val: datasets/cat_dog/images/val

nc: 2
names:
  - cat
  - dog

本地工程中的data/coco.yaml仍保留COCO 80类配置,其中已经包含cat、dog。若要进一步面向校园流浪动物定制模型,可以把类别收敛到猫、狗及少量校园相关干扰类别,从而降低误检风险。

5.2 YOLOv7结构:Input、Backbone、Head分别做什么

YOLOv7可按三个部分理解:

结构 作用 在本项目中的意义
Input 输入图像并进行尺寸适配、归一化等处理 接收Astra Pro或摄像头画面
Backbone 提取多层视觉特征 捕捉猫狗的轮廓、纹理、姿态等特征
Head 输出类别、置信度和检测框 给出“这是猫/狗”和“它在画面中的位置”

项目方案中特别强调了ELAN和MP结构。ELAN可以增强网络的特征表达能力,让模型在多层特征之间保持更丰富的梯度路径;MP结构则用于下采样与特征聚合。对于猫狗识别来说,这类结构的意义在于:同一只猫狗可能出现侧身、趴下、遮挡、逆光、远距离等不同状态,网络需要从局部纹理和整体轮廓中同时提取可用信息。

5.3 推理流程:从摄像头画面到检测框

本地detect.py的核心逻辑可以概括为“读图 → 预处理 → 模型推理 → 非极大值抑制 → 画框/保存结果”。理解性伪代码如下:

model = load_yolov7_weights("yolov7.pt")

for rgb_frame in camera_stream:
    image = resize_and_normalize(rgb_frame, size=640)
    raw_pred = model(image)

    detections = non_max_suppression(
        raw_pred,
        conf_threshold=0.25,
        iou_threshold=0.45
    )

    for box, score, class_id in detections:
        if class_id in ["cat", "dog"]:
            draw_box(rgb_frame, box, label=f"{class_id} {score:.2f}")

这里的non_max_suppression很关键。模型可能对同一只猫输出多个重叠检测框,NMS会保留置信度更高、位置更合理的框,避免系统把一只动物误认为多个目标。

实际运行时,可以用类似下面的方式对图片、视频或摄像头画面进行检测:

python detect.py --weights yolov7.pt --conf 0.25 --img-size 640 --source inference/images/horses.jpg
python detect.py --weights yolov7.pt --conf 0.25 --img-size 640 --source 0

5.4 检测框为什么还不够?深度相机的价值在这里

普通RGB摄像头只能告诉机器人:画面里哪里有猫狗。

深度相机进一步告诉机器人:猫狗离我多远、在空间中的大致位置在哪里。

这就是Astra Pro这类RGB-D相机的关键价值。RGB负责识别,Depth负责空间测量。把YOLOv7的检测框与深度图对齐后,可以在检测框中心点或目标区域内采样深度值,得到目标距离估计。这样机器人才能从“看见动物”升级到“知道动物在前方几米处”。

更稳妥的做法不是只取检测框中心一个像素,而是在检测框中间区域取一小块深度值,再用中位数过滤异常点:

def estimate_target_distance(depth_map, box):
    x1, y1, x2, y2 = box
    cx1 = int(x1 + 0.35 * (x2 - x1))
    cx2 = int(x1 + 0.65 * (x2 - x1))
    cy1 = int(y1 + 0.35 * (y2 - y1))
    cy2 = int(y1 + 0.65 * (y2 - y1))

    roi_depth = depth_map[cy1:cy2, cx1:cx2]
    valid_depth = roi_depth[(roi_depth > 200) & (roi_depth < 6000)]

    if len(valid_depth) == 0:
        return None

    return median(valid_depth) / 1000.0  # millimeters to meters

本地video.py中已经体现了这种设计方向:脚本同时处理两路画面,并预留了根据目标框中心点计算距离的逻辑。虽然距离计算调用在当前代码中处于注释状态,但整体流程已经指向“检测框 + 深度数据”的融合方案。

6. 追踪关键:MedianFlow让识别结果从“单帧”变成“连续轨迹”

单帧识别只能回答“这一帧里有没有猫狗”。但机器人管理流浪动物时,更重要的是连续观察:它往哪里走?是否靠近危险区域?是否已经被记录过?

MedianFlow是一种经典的目标追踪算法,它通过前向和后向光流一致性判断跟踪点是否可靠。简单说,它会检查一个点从上一帧跟到下一帧后,再反向跟回上一帧是否还能回到原位置;如果偏差很大,说明这个点不可靠,需要剔除。

它和YOLOv7的配合关系可以这样理解:

YOLOv7:负责发现目标,并给出初始检测框
MedianFlow:负责在短时间连续帧里跟住这个框
YOLOv7:隔几帧重新校准,避免追踪漂移

理解性伪代码如下:

tracker = None
frame_count = 0

for frame in camera_stream:
    frame_count += 1

    if tracker is None or frame_count % 10 == 0:
        detections = yolov7_detect(frame)
        target = select_best_cat_or_dog(detections)
        if target:
            tracker = init_medianflow_tracker(frame, target.box)
            animal_state = "detected"
    else:
        ok, box = tracker.update(frame)
        animal_state = "tracking" if ok else "lost"

    if animal_state in ["detected", "tracking"]:
        update_target_trajectory(box)

这种方法适合短时间、局部连续的目标跟踪。对本项目而言,YOLOv7负责定期“重新确认目标”,MedianFlow负责在相邻帧之间保持目标连续性。这样可以减少每一帧都完全依赖检测器的压力,也能提高运动过程中的目标稳定性。

7. 从巡逻到建档:完整工作流程

一个典型任务可以拆成六步:

19f06d04dcb47.png

如果用程序流程表达,这个闭环可以写成:

while patrol_task_active:
    rgb, depth = astra_pro.read()
    lidar_scan = lidar.read()

    pose, map_grid = fastslam.update(lidar_scan)
    detections = yolov7.detect(rgb)

    for det in detections:
        if det.label not in ["cat", "dog"]:
            continue

        distance = estimate_target_distance(depth, det.box)
        target_point = project_to_robot_frame(det.box, distance)

        if distance is not None and distance > 1.5:
            global_path = astar.plan(map_grid, pose, target_point)
            cmd = dwa.control(pose, global_path, lidar_scan)
            stm32.send(cmd)
        else:
            stm32.send({"linear_velocity": 0.0, "angular_velocity": 0.0})
            save_or_update_animal_archive(det, pose, distance)

其中,1.5米可以理解为安全观察距离。实际项目中,这个距离需要根据相机视场、小车刹停距离、动物应激反应和校园人流情况调整。

7.1 动物档案应该记录什么?

档案不是简单存一张照片,而是将“某次发现”变成可持续管理的数据。一个动物档案可以设计成下面这种结构:

{
  "animal_id": "CAT-HDU-0007",
  "species": "cat",
  "first_seen": "2026-06-26 09:35:12",
  "last_seen": "2026-06-26 10:18:40",
  "appearance": {
    "color": "orange-white",
    "body_size": "medium",
    "features": ["white paws", "left ear notch"]
  },
  "health_record": {
    "vaccinated": false,
    "sterilized": "unknown",
    "visible_injury": false
  },
  "trajectory": [
    {"location": "canteen east lawn", "distance_m": 2.4},
    {"location": "dormitory walkway", "distance_m": 3.1}
  ],
  "image_refs": [
    "archive/CAT-HDU-0007/20260626_093512.jpg"
  ]
}

这类结构能支持后续的管理动作:确认是否为已记录个体、统计出现频率、标记健康状态、安排投喂或救助。

7.2 系统状态机:机器人什么时候巡逻,什么时候停下?

为了避免逻辑混乱,移动机器人通常会用状态机管理任务:

PATROL
  ↓ 检测到猫狗
APPROACH
  ↓ 到达安全观察距离
OBSERVE
  ↓ 完成拍摄与识别
ARCHIVE
  ↓ 档案更新完成
TRACK_OR_RETURN
  ↓ 目标离开 / 巡逻任务继续
PATROL

对应到代码层,可以理解为:

if state == "PATROL" and animal_detected:
    state = "APPROACH"
elif state == "APPROACH" and distance <= safe_distance:
    state = "OBSERVE"
elif state == "OBSERVE" and image_quality_ok:
    state = "ARCHIVE"
elif state == "ARCHIVE" and archive_saved:
    state = "TRACK_OR_RETURN"

8. 应用场景:它能在校园里做什么?

19f03896c72fe.png

8.1 定点巡逻:把“偶然看见”变成“持续巡查”

机器人可以在流浪动物常出现的区域巡逻,例如宿舍楼下、食堂后门、草坪、教学楼连廊附近。当检测到猫狗时,小车停下并进行识别,记录时间和位置。

8.2 自主投喂:把投喂变得可控

项目方案中提到机器人可移动到指定地点对小动物进行投喂。相比随机投喂,机器人投喂更适合与管理策略结合,例如固定地点、固定时间、固定数量,减少动物无序聚集。

8.3 档案建立:给每只动物一个可追踪编号

系统可以为小动物建立数据库档案,记录类别、照片、活动地点、出现频率、疫苗接种与健康状态。这个能力是项目最具管理意义的部分,因为它把“校园里有很多猫狗”变成了“有哪些个体、它们在哪里、状态如何”。

8.4 外来动物识别:发现变化,而不仅是发现动物

当机器人再次巡逻时,可以识别已经存在于数据库内的动物,并对新出现的外来动物进行区分。这对于疾病防控、数量评估和救助管理都很关键。

9. 测试方案:看得准,也要走得稳

项目测试分为导航测试和识别测试。

导航测试关注机器人能否在室外草地、室内食堂、教学楼等场景中到达动物附近。测试时,小车开启导航节点,根据目标位置移动并避开障碍物,以此验证导航精度和运动稳定性。

识别测试关注模型能否准确判断动物身份。测试方式是将现有动物个体数据作为输入,与测试集结果进行对比。当小车移动到动物附近后,系统处理深度相机数据,完成动物类别或身份判断,并评估识别功能是否可靠。

这两个测试必须组合起来看:如果只识别准确但无法靠近,系统不能形成管理闭环;如果能移动但识别不稳定,也无法建立可信档案。移动机器人项目的难点就在于“感知、决策、控制”必须同时成立。

10. 项目创新点与技术优势

▍创新点一:把3D相机用于校园流浪动物管理

传统摄像头只能提供二维画面,Astra Pro提供RGB-D数据,让系统同时获得外观特征和距离信息。这让机器人可以做目标识别、避障、追踪和空间定位,而不是只做静态拍摄。

▍创新点二:YOLOv7提升实时识别能力

项目在参考R-CNN、VGG16、YOLO等典型识别算法后,选择YOLOv7作为猫狗识别核心。相比两阶段检测方法,YOLOv7更适合边缘侧实时推理;相比普通分类网络,它能同时输出目标位置,更适合机器人导航和追踪。

▍创新点三:导航、识别、追踪形成闭环

FastSLAM、A*、DWA解决“到哪里去、怎么过去”的问题;YOLOv7解决“目标是谁、在哪里”的问题;MedianFlow解决“目标移动后如何持续观察”的问题。多个模块配合,使系统从单点识别扩展为完整的移动管理方案。

▍创新点四:考虑RGB-D噪声处理

项目方案中提到,RGB-D相机采集图像时可能存在较多噪声,因此采用Orbbec SDK相关滤波算法对原始图像进行处理。深度图噪声会直接影响距离估计,如果不处理,检测框中心点的深度值可能跳变,导致机器人误判距离。因此滤波不是简单的画面优化,而是影响导航与追踪可靠性的关键步骤。

11. 项目可继续深化的方向

这个项目已经完成了“移动机器人 + 3D视觉 + 猫狗识别 + 导航追踪”的基础框架。若继续迭代,可以重点增强以下能力:

• 个体重识别:在猫狗类别识别之外,进一步识别“是哪一只猫/狗”。

• 档案系统:增加后台管理界面,支持照片、位置轨迹、健康状态、疫苗记录。

• 边缘部署优化:将YOLOv7模型导出为ONNX或TensorRT,提高树莓派或边缘设备推理效率。

• 投喂策略:结合出现频率和健康状态,制定更精细的定点投喂计划。

• 校园管理联动:与保卫处、后勤、动物保护社团或校医院形成数据共享机制。

12. 结语:真正有价值的不是“识别猫狗”,而是让管理变得温和、连续、可执行

“利用深度相机实现对校园流浪动物的管理”这个项目的亮点,不只是把YOLOv7放在小车上跑起来,而是把3D视觉、机器人导航、目标追踪和动物档案管理放进同一个应用场景里。

它解决的是一个很真实的问题:校园流浪动物既需要被关注,也需要被有序管理。通过Astra Pro深度相机、树莓派4B、STM32和四轮移动底盘的组合,系统把流浪动物从“看见一次”变成“持续记录”,从“人工经验管理”推进到“数据辅助管理”。

如果说普通摄像头是校园里的眼睛,那么这套3D视觉机器人系统更像是一名会移动、会记录、会判断距离的巡查员。它让技术不只停留在演示,而是走向一个更具体、更温和、也更可落地的校园治理场景。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注