软触发同步过程记录

1. 最开始拿到三个支持硬触发的工业相机,通过按键触发相机,读取摄像头数据

2. 和厂商交流后更换软触发相机,三个软触发相机接入能实现同步,继续采购5个相机

3. 发现电脑只能接入5个摄像头,多接入也带不动,需要使用USB3.0集线器(相机厂商称Hub),采购10孔的hub

4. 使用hub8个相机能够成功接入并运行,但不能同步,使用多线程方式可以同步,具体代码如下:

'''
Descripttion: 
version: 
Author: jhq
Date: 2022-05-24 22:19:46
LastEditors: jhq
LastEditTime: 2022-05-24 22:53:31
'''

import cv2
import numpy as np
import os

from threading import Thread, Event
from queue import Queue
import time

# Event
# Barrier
# Queue

event = Event()

def get_frame(cap, dir_name):  
    index = 1
    while True:
        event.wait()
        cap.set(13, 1.0)
        _, frame = cap.read()
        cv2.imwrite(dir_name+str(index)+'.jpg', frame)
        index += 1

if __name__=='__main__':
    # cap_queue = Queue(maxsize=10)
    cap_list = []
    dir_list = []
    cap0 = cv2.VideoCapture(0)
    cap1 = cv2.VideoCapture(2)
    cap2 = cv2.VideoCapture(4)
    cap3 = cv2.VideoCapture(6)
    cap4 = cv2.VideoCapture(8)
    cap5 = cv2.VideoCapture(10)
    cap6 = cv2.VideoCapture(12)
    cap7 = cv2.VideoCapture(14)
    flag0 = cap0.isOpened()
    flag1 = cap1.isOpened()
    flag2 = cap2.isOpened()
    flag3 = cap3.isOpened()
    flag4 = cap4.isOpened()
    flag5 = cap5.isOpened()
    flag6 = cap6.isOpened()
    flag7 = cap7.isOpened()

    cap0.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
    cap0.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
    cap1.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
    cap1.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
    cap2.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
    cap2.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
    cap3.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
    cap3.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
    cap4.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
    cap4.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
    cap5.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
    cap5.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
    cap6.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度    
    cap6.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
    cap7.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
    cap7.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
    cap_list.append(cap0)
    cap_list.append(cap1)
    cap_list.append(cap2)
    cap_list.append(cap3)
    cap_list.append(cap4)
    cap_list.append(cap5)
    cap_list.append(cap6)
    cap_list.append(cap7)

    # dir_path = '/home/hejiahui/jhq/data/img_0509/0'
    file_path = os.getcwd()
    dataset_path = os.path.join(file_path, 'dataset')
    if not os.path.exists(dataset_path):
        os.makedirs(dataset_path)  
    for i in range(len(cap_list)):
        cam_path = dataset_path+ '0' + str(i) + '/'
        if not os.path.exists(cam_path):
            os.makedirs(cam_path)   
        dir_list.append(cam_path)
    for i in range(len(cap_list)):
        Thread(target=get_frame, args=(cap_list[i], dir_list[i])).start()
    time.sleep(2)
    while True:
        event.clear()
        val =input("please enter 's'\n")
        if val == "s":  
            event.set()
        
           

 5. 故保存单线程拍摄的相片观察,代码如下:

import cv2
import time
import numpy as np
import os

file_path = os.getcwd()
dataset_path = os.path.join(file_path, 'dataset')
if not os.path.exists(dataset_path):
    os.makedirs(dataset_path) 
cap_list = []

cap0 = cv2.VideoCapture(0) # 通过摄像头编号接入
cap_list.append(cap0)
cap1 = cv2.VideoCapture(2)
cap_list.append(cap1)
cap2 = cv2.VideoCapture(4)
cap_list.append(cap2)
cap3 = cv2.VideoCapture(6)
cap_list.append(cap3)
cap4 = cv2.VideoCapture(8)
cap_list.append(cap4)
cap5 = cv2.VideoCapture(10)
cap_list.append(cap5)
cap6 = cv2.VideoCapture(12)
cap_list.append(cap6)
cap7 = cv2.VideoCapture(14)
cap_list.append(cap7)

