基于opencv的单摆绳长测量系统(电赛2021D题)

该文详细介绍了使用OpenCV库,通过树莓派和Jetsonnano设备,结合mjpg-streamer实现实时视频流传输和图像处理,以测量单摆的绳长。主要步骤包括图像传输、接收、处理和周期计算。在图像处理中,运用差帧法检测目标,并计算周期来估算绳长。文章还提出了改进思路,如轮廓处理优化和硬件升级以提高准确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(一)前言

在做嵌入式系统的过程中,其实需要自己从零开始写的代码量是比较少的。可以开玩笑的说只需要ctrl+c 和ctrl+v ,但是关键是要寻找所谓一段段代码的“API”接口。特别是在树莓派和jeston nano 这种系统上,很多指令的操作其实压根就不知道干嘛,就是对着教程一顿输入,然后就好了。

实验原理
在这里插入图片描述在这里插入图片描述

(二)题目

在这里插入图片描述在这里插入图片描述

(三)实现过程

全部直接按照这个: opencv-python——使用mjpg-streamer实现实时视频流获取并进行远程图像处理操作

1.图像传输到网络

树莓派和jetson nano 直接按照里面的命令输入整体步骤
1.1 安装依赖
树莓派

sudo apt-get install subversion  libv4l-dev  libjpeg62-dev  imagemagick libjpeg62-dev libjpeg8-dev

Jetson nano

sudo apt-get install libjpeg8-dev libv4l-dev

1.2. 下载mjpg-streamer包

git clone https://github.com.cnpmjs.org/jacksonliam/mjpg-streamer

1.3. 进入文件夹编译

cd mjpg-streamer/mjpg-streamer-experimental
make
sudo make install

1.4. 运行指令
命令行的参数都可以修改 :
比如设备号/dev/video0 和 设备类型 ./input_uvc.so
-f 30 应该是视频的帧率

(如果固定这个可以通过帧率的计算周期比赛的时候应该是没有输入固定帧率的)

./mjpg_streamer -i "./input_uvc.so -d /dev/video0 -f 30 -r 1280x720" -o "./output_http.so -w ./www"

2.图像接收

# -*- coding:utf-8 -*-
import cv2
import numpy as np
from urllib import request

url = "http://localhost:8080/?action=snapshot"

def downloadImg():
    global url
    with request.urlopen(url) as f:
        data = f.read()
        img1 = np.frombuffer(data, np.uint8)
        img_cv = cv2.imdecode(img1, cv2.IMREAD_ANYCOLOR)
        return img_cv

while True:    
    image = downloadImg() 
    cv2.imshow("frame", image)
    dst = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # BGR转HSV
    cv2.imshow('output', dst)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break    
cv2.destroyAllWindows()

里面只要注意

url = "http://localhost:8080/?action=snapshot"
localhost是树莓派的ip地址

注释:

with as 语句操作上下文管理器(context manager),它能够帮助我们自动分配并且释放资源。
无论期间是否抛出异常,都能保证 with as 语句执行完毕后自动关闭已经打开的文件

with request.urlopen(url) as f:

request.urlopen(url)
首先定义好需要访问的网站网页
然后使用request.urlopen打开相应url并把相应页面作为返回
把返回结果读取出来
示例

import numpy as np
import urllib.request
import cv2
  
# read the image url
url = 'https:'
  
  
with urllib.request.urlopen(url) as resp:
    
    # read image as an numpy array
    image = np.asarray(bytearray(resp.read()), dtype="uint8")
      
    # use imdecode function
    image = cv2.imdecode(image, cv2.IMREAD_COLOR)
  
    # display image
    cv2.imwrite("result.jpg", image)

3.opencv图像处理

利用差帧法进行目标的动态检测,差帧法对背景以及光线的要求没那么高,并且这个代码使用的是前一帧与后一帧的差帧图像。

import cv2 as cv
import numpy as np
flag = 0

while(True):
    ret, src = capture.read()

    gray = cv.cvtColor(src, cv.COLOR_BGRA2GRAY)

    if flag == 0:
        imagelast = gray
        flag = 1
        continue
    else:

        diff = cv.absdiff(imagelast, gray)
        imagelast = gray
        #二值化CV_THRESH_BINARY);
        res, binary = cv.threshold(diff, 40, 255, cv.THRESH_BINARY)
        kernel1 = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
        kernel2 = cv.getStructuringElement(cv.MORPH_RECT, (18, 18))

        dst1 = cv.erode(binary, kernel1)
        dst2 = cv.dilate(dst1, kernel2)

        cv.namedWindow("dst2", cv.WINDOW_AUTOSIZE)
        cv.imshow("dst2", dst2)


 	cloneimage, contours, heriachy = cv.findContours(dst2, 	cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)。
    c = max(contours, key=cv.contourArea, default=None)
    if c is not None:
        rect = cv.minAreaRect(c)
        box = cv.boxPoints(rect)
        x1 = int((np.int0(box)[0][0]+np.int0(box)[3][0])/2)
        x2 = int((np.int0(box)[2][0] + np.int0(box)[1][0])/2)
        x = (x1+x2)/2
        cv.drawContours(src, [np.int0(box)], -1, (0, 255, 0), 2)
  
  	cv.namedWindow("input", cv.WINDOW_AUTOSIZE)
  	cv.imshow("input", src)

效果图在这里插入图片描述在这里插入图片描述

4.周期T的计算

