【OpenCV学习】平面走向变化与研究 机器视觉 视觉标定

前言

基于当前的学习内容以及工作的重心,展开对OpenCV部分的研究与学习,在研究的过程中,对标国内外比较优秀的软体算子进行开发,尝试对常用的软件的算子进行封装,并进行一定的测试。真正做核心的算法内容构建,可能会考虑更底层的C++之类的语言,但做视觉初步学习的状态下,个人认为以python尝试进行构建,可能会相对方便。本人也是从纯全栈开发转向算法视觉研究,中间存在的问题欢迎大家一同批评指正,谢谢。

个人感觉可能从实际的案例进行入手会比按教程来的实在,故尝试进行阶段性划分以公司中具体任务划分模块细化研究,当然初心是为了构建一个属于自己可用的算法库。由于工作原因,可能也会对实际的专题内容无法成模块的进行梳理和研究,但都会尝试以实际生产可用的状态,尝试进行内容编写与学习。

环境配置

编辑器:Pycharm
Python版本:python3.8
imagepng
imagepng

平面(XY轴)走向变化

基础思考

在相机与平面固定的状态下,进行区块的内容校准。

  1. 利用相机进行拍摄,获取相机拍摄下方块的像素坐标以及长宽
  2. 利用实际空间中的间距,进行距离换算
  3. 有实际换算后的值,进行移动时的标定基数相乘即可得到实际坐标,依据实际情况添加偏移量
  4. 在触发设备移动的过程中还需将移动的距离进行脉冲信号的转化

预计在生产中的使用如下:
进行相机的基础内容校准,单位换算,得到对应的偏移量和换算值。进行初始位的设计,进行视觉识别(深度学习/机器视觉),识别到物体后,给定一个像素坐标,移动到固定位子,进行Z轴的电机或气缸的操作,回到初始位子,进行放置位子的识别(深度学习/机器视觉),移动到放置物体的位子,Z轴的电器或气缸执行操作,回到初始位子,循环往复的执行。
imagepng

思路流程

A[相机拍摄获取像素坐标] --> B(实际空间距离换算);
B --> C[计算移动标定基数];
C --> D[添加偏移量调整];
D --> E(脉冲信号转化);
E --> F[设备移动控制];
F --> G[视觉识别物体(初始位)];
G --> H[移动至固定位置];
H --> I[Z轴操作下降];
I --> J[回到初始位置];
J --> K[视觉识别放置位置];
K --> L[移动至放置位置];
L --> M[Z轴操作放置];
M --> N[回到初始位置];
N --> O[循环执行];

总结

节点

数据采集节点:首先利用相机对目标区块进行拍摄,获取其在图像中的像素坐标及其尺寸(长宽)。
空间换算节点:基于实际物理空间中的已知间距,将像素坐标转换为实际空间坐标,完成从像素空间到物理空间的校准。
标定与偏移调整:根据换算结果,设定移动时的标定基数,并根据具体需求加入必要的偏移量调整,确保定位精确。
运动控制:在设备移动过程中,将计算出的实际坐标转换为控制信号,通过脉冲信号的形式驱动机械装置(如电机或气缸)执行精确定位。

运作

初始位视觉识别:使用深度学习或机器视觉技术识别目标物体。
移动至固定位:根据识别结果,移动设备到预设的处理位置。
Z轴操作:执行Z轴方向上的动作,如下降以拾取或放置物体。
返回初始位:操作完成后,设备返回初始位置准备下一次循环。
放置位识别与移动:再次识别放置位置,移动并执行放下物体的操作。
整个流程设计旨在实现自动化、高精度的物体搬运与放置任务,通过不断的视觉反馈与精确控制循环,确保生产过程的高效与准确。

脚本实现

预计设计如下:

  • 相机控制类:初始化相机,捕获画面,释放相机资源
  • 空间标定类:初始化单位长度,像素坐标转化实际坐标,计算两个坐标偏移量
  • 脉冲控制类:初始化脉冲单位长度,移动位子脉冲换算
  • 目标识别类(YOLO实现):初始化识别模型,识别目标,识别目标坐标

相机控制类

import cv2


class CameraCapture:
    """
    相机捕获类
    """
    def __init__(self, camera_index=0):
        """
        初始化相机
        :param camera_index: 相机索引,默认为0
        """
        self.cap = cv2.VideoCapture(camera_index)

    def capture_image(self):
        """
        捕获图像
        :return: 捕获到的图像
        """
        ret, frame = self.cap.read()
        if not ret:
            raise Exception("Failed to capture image")
        return frame

    def release(self):
        """
        释放相机资源
        """
        self.cap.release()

if __name__ == '__main__':
    # 示例使用
    camera = CameraCapture()
    image = camera.capture_image()
    cv2.imshow("Image", image)
    cv2.waitKey(0)
    camera.release()

空间标定类

class SpaceCalibration:
    """
    空间标定类
    """

    def __init__(self, pixel_per_unit):
        """
        初始化
        :param pixel_per_unit: 每像素对应的物理单位长度
        """
        self.pixel_per_unit = pixel_per_unit

    def pixel_to_physical(self, pixel_coords, pixel_size=(1,1)):
        """
        将像素坐标转换为物理坐标
        :param pixel_coords: 像素坐标
        :param pixel_size: 像素大小
        :return: 物理坐标
        """
        x, y = pixel_coords
        width, height = pixel_size
        return (x / self.pixel_per_unit, y / self.pixel_per_unit), (
            width / self.pixel_per_unit, height / self.pixel_per_unit)

    def calculate_offset(self, base_position, target_position):
        """
        计算两个位置之间的偏移量
        :param base_position: 基准位置
        :param target_position: 目标位置
        :return: 偏移量
        """
        return target_position[0] - base_position[0], target_position[1] - base_position[1]