flag0 = cap0.isOpened()  # 测试摄像头是否正常
flag1 = cap1.isOpened()
flag2 = cap2.isOpened()
flag3 = cap3.isOpened()
flag4 = cap4.isOpened()
flag5 = cap5.isOpened()
flag6 = cap6.isOpened()
flag7 = cap7.isOpened()

cap0.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
cap0.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
cap1.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
cap1.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
cap2.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
cap2.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
cap3.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
cap3.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
cap4.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
cap4.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
cap5.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
cap5.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
cap6.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
cap6.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
cap7.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
cap7.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
# pro = cap0.get(21)
# print(f'exposure:{pro}')
# _ = cap0.set(21, 3.0)
# print(f'_:{cap0.get(21)}')

# for i in range(47):
#     print("camera.={} No.={} parameter={} camera.={} No.={} parameter={}".format('black_0',i,cap0.get(i),'light_1',i,cap1.get(i)))

for cap in cap_list:
    res, frame = cap.read()   
    while res:
        res, frame = cap.read()   

index = 1
while index < 30: 
    cap0.set(13, 1.0)  # 设置色调=1触发
    cap1.set(13, 1.0)
    cap2.set(13, 1.0)
    cap3.set(13, 1.0)    
    cap4.set(13, 1.0)
    cap5.set(13, 1.0)
    cap6.set(13, 1.0)
    cap7.set(13, 1.0)
    res0, frame0 = cap0.read()   # 读取摄像头数据
    res1, frame1 = cap1.read()
    res2, frame2 = cap2.read()
    res3, frame3 = cap3.read()
    res4, frame4 = cap4.read()
    res5, frame5 = cap5.read()
    res6, frame6 = cap6.read()
    res7, frame7 = cap7.read()

    if res0 and res1 and res2 and res3 and res4 and res5 and res6 and res7:
    # if res0 and res1 and res2:
        # frame_test = np.zeros((480, 640, 3))
        frame_test = np.zeros(frame0.shape, dtype=np.uint8)
        vs1 = np.hstack((frame0, frame1, frame2))
        vs2 = np.hstack((frame3, frame4, frame5))
        vs3 = np.hstack((frame6, frame7, frame_test))  # 合并摄像头数据
        # vs1 = np.hstack((frame0, frame1))
        # vs2 = np.hstack((frame2, frame3))
        result = np.vstack((vs1, vs2, vs3))
        # cv2.imwrite('camare:', result)   # 显示数据
        cv2.imwrite(os.path.join(dataset_path, str(index)+'.jpg'), result)
        index += 1
    else:
        # print(f'res0:{res0},res1:{res1},res2:{res2}')
        print(f'res0:{res0},res1:{res1},res2:{res2},res3:{res3},res4:{res4},res5:{res5},res6:{res6}, res7:{res7}')
        break
        # print(f'res0:{success0},res1:{success1},res2:{success2},res3:{success3},res4:{success4},res5:{success5},res6:{success6}, res7:{success7}')

cap0.release()
cap1.release()
cap2.release()
cap3.release()
cap4.release()
cap5.release()
cap6.release()
cap7.release()


6. 通过观察单线程保存图片,发现图片之间存在时间gap,比如某个图片的时间是另一个图片的下一个时间,进而怀疑是否是相机接入过程中存在数据缓存,导致读取的数据不同步。

7. 在触发之前将相机缓存数据读掉,使用多线程清数据,不然会很慢,代码如下:

import cv2
import numpy as np
import threading
from subprocess import PIPE,Popen

cam_list_ = [0, 2, 4, 6, 8, 10, 12, 14]
cap_list = []