根据论文《基于图像处理的单摆测量重力加速度实验的研究》中的思路
在这里插入图片描述当时我们我们对这个问题的考虑并不是特别全面,而整个测量系统最关键的地方就是在于我们如何比较准确的计算出周期,这就是直接影响到我们测量的绳长的最关键因素。
4.1计算总时间

要加入一下
import time

定义一个空的组:
k = []

每处理完一帧图像之后将图像的中心点center存入
k.append(center)

长度为1时,是第1帧,长度为100,是第100if len(k) == 1:
	t0 = time.time()
if len(k) == 100:
	t1 = time.time()
	time = t1 - t0

这样就计算出来了,这100帧总共的时间

4.2 运动分析以及选数据点
我们近似认为物体在做简谐运动,也就是说x-t和y-t的图像都应该是类似于正弦函数。对物体进行拍照,处理后图像应该是变成离散的点了,可以理解为x-n和y-n的图像。至于是取x-n还是y-n取决于图像处理的质量,有时候x-n拟合的比较好,有时候y-n拟合的比较好。

如下图:这个图像处理后得到的点拟合出来就比较一般。
在这里插入图片描述
下图是当时比较理想的图像处理结果。我记得这个是y-n的图像拟合,而且当时x-n图像拟合的结果如上图类似的状况,并不好。
在这里插入图片描述给出MATLAB的拟合代码
输入x=(x,y)就行了

n1=length(x(:,1));
kk=1:n1;
pp=spline(kk,x(1:n1));
kk1=linspace(0,99,100);
yy = ppval(pp,kk1);
plot(kk1,yy)
hold on
plot(x(1:100),'*')

4.3获取波峰点或波谷点
如上图所示的图像,两个波峰点或波谷点之间的时间即为运动周期T

存储:一系列波峰在第几点
k_num = []

寻找波峰
for i in range(1,99)
	if k[i-1]<=k[i] and k[i+1]<=k[i]
		k_num.append(i)

4.4计算时间

有几个波峰
n_top = len(k_num)

这几个波峰之间有多少个点(最后一个波峰点的序列号 - 第一个波峰点的序列号)
number_point = k_num[-1] - k_num[0]

周期 = (这N个波峰之间点的个数/100个点)*总时间/(N个波峰-1)   time :4.1中已经求出来了
T = (number_point/100)*time/(n_top-1)

(四)改进思路

1.轮廓处理
出现这种情况
在这里插入图片描述在这里插入图片描述方法一:如果进行轮廓的合并处理,就能把这两个轮廓合成一个大的轮廓,再找外接矩形,就能极大的减小这种情况带来的误差。
方法二:找到多个轮廓时,我们会选择采取选择面积最大的轮廓或者周长最长的轮廓。而一般来说同一个物体如果经过图像处理后得到了多个轮廓,那这几个轮廓在y轴的相差量会比较大,而在x轴的相差量会毕竟小。可以结合4.2中的拟合,尽量采用x-n的图像。

2.硬件升级
做一个白色的背景板,将这套系统放置在环境光线稳定的条件下,那么我认为可以不用差帧法,而使用HSV色域的颜色识别,那么准确的将大大提高。
注意:还是找最大轮廓,比较保险。但是其实也无所谓,因为HSV色域的识别在上述条件下我觉得是十分好的方案。当时我们也是理解错题目要求了,以为不能搭建这种完美的识别条件。

import numpy
import cv2 as cv

col_black = (0,0,0,180,255,46)# black
col_red = (0,100,80,10,255,255)# red
col_blue = (90,90,90,110,255,255)# blue
col_green= (65,70,70,85,255,255)# green
col_yellow = (26,43,46,34,255,255)# yellow

capture = cv.VideoCapture(0)
while(True):
        ret, src = capture.read()

        hsv = cv.cvtColor(src, cv.COLOR_BGR2HSV)
        # 颜色的范围        # 第二个参数:lower指的是图像中低于这个lower的值,图像值变为0
        # 第三个参数:upper指的是图像中高于这个upper的值,图像值变为0
        # 而在lower~upper之间的值变成255
        kernel = numpy.ones((5, 5), numpy.uint8)
        hsv_erode = cv.erode(hsv, kernel, iterations=1)
        hsv_dilate = cv.dilate(hsv_erode, kernel, iterations=1)
        lowerbH = col_red[0]
        lowerbS = col_red[1]
        lowerbV = col_red[2]
        upperbH = col_red[3]
        upperbS = col_red[4]
        upperbV = col_red[5]
        mask = cv.inRange(hsv_dilate, (lowerbH, lowerbS, lowerbV), (upperbH, upperbS, upperbV))



		# 找轮廓只能找二值化图像
        cloneimage, contours, heriachy = cv.findContours(mask, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
		#cv.boundingRect计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的
        bounding_boxes = [cv.boundingRect(cnt) for cnt in contours]
		#还是找最大轮廓吧 比较保险 其实也无所谓 
        for bbox in bounding_boxes:
            [x, y, w, h] = bbox
            cv.rectangle(src, (x, y), (x + w, y + h), (0, 255, 0), 2)
            x1 = x + w/2
            y1 = y + h/2
            print(x1,y1)

        cv.namedWindow("input", cv.WINDOW_AUTOSIZE)
        cv.imshow("input", src)

        c = cv.waitKey(0)
        if c == 27:
            break
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云影点灯大师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值