前言
基于当前的学习内容以及工作的重心,展开对OpenCV部分的研究与学习,在研究的过程中,对标国内外比较优秀的软体算子进行开发,尝试对常用的软件的算子进行封装,并进行一定的测试。真正做核心的算法内容构建,可能会考虑更底层的C++之类的语言,但做视觉初步学习的状态下,个人认为以python尝试进行构建,可能会相对方便。本人也是从纯全栈开发转向算法视觉研究,中间存在的问题欢迎大家一同批评指正,谢谢。
个人感觉可能从实际的案例进行入手会比按教程来的实在,故尝试进行阶段性划分以公司中具体任务划分模块细化研究,当然初心是为了构建一个属于自己可用的算法库。由于工作原因,可能也会对实际的专题内容无法成模块的进行梳理和研究,但都会尝试以实际生产可用的状态,尝试进行内容编写与学习。
环境配置
编辑器:Pycharm
Python版本:python3.8
平面(XY轴)走向变化
基础思考
在相机与平面固定的状态下,进行区块的内容校准。
- 利用相机进行拍摄,获取相机拍摄下方块的像素坐标以及长宽
- 利用实际空间中的间距,进行距离换算
- 有实际换算后的值,进行移动时的标定基数相乘即可得到实际坐标,依据实际情况添加偏移量
- 在触发设备移动的过程中还需将移动的距离进行脉冲信号的转化
预计在生产中的使用如下:
进行相机的基础内容校准,单位换算,得到对应的偏移量和换算值。进行初始位的设计,进行视觉识别(深度学习/机器视觉),识别到物体后,给定一个像素坐标,移动到固定位子,进行Z轴的电机或气缸的操作,回到初始位子,进行放置位子的识别(深度学习/机器视觉),移动到放置物体的位子,Z轴的电器或气缸执行操作,回到初始位子,循环往复的执行。
思路流程
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()