def find_cam():
    '''
        根据设备名称获取相机设备号
        使用前需要先安装v4l-utils
        Installing v4l-utils (debian) gives one the handy v4l2-ctl command:
        $ v4l2-ctl --list-devices

        KS2A543: KS2A543 (usb-0000:00:14.0-2.1):
            /dev/video2
            /dev/video3

        KS2A543: KS2A543 (usb-0000:00:14.0-2.2):
            /dev/video4
            /dev/video5

        KS2A543: KS2A543 (usb-0000:00:14.0-2.3):
            /dev/video6
            /dev/video7

        KS2A543: KS2A543 (usb-0000:00:14.0-2.4):
            /dev/video8
            /dev/video9
    '''
    cam_list = []
    cmd = ["/usr/bin/v4l2-ctl", "--list-devices"]
    out, err = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate()
    out = out.strip()
    for dev_name, dev_idx1, dev_idx2 in [i.split(b'\n\t') for i in out.split(b'\n\n')]:
        index = dev_idx1.decode('utf8').split('video')[1]
        cam_list.append(index)
    return cam_list

cam_list = find_cam()
print(cam_list)

for cam_num in cam_list:     # 根据摄像头编号创建视频处理句柄
    cap = cv2.VideoCapture(int(cam_num))
    cap_list.append(cap)

flag = 0
for cap in cap_list:    # 判断摄像头是否可用
    if cap.isOpened():
        flag += 1

def read_camera_cache(cap):   # 清除摄像头缓存数据
    success = cap.grab()
    while success:
        success = cap.grab()

threads = []

for cap in cap_list:
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
    t = threading.Thread(target=read_camera_cache, args=(cap,))   # 多线程清除摄像头缓存数据
    threads.append(t)
    t.start()

for t in threads:   # 父进程等待清缓存的子进程执行完毕
    t.join()

index = 1
while flag == 8:    
    frame_list = []
    res_flag = 0
    for cap in cap_list:
        cap.set(13, 1.0) # 设置色调=1触发
    for cap in cap_list:
        res, frame = cap.read()   # 读取摄像头数据
        res_flag += 1
        frame_list.append(frame)

    if res_flag == 8:
    # if res0 and res1 and res2:
        # frame_test = np.zeros((480, 640, 3))
        frame_test = np.zeros(frame_list[0].shape, dtype=np.uint8)
        vs1 = np.hstack((frame_list[0], frame_list[1], frame_list[2]))
        vs2 = np.hstack((frame_list[3], frame_list[4], frame_list[5]))
        vs3 = np.hstack((frame_list[6], frame_list[7], frame_test))  # 合并摄像头数据
        result = np.vstack((vs1, vs2, vs3))
        cv2.imshow('camare:', result)   # 显示数据
    else:
        # print(f'res0:{res0},res1:{res1},res2:{res2}')
        print(f'res:{res_flag}')
        # print(f'res0:{success0},res1:{success1},res2:{success2},res3:{success3},res4:{success4},res5:{success5},res6:{success6}, res7:{success7}')

    k = cv2.waitKey(1) & 0xFF
    if k == ord('q'):
        break
    if k == ord('s'):  # 按下s键,显示暂停
        # time.sleep(7)
        cv2.waitKey(7000)
        index += 1

cv2.destroyAllWindows()
for cap in cap_list:
    cap.release()    # 释放摄像头资源

8. 优化,清数据代码,通过装饰器中断执行太久的cap.read()操作,代码如下:

import cv2
import numpy as np
import threading
from subprocess import PIPE,Popen
# import eventlet    # 没有效果
import time
import signal

cam_list_ = [0, 2, 4, 6, 8, 10, 12, 14]
cap_list = []

def set_timeout(num, callback):   # 只能在linux系统上用,且只能运行在主线程
  def wrap(func):
    def handle(signum, frame): # 收到信号 SIGALRM 后的回调函数,第一个参数是信号的数字,第二个参数是the interrupted stack frame.
      raise RuntimeError
    def to_do(*args, **kwargs):
      try:
        signal.signal(signal.SIGALRM, handle) # 设置信号和回调函数
        signal.alarm(num) # 设置 num 秒的闹钟
        print('start clear camera cache.')
        r = func(*args, **kwargs)
        print('close alarm signal.')
        signal.alarm(0) # 关闭闹钟
        return r
      except RuntimeError as e:
        callback()
    return to_do
  return wrap