if __name__ == '__main__':
    # 示例使用
    calibration = SpaceCalibration(10)  # 假设每10个像素对应1单位长度
    physical_coords, _ = calibration.pixel_to_physical((100, 200), (30, 40))
    calculate_offset = calibration.calculate_offset((20, 0), (100, 100))
    print("physical_coords", physical_coords)
    print("calculate_offset", calculate_offset)

脉冲控制类

class MotionControl:
    """
    脉冲控制类
    """

    def __init__(self, conversion_factor):
        """
        初始化MotionControl类
        :param conversion_factor: 脉冲数/单位长度
        """
        self.conversion_factor = conversion_factor

    def move_to(self, target_position, current_position=(0, 0)):
        """
        移动到目标位置
        :param target_position: 目标位置
        :param current_position: 当前位置
        """
        offset = self.calculate_offset(current_position, target_position)
        pulses = self.pulses_needed(offset)
        print(f"{current_position}移动到{target_position}需要移动{pulses}个脉冲")

    def calculate_offset(self, base_position, target_position):
        """
        计算偏移量
        :param base_position: 基准位置
        :param target_position: 目标位置
        :return: 偏移量
        """
        return tuple(b - t for b, t in zip(base_position, target_position))

    def pulses_needed(self, offset):
        """
        计算脉冲数
        :param offset: 偏移量
        :return: 脉冲数
        """
        return tuple(abs(o) * self.conversion_factor for o in offset)

if __name__ == '__main__':
    # 示例使用
    motion = MotionControl(100)  # 假设每单位长度需要100个脉冲
    motion.move_to((5, 5))

识别控制类

from ultralytics import YOLO


class VisionSystem:
    """
    视觉系统类,用于识别物体和确定物体位置
    """

    def __init__(self, model_path=r"E:\Code\opencv_learn\opencv_learn\positioning\model\yolov8n.pt"):
        """
        初始化视觉系统,加载YOLO模型
        :param model_path: YOLO模型路径
        """
        self.model = YOLO(model_path)

    def recognize_object(self, image):
        """
        识别图像中的物体,返回物体名称
        :param image: 图像
        :return: 物体名称 置信度
        """
        results = self.model(image)
        for result in results:
            boxes = result.boxes  # 获取所有边界框的信息
            for box in boxes:
                class_id = box.cls[0].item()  # 获取类别的索引
                class_name = self.model.names[class_id]  # 从模型的类别名称列表中获取类别名
                confidence = box.conf[0].item()  # 获取该预测的置信度
                bbox = box.xyxy[0].tolist()  # 获取边界框的坐标,格式为[x_min, y_min, x_max, y_max]
                return class_name, confidence, bbox

    def identify_placement(self, bbox):
        """
        识别物体的位置,返回位置
        :param image: 图像
        :return: 位置
        """
        center_x = (bbox[0] + bbox[2]) / 2
        center_y = (bbox[1] + bbox[3]) / 2
        center_position = (center_x, center_y)
        return center_position

if __name__ == '__main__':
    # 示例使用
    vision = VisionSystem()
    image = r"E:\Code\opencv_learn\opencv_learn\img\girl.png"  # 获取图像
    class_name, confidence, bbox = vision.recognize_object(image)
    if class_name is not None:
        print(f"检测对象: {class_name}")
        placement = vision.identify_placement(bbox)
        print(f"像素位子: {placement}")
    else:
        print("未检测到对象。")

主函数-测试

from positioning.CameraCapture import CameraCapture
from positioning.MotionControl import MotionControl
from positioning.SpaceCalibration import SpaceCalibration
from positioning.VisionSystem import VisionSystem


class WorkflowManager:
    """
    工作流管理器,用于协调相机、视觉系统、空间标定、电机控制等模块之间的协作。
    """

    def __init__(self, camera, calibration, motion, vision):
        """
        初始化工作流管理器,传入相机、空间标定、电机控制、视觉系统等模块的实例。
        :param camera: 相机实例
        :param calibration: 空间标定实例
        :param motion: 电机控制实例
        :param vision: 视觉系统实例
        """
        self.camera = camera
        self.calibration = calibration
        self.motion = motion
        self.vision = vision

    def run_calibration_workflow(self):
        """
        运行标定工作流,包括相机、视觉系统、空间标定、电机控制等模块的协作。
        """
        image = self.camera.capture_image()
        class_name, confidence, bbox = vision.recognize_object(image)
        if class_name is not None:
            print(f"检测对象: {class_name}")
            placement = vision.identify_placement(bbox)
            print(f"像素位子: {placement}")
            # 换算实际坐标
            physical_coords, _ = calibration.pixel_to_physical(placement, (1, 1))
            print(f"实际坐标: {physical_coords}")
            # 换算脉冲数
            motion.move_to(physical_coords)
            print("移动完成。")
        else:
            print("未检测到对象。")


if __name__ == '__main__':
    # 示例使用
    # 运行标定工作流,包括相机、视觉系统、空间标定、电机控制等模块的协作。
    # 获取画面
    camera = CameraCapture()
    # 定义空间坐标
    calibration = SpaceCalibration(10)
    # 定义电机控制
    motion = MotionControl(100)
    # 定义识别程序
    vision = VisionSystem()
    # 初始化中心控制模块
    manager = WorkflowManager(camera, calibration, motion, vision)
    manager.run_calibration_workflow()
    camera.release()

效果

image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值