def after_timeout(): # 超时后的处理函数
  print("end clear camera cache !")


def find_cam():
    '''
        根据设备名称获取相机设备号
        使用前需要先安装v4l-utils
        Installing v4l-utils (debian) gives one the handy v4l2-ctl command:
        $ v4l2-ctl --list-devices

        KS2A543: KS2A543 (usb-0000:00:14.0-2.1):
            /dev/video2
            /dev/video3

        KS2A543: KS2A543 (usb-0000:00:14.0-2.2):
            /dev/video4
            /dev/video5

        KS2A543: KS2A543 (usb-0000:00:14.0-2.3):
            /dev/video6
            /dev/video7

        KS2A543: KS2A543 (usb-0000:00:14.0-2.4):
            /dev/video8
            /dev/video9
    '''
    cam_list = []
    cmd = ["/usr/bin/v4l2-ctl", "--list-devices"]
    out, err = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate()
    out = out.strip()
    for dev_name, dev_idx1, dev_idx2 in [i.split(b'\n\t') for i in out.split(b'\n\n')]:
        index = dev_idx1.decode('utf8').split('video')[1]
        cam_list.append(index)
    return cam_list

start_time = time.time()
cam_list = find_cam()
print(cam_list)

for cam_num in cam_list:     # 根据摄像头编号创建视频处理句柄
    cap = cv2.VideoCapture(int(cam_num))
    cap_list.append(cap)

flag = 0
for cap in cap_list:    # 判断摄像头是否可用
    if cap.isOpened():
        flag += 1

@set_timeout(1, after_timeout)   # signal.alarm(num) num只支持int
def clear_camera_cache_dec(cap):   # 清除摄像头缓存数据, 有装饰器,只能在主线程运行
    success = cap.grab()
    while success:
        success = cap.grab()

def clear_camera_cache(cap):   # 清除摄像头缓存数据,无装饰器,可在子线程运行
    success = cap.grab()
    while success:
        success = cap.grab()

threads = []

# for cap in cap_list:
#     cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
#     cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
#     t = threading.Thread(target=clear_camera_cache, args=(cap,))   # 多线程清除摄像头缓存数据
#     threads.append(t)
#     t.start()

# for t in threads:   # 父进程等待清缓存的子进程执行完毕
#     t.join()

for cap in cap_list:
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)  #设置宽度
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 360)  #设置高度
    clear_camera_cache_dec(cap)

end_time = time.time()
print(f'time:{end_time-start_time}')
index = 1
frame_test = np.zeros((360, 640, 3), dtype=np.uint8)
while flag == 8:    
    frame_list = []
    res_flag = 0
    for cap in cap_list:
        cap.set(13, 1.0) # 设置色调=1触发
    for cap in cap_list:
        res, frame = cap.read()   # 读取摄像头数据
        if res:
            res_flag += 1
            frame_list.append(frame)

    if res_flag == 8:
    # if res0 and res1 and res2:        
        vs1 = np.hstack((frame_list[0], frame_list[1], frame_list[2]))
        vs2 = np.hstack((frame_list[3], frame_list[4], frame_list[5]))
        vs3 = np.hstack((frame_list[6], frame_list[7], frame_test))  # 合并摄像头数据
        result = np.vstack((vs1, vs2, vs3))
        cv2.imshow('camare:', result)   # 显示数据
    else:
        # print(f'res0:{res0},res1:{res1},res2:{res2}')
        print(f'res:{res_flag}')
        # print(f'res0:{success0},res1:{success1},res2:{success2},res3:{success3},res4:{success4},res5:{success5},res6:{success6}, res7:{success7}')

    k = cv2.waitKey(1) & 0xFF
    if k == ord('q'):
        break
    if k == ord('s'):  # 按下s键,显示暂停
        # time.sleep(7)
        cv2.waitKey(6000)
        index += 1

cv2.destroyAllWindows()
for cap in cap_list:
    cap.release()    # 释放摄像头资源

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: STM32定时器触发同步规则组是一种在STM32微控制器中使用的定时器配置模式。它的作用是利用定时器触发来实现多个外设之间的同步操作。 在STM32微控制器中,有多个定时器模块可用于不同的应用需求。这些定时器模块提供了多种功能,如计时、PWM输出、编码器输入等。在一些应用中,我们可能需要多个定时器模块以同步运行,此时就可以使用定时器触发同步规则组。 定时器触发同步规则组允许我们设置一个主定时器,并将其他定时器模块设置为从定时器。主定时器可以通过定时器的输出信号来驱动其他定时器模块的启动、停止、重装载等操作。这样,所有定时器模块的运行将同步在一起,提高了多个外设之间的协同工作效率。 在使用定时器触发同步规则组时,我们需要先配置定时器模块的时钟源、计数模式、重载值等参数。然后,在主定时器溢出或其他特定事件发生时,它会触发其他定时器模块执行相应的操作。这些触发事件可以是定时器中断、外部信号触发、输入捕获等。 总的来说,通过STM32定时器触发同步规则组的使用,我们可以实现多个外设之间的同步操作,提高系统的性能和效率。但在配置和使用过程中,我们需要仔细考虑各个定时器模块的功能和参数设置,以确保它们能够按照要求同步工作。 ### 回答2: STM32定时器触发同步规则组是指在STM32系列微控制器中,通过配置定时器的触发源和同步规则,实现定时器之间的同步操作。 同步规则组主要包括以下几种: 1. 同步:通过编程方式触发定时器的启动和停止操作,使多个定时器在同一时刻开始计时或停止计时。 2. 外部触发同步:通过配置定时器的外部触发源,将多个定时器连接到同一个外部事件源上,使得这些定时器在相同的外部事件触发时开始计时或停止计时。 3. 主从同步:通过配置定时器的主时钟源(Master)和从时钟源(Slave),将多个定时器连接为主从关系,使得主定时器的计时周期可以控制从定时器的启动和停止。 4. 多级级联同步:通过配置定时器的级联模式,将多个定时器级联在一起,使得整个定时器系统按照级联顺序依次开始计时或停止计时。 通过合理配置同步规则组,可以实现多个定时器之间的同步操作,提高系统的定时精度和稳定性。同时,也可以根据具体应用需求,灵活选择不同的同步规则组。在STM32系列微控制器中,定时器触发同步规则组使得定时器的应用更加灵活和可靠。 ### 回答3: STM32定时器触发同步规则组是指具有相同触发源的多个定时器组成的一组规则。在STM32微控制器中,我们可以使用定时器触发同步规则组来实现对各个定时器的同步控制。 定时器触发同步规则组通常由一个主定时器和多个从定时器组成。主定时器负责产生触发信号,然后通过触发信号来启动或停止从定时器的计数。这样可以保证多个定时器的计数始终保持同步。 在使用STM32定时器触发同步规则组时,我们可以通过配置主定时器的计数模式、预分频器和自动重载寄存器等参数来控制触发信号的产生频率和触发时机。同时,我们还可以通过配置从定时器的计数模式、预分频器和自动重载寄存器等参数来控制从定时器的计数方式和计数范围。 通过定时器触发同步规则组,我们可以实现多个定时器之间的同步操作。例如,我们可以将主定时器的触发信号连接到从定时器的启动输入,当主定时器计数满足预设条件时,触发信号会传递到从定时器并启动其计数。这样就可以实现多个定时器同时开始计数,从而实现系统中多个定时任务的同步执行。 总之,STM32定时器触发同步规则组是一种用于实现多个定时器之间同步控制的功能强大的技术。它可以帮助我们实现复杂的定时任务,并提高系统的可靠性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值