2023年电赛E题-运动目标控制与自动追踪系统(OpenCv)

2023-11-06 21:59

本文主要是介绍2023年电赛E题-运动目标控制与自动追踪系统(OpenCv),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

比赛经历以及思路

步进电机云台代码

找激光坐标代码

找轮廓形状代码

找黑色电工胶带代码

“指哪打哪”云台运动代码

直线拟合找黑框中心点代码

完整代码


比赛经历以及思路

        在导师分配完参赛成员后,我被分到了和大三的学长学姐一队。我主要是负责软件部分,学长主要负责硬件部分,学姐则是负责论文的撰写。虽然我们队伍的分工已经很明确了,但是到硬件部分,该帮忙的地方还是要一起完成,这才是一个团队该有的样子。​​​​​​​

       第一天题目刚发下来,我们就懵了。本来是想做小车的题目,没想到陆空合体了,所以只能换题目做,之前我把2022年省赛的声源定位做过一遍,今年本来想选F题声传播的定位系统,但是好像这个E题更简单些,所以果断选择E题(专挑软柿子捏),我的队友也表示认同。

        第一天上午确认完题目后,我们就猜到今年肯定超级多人选择E题,我们用的是树莓派,毕竟要实现起来相对比较简单,所以要比拼的一定是精度!第一天上午到下午这段时间我一直在调舵机,但是我们的舵机性能比较差,运动起来不是那么的平稳,毕竟是老舵机了。网上新买的装备也一时没有到货,我们决定还是要用步进电机来做。一开始不确定能不能用步进电机,但是组委会是说不让用打字机那样的步进电机,也就是步进电机不能控制平移,我们是用步进电机模拟二维舵机云台,只是用于旋转是可以的。

        第一天下午,我是想把激光笔回归中心点的坐标给写死,但是感觉换个环境后回归中心点时的误差会比较大。经过仔细读题后,我们是可以用铅笔画一个黑框(width<1mm)出来的。到晚上我就准备用OpenCv去识别那个黑框,然后把中心点拟合出来。

        第一天晚上,我以为识别铅笔黑框不会太难,但是我开始写代码的时候发现,这个铅笔框也太细了吧,根本识别不出来,一个小小的阴影可能都比黑色边框更明显!经过我一番猛调阈值后,那个轮廓终于有了一丁点样子。但是边框不是连续的,一个边可能就几个像素点经过筛选后被保留下来。不过对于我这种机器视觉大师来说,识别这种小黑框根本不在话下(bushi)。

  

        小学我们就学过,两点确定一条直线,虽然一条边上的黑色像素点不连续,但是我可以根据这些像素点fitLine拟合出一条直线来。四条边拟合出四条直线,然后直线两两相交就可以把四个边长的交点坐标算出来,那么中心点的坐标不就手到擒来🚀!

        至于识别红色激光笔的坐标,相较于拟合铅笔黑色边框,识别红色激光笔就太简单了,先一个hsv变换,再做一个开运算,设置一下红色的阈值,最后用inRange函数二值化处理一下,就能把激光笔的颜色筛选出来。

        写完这些代码已经到了第二天凌晨2点,第一天就选择熬夜显然不是明智之举,所以我小睡了一会。大概在早上七点,我就起床去实验室了,去到实验室后突然感觉时间还很充足。我就在帮忙搭硬件,最后测试的时候,能不能拿奖拼的还是硬件够不够硬朗。去年学长学姐好像就是因为电机临时坏掉了,错失省奖!

        第二天早上,我就在用A4白纸去贴一个60cm * 60cm的屏幕,A4纸太小了,贴起来一定要严丝合缝,不然出现褶皱,识别黑框就会有问题(虽然经过我的代码优化后,没有多大问题,但是做事还是要做细致点😃),因此我做一块新的白色屏幕花了好久。

        第二天下午,我们就开始搭铁架台,这个铁架台后续测试的时候把我们害惨了!铁甲台的主要作用就是放我们的一些装置,一开始以为测试的地方是一片空地,装置随便摆放肯定不好。没想到全省也就我们学校几个组有个台子,其他的组都是用测试地点的凳子(不是不能用封箱以外的东西吗?有点想吐槽!)

        第二天晚上熬了一整夜(非明智之举)。激光云台装置离屏幕装置是固定1m的距离,摄像头拍到的像素坐标和实际距离需要进行换算,最后通过arctan数学换算得到二维云台需要运动的角度。写完这个函数,就可以让红色激光笔在白色屏幕上“指哪打哪”!当时经过换算好像是1个像素点对应实际距离1.355mm。

        第三天凌晨3点多就把要求1部分做完了,测试了一遍,回归中心点非常完美。进度虽然有点慢。但是我已经把最艰难的地方给攻克了,说实话这个进度还是有点慌的!​​​​​​​

         一开始学长和我说,第四天八点就要封箱了。我以为是第四天早上八点就封箱,第三天我就开始猛肝了,所以很多实验结果的图和视频都没来得及拍😭。要求2部分就是红色激光笔绕着黑色边框走一圈,第一天我就把黑色边框给拟合出来了,而且“指哪打哪”的代码也写好了,因此要求2一下子就被解决了。

        题目是说循黑色边框不能超出边框2cm,所以每次绕边长走的时候要矫正一下累计误差。本来是想在边长里面分400个点出来,让红色激光笔走的更丝滑一点,由于时间问题,最后就是每四个点矫正一次,走黑框走的也是非常完美!

        这可能就是搞科研和比赛的差别吧,搞科研就是要做到精益求精,比赛则是要在短时间内把事情做好。所以不能花太多时间来做到绝对完美,但也要尽量做到比别人更好!

        其实要求3和要求4本质上是一样的,一个是平着放一个是要旋转一定的角度放,但是我是用机器视觉来做,所以怎么样放都无所谓。我们要让红色激光笔在粘了1.8cm宽电工胶带的A4纸上走一圈。一开始我觉得我这个题目是不是太弱智了,1mm的黑色边框我都能走,1.8cm的电工胶带我还走不了?还放在要求3和要求4,瞧不起谁呢!

        没想到是我肤浅了!激光笔打在电工胶带上就被吞噬了🤯,电工胶带会吸收激光,导致红色激光笔的坐标识别不到。一个比较好的处理结果就是膨胀!膨胀!膨胀!(机器视觉里的专业名词:膨胀,大致的意思就是让细的东西变粗)如果不知道红色激光笔在黑色电工胶带上的坐标,那么就没办法消除累计误差。出一次黑色电工胶带就扣2分!

        要想激光笔在黑色电工胶正中间走(红色激光点不出黑色电工胶),就要找到外轮廓线以及内轮廓线,算出黑色电工胶带的中间框线。找轮廓真是OpenCv的一个必修课啊!

        第三天晚上到第四天凌晨,把基础部分做完感觉舒畅多了。差不多连续工作72小时,有点累啊,虽然有时候在摸鱼🦑。然后我去给学姐讲了下基本思路,让她好写论文。(学姐是真漂亮,而且很温柔,人特别好,特别爱笑,非常棒!)

        学长做事也非常认真!他把需要用到的杜邦线全都归类整理好了,对此我赞不绝口!到时候测试的时候就不怕线突然掉了不知道接哪里了。

        第四天早上,要解决掉发挥部分的第一问,绿色激光追踪红色激光。直接把“指哪打哪”云台的代码和检测红色激光笔以及绿色激光笔坐标的代码结合起来就可以了。

        最难的是发挥部分2,核心代码依旧是检测到红绿激光的坐标,“指哪打哪”云台代码,追踪。但是我测试出来效果不是很稳定,这里就不分享具体思路了。由于时间有限以及能力有限,我也没具体优化,主要是太困了没精力肝了,果然前期熬夜是不明智的抉择。之后的时间就去封箱了!原来学长的意思是第四天晚上8点封箱。

        封完箱,把作品放到车上后,终于体会到了什么是“轻舟已过万重山”!电赛四天三夜,我三夜未睡,两天没吃,不过感觉非常充实,我真的太喜欢这个团队了!可惜学长学姐大三了,之后要准备考研了,而我才大一,以后见面的机会就几乎没有啦😭!

步进电机云台代码

import RPi.GPIO as GPIO
import time
import numpy as np
import math# 规定GPIO引脚
IN1 = 18      # 接PUL-
IN2 = 16      # 接PUL+
IN3 = 15      # 接DIR-
IN4 = 13      # 接DIR+# 云台上面的电机驱动器,物理引脚
IN12 = 38      # 接PUL-
IN22 = 40      # 接PUL+
IN32 = 36      # 接DIR-
IN42 = 32      # 接DIR+delay = 0.0001def setup():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical locationGPIO.setup(IN1, GPIO.OUT)      # Set pin's mode is outputGPIO.setup(IN2, GPIO.OUT)GPIO.setup(IN3, GPIO.OUT)GPIO.setup(IN4, GPIO.OUT)def setup2():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical locationGPIO.setup(IN12, GPIO.OUT)      # Set pin's mode is outputGPIO.setup(IN22, GPIO.OUT)GPIO.setup(IN32, GPIO.OUT)GPIO.setup(IN42, GPIO.OUT)def setStep(w1, w2, w3, w4):GPIO.output(IN1, w1)GPIO.output(IN2, w2)GPIO.output(IN3, w3)GPIO.output(IN4, w4)def setStep2(w1, w2, w3, w4):GPIO.output(IN12, w1)GPIO.output(IN22, w2)GPIO.output(IN32, w3)GPIO.output(IN42, w4)def stop():setStep(0, 0, 0, 0)def stop2():setStep2(0, 0, 0, 0)def rightward(steps):global delayfor i in range(0, steps):setStep(1, 0, 1, 0)time.sleep(delay)setStep(0, 1, 1, 0)time.sleep(delay)setStep(0, 1, 0, 1)time.sleep(delay)setStep(1, 0, 0, 1)time.sleep(delay)stop()def rightward2(steps):global delayfor i in range(0, steps):setStep2(1, 0, 1, 0)time.sleep(delay)setStep2(0, 1, 1, 0)time.sleep(delay)setStep2(0, 1, 0, 1)time.sleep(delay)setStep2(1, 0, 0, 1)time.sleep(delay)stop2()def leftward(steps):global delayfor i in range(0, steps):setStep(1, 0, 0, 1)time.sleep(delay)setStep(0, 1, 0, 1)time.sleep(delay)setStep(0, 1, 1, 0)time.sleep(delay)setStep(1, 0, 1, 0)time.sleep(delay)stop()def leftward2(steps):global delayfor i in range(0, steps):setStep2(1, 0, 0, 1)time.sleep(delay)setStep2(0, 1, 0, 1)time.sleep(delay)setStep2(0, 1, 1, 0)time.sleep(delay)setStep2(1, 0, 1, 0)time.sleep(delay)stop2()def downward(steps):global delayfor i in range(0, steps):setStep(1, 0, 0, 1)time.sleep(delay)setStep(0, 1, 0, 1)time.sleep(delay)setStep(0, 1, 1, 0)time.sleep(delay)setStep(1, 0, 1, 0)time.sleep(delay)stop()def downward2(steps):global delayfor i in range(0, steps):setStep2(1, 0, 0, 1)time.sleep(delay)setStep2(0, 1, 0, 1)time.sleep(delay)setStep2(0, 1, 1, 0)time.sleep(delay)setStep2(1, 0, 1, 0)time.sleep(delay)stop2()def upward(steps):global delayfor i in range(0, steps):setStep(1, 0, 1, 0)time.sleep(delay)setStep(0, 1, 1, 0)time.sleep(delay)setStep(0, 1, 0, 1)time.sleep(delay)setStep(1, 0, 0, 1)time.sleep(delay)stop()def upward2(steps):global delayfor i in range(0, steps):setStep2(1, 0, 1, 0)time.sleep(delay)setStep2(0, 1, 1, 0)time.sleep(delay)setStep2(0, 1, 0, 1)time.sleep(delay)setStep2(1, 0, 0, 1)time.sleep(delay)stop2()# 测试函数
# angle单位:度  0.036= 360/10000
def loop(angle):print ("rightward--->leftward:")rightward(int(angle/360*6400))   # 发射脉冲时间间隔0.0001(单位秒)   脉冲个数angle/0.036leftward(int(angle/360*6400))print ("stop...")time.sleep(1)          # sleep 1sdef loop2(angle):print ("upward--->downward:")upward2(int(angle/360*6400))   # 发射脉冲时间间隔0.0001(单位秒)   脉冲个数angle/0.036stop2()downward2(int(angle/360*6400))print ("stop...")stop2()                 # stoptime.sleep(1)          # sleep 3sdef destroy():GPIO.cleanup()             # 释放数据if __name__ == '__main__':     # Program start from heresetup()setup2()while True:try:# 测试1:loop是下面的电机测试,loop2是上面的电机测试,若两者同时测试,要注意电机被线绕住。# 左转是leftward()函数,右转是rightward()函数# 上转是upward()函数,下转是downward()函数a = input("please input angle:")  t0 = time.time()# loop(float(a))    # 下面的电机可0~360度测试,loop2(float(a))  # 由于位置限制,上面电机的角度不能大,可输入0~30度测试print(time.time() - t0)       except KeyboardInterrupt:  # When 'Ctrl+C' is pressed, the child function destroy() will be  executed.destroy()

找激光坐标代码

import cv2
import numpy as npdef findLaser(img):"""找到图片中点绿色激光点与红色激光点并定位中心:param img: 需要处理点图片:return: 绿色激光点中心(x1, y1);红色激光点中心(x2, y2)"""cX1, cY1, cX2, cY2 = None, None, None, NonegreenLaser = 'green'redLaser = 'red'# 色系下限上限表color_dist = {'red': {'Lower': np.array([0, 60, 60]), 'Upper': np.array([6, 255, 255])},'green': {'Lower': np.array([35, 43, 35]), 'Upper': np.array([90, 255, 255])},}# 灰度图像处理# gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#灰度图# cv2.imshow('gray', gray)# 高斯滤波blurred = cv2.GaussianBlur(img, (11, 11), 0)# cv2.imshow('blurred', blurred)# 创建运算核kernel = np.ones((1, 1), np.uint8)# 腐蚀# erode = cv2.erode(img, kernel, iterations=1)# 膨胀# img_dilate = cv2.dilate(blurred, kernel, iterations = 5)# 开运算opening = cv2.morphologyEx(blurred, cv2.MORPH_OPEN, kernel)# 二值化处理thresh = cv2.threshold(opening, 230, 255, cv2.THRESH_BINARY)[1]cv2.imshow('thresh', thresh)hsv = cv2.cvtColor(thresh, cv2.COLOR_BGR2HSV)  # 转化成HSV图像# 颜色二值化筛选处理inRange_hsv_green = cv2.inRange(hsv, color_dist[greenLaser]['Lower'], color_dist[greenLaser]['Upper'])inRange_hsv_red = cv2.inRange(hsv, color_dist[redLaser]['Lower'], color_dist[redLaser]['Upper'])cv2.imshow('inrange_hsv_green', inRange_hsv_green)cv2.imshow('inrange_hsv_red', inRange_hsv_red)# 找绿色激光点try:cnts1 = cv2.findContours(inRange_hsv_green.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]c1 = max(cnts1, key=cv2.contourArea)M = cv2.moments(c1)cX1 = int(M["m10"] / M["m00"])cY1 = int(M["m01"] / M["m00"])cv2.circle(img, (cX1, cY1), 3, (0, 255, 0), -1)rect = cv2.minAreaRect(c1)box = cv2.boxPoints(rect)cv2.drawContours(img, [np.int0(box)], -1, (0, 255, 0), 2)except:print('没有找到绿色的激光')# 找红色激光点try:cnts2 = cv2.findContours(inRange_hsv_red.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]c2 = max(cnts2, key=cv2.contourArea)M = cv2.moments(c2)cX2 = int(M["m10"] / M["m00"])cY2 = int(M["m01"] / M["m00"])cv2.circle(img, (cX2, cY2), 3, (0, 0, 255), -1)rect = cv2.minAreaRect(c2)box = cv2.boxPoints(rect)cv2.drawContours(img, [np.int0(box)], -1, (0, 0, 255), 2)except:print('没有找到红色的激光')return cX1, cY1, cX2, cY2if __name__ == '__main__':img = cv2.imread('/Users/duanhao/Desktop/Electronic Design Competion/2023电子设计大赛/laser/15.jpg')x1, y1, x2, y2 = findLaser(img)print(f'绿色激光点坐标点({x1}, {y1})')print(f'红色激光点坐标点({x2}, {y2})')cv2.imshow('camera', img)cv2.waitKey()cv2.destroyAllWindows()

找轮廓形状代码

import cv2
import numpy as np#定义形状检测函数
def ShapeDetection(img):contours,hierarchy = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)  #寻找轮廓点for obj in contours:area = cv2.contourArea(obj)  #计算轮廓内区域的面积cv2.drawContours(imgContour, obj, -1, (255, 0, 0), 4)  #绘制轮廓线perimeter = cv2.arcLength(obj,True)  #计算轮廓周长approx = cv2.approxPolyDP(obj,0.02*perimeter,True)  #获取轮廓角点坐标CornerNum = len(approx)   #轮廓角点的数量x, y, w, h = cv2.boundingRect(approx)  #获取坐标值和宽度、高度#轮廓对象分类if CornerNum ==3: objType ="triangle"elif CornerNum == 4:for i in range(0,4):print("矩形角坐标%d:"%(i+1),approx[i][0][0], approx[i][0][1])cv2.circle(imgContour, (approx[i][0][0], approx[i][0][1]), 5, (0,255,255), 3)if w==h: objType= "Square"else:objType="Rectangle "elif CornerNum>4: objType= " red"else:objType="N"cv2.rectangle(imgContour,(x,y),(x+w,y+h),(0,0,255),2)  #绘制边界框cv2.putText(imgContour,objType,(x+(w//2),y+(h//2)),cv2.FONT_HERSHEY_COMPLEX,0.6,(0,0,0),1)  #绘制文字path = '/Users/duanhao/Desktop/black/2.jpg'
img = cv2.imread(path)
imgContour = img.copy()imgGray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)  #转灰度图
imgBlur = cv2.GaussianBlur(imgGray,(5,5),1)  #高斯模糊
imgCanny = cv2.Canny(imgBlur,60,60)  #Canny算子边缘检测
ShapeDetection(imgCanny)  #形状检测cv2.imshow("Original img", img)
cv2.imshow("imgGray", imgGray)
cv2.imshow("imgBlur", imgBlur)
cv2.imshow("imgCanny", imgCanny)
cv2.imshow("shape Detection", imgContour)cv2.waitKey(0)

找黑色电工胶带代码

import cv2
import numpy as np#定义形状检测函数
def ShapeDetection(img):contours,hierarchy = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)  #寻找轮廓点for obj in contours:area = cv2.contourArea(obj)  #计算轮廓内区域的面积# cv2.drawContours(imgContour, obj, -1, (255, 0, 0), 4)  #绘制轮廓线perimeter = cv2.arcLength(obj,True)  #计算轮廓周长approx = cv2.approxPolyDP(obj,0.02*perimeter,True)  #获取轮廓角点坐标CornerNum = len(approx)   #轮廓角点的数量x, y, w, h = cv2.boundingRect(approx)  #获取坐标值和宽度、高度if CornerNum == 4:for i in range(0,4):cv2.circle(imgContour, (approx[i][0][0], approx[i][0][1]), 2, (0,0,255), 3)axis.append([approx[i][0][0], approx[i][0][1]])# cv2.rectangle(imgContour,(x, y),(x+w, y+h),(0,0,255),2)  #绘制边界框if __name__ == '__main__':axis = []# 导入图片imgimg = cv2.imread('/Users/duanhao/Desktop/Electronic Design Competion/2023电子设计大赛/black/5.jpg')# 需要裁剪的区域###################################hStart, hEnd, wStart, wEnd = 250, 650, 320, 700################################################cropImg = img[hStart:hEnd, wStart:wEnd]cv2.imshow('cropImg', cropImg)# 灰度图像处理gray = cv2.cvtColor(cropImg, cv2.COLOR_BGR2GRAY)#灰度图cv2.imshow('gray', gray)imgContour = cropImg.copy()# 图像inRange二值化处理hsv = cv2.cvtColor(cropImg, cv2.COLOR_BGR2HSV)l_g = np.array([0, 0, 0])  # 阈值下限u_g = np.array([255, 113, 46])  # 阈值上限mask = cv2.inRange(hsv, l_g, u_g)cv2.imshow('mask', mask)ShapeDetection(mask)# 从逆时针改为顺时针axis[0], axis[2] = axis[2], axis[0]print(axis)cv2.imshow("crop", imgContour)cv2.waitKey(0)cv2.destroyAllWindows()

“指哪打哪”云台运动代码

import cv2
import math
import numpy as np
import RPi.GPIO as GPIO
import time# 规定GPIO引脚
IN1 = 18  # 接PUL-
IN2 = 16  # 接PUL+
IN3 = 15  # 接DIR-
IN4 = 13  # 接DIR+# 云台上面的电机驱动器,物理引脚
IN12 = 38  # 接PUL-
IN22 = 40  # 接PUL+
IN32 = 36  # 接DIR-
IN42 = 32  # 接DIR+delay = 0.0001def downsetup():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)  # Numbers GPIOs by physical locationGPIO.setup(IN1, GPIO.OUT)  # Set pin's mode is outputGPIO.setup(IN2, GPIO.OUT)GPIO.setup(IN3, GPIO.OUT)GPIO.setup(IN4, GPIO.OUT)def upsetup():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)  # Numbers GPIOs by physical locationGPIO.setup(IN12, GPIO.OUT)  # Set pin's mode is outputGPIO.setup(IN22, GPIO.OUT)GPIO.setup(IN32, GPIO.OUT)GPIO.setup(IN42, GPIO.OUT)def downsetStep(w1, w2, w3, w4):GPIO.output(IN1, w1)GPIO.output(IN2, w2)GPIO.output(IN3, w3)GPIO.output(IN4, w4)def upsetStep(w1, w2, w3, w4):GPIO.output(IN12, w1)GPIO.output(IN22, w2)GPIO.output(IN32, w3)GPIO.output(IN42, w4)def downstop():# 下面的电机停止downsetStep(0, 0, 0, 0)def upstop():# 上面的电机停止upsetStep(0, 0, 0, 0)def rightward(steps):# 下面的电机向右转动global delayfor i in range(0, steps):downsetStep(1, 0, 1, 0)time.sleep(delay)downsetStep(0, 1, 1, 0)time.sleep(delay)downsetStep(0, 1, 0, 1)time.sleep(delay)downsetStep(1, 0, 0, 1)time.sleep(delay)downstop()# def rightward2(steps):
#     global delay
#     for i in range(0, steps):
#         upsetStep(1, 0, 1, 0)
#         time.sleep(delay)
#         upsetStep(0, 1, 1, 0)
#         time.sleep(delay)
#         upsetStep(0, 1, 0, 1)
#         time.sleep(delay)
#         upsetStep(1, 0, 0, 1)
#         time.sleep(delay)
#     upstop()def leftward(steps):# 下面的电机向左转动global delayfor i in range(0, steps):downsetStep(1, 0, 0, 1)time.sleep(delay)downsetStep(0, 1, 0, 1)time.sleep(delay)downsetStep(0, 1, 1, 0)time.sleep(delay)downsetStep(1, 0, 1, 0)time.sleep(delay)downstop()# def leftward2(steps):
#     global delay
#     for i in range(0, steps):
#         upsetStep(1, 0, 0, 1)
#         time.sleep(delay)
#         upsetStep(0, 1, 0, 1)
#         time.sleep(delay)
#         upsetStep(0, 1, 1, 0)
#         time.sleep(delay)
#         upsetStep(1, 0, 1, 0)
#         time.sleep(delay)
#     upstop()# def downward(steps):
#     global delay
#     for i in range(0, steps):
#         downsetStep(1, 0, 0, 1)
#         time.sleep(delay)
#         downsetStep(0, 1, 0, 1)
#         time.sleep(delay)
#         downsetStep(0, 1, 1, 0)
#         time.sleep(delay)
#         downsetStep(1, 0, 1, 0)
#         time.sleep(delay)
#     downstop()def downward2(steps):# 上面的电机向下转动global delayfor i in range(0, steps):upsetStep(1, 0, 0, 1)time.sleep(delay)upsetStep(0, 1, 0, 1)time.sleep(delay)upsetStep(0, 1, 1, 0)time.sleep(delay)upsetStep(1, 0, 1, 0)time.sleep(delay)upstop()# def upward(steps):
#     global delay
#     for i in range(0, steps):
#         downsetStep(1, 0, 1, 0)
#         time.sleep(delay)
#         downsetStep(0, 1, 1, 0)
#         time.sleep(delay)
#         downsetStep(0, 1, 0, 1)
#         time.sleep(delay)
#         downsetStep(1, 0, 0, 1)
#         time.sleep(delay)
#     downstop()def upward2(steps):# 上面的电机向上转动global delayfor i in range(0, steps):upsetStep(1, 0, 1, 0)time.sleep(delay)upsetStep(0, 1, 1, 0)time.sleep(delay)upsetStep(0, 1, 0, 1)time.sleep(delay)upsetStep(1, 0, 0, 1)time.sleep(delay)upstop()# 测试函数
# angle单位:度  0.036= 360/10000
# def loop(angle):
#     print ("rightward--->leftward:")
#     rightward(int(angle/360*6400))   # 发射脉冲时间间隔0.0001(单位秒)   脉冲个数angle/0.036
#     leftward(int(angle/360*6400))
#     print ("downstop...")
#     time.sleep(1)          # sleep 1s# def loop2(angle):
#     print ("upward--->downward:")
#     upward2(int(angle/360*6400))   # 发射脉冲时间间隔0.0001(单位秒)   脉冲个数angle/0.036
#     upstop()
#     downward2(int(angle/360*6400))#     print ("downstop...")
#     upstop()                 # downstop
#     time.sleep(1)          # sleep 3sdef destroy():GPIO.cleanup()  # 释放数据curpos = np.array([-1000, 1000])  # mm,mmpi = 3.14159# 坐标原点在左上角,向左为x轴,向下为y轴。坐标单位像素个数
# x轴移动距离:dx:移动的距离
# 转换:1像素=1.75mm#################测试程序
def xrunpixel(dxpixel):# 下面的电机转动多少像素的位置xangle = math.atan(dxpixel / 1000 / 1.75) * 180 / pi  # 单位:度,1000mm需转换为像素个数单位if xangle > 0:rightward(int(xangle / 360 * 6400))else:xangle = -xangleleftward(int(xangle / 360 * 6400))# y轴移动距离:dy:移动的距离(像素=mm/1.75)
def yrunpixel(dypixel):# 上面的电机转动多少像素的位置yangle = math.atan(dypixel / 1000 / 1.75) * 180 / piif yangle > 0:upward(int(yangle / 360 * 6400))else:yangle = -yangledownward(int(yangle / 360 * 6400))###################### dxstep:x移动的距离(step,int,步进电机步数)
def xrunstep(dxstep):# 下面的电机转动的步数if dxstep > 0:rightward(dxstep)else:dxstep = -dxstepleftward(dxstep)# dystep:x移动的距离(步长数,int)
def yrunstep(dystep):# 上面的电机转动的步数if dystep > 0:downward2(dystep)else:dystep = -dystepupward2(dystep)# linepos = np.array([[1,2],[3,4],[5,6]])  # 像素单位# def runline(linepos):
#     points = linepos.shape[0]  # 总点数
#     xcumsum = np.cumsum(linepos[:,0])
#     ycumsum = np.cumsum(linepos[:,1])
#     xcumsum = int(math.atan(ycumsum/1374)*1018.591636)     # 1374=1000*1.374,1018.591636 = 1/math.pi*180/360*6400
#     ycumsum = int(math.atan(ycumsum/1374)*1018.591636)#     for i in range(1,points):
#         xrunstep(xcumsum[i] - xcumsum[i-1])
#         yrunstep(ycumsum[i] - ycumsum[i-1])# points[0,:] 起点位置,points[1,:] 目标位置,# 像素单位
points = np.array([[1, 2], [3, 4]])def runtwopoints(points):# 从第一个点的位置移动到第二点的位置temp = points[1, 0] - points[0, 0]xstep = int(math.atan(temp / 738) * 1018.591636)  # int((points[1,0]-points[0,0])*0.57142857)temp = points[1, 1] - points[0, 1]ystep = int(math.atan(temp / 738) * 1018.591636)print('xstep = ', xstep)print('ystep = ', ystep)xrunstep(xstep)yrunstep(ystep)#########测试程序
# x坐标max: 570,1个像素0.738mm,对应距离1000mm
zeropos = np.array([100, 100])  # 像素
curpos = np.array([500, 350])  # 像素def returnZero():# 从当前位置curpos回到原点zeroposglobal curposglobal zeroposglobal pointspoints[0, :] = curpospoints[1, :] = zeroposruntwopoints(points)# boundary =  np.array([[300, 300], [295, 295], [290, 290], [285, 285], [280, 280], [275, 275], [270, 271], [265, 266], [260, 261], [255, 256], [250, 251], [245, 246], [240, 242], [235, 237], [230, 232], [225, 227], [220, 222], [215, 218], [210, 213], [205, 208], [200, 203], [195, 198], [190, 193], [185, 189], [180, 184], [175, 179], [170, 174], [165, 169], [160, 165], [155, 160],
#                      [150, 155], [145, 150], [140, 145], [135, 140], [130, 136], [125, 131], [120, 126], [115, 121], [110, 116], [105, 111], [100, 107], [95, 102], [90, 97], [85, 92], [80, 87], [75, 83], [70, 78], [65, 73], [60, 68], [55, 63], [50, 58], [45, 54], [40, 49], [35, 44], [30, 39], [25, 34], [20, 30], [25, 29], [30, 29], [35, 29], [40, 29], [45, 29],
#                      [50, 29], [55, 29], [60, 29], [65, 29], [70, 28], [75, 28], [80, 28], [85, 28], [90, 28], [95, 28], [100, 28], [105, 28], [110, 28], [115, 28], [120, 27], [125, 27], [130, 27], [135, 27], [140, 27], [145, 27], [150, 27], [155, 27], [160, 27], [165, 26], [170, 26], [175, 26], [180, 26], [185, 26], [190, 26], [195, 26], [200, 26], [205, 26], [210, 26],
#                      [215, 25], [220, 25], [225, 25], [230, 25], [235, 25], [240, 25], [245, 25], [250, 25], [255, 25], [260, 25], [265, 24], [270, 24], [275, 24], [280, 24], [285, 24], [290, 24], [295, 24], [300, 24], [305, 24], [310, 23], [315, 23], [320, 23], [325, 23], [330, 23], [335, 23], [340, 23], [345, 23], [350, 23], [355, 23], [360, 22], [365, 22], [370, 22], [375, 22],
#                      [380, 22], [385, 22], [390, 22], [395, 22], [400, 22], [405, 21], [410, 21], [415, 21], [420, 21], [425, 21], [430, 21], [435, 21], [440, 21], [445, 21], [450, 21], [455, 20], [460, 20], [465, 20], [470, 20], [475, 20], [480, 20], [485, 20], [490, 20], [495, 20], [500, 20], [500, 20], [499, 24], [498, 29], [497, 34], [496, 39], [495, 44],
#                      [494, 48], [493, 53], [492, 58], [491, 63], [490, 68], [489, 72], [488, 77], [487, 82], [486, 87], [485, 92], [484, 96], [483, 101], [482, 106], [481, 111], [480, 116], [479, 120], [478, 125], [477, 130], [476, 135], [475, 140], [474, 144], [473, 149], [472, 154], [471, 159], [470, 164], [469, 168], [468, 173], [467, 178], [466, 183], [465, 188], [464, 192],
#                      [463, 197], [462, 202], [461, 207], [460, 212], [459, 216], [458, 221], [457, 226], [456, 231], [455, 236], [454, 240], [453, 245], [452, 250], [451, 255], [450, 260], [449, 264], [448, 269], [447, 274], [446, 279], [445, 284], [444, 288], [443, 293], [442, 298], [441, 303], [440, 308], [439, 312], [438, 317], [437, 322], [436, 327], [435, 332],
#                      [434, 336], [433, 341], [432, 346], [431, 351], [430, 356], [429, 360], [428, 365], [427, 370], [426, 375], [425, 380], [424, 384], [423, 389], [422, 394], [421, 399], [420, 404], [419, 408], [418, 413], [417, 418], [416, 423], [415, 428], [414, 432], [413, 437], [412, 442], [411, 447], [410, 452], [409, 456], [408, 461], [407, 466], [406, 471], [405, 476],
#                      [404, 480], [403, 485], [402, 490], [400, 500], [395, 497], [390, 494], [385, 491], [380, 489], [375, 486], [370, 483], [365, 481], [360, 478], [355, 475], [350, 472], [345, 470], [340, 467], [335, 464], [330, 462], [325, 459], [320, 456], [315, 454], [310, 451], [305, 448], [300, 445], [295, 443], [290, 440], [285, 437], [280, 435], [275, 432],
#                      [270, 429], [265, 427], [260, 424], [255, 421], [250, 418], [245, 416], [240, 413], [235, 410], [230, 408], [225, 405], [220, 402], [215, 400], [210, 397], [205, 394], [200, 391], [195, 389], [190, 386], [185, 383], [180, 381], [175, 378], [170, 375], [165, 372], [160, 370], [155, 367], [150, 364], [145, 362], [140, 359], [135, 356], [130, 354], [125, 351],
#                      [120, 348], [115, 345], [110, 343], [105, 340], [100, 337], [95, 335], [90, 332], [85, 329], [80, 327], [75, 324], [70, 321], [65, 318], [60, 316], [55, 313], [50, 310], [45, 308], [40, 305], [35, 302], [30, 300], [29, 273], [28, 246], [27, 219], [26, 192], [25, 165], [24, 138], [23, 111], [22, 84]])
# def runboundary():
#     global boundary
#     num = boundary.shape[0]
#     #print('num = ',num)
#     for i in range(0,num-1):
#         #print('i = ',i)
#         temp = boundary[i:i+2,:]
#         #print('temp = ',temp)
#         runtwopoints(temp)def runMotor(x1, y1, x2, y2):x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)# 从(x1, y1) -> (x2, y2)points = np.array([[x1, y1], [x2, y2]])print(points)runtwopoints(points)

直线拟合找黑框中心点代码

import cv2
import numpy as npdef findLaser(crop):"""找到图片中点绿色激光点与红色激光点并定位中心:param crop: 需要处理点图片:return: 绿色激光点中心(x1, y1);红色激光点中心(x2, y2)"""cX1, cY1, cX2, cY2 = None, None, None, NonegreenLaser = 'green'redLaser = 'red'# 色系下限上限表color_dist = {'red': {'Lower': np.array([0, 60, 60]), 'Upper': np.array([6, 255, 255])},'green': {'Lower': np.array([35, 43, 35]), 'Upper': np.array([90, 255, 255])},}# 灰度图像处理# gray = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY)#灰度图# cv2.imshow('gray', gray)# 高斯滤波blurred = cv2.GaussianBlur(crop, (11, 11), 0)# cv2.imshow('blurred', blurred)# 创建运算核kernel = np.ones((1, 1), np.uint8)# 腐蚀# erode = cv2.erode(crop, kernel, iterations=1)# 膨胀# crop_dilate = cv2.dilate(blurred, kernel, iterations = 5)# 开运算opening = cv2.morphologyEx(blurred, cv2.MORPH_OPEN, kernel)# 二值化处理thresh = cv2.threshold(opening, 230, 255, cv2.THRESH_BINARY)[1]# cv2.imshow('thresh', thresh)hsv = cv2.cvtColor(thresh, cv2.COLOR_BGR2HSV)  # 转化成HSV图像# 颜色二值化筛选处理inRange_hsv_green = cv2.inRange(hsv, color_dist[greenLaser]['Lower'], color_dist[greenLaser]['Upper'])inRange_hsv_red = cv2.inRange(hsv, color_dist[redLaser]['Lower'], color_dist[redLaser]['Upper'])# cv2.imshow('inrange_hsv_green', inRange_hsv_green)# cv2.imshow('inrange_hsv_red', inRange_hsv_red)# 找绿色激光点try:cnts1 = cv2.findContours(inRange_hsv_green.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]c1 = max(cnts1, key=cv2.contourArea)M = cv2.moments(c1)cX1 = int(M["m10"] / M["m00"])cY1 = int(M["m01"] / M["m00"])cv2.circle(crop, (cX1, cY1), 3, (0, 255, 0), -1)rect = cv2.minAreaRect(c1)box = cv2.boxPoints(rect)cv2.drawContours(crop, [np.int0(box)], -1, (0, 255, 0), 2)except:print('没有找到绿色的激光')# 找红色激光点try:cnts2 = cv2.findContours(inRange_hsv_red.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]c2 = max(cnts2, key=cv2.contourArea)M = cv2.moments(c2)cX2 = int(M["m10"] / M["m00"])cY2 = int(M["m01"] / M["m00"])cv2.circle(crop, (cX2, cY2), 3, (0, 0, 255), -1)rect = cv2.minAreaRect(c2)box = cv2.boxPoints(rect)cv2.drawContours(crop, [np.int0(box)], -1, (0, 0, 255), 2)except:print('没有找到红色的激光')return cX1, cY1, cX2, cY2def draw_fit_line(dire, type):""":param dire: 传入需要拟合的含有白色直线的图像:return: 传出直线一般式参数a,b,c"""dic = {'up': (hStart, wStart + leftRight_distance), 'down': (hEnd - upDown_distance, wStart + leftRight_distance),'left': (hStart + upDown_distance, wStart), 'right': (hStart + upDown_distance, wEnd - leftRight_distance)}# 存放直线的像素点坐标axis = []for i in range(len(dire)):for j in range(len(dire[i])):if dire[i][j] == 255: # 判断为白色像素# 对应在原图中的高宽坐标h = i + dic[type][0]w = j + dic[type][1]axis.append([w, h])data_np = np.array(axis) # 坐标矩阵化output = cv2.fitLine(data_np, cv2.DIST_L2, 0, 0.01, 0.01)  # 直线拟合k = output[1] / output[0]b = output[3] - k * output[2]return k, bdef crop(h, w, cnt):'''将图形裁剪出上下左右四个部分的中间位置:param cnt: 需要裁剪的图片:return:'''global upDown_distance, leftRight_distance# 上下部分裁剪的宽度upDown_distance = h# 左右部分裁剪的宽度leftRight_distance = wup = cnt[0:upDown_distance, leftRight_distance:wEnd - wStart - leftRight_distance]down = cnt[hEnd - hStart - upDown_distance:hEnd - hStart, leftRight_distance:wEnd - wStart - leftRight_distance]left = cnt[upDown_distance:hEnd - hStart - upDown_distance, 0:leftRight_distance]right = cnt[upDown_distance:hEnd - hStart - upDown_distance, wEnd -wStart - leftRight_distance:wEnd - wStart]# 显示四个边长裁剪过后的图片cv2.imshow('up', up)cv2.imshow('down', down)cv2.imshow('left', left)cv2.imshow('right', right)return up, down, left, rightdef getCoordinate(k1, b1, k2, b2):x = (b2 - b1) / (k1 - k2)y = k1 * x + b1return int(x), int(y)def drawCenter(a, b, c, d):# 显示边长交点cv2.circle(img, (a[0], a[1]), 3, (0, 0, 255), -1)cv2.circle(img, (b[0], b[1]), 3, (0, 0, 255), -1)cv2.circle(img, (c[0], c[1]), 3, (0, 0, 255), -1)cv2.circle(img, (d[0], d[1]), 3, (0, 0, 255), -1)# 画边界线cv2.line(img, (a[0], a[1]), (b[0], b[1]), (0, 255, 0), 1)cv2.line(img, (b[0], b[1]), (c[0], c[1]), (0, 255, 0), 1)cv2.line(img, (a[0], a[1]), (d[0], d[1]), (0, 255, 0), 1)cv2.line(img, (c[0], c[1]), (d[0], d[1]), (0, 255, 0), 1)xCenter = (a[0] + b[0] + c[0] + d[0])//4yCenter = (a[1] + b[1] + c[1] + d[1])//4cv2.circle(img, (xCenter, yCenter), 4, (0, 0, 255), -1)return xCenter, yCenterif __name__ == "__main__":# 导入图片imgimg = cv2.imread('/Users/duanhao/Desktop/Electronic Design Competion/2023电子设计大赛/laser/2.jpg')# 需要裁剪的区域###################################hStart, hEnd, wStart, wEnd = 50, 650, 450, 1100################################################cropImg = img[hStart:hEnd, wStart:wEnd]cv2.imshow('cropImg', cropImg)# 找激光点 ->xc1, yc1, xc2, yc2 = findLaser(cropImg)# 对应到原点中的激光坐标try:xc2, yc2 = xc2 + wStart, yc2 + hStartprint('红色激光点坐标:', xc2, yc2)except:print('无法返回对应原图中激光笔坐标')# 图像inRange二值化处理hsv = cv2.cvtColor(cropImg, cv2.COLOR_BGR2HSV)l_g = np.array([0, 0, 0])  # 阈值下限u_g = np.array([255, 255, 116])  # 阈值上限mask = cv2.inRange(hsv, l_g, u_g)cv2.imshow('mask', mask)# 传入二值化后的图片返回裁剪出来的四个边长举行# 上下部分裁剪的宽度upDown_distance = 150# 左右部分裁剪的宽度leftRight_distance = 150try:# 传入参数1:上下部分裁剪宽度; 参数2:左右部分裁剪宽度up, down, left, right = crop(upDown_distance, leftRight_distance, mask)# 在img原图中拟合四条直线 -> 得到拟合直线的k,bup_k, up_b = draw_fit_line(up, 'up')down_k, down_b = draw_fit_line(down, 'down')left_k, left_b = draw_fit_line(left, 'left')right_k, right_b = draw_fit_line(right, 'right')# 求出A,B, C, D四个边长交点坐标 -> 顺时针方向定义ABCD点A = getCoordinate(up_k, up_b, left_k, left_b)B = getCoordinate(up_k, up_b, right_k, right_b)C = getCoordinate(down_k, down_b, right_k, right_b)D = getCoordinate(down_k, down_b, left_k, left_b)# 画出边界线以及中心点xCenter, yCenter = drawCenter(A, B, C, D)print('中心点坐标:', xCenter, yCenter)except:print('没有裁剪好图片,识别不到黑色方框!')print(f'原来图形尺寸为{img.shape[0:1]}')cv2.imshow('img', img)cv2.waitKey(0)cv2.destroyAllWindows()

完整代码

要求1:​​​​​​​

import cv2
import math
import numpy as np
import RPi.GPIO as GPIO
import time# 坐标方向:水平向右为x正向, left-----right,垂直向下为y正方向,向下为up,向上为down
# 像素间隔:0.738mm/每像素# 规定GPIO引脚
IN1 = 18      # 接PUL-
IN2 = 16      # 接PUL+
IN3 = 15      # 接DIR-
IN4 = 13      # 接DIR+# 云台上面的电机驱动器,物理引脚
IN12 = 38      # 接PUL-
IN22 = 40      # 接PUL+
IN32 = 36      # 接DIR-
IN42 = 32      # 接DIR+delay = 0.0001def downsetup():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical locationGPIO.setup(IN1, GPIO.OUT)      # Set pin's mode is outputGPIO.setup(IN2, GPIO.OUT)GPIO.setup(IN3, GPIO.OUT)GPIO.setup(IN4, GPIO.OUT)def upsetup():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical locationGPIO.setup(IN12, GPIO.OUT)      # Set pin's mode is outputGPIO.setup(IN22, GPIO.OUT)GPIO.setup(IN32, GPIO.OUT)GPIO.setup(IN42, GPIO.OUT)def downsetStep(w1, w2, w3, w4):GPIO.output(IN1, w1)GPIO.output(IN2, w2)GPIO.output(IN3, w3)GPIO.output(IN4, w4)def upsetStep(w1, w2, w3, w4):GPIO.output(IN12, w1)GPIO.output(IN22, w2)GPIO.output(IN32, w3)GPIO.output(IN42, w4)def downstop():# 下面的电机停止downsetStep(0, 0, 0, 0)def upstop():# 上面的电机停止upsetStep(0, 0, 0, 0)def rightward(steps):# 下面的电机向右转动global delayfor i in range(0, steps):downsetStep(1, 0, 1, 0)time.sleep(delay)downsetStep(0, 1, 1, 0)time.sleep(delay)downsetStep(0, 1, 0, 1)time.sleep(delay)downsetStep(1, 0, 0, 1)time.sleep(delay)downstop()def leftward(steps):# 下面的电机向左转动global delayfor i in range(0, steps):downsetStep(1, 0, 0, 1)time.sleep(delay)downsetStep(0, 1, 0, 1)time.sleep(delay)downsetStep(0, 1, 1, 0)time.sleep(delay)downsetStep(1, 0, 1, 0)time.sleep(delay)downstop()def upward2(steps):# 上面的电机向下转动global delayfor i in range(0, steps):upsetStep(1, 0, 0, 1)time.sleep(delay)upsetStep(0, 1, 0, 1)time.sleep(delay)upsetStep(0, 1, 1, 0)time.sleep(delay)upsetStep(1, 0, 1, 0)time.sleep(delay)upstop()def downward2(steps):# 上面的电机向上转动global delayfor i in range(0, steps):upsetStep(1, 0, 1, 0)time.sleep(delay)upsetStep(0, 1, 1, 0)time.sleep(delay)upsetStep(0, 1, 0, 1)time.sleep(delay)upsetStep(1, 0, 0, 1)time.sleep(delay)upstop()def destroy():GPIO.cleanup()             # 释放数据curpos = np.array([-1000,1000])  # mm,mmpi = 3.14159
# 坐标原点在左上角,向左为x轴,向下为y轴。坐标单位像素个数
# x轴移动距离:dx:移动的距离
# 转换:1像素=1.75mmdef xrunpixel(dxpixel):# 下面的电机转动多少像素的位置xangle = math.atan(dxpixel/1000/1.75)*180/pi   # 单位:度,1000mm需转换为像素个数单位if xangle > 0:rightward(int(xangle/360*6400))else:xangle = -xangleleftward(int(xangle/360*6400))# y轴移动距离:dy:移动的距离(像素=mm/1.75)
def yrunpixel(dypixel):# 上面的电机转动多少像素的位置yangle = math.atan(dypixel/1000/1.75)*180/piif yangle > 0:upward(int(yangle/360*6400))else:yangle = -yangledownward(int(yangle/360*6400))# dxstep:x移动的距离(step,int,步进电机步数)
def xrunstep(dxstep):# 下面的电机转动的步数if dxstep > 0:rightward(dxstep)else:dxstep = -dxstepleftward(dxstep)
# dystep:x移动的距离(步长数,int)
def yrunstep(dystep):# 上面的电机转动的步数if dystep > 0:upward2(dystep)else:dystep = -dystepdownward2(dystep)        #points[0,:] 起点位置,points[1,:] 目标位置,# 像素单位
points = np.array([[1,2],[3,4]])  
def runtwopoints(points):points = np.array(points)steps = []dstep = 2temp = points[1,0]-points[0,0]xstep = int(math.atan(temp/732)*1018.591636)  #int((points[1,0]-points[0,0])*0.57142857)
#     xstep = int(math.atan(temp/890)*1018.591636)  #int((points[1,0]-points[0,0])*0.57142857)temp = points[1,1]-points[0,1]ystep = int(math.atan(temp/740)*1018.591636)
#     ystep = int(math.atan(temp/974)*1018.591636)if xstep >= 0:absxstep = xstepxdir = 1else:absxstep = -xstepxdir = 0if ystep >= 0:absystep = ystepydir = 1else:absystep = -ystepydir = 0       # 得到xstep和ystep的最大值if absxstep >= absystep:   # 说明较长边在x轴maxdir = 'x' absmaxstep = absxstepmaxstep = xstepminstep = ystepelse:maxdir = 'y'absmaxstep = absystepmaxstep = ystepminstep = xstep# 分段运行节数sections = int(absmaxstep/dstep)+1dstepmin = minstep/sectionsdstepmax = maxstep/sectionsfor i in range(1,sections+1):if maxdir == 'x': # 沿x边走,先算累计步长steps.append([round(i * dstepmax),round(i * dstepmin)])else:  # 沿y边走,先算累计步长steps.append([round(i * dstepmin),round(i * dstepmax)])if  steps[sections-1][0] != xstep:steps[sections-1][0] == xstepif  steps[sections-1][1] != ystep:steps[sections-1][1] == ystep #print('steps=', steps)#print()for i in range(sections-1,0,-1):steps[i][0] -= steps[i-1][0]steps[i][1] -= steps[i-1][1]  #print('steps=', steps)       points = len(steps)  # 总点数for i in range(points):   xrunstep(steps[i][0])yrunstep(steps[i][1])upstop()downstop()# x坐标max: 570,1个像素0.738mm,对应距离1000mm
zeropos = np.array([100,100]) # 像素
curpos = np.array([500,350])  # 像素
def returnZero():# 从当前位置curpos回到原点zeroposglobal curposglobal zeroposglobal pointspoints[0,:] = curpospoints[1,:] = zeroposruntwopoints(points)def findLaser(crop):"""找到图片中点绿色激光点与红色激光点并定位中心:param crop: 需要处理点图片:return: 绿色激光点中心(x1, y1);红色激光点中心(x2, y2)"""cX1, cY1, cX2, cY2 = None, None, None, NonegreenLaser = 'green'redLaser = 'red'# 色系下限上限表color_dist = {'red': {'Lower': np.array([0, 60, 60]), 'Upper': np.array([6, 255, 255])},'green': {'Lower': np.array([35, 43, 35]), 'Upper': np.array([90, 255, 255])},}# 高斯滤波blurred = cv2.GaussianBlur(crop, (11, 11), 0)# cv2.imshow('blurred', blurred)# 创建运算核kernel = np.ones((1, 1), np.uint8)# 开运算opening = cv2.morphologyEx(blurred, cv2.MORPH_OPEN, kernel)# 二值化处理thresh = cv2.threshold(opening, 230, 255, cv2.THRESH_BINARY)[1]# cv2.imshow('thresh', thresh)hsv = cv2.cvtColor(thresh, cv2.COLOR_BGR2HSV)  # 转化成HSV图像# 颜色二值化筛选处理inRange_hsv_green = cv2.inRange(hsv, color_dist[greenLaser]['Lower'], color_dist[greenLaser]['Upper'])inRange_hsv_red = cv2.inRange(hsv, color_dist[redLaser]['Lower'], color_dist[redLaser]['Upper'])#     # 找绿色激光点
#     try:
#         cnts1 = cv2.findContours(inRange_hsv_green.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
#         c1 = max(cnts1, key=cv2.contourArea)
#         M = cv2.moments(c1)
#         cX1 = int(M["m10"] / M["m00"])
#         cY1 = int(M["m01"] / M["m00"])
#         cv2.circle(crop, (cX1, cY1), 3, (0, 255, 0), -1)
#         rect = cv2.minAreaRect(c1)
#         box = cv2.boxPoints(rect)
# #         cv2.drawContours(crop, [np.int0(box)], -1, (0, 255, 0), 2)
#     except:
#         print('没有找到绿色的激光')# 找红色激光点try:cnts2 = cv2.findContours(inRange_hsv_red.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]c2 = max(cnts2, key=cv2.contourArea)M = cv2.moments(c2)cX2 = int(M["m10"] / M["m00"])cY2 = int(M["m01"] / M["m00"])cv2.circle(crop, (cX2, cY2), 3, (0, 0, 255), -1)rect = cv2.minAreaRect(c2)box = cv2.boxPoints(rect)
#         cv2.drawContours(crop, [np.int0(box)], -1, (0, 0, 255), 2)except:print('没有找到红色的激光')return cX1, cY1, cX2, cY2def draw_fit_line(dire, type):""":param dire: 传入需要拟合的含有白色直线的图像:return: 传出直线一般式参数a,b,c"""dic = {'up': (hStart, wStart + leftRight_distance), 'down': (hEnd - upDown_distance, wStart + leftRight_distance),'left': (hStart + upDown_distance, wStart), 'right': (hStart + upDown_distance, wEnd - leftRight_distance)}# 存放直线的像素点坐标axis = []for i in range(len(dire)):for j in range(len(dire[i])):if dire[i][j] == 255: # 判断为白色像素# 对应在原图中的高宽坐标h = i + dic[type][0]w = j + dic[type][1]axis.append([w, h])data_np = np.array(axis) # 坐标矩阵化output = cv2.fitLine(data_np, cv2.DIST_L2, 0, 0.01, 0.01)  # 直线拟合
#     print(output)k = output[1] / output[0]b = output[3] - k * output[2]return k, bdef crop(h, w, cnt):'''将图形裁剪出上下左右四个部分的中间位置:param cnt: 需要裁剪的图片:return:'''global upDown_distance, leftRight_distance# 上下部分裁剪的宽度upDown_distance = h# 左右部分裁剪的宽度leftRight_distance = wup = cnt[0:upDown_distance, leftRight_distance:wEnd - wStart - leftRight_distance]down = cnt[hEnd - hStart - upDown_distance:hEnd - hStart, leftRight_distance:wEnd - wStart - leftRight_distance]left = cnt[upDown_distance:hEnd - hStart - upDown_distance, 0:leftRight_distance]right = cnt[upDown_distance:hEnd - hStart - upDown_distance, wEnd -wStart - leftRight_distance:wEnd - wStart]# 显示四个边长裁剪过后的图片
#     cv2.imshow('up', up)
#     cv2.imshow('down', down)
#     cv2.imshow('left', left)
#     cv2.imshow('right', right)return up, down, left, rightdef getCoordinate(k1, b1, k2, b2):x = (b2 - b1) / (k1 - k2)y = k1 * x + b1return int(x), int(y)def drawCenter(a, b, c, d):# 显示边长交点cv2.circle(img, (a[0], a[1]), 3, (0, 0, 255), -1)cv2.circle(img, (b[0], b[1]), 3, (0, 0, 255), -1)cv2.circle(img, (c[0], c[1]), 3, (0, 0, 255), -1)cv2.circle(img, (d[0], d[1]), 3, (0, 0, 255), -1)# 画边界线cv2.line(img, (a[0], a[1]), (b[0], b[1]), (0, 255, 0), 1)cv2.line(img, (b[0], b[1]), (c[0], c[1]), (0, 255, 0), 1)cv2.line(img, (a[0], a[1]), (d[0], d[1]), (0, 255, 0), 1)cv2.line(img, (c[0], c[1]), (d[0], d[1]), (0, 255, 0), 1)xCenter = (a[0] + b[0] + c[0] + d[0])//4yCenter = (a[1] + b[1] + c[1] + d[1])//4cv2.circle(img, (xCenter, yCenter), 4, (0, 0, 255), -1)return xCenter, yCenterif __name__ == "__main__":downsetup()upsetup()upstop()downstop()cap = cv2.VideoCapture(0)cap.set(3,1280)cap.set(4,720)  #设置窗口的大小for i in range(4):for j in range(6):ret, img = cap.read()# 需要裁剪的区域###################################hStart, hEnd, wStart, wEnd = 200, 640, 320, 760################################################cropImg = img[hStart:hEnd, wStart:wEnd]# 找激光点 ->xc1, yc1, xc2, yc2 = findLaser(cropImg)# 对应到原点中的激光坐标xc2, yc2 = xc2 + wStart, yc2 + hStartprint('红色激光点坐标:', xc2, yc2)# 图像inRange二值化处理hsv = cv2.cvtColor(cropImg, cv2.COLOR_BGR2HSV)l_g = np.array([0, 0, 0])  # 阈值下限u_g = np.array([255, 255, 110])  # 阈值上限   90-120mask = cv2.inRange(hsv, l_g, u_g)#         cv2.imshow('cropImg', cropImg)        
#         cv2.imshow('mask', mask)        
#         cv2.waitKey(0)
#         cv2.destroyAllWindows()# 传入二值化后的图片返回裁剪出来的四个边长举行# 上下部分裁剪的宽度upDown_distance = 80# 左右部分裁剪的宽度leftRight_distance = 80try:# 传入参数1:上下部分裁剪宽度; 参数2:左右部分裁剪宽度up, down, left, right = crop(upDown_distance, leftRight_distance, mask)# 在img原图中拟合四条直线 -> 得到拟合直线的k,bup_k, up_b = draw_fit_line(up, 'up')down_k, down_b = draw_fit_line(down, 'down')left_k, left_b = draw_fit_line(left, 'left')right_k, right_b = draw_fit_line(right, 'right')# 求出A,B, C, D四个边长交点坐标 -> 顺时针方向定义ABCD点A = getCoordinate(up_k, up_b, left_k, left_b)B = getCoordinate(up_k, up_b, right_k, right_b)C = getCoordinate(down_k, down_b, right_k, right_b)D = getCoordinate(down_k, down_b, left_k, left_b)# 画出边界线以及中心点xCenter, yCenter = drawCenter(A, B, C, D)print('四个顶点坐标', A, B, C, D)print('中心点坐标:', xCenter, yCenter)except:print('没有裁剪好图片,识别不到黑色方框!')print(f'原来图形尺寸为{img.shape[0:1]}')xCenter, yCenter = 545, 420points = np.array([[xc2, yc2],[xCenter,yCenter]])  print('points', points)runtwopoints(points)

要求2:

import cv2
import math
import numpy as np
import RPi.GPIO as GPIO
import time# 坐标方向:水平向右为x正向, left-----right,垂直向下为y正方向,向下为up,向上为down
# 像素间隔:0.738mm/每像素# 规定GPIO引脚
IN1 = 18      # 接PUL-
IN2 = 16      # 接PUL+
IN3 = 15      # 接DIR-
IN4 = 13      # 接DIR+# 云台上面的电机驱动器,物理引脚
IN12 = 38      # 接PUL-
IN22 = 40      # 接PUL+
IN32 = 36      # 接DIR-
IN42 = 32      # 接DIR+delay = 0.0001def downsetup():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical locationGPIO.setup(IN1, GPIO.OUT)      # Set pin's mode is outputGPIO.setup(IN2, GPIO.OUT)GPIO.setup(IN3, GPIO.OUT)GPIO.setup(IN4, GPIO.OUT)def upsetup():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical locationGPIO.setup(IN12, GPIO.OUT)      # Set pin's mode is outputGPIO.setup(IN22, GPIO.OUT)GPIO.setup(IN32, GPIO.OUT)GPIO.setup(IN42, GPIO.OUT)def downsetStep(w1, w2, w3, w4):GPIO.output(IN1, w1)GPIO.output(IN2, w2)GPIO.output(IN3, w3)GPIO.output(IN4, w4)def upsetStep(w1, w2, w3, w4):GPIO.output(IN12, w1)GPIO.output(IN22, w2)GPIO.output(IN32, w3)GPIO.output(IN42, w4)def downstop():# 下面的电机停止downsetStep(0, 0, 0, 0)def upstop():# 上面的电机停止upsetStep(0, 0, 0, 0)def rightward(steps):# 下面的电机向右转动global delayfor i in range(0, steps):downsetStep(1, 0, 1, 0)time.sleep(delay)downsetStep(0, 1, 1, 0)time.sleep(delay)downsetStep(0, 1, 0, 1)time.sleep(delay)downsetStep(1, 0, 0, 1)time.sleep(delay)downstop()def leftward(steps):# 下面的电机向左转动global delayfor i in range(0, steps):downsetStep(1, 0, 0, 1)time.sleep(delay)downsetStep(0, 1, 0, 1)time.sleep(delay)downsetStep(0, 1, 1, 0)time.sleep(delay)downsetStep(1, 0, 1, 0)time.sleep(delay)downstop()def upward2(steps):# 上面的电机向下转动global delayfor i in range(0, steps):upsetStep(1, 0, 0, 1)time.sleep(delay)upsetStep(0, 1, 0, 1)time.sleep(delay)upsetStep(0, 1, 1, 0)time.sleep(delay)upsetStep(1, 0, 1, 0)time.sleep(delay)upstop()def downward2(steps):# 上面的电机向上转动global delayfor i in range(0, steps):upsetStep(1, 0, 1, 0)time.sleep(delay)upsetStep(0, 1, 1, 0)time.sleep(delay)upsetStep(0, 1, 0, 1)time.sleep(delay)upsetStep(1, 0, 0, 1)time.sleep(delay)upstop()def destroy():GPIO.cleanup()             # 释放数据curpos = np.array([-1000,1000])  # mm,mmpi = 3.14159
# 坐标原点在左上角,向左为x轴,向下为y轴。坐标单位像素个数
# x轴移动距离:dx:移动的距离
# 转换:1像素=1.75mmdef xrunpixel(dxpixel):# 下面的电机转动多少像素的位置xangle = math.atan(dxpixel/1000/1.75)*180/pi   # 单位:度,1000mm需转换为像素个数单位if xangle > 0:rightward(int(xangle/360*6400))else:xangle = -xangleleftward(int(xangle/360*6400))# y轴移动距离:dy:移动的距离(像素=mm/1.75)
def yrunpixel(dypixel):# 上面的电机转动多少像素的位置yangle = math.atan(dypixel/1000/1.75)*180/piif yangle > 0:upward(int(yangle/360*6400))else:yangle = -yangledownward(int(yangle/360*6400))# dxstep:x移动的距离(step,int,步进电机步数)
def xrunstep(dxstep):# 下面的电机转动的步数if dxstep > 0:rightward(dxstep)else:dxstep = -dxstepleftward(dxstep)
# dystep:x移动的距离(步长数,int)
def yrunstep(dystep):# 上面的电机转动的步数if dystep > 0:upward2(dystep)else:dystep = -dystepdownward2(dystep)        #points[0,:] 起点位置,points[1,:] 目标位置,# 像素单位
points = np.array([[1,2],[3,4]])  
def runtwopoints(points):points = np.array(points)steps = []dstep = 2temp = points[1,0]-points[0,0]xstep = int(math.atan(temp/732)*1018.591636)  #int((points[1,0]-points[0,0])*0.57142857)
#     xstep = int(math.atan(temp/890)*1018.591636)  #int((points[1,0]-points[0,0])*0.57142857)temp = points[1,1]-points[0,1]ystep = int(math.atan(temp/740)*1018.591636)
#     ystep = int(math.atan(temp/974)*1018.591636)if xstep >= 0:absxstep = xstepxdir = 1else:absxstep = -xstepxdir = 0if ystep >= 0:absystep = ystepydir = 1else:absystep = -ystepydir = 0       # 得到xstep和ystep的最大值if absxstep >= absystep:   # 说明较长边在x轴maxdir = 'x' absmaxstep = absxstepmaxstep = xstepminstep = ystepelse:maxdir = 'y'absmaxstep = absystepmaxstep = ystepminstep = xstep# 分段运行节数sections = int(absmaxstep/dstep)+1dstepmin = minstep/sectionsdstepmax = maxstep/sectionsfor i in range(1,sections+1):if maxdir == 'x': # 沿x边走,先算累计步长steps.append([round(i * dstepmax),round(i * dstepmin)])else:  # 沿y边走,先算累计步长steps.append([round(i * dstepmin),round(i * dstepmax)])if  steps[sections-1][0] != xstep:steps[sections-1][0] == xstepif  steps[sections-1][1] != ystep:steps[sections-1][1] == ystep #print('steps=', steps)#print()for i in range(sections-1,0,-1):steps[i][0] -= steps[i-1][0]steps[i][1] -= steps[i-1][1]  #print('steps=', steps)       points = len(steps)  # 总点数for i in range(points):   xrunstep(steps[i][0])yrunstep(steps[i][1])upstop()downstop()# x坐标max: 570,1个像素0.738mm,对应距离1000mm
zeropos = np.array([100,100]) # 像素
curpos = np.array([500,350])  # 像素
def returnZero():# 从当前位置curpos回到原点zeroposglobal curposglobal zeroposglobal pointspoints[0,:] = curpospoints[1,:] = zeroposruntwopoints(points)def findLaser(crop):"""找到图片中点绿色激光点与红色激光点并定位中心:param crop: 需要处理点图片:return: 绿色激光点中心(x1, y1);红色激光点中心(x2, y2)"""cX1, cY1, cX2, cY2 = None, None, None, NonegreenLaser = 'green'redLaser = 'red'# 色系下限上限表color_dist = {'red': {'Lower': np.array([0, 60, 60]), 'Upper': np.array([6, 255, 255])},'green': {'Lower': np.array([35, 43, 35]), 'Upper': np.array([90, 255, 255])},}# 灰度图像处理# gray = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY)#灰度图# cv2.imshow('gray', gray)# 高斯滤波blurred = cv2.GaussianBlur(crop, (11, 11), 0)# cv2.imshow('blurred', blurred)# 创建运算核kernel = np.ones((1, 1), np.uint8)# 腐蚀# erode = cv2.erode(crop, kernel, iterations=1)# 膨胀# crop_dilate = cv2.dilate(blurred, kernel, iterations = 5)# 开运算opening = cv2.morphologyEx(blurred, cv2.MORPH_OPEN, kernel)# 二值化处理thresh = cv2.threshold(opening, 230, 255, cv2.THRESH_BINARY)[1]# cv2.imshow('thresh', thresh)hsv = cv2.cvtColor(thresh, cv2.COLOR_BGR2HSV)  # 转化成HSV图像# 颜色二值化筛选处理inRange_hsv_green = cv2.inRange(hsv, color_dist[greenLaser]['Lower'], color_dist[greenLaser]['Upper'])inRange_hsv_red = cv2.inRange(hsv, color_dist[redLaser]['Lower'], color_dist[redLaser]['Upper'])# cv2.imshow('inrange_hsv_green', inRange_hsv_green)# cv2.imshow('inrange_hsv_red', inRange_hsv_red)#     # 找绿色激光点
#     try:
#         cnts1 = cv2.findContours(inRange_hsv_green.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
#         c1 = max(cnts1, key=cv2.contourArea)
#         M = cv2.moments(c1)
#         cX1 = int(M["m10"] / M["m00"])
#         cY1 = int(M["m01"] / M["m00"])
#         cv2.circle(crop, (cX1, cY1), 3, (0, 255, 0), -1)
#         rect = cv2.minAreaRect(c1)
#         box = cv2.boxPoints(rect)
# #         cv2.drawContours(crop, [np.int0(box)], -1, (0, 255, 0), 2)
#     except:
#         print('没有找到绿色的激光')# 找红色激光点try:cnts2 = cv2.findContours(inRange_hsv_red.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]c2 = max(cnts2, key=cv2.contourArea)M = cv2.moments(c2)cX2 = int(M["m10"] / M["m00"])cY2 = int(M["m01"] / M["m00"])cv2.circle(crop, (cX2, cY2), 3, (0, 0, 255), -1)rect = cv2.minAreaRect(c2)box = cv2.boxPoints(rect)
#         cv2.drawContours(crop, [np.int0(box)], -1, (0, 0, 255), 2)except:print('没有找到红色的激光')return cX1, cY1, cX2, cY2def draw_fit_line(dire, type):""":param dire: 传入需要拟合的含有白色直线的图像:return: 传出直线一般式参数a,b,c"""dic = {'up': (hStart, wStart + leftRight_distance), 'down': (hEnd - upDown_distance, wStart + leftRight_distance),'left': (hStart + upDown_distance, wStart), 'right': (hStart + upDown_distance, wEnd - leftRight_distance)}# 存放直线的像素点坐标axis = []for i in range(len(dire)):for j in range(len(dire[i])):if dire[i][j] == 255: # 判断为白色像素# 对应在原图中的高宽坐标h = i + dic[type][0]w = j + dic[type][1]axis.append([w, h])data_np = np.array(axis) # 坐标矩阵化output = cv2.fitLine(data_np, cv2.DIST_L2, 0, 0.01, 0.01)  # 直线拟合k = output[1] / output[0]b = output[3] - k * output[2]return k, bdef crop(h, w, cnt):'''将图形裁剪出上下左右四个部分的中间位置:param cnt: 需要裁剪的图片:return:'''global upDown_distance, leftRight_distance# 上下部分裁剪的宽度upDown_distance = h# 左右部分裁剪的宽度leftRight_distance = wup = cnt[0:upDown_distance, leftRight_distance:wEnd - wStart - leftRight_distance]down = cnt[hEnd - hStart - upDown_distance:hEnd - hStart, leftRight_distance:wEnd - wStart - leftRight_distance]left = cnt[upDown_distance:hEnd - hStart - upDown_distance, 0:leftRight_distance]right = cnt[upDown_distance:hEnd - hStart - upDown_distance, wEnd -wStart - leftRight_distance:wEnd - wStart]# 显示四个边长裁剪过后的图片
#     cv2.imshow('up', up)
#     cv2.imshow('down', down)
#     cv2.imshow('left', left)
#     cv2.imshow('right', right)return up, down, left, rightdef getCoordinate(k1, b1, k2, b2):x = (b2 - b1) / (k1 - k2)y = k1 * x + b1return [int(x), int(y)]def drawCenter(a, b, c, d):# 显示边长交点cv2.circle(img, (a[0], a[1]), 3, (0, 0, 255), -1)cv2.circle(img, (b[0], b[1]), 3, (0, 0, 255), -1)cv2.circle(img, (c[0], c[1]), 3, (0, 0, 255), -1)cv2.circle(img, (d[0], d[1]), 3, (0, 0, 255), -1)# 画边界线cv2.line(img, (a[0], a[1]), (b[0], b[1]), (0, 255, 0), 1)cv2.line(img, (b[0], b[1]), (c[0], c[1]), (0, 255, 0), 1)cv2.line(img, (a[0], a[1]), (d[0], d[1]), (0, 255, 0), 1)cv2.line(img, (c[0], c[1]), (d[0], d[1]), (0, 255, 0), 1)xCenter = (a[0] + b[0] + c[0] + d[0])//4yCenter = (a[1] + b[1] + c[1] + d[1])//4cv2.circle(img, (xCenter, yCenter), 4, (0, 0, 255), -1)return xCenter, yCenterif __name__ == "__main__":downsetup()upsetup()upstop()downstop()cap = cv2.VideoCapture(0)cap.set(3,1280)cap.set(4,720)  #设置窗口的大小ret, img = cap.read()# 需要裁剪的区域###################################hStart, hEnd, wStart, wEnd = 200, 640, 320, 760################################################cropImg = img[hStart:hEnd, wStart:wEnd]# 找激光点 ->xc1, yc1, xc2, yc2 = findLaser(cropImg)# 对应到原点中的激光坐标xc2, yc2 = xc2 + wStart, yc2 + hStartprint('红色激光点坐标:', xc2, yc2)# 图像inRange二值化处理hsv = cv2.cvtColor(cropImg, cv2.COLOR_BGR2HSV)l_g = np.array([0, 0, 0])  # 阈值下限u_g = np.array([255, 255, 100])  # 阈值上限   90-120mask = cv2.inRange(hsv, l_g, u_g)#     cv2.imshow('cropImg', cropImg)        
#     cv2.imshow('mask', mask)        
#     cv2.waitKey(0)
#     cv2.destroyAllWindows()# 传入二值化后的图片返回裁剪出来的四个边长举行# 上下部分裁剪的宽度upDown_distance = 80# 左右部分裁剪的宽度leftRight_distance = 80try:# 传入参数1:上下部分裁剪宽度; 参数2:左右部分裁剪宽度up, down, left, right = crop(upDown_distance, leftRight_distance, mask)# 在img原图中拟合四条直线 -> 得到拟合直线的k,bup_k, up_b = draw_fit_line(up, 'up')down_k, down_b = draw_fit_line(down, 'down')left_k, left_b = draw_fit_line(left, 'left')right_k, right_b = draw_fit_line(right, 'right')# 求出A,B, C, D四个边长交点坐标 -> 顺时针方向定义ABCD点A = getCoordinate(up_k, up_b, left_k, left_b)B = getCoordinate(up_k, up_b, right_k, right_b)C = getCoordinate(down_k, down_b, right_k, right_b)D = getCoordinate(down_k, down_b, left_k, left_b)# 画出边界线以及中心点xCenter, yCenter = drawCenter(A, B, C, D)print('四个顶点坐标', A, B, C, D)print('中心点坐标:', xCenter, yCenter)except:print('没有裁剪好图片,识别不到黑色方框!')print(f'原来图形尺寸为{img.shape[0:1]}')xCenter, yCenter = 545, 420allpoint = np.array([A,B,C,D,A])print(allpoint)for i in range(len(allpoint)):for kk in range(3):            for j in range(6):ret, img = cap.read()# 需要裁剪的区域###################################hStart, hEnd, wStart, wEnd = 200, 630, 320, 750################################################cropImg = img[hStart:hEnd, wStart:wEnd]# 找激光点 ->xc1, yc1, xc2, yc2 = findLaser(cropImg)# 对应到原点中的激光坐标xc2, yc2 = xc2 + wStart, yc2 + hStartprint('红色激光点坐标:', xc2, yc2)points = np.array([[xc2, yc2],allpoint[i,:]])print('points', points)runtwopoints(points)

要求3和要求4:

import cv2
import math
import numpy as np
import RPi.GPIO as GPIO
import time# 坐标方向:水平向右为x正向, left-----right,垂直向下为y正方向,向下为up,向上为down
# 像素间隔:0.738mm/每像素# 规定GPIO引脚
IN1 = 18      # 接PUL-
IN2 = 16      # 接PUL+
IN3 = 15      # 接DIR-
IN4 = 13      # 接DIR+# 云台上面的电机驱动器,物理引脚
IN12 = 38      # 接PUL-
IN22 = 40      # 接PUL+
IN32 = 36      # 接DIR-
IN42 = 32      # 接DIR+delay = 0.0001def downsetup():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical locationGPIO.setup(IN1, GPIO.OUT)      # Set pin's mode is outputGPIO.setup(IN2, GPIO.OUT)GPIO.setup(IN3, GPIO.OUT)GPIO.setup(IN4, GPIO.OUT)def upsetup():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical locationGPIO.setup(IN12, GPIO.OUT)      # Set pin's mode is outputGPIO.setup(IN22, GPIO.OUT)GPIO.setup(IN32, GPIO.OUT)GPIO.setup(IN42, GPIO.OUT)def downsetStep(w1, w2, w3, w4):GPIO.output(IN1, w1)GPIO.output(IN2, w2)GPIO.output(IN3, w3)GPIO.output(IN4, w4)def upsetStep(w1, w2, w3, w4):GPIO.output(IN12, w1)GPIO.output(IN22, w2)GPIO.output(IN32, w3)GPIO.output(IN42, w4)def downstop():# 下面的电机停止downsetStep(0, 0, 0, 0)def upstop():# 上面的电机停止upsetStep(0, 0, 0, 0)def rightward(steps):# 下面的电机向右转动global delayfor i in range(0, steps):downsetStep(1, 0, 1, 0)time.sleep(delay)downsetStep(0, 1, 1, 0)time.sleep(delay)downsetStep(0, 1, 0, 1)time.sleep(delay)downsetStep(1, 0, 0, 1)time.sleep(delay)downstop()def leftward(steps):# 下面的电机向左转动global delayfor i in range(0, steps):downsetStep(1, 0, 0, 1)time.sleep(delay)downsetStep(0, 1, 0, 1)time.sleep(delay)downsetStep(0, 1, 1, 0)time.sleep(delay)downsetStep(1, 0, 1, 0)time.sleep(delay)downstop()def upward2(steps):# 上面的电机向下转动global delayfor i in range(0, steps):upsetStep(1, 0, 0, 1)time.sleep(delay)upsetStep(0, 1, 0, 1)time.sleep(delay)upsetStep(0, 1, 1, 0)time.sleep(delay)upsetStep(1, 0, 1, 0)time.sleep(delay)upstop()def downward2(steps):# 上面的电机向上转动global delayfor i in range(0, steps):upsetStep(1, 0, 1, 0)time.sleep(delay)upsetStep(0, 1, 1, 0)time.sleep(delay)upsetStep(0, 1, 0, 1)time.sleep(delay)upsetStep(1, 0, 0, 1)time.sleep(delay)upstop()def destroy():GPIO.cleanup()             # 释放数据curpos = np.array([-1000,1000])  # mm,mmpi = 3.14159
# 坐标原点在左上角,向左为x轴,向下为y轴。坐标单位像素个数
# x轴移动距离:dx:移动的距离
# 转换:1像素=1.75mmdef xrunpixel(dxpixel):# 下面的电机转动多少像素的位置xangle = math.atan(dxpixel/1000/1.75)*180/pi   # 单位:度,1000mm需转换为像素个数单位if xangle > 0:rightward(int(xangle/360*6400))else:xangle = -xangleleftward(int(xangle/360*6400))# y轴移动距离:dy:移动的距离(像素=mm/1.75)
def yrunpixel(dypixel):# 上面的电机转动多少像素的位置yangle = math.atan(dypixel/1000/1.75)*180/piif yangle > 0:upward(int(yangle/360*6400))else:yangle = -yangledownward(int(yangle/360*6400))# dxstep:x移动的距离(step,int,步进电机步数)
def xrunstep(dxstep):# 下面的电机转动的步数if dxstep > 0:rightward(dxstep)else:dxstep = -dxstepleftward(dxstep)
# dystep:x移动的距离(步长数,int)
def yrunstep(dystep):# 上面的电机转动的步数if dystep > 0:upward2(dystep)else:dystep = -dystepdownward2(dystep)        #points[0,:] 起点位置,points[1,:] 目标位置,# 像素单位
# points = np.array([[1,2],[3,4]])  
def runtwopoints(points):points = np.array(points)steps = []dstep = 2temp = points[1,0]-points[0,0]xstep = int(math.atan(temp/732)*1018.591636)  #int((points[1,0]-points[0,0])*0.57142857)
#     xstep = int(math.atan(temp/890)*1018.591636)  #int((points[1,0]-points[0,0])*0.57142857)temp = points[1,1]-points[0,1]ystep = int(math.atan(temp/740)*1018.591636)
#     ystep = int(math.atan(temp/974)*1018.591636)#print('xstep =',xstep,'ystep =',ystep)#print()if xstep >= 0:absxstep = xstepxdir = 1else:absxstep = -xstepxdir = 0if ystep >= 0:absystep = ystepydir = 1else:absystep = -ystepydir = 0       # 得到xstep和ystep的最大值if absxstep >= absystep:   # 说明较长边在x轴maxdir = 'x' absmaxstep = absxstepmaxstep = xstepminstep = ystepelse:maxdir = 'y'absmaxstep = absystepmaxstep = ystepminstep = xstep# 分段运行节数sections = int(absmaxstep/dstep)+1dstepmin = minstep/sectionsdstepmax = maxstep/sectionsfor i in range(1,sections+1):if maxdir == 'x': # 沿x边走,先算累计步长steps.append([round(i * dstepmax),round(i * dstepmin)])else:  # 沿y边走,先算累计步长steps.append([round(i * dstepmin),round(i * dstepmax)])if  steps[sections-1][0] != xstep:steps[sections-1][0] == xstepif  steps[sections-1][1] != ystep:steps[sections-1][1] == ystep #print('steps=', steps)#print()for i in range(sections-1,0,-1):steps[i][0] -= steps[i-1][0]steps[i][1] -= steps[i-1][1]  #print('steps=', steps)       points = len(steps)  # 总点数for i in range(points):   xrunstep(steps[i][0])yrunstep(steps[i][1])upstop()downstop()def findRedBlackLaser(crop):"""找到图片中点绿色激光点与红色激光点并定位中心:param crop: 需要处理点图片:return: 绿色激光点中心(x1, y1);红色激光点中心(x2, y2)"""cX1, cY1, cX2, cY2 = None, None, None, None# Get HSV values from the GUI sliders.
#     lowHue = 170
#     lowSat = 81
#     lowVal = 0
#     highHue = 255
#     highSat = 255
#     highVal = 67lowHue = 173lowSat = 36lowVal = 175highHue = 200highSat = 79highVal = 175frame = crop# Show the original image.
#     cv2.imshow('frame', frame)# Blur methods available, comment or uncomment to try different blur methods.
#     frameBGR = cv2.GaussianBlur(frame, (7, 7), 0)#frameBGR = cv2.medianBlur(frameBGR, 7)#frameBGR = cv2.bilateralFilter(frameBGR, 15 ,75, 75)frameBGR = frame# Show blurRed image.# HSV (Hue, Saturation, Value).# Convert the frame to HSV colour model.hsv = cv2.cvtColor(frameBGR, cv2.COLOR_BGR2HSV)# HSV values to define a colour range.colorLow = np.array([lowHue, lowSat, lowVal])colorHigh = np.array([highHue, highSat, highVal])mask = cv2.inRange(hsv, colorLow, colorHigh)# Show the first mask
#     cv2.imshow('mask-plain', mask)kernal = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernal)mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernal)# Show morphological transformation mask
#     cv2.imshow('mask', mask)# 找红色激光点try:cnts2 = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]c2 = max(cnts2, key=cv2.contourArea)M = cv2.moments(c2)cX2 = int(M["m10"] / M["m00"])cY2 = int(M["m01"] / M["m00"])cv2.circle(frame, (cX2, cY2), 3, (0, 0, 255), -1)rect = cv2.minAreaRect(c2)box = cv2.boxPoints(rect)
#         cv2.drawContours(crop, [np.int0(box)], -1, (0, 0, 255), 2)except:print('Blackline没有找到红色的激光')return cX1, cY1, cX2, cY2def findLaser(crop):"""找到图片中点绿色激光点与红色激光点并定位中心:param crop: 需要处理点图片:return: 绿色激光点中心(x1, y1);红色激光点中心(x2, y2)"""cX1, cY1, cX2, cY2 = None, None, None, NonegreenLaser = 'green'redLaser = 'red'# 色系下限上限表color_dist = {'red': {'Lower': np.array([0, 60, 60]), 'Upper': np.array([6, 255, 255])},'green': {'Lower': np.array([35, 43, 35]), 'Upper': np.array([90, 255, 255])},}# 灰度图像处理# gray = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY)#灰度图# cv2.imshow('gray', gray)# 高斯滤波blurred = cv2.GaussianBlur(crop, (11, 11), 0)# cv2.imshow('blurred', blurred)# 创建运算核kernel = np.ones((1, 1), np.uint8)# 腐蚀# erode = cv2.erode(crop, kernel, iterations=1)# 膨胀# crop_dilate = cv2.dilate(blurred, kernel, iterations = 5)# 开运算opening = cv2.morphologyEx(blurred, cv2.MORPH_OPEN, kernel)# 二值化处理thresh = cv2.threshold(opening, 230, 255, cv2.THRESH_BINARY)[1]# cv2.imshow('thresh', thresh)hsv = cv2.cvtColor(thresh, cv2.COLOR_BGR2HSV)  # 转化成HSV图像# 颜色二值化筛选处理inRange_hsv_green = cv2.inRange(hsv, color_dist[greenLaser]['Lower'], color_dist[greenLaser]['Upper'])inRange_hsv_red = cv2.inRange(hsv, color_dist[redLaser]['Lower'], color_dist[redLaser]['Upper'])# cv2.imshow('inrange_hsv_green', inRange_hsv_green)# cv2.imshow('inrange_hsv_red', inRange_hsv_red)
#     # 找绿色激光点
#     try:
#         cnts1 = cv2.findContours(inRange_hsv_green.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
#         c1 = max(cnts1, key=cv2.contourArea)
#         M = cv2.moments(c1)
#         cX1 = int(M["m10"] / M["m00"])
#         cY1 = int(M["m01"] / M["m00"])
#         cv2.circle(crop, (cX1, cY1), 3, (0, 255, 0), -1)
#         rect = cv2.minAreaRect(c1)
#         box = cv2.boxPoints(rect)
# #         cv2.drawContours(crop, [np.int0(box)], -1, (0, 255, 0), 2)
#     except:
#         print('没有找到绿色的激光')# 找红色激光点try:cnts2 = cv2.findContours(inRange_hsv_red.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]c2 = max(cnts2, key=cv2.contourArea)M = cv2.moments(c2)cX2 = int(M["m10"] / M["m00"])cY2 = int(M["m01"] / M["m00"])cv2.circle(crop, (cX2, cY2), 3, (0, 0, 255), -1)rect = cv2.minAreaRect(c2)box = cv2.boxPoints(rect)
#         cv2.drawContours(crop, [np.int0(box)], -1, (0, 0, 255), 2)except:print('没有找到红色的激光')return cX1, cY1, cX2, cY2def draw_fit_line(dire, type):""":param dire: 传入需要拟合的含有白色直线的图像:return: 传出直线一般式参数a,b,c"""dic = {'up': (hStart, wStart + leftRight_distance), 'down': (hEnd - upDown_distance, wStart + leftRight_distance),'left': (hStart + upDown_distance, wStart), 'right': (hStart + upDown_distance, wEnd - leftRight_distance)}# 存放直线的像素点坐标axis = []for i in range(len(dire)):for j in range(len(dire[i])):if dire[i][j] == 255: # 判断为白色像素# 对应在原图中的高宽坐标h = i + dic[type][0]w = j + dic[type][1]axis.append([w, h])data_np = np.array(axis) # 坐标矩阵化output = cv2.fitLine(data_np, cv2.DIST_L2, 0, 0.01, 0.01)  # 直线拟合
#     print(output)k = output[1] / output[0]b = output[3] - k * output[2]return k, bdef crop(h, w, cnt):'''将图形裁剪出上下左右四个部分的中间位置:param cnt: 需要裁剪的图片:return:'''global upDown_distance, leftRight_distance# 上下部分裁剪的宽度upDown_distance = h# 左右部分裁剪的宽度leftRight_distance = wup = cnt[0:upDown_distance, leftRight_distance:wEnd - wStart - leftRight_distance]down = cnt[hEnd - hStart - upDown_distance:hEnd - hStart, leftRight_distance:wEnd - wStart - leftRight_distance]left = cnt[upDown_distance:hEnd - hStart - upDown_distance, 0:leftRight_distance]right = cnt[upDown_distance:hEnd - hStart - upDown_distance, wEnd -wStart - leftRight_distance:wEnd - wStart]# 显示四个边长裁剪过后的图片
#     cv2.imshow('up', up)
#     cv2.imshow('down', down)
#     cv2.imshow('left', left)
#     cv2.imshow('right', right)return up, down, left, rightdef getCoordinate(k1, b1, k2, b2):x = (b2 - b1) / (k1 - k2)y = k1 * x + b1return [int(x), int(y)]def drawCenter(a, b, c, d):# 显示边长交点cv2.circle(img, (a[0], a[1]), 3, (0, 0, 255), -1)cv2.circle(img, (b[0], b[1]), 3, (0, 0, 255), -1)cv2.circle(img, (c[0], c[1]), 3, (0, 0, 255), -1)cv2.circle(img, (d[0], d[1]), 3, (0, 0, 255), -1)# 画边界线cv2.line(img, (a[0], a[1]), (b[0], b[1]), (0, 255, 0), 1)cv2.line(img, (b[0], b[1]), (c[0], c[1]), (0, 255, 0), 1)cv2.line(img, (a[0], a[1]), (d[0], d[1]), (0, 255, 0), 1)cv2.line(img, (c[0], c[1]), (d[0], d[1]), (0, 255, 0), 1)xCenter = (a[0] + b[0] + c[0] + d[0])//4yCenter = (a[1] + b[1] + c[1] + d[1])//4cv2.circle(img, (xCenter, yCenter), 4, (0, 0, 255), -1)return xCenter, yCenter#定义形状检测函数
def ShapeDetection(img):axis = []contours,hierarchy = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)  #寻找轮廓点contour = max(contours, key = cv2.contourArea)area = cv2.contourArea(contour)  #计算轮廓内区域的面积# cv2.drawContours(imgContour, obj, -1, (255, 0, 0), 4)  #绘制轮廓线perimeter = cv2.arcLength(contour,True)  #计算轮廓周长approx = cv2.approxPolyDP(contour,0.02*perimeter,True)  #获取轮廓角点坐标CornerNum = len(approx)   #轮廓角点的数量x, y, w, h = cv2.boundingRect(approx)  #获取坐标值和宽度、高度print(x, y, w, h)print(CornerNum)if CornerNum >= 4:for i in range(0,4):cv2.circle(imgContour, (approx[i][0][0], approx[i][0][1]), 2, (0,0,255), 3)approx[i][0][0] += wStartapprox[i][0][1] += hStartaxis.append([approx[i][0][0], approx[i][0][1]])# 从逆时针改为顺时针if axis != []:axis[0], axis[2] = axis[2], axis[0]axis.append(axis[0])return axisif __name__ == "__main__":downsetup()upsetup()upstop()downstop()cap = cv2.VideoCapture(0)cap.set(3,1280)cap.set(4,720)  #设置窗口的大小ret, img = cap.read()# 需要裁剪的区域###################################hStart, hEnd, wStart, wEnd = 200, 640, 320, 760################################################cropImg = img[hStart:hEnd, wStart:wEnd]xc1, yc1, xc2, yc2 = 427, 432, 427, 432xc1b, yc1b, xc2b, yc2b = xc1, yc1, xc2, yc2# 找激光点 ->xc1, yc1, xc2, yc2 = findLaser(cropImg)if xc2 == None:xc1, yc1, xc2, yc2 = findRedBlackLaser(cropImg)if xc2 == None:xc1, yc1, xc2, yc2 = xc1b, yc1b, xc2b, yc2b# 对应到原点中的激光坐标xc2, yc2 = xc2 + wStart, yc2 + hStartprint('红色激光点坐标:', xc2, yc2)    # 灰度图像处理gray = cv2.cvtColor(cropImg, cv2.COLOR_BGR2GRAY)#灰度图imgContour = cropImg.copy()# 图像inRange二值化处理hsv = cv2.cvtColor(cropImg, cv2.COLOR_BGR2HSV)l_g = np.array([0, 0, 0])  # 阈值下限u_g = np.array([255, 255, 46])  # 阈值上限mask = cv2.inRange(hsv, l_g, u_g)axis = ShapeDetection(mask)print('axis=', axis)
#     cv2.imshow('img', img)cv2.imshow('cropImg', cropImg)cv2.imshow('mask', mask)cv2.imshow("crop", imgContour)cv2.waitKey(0)cv2.destroyAllWindows()runpoint = np.zeros((6,2))allpoint = np.array(axis)runpoint[0,:] = np.array([xc2, yc2])runpoint[1:6,:] = allpointprint(runpoint)for i in range(len(runpoint)-1):points = np.array([runpoint[i,:],runpoint[i+1,:]])print('points', points)runtwopoints(points)#     for i in range(len(allpoint)):
#         for kk in range(3):            
#             for j in range(6):
#                 ret, img = cap.read()
#             
#             # 需要裁剪的区域###################################
#             hStart, hEnd, wStart, wEnd = 200, 640, 320, 760
#             ################################################
#             cropImg = img[hStart:hEnd, wStart:wEnd]
# 
#             # 找激光点 ->
#             xc1, yc1, xc2, yc2 = findLaser(cropImg)
#             if xc2 == None:
#                 xc1, yc1, xc2, yc2 = findRedBlackLaser(cropImg)
#             if xc2 == None:
#                 xc1, yc1, xc2, yc2 = xc1b, yc1b, xc2b, yc2b          
#             # 对应到原点中的激光坐标
#             xc2, yc2 = xc2 + wStart, yc2 + hStart
#             print('Now红色激光点坐标:', xc2, yc2)
#             
#             points = np.array([[xc2, yc2],allpoint[i,:]])
#             print('points', points)
#             runtwopoints(points)

发挥1:

import cv2
import math
import numpy as np
import RPi.GPIO as GPIO
import time# 坐标方向:水平向右为x正向, left-----right,垂直向下为y正方向,向下为up,向上为down
# 像素间隔:0.738mm/每像素# 规定GPIO引脚
IN1 = 18      # 接PUL-
IN2 = 16      # 接PUL+
IN3 = 15      # 接DIR-
IN4 = 13      # 接DIR+# 云台上面的电机驱动器,物理引脚
IN12 = 38      # 接PUL-
IN22 = 40      # 接PUL+
IN32 = 36      # 接DIR-
IN42 = 32      # 接DIR+delay = 0.0001Buzzer = 12    # pin22
Light = 11     # pin12def lightBuzerSetup():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)  # Numbers GPIOs by physical locationGPIO.setup(Buzzer, GPIO.OUT)GPIO.setup(Light, GPIO.OUT)#GPIO.PWM(Buzzer, 440)def on():GPIO.output(Buzzer, GPIO.HIGH)  # 低电平是响GPIO.output(Light, GPIO.LOW)     # 高电平是亮def off():GPIO.output(Buzzer, GPIO.LOW)  # 高电平停止GPIO.output(Light, GPIO.HIGH)  #低电平是暗def beep(x):    #响1秒后停止1秒for i in range(5):on()time.sleep(x)off()time.sleep(x)#  关闭程序,释放接口
def beepdestroy():GPIO.output(Buzzer, GPIO.HIGH)GPIO.output(Light, GPIO.LOW)GPIO.cleanup() # Release resourcedef downsetup():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical locationGPIO.setup(IN1, GPIO.OUT)      # Set pin's mode is outputGPIO.setup(IN2, GPIO.OUT)GPIO.setup(IN3, GPIO.OUT)GPIO.setup(IN4, GPIO.OUT)def upsetup():GPIO.setwarnings(False)GPIO.setmode(GPIO.BOARD)       # Numbers GPIOs by physical locationGPIO.setup(IN12, GPIO.OUT)      # Set pin's mode is outputGPIO.setup(IN22, GPIO.OUT)GPIO.setup(IN32, GPIO.OUT)GPIO.setup(IN42, GPIO.OUT)def downsetStep(w1, w2, w3, w4):GPIO.output(IN1, w1)GPIO.output(IN2, w2)GPIO.output(IN3, w3)GPIO.output(IN4, w4)def upsetStep(w1, w2, w3, w4):GPIO.output(IN12, w1)GPIO.output(IN22, w2)GPIO.output(IN32, w3)GPIO.output(IN42, w4)def downstop():# 下面的电机停止downsetStep(0, 0, 0, 0)def upstop():# 上面的电机停止upsetStep(0, 0, 0, 0)def rightward(steps):# 下面的电机向右转动global delayfor i in range(0, steps):downsetStep(1, 0, 1, 0)time.sleep(delay)downsetStep(0, 1, 1, 0)time.sleep(delay)downsetStep(0, 1, 0, 1)time.sleep(delay)downsetStep(1, 0, 0, 1)time.sleep(delay)downstop()def leftward(steps):# 下面的电机向左转动global delayfor i in range(0, steps):downsetStep(1, 0, 0, 1)time.sleep(delay)downsetStep(0, 1, 0, 1)time.sleep(delay)downsetStep(0, 1, 1, 0)time.sleep(delay)downsetStep(1, 0, 1, 0)time.sleep(delay)downstop()def upward2(steps):# 上面的电机向下转动global delayfor i in range(0, steps):upsetStep(1, 0, 0, 1)time.sleep(delay)upsetStep(0, 1, 0, 1)time.sleep(delay)upsetStep(0, 1, 1, 0)time.sleep(delay)upsetStep(1, 0, 1, 0)time.sleep(delay)upstop()def downward2(steps):# 上面的电机向上转动global delayfor i in range(0, steps):upsetStep(1, 0, 1, 0)time.sleep(delay)upsetStep(0, 1, 1, 0)time.sleep(delay)upsetStep(0, 1, 0, 1)time.sleep(delay)upsetStep(1, 0, 0, 1)time.sleep(delay)upstop()def destroy():GPIO.cleanup()             # 释放数据curpos = np.array([-1000,1000])  # mm,mmpi = 3.14159
# 坐标原点在左上角,向左为x轴,向下为y轴。坐标单位像素个数
# x轴移动距离:dx:移动的距离
# 转换:1像素=1.75mmdef xrunpixel(dxpixel):# 下面的电机转动多少像素的位置xangle = math.atan(dxpixel/1000/1.75)*180/pi   # 单位:度,1000mm需转换为像素个数单位if xangle > 0:rightward(int(xangle/360*6400))else:xangle = -xangleleftward(int(xangle/360*6400))# y轴移动距离:dy:移动的距离(像素=mm/1.75)
def yrunpixel(dypixel):# 上面的电机转动多少像素的位置yangle = math.atan(dypixel/1000/1.75)*180/piif yangle > 0:upward(int(yangle/360*6400))else:yangle = -yangledownward(int(yangle/360*6400))# dxstep:x移动的距离(step,int,步进电机步数)
def xrunstep(dxstep):# 下面的电机转动的步数if dxstep > 0:rightward(dxstep)else:dxstep = -dxstepleftward(dxstep)
# dystep:x移动的距离(步长数,int)
def yrunstep(dystep):# 上面的电机转动的步数if dystep > 0:upward2(dystep)else:dystep = -dystepdownward2(dystep)        #points[0,:] 起点位置,points[1,:] 目标位置,# 像素单位
points = np.array([[1,2],[3,4]])  
def runtwopoints(points):points = np.array(points)steps = []dstep = 2temp = points[1,0]-points[0,0]xstep = int(math.atan(temp/732)*1018.591636)  #int((points[1,0]-points[0,0])*0.57142857)
#     xstep = int(math.atan(temp/890)*1018.591636)  #int((points[1,0]-points[0,0])*0.57142857)temp = points[1,1]-points[0,1]ystep = int(math.atan(temp/740)*1018.591636)
#     ystep = int(math.atan(temp/974)*1018.591636)#print('xstep =',xstep,'ystep =',ystep)#print()if xstep >= 0:absxstep = xstepxdir = 1else:absxstep = -xstepxdir = 0if ystep >= 0:absystep = ystepydir = 1else:absystep = -ystepydir = 0       # 得到xstep和ystep的最大值if absxstep >= absystep:   # 说明较长边在x轴maxdir = 'x' absmaxstep = absxstepmaxstep = xstepminstep = ystepelse:maxdir = 'y'absmaxstep = absystepmaxstep = ystepminstep = xstep# 分段运行节数sections = int(absmaxstep/dstep)+1dstepmin = minstep/sectionsdstepmax = maxstep/sectionsfor i in range(1,sections+1):if maxdir == 'x': # 沿x边走,先算累计步长steps.append([round(i * dstepmax),round(i * dstepmin)])else:  # 沿y边走,先算累计步长steps.append([round(i * dstepmin),round(i * dstepmax)])if  steps[sections-1][0] != xstep:steps[sections-1][0] == xstepif  steps[sections-1][1] != ystep:steps[sections-1][1] == ystep #print('steps=', steps)#print()for i in range(sections-1,0,-1):steps[i][0] -= steps[i-1][0]steps[i][1] -= steps[i-1][1]  #print('steps=', steps)       points = len(steps)  # 总点数for i in range(points):   xrunstep(steps[i][0])yrunstep(steps[i][1])upstop()downstop()def findLaser(crop):"""找到图片中点绿色激光点与红色激光点并定位中心:param crop: 需要处理点图片:return: 绿色激光点中心(x1, y1);红色激光点中心(x2, y2)"""cX1, cY1, cX2, cY2 = None, None, None, NonegreenLaser = 'green'redLaser = 'red'# 色系下限上限表color_dist = {'red': {'Lower': np.array([0, 60, 60]), 'Upper': np.array([6, 255, 255])},'green': {'Lower': np.array([35, 43, 35]), 'Upper': np.array([90, 255, 255])},}# 灰度图像处理# gray = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY)#灰度图# cv2.imshow('gray', gray)# 高斯滤波blurred = cv2.GaussianBlur(crop, (11, 11), 0)# cv2.imshow('blurred', blurred)# 创建运算核kernel = np.ones((1, 1), np.uint8)# 腐蚀# erode = cv2.erode(crop, kernel, iterations=1)# 膨胀# crop_dilate = cv2.dilate(blurred, kernel, iterations = 5)# 开运算opening = cv2.morphologyEx(blurred, cv2.MORPH_OPEN, kernel)# 二值化处理thresh = cv2.threshold(opening, 230, 255, cv2.THRESH_BINARY)[1]# cv2.imshow('thresh', thresh)hsv = cv2.cvtColor(thresh, cv2.COLOR_BGR2HSV)  # 转化成HSV图像# 颜色二值化筛选处理inRange_hsv_green = cv2.inRange(hsv, color_dist[greenLaser]['Lower'], color_dist[greenLaser]['Upper'])inRange_hsv_red = cv2.inRange(hsv, color_dist[redLaser]['Lower'], color_dist[redLaser]['Upper'])# cv2.imshow('inrange_hsv_green', inRange_hsv_green)# cv2.imshow('inrange_hsv_red', inRange_hsv_red)# 找绿色激光点try:cnts1 = cv2.findContours(inRange_hsv_green.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]c1 = max(cnts1, key=cv2.contourArea)M = cv2.moments(c1)cX1 = int(M["m10"] / M["m00"])cY1 = int(M["m01"] / M["m00"])cv2.circle(crop, (cX1, cY1), 3, (0, 255, 0), -1)rect = cv2.minAreaRect(c1)box = cv2.boxPoints(rect)
#         cv2.drawContours(crop, [np.int0(box)], -1, (0, 255, 0), 2)except:print('没有找到绿色的激光')# 找红色激光点try:cnts2 = cv2.findContours(inRange_hsv_red.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]c2 = max(cnts2, key=cv2.contourArea)M = cv2.moments(c2)cX2 = int(M["m10"] / M["m00"])cY2 = int(M["m01"] / M["m00"])cv2.circle(crop, (cX2, cY2), 3, (0, 0, 255), -1)rect = cv2.minAreaRect(c2)box = cv2.boxPoints(rect)
#         cv2.drawContours(crop, [np.int0(box)], -1, (0, 0, 255), 2)except:print('没有找到红色的激光')return cX1, cY1, cX2, cY2def draw_fit_line(dire, type):""":param dire: 传入需要拟合的含有白色直线的图像:return: 传出直线一般式参数a,b,c"""dic = {'up': (hStart, wStart + leftRight_distance), 'down': (hEnd - upDown_distance, wStart + leftRight_distance),'left': (hStart + upDown_distance, wStart), 'right': (hStart + upDown_distance, wEnd - leftRight_distance)}# 存放直线的像素点坐标axis = []for i in range(len(dire)):for j in range(len(dire[i])):if dire[i][j] == 255: # 判断为白色像素# 对应在原图中的高宽坐标h = i + dic[type][0]w = j + dic[type][1]axis.append([w, h])data_np = np.array(axis) # 坐标矩阵化output = cv2.fitLine(data_np, cv2.DIST_L2, 0, 0.01, 0.01)  # 直线拟合
#     print(output)k = output[1] / output[0]b = output[3] - k * output[2]return k, bdef crop(h, w, cnt):'''将图形裁剪出上下左右四个部分的中间位置:param cnt: 需要裁剪的图片:return:'''global upDown_distance, leftRight_distance# 上下部分裁剪的宽度upDown_distance = h# 左右部分裁剪的宽度leftRight_distance = wup = cnt[0:upDown_distance, leftRight_distance:wEnd - wStart - leftRight_distance]down = cnt[hEnd - hStart - upDown_distance:hEnd - hStart, leftRight_distance:wEnd - wStart - leftRight_distance]left = cnt[upDown_distance:hEnd - hStart - upDown_distance, 0:leftRight_distance]right = cnt[upDown_distance:hEnd - hStart - upDown_distance, wEnd -wStart - leftRight_distance:wEnd - wStart]# 显示四个边长裁剪过后的图片
#     cv2.imshow('up', up)
#     cv2.imshow('down', down)
#     cv2.imshow('left', left)
#     cv2.imshow('right', right)return up, down, left, rightdef getCoordinate(k1, b1, k2, b2):x = (b2 - b1) / (k1 - k2)y = k1 * x + b1return int(x), int(y)def drawCenter(a, b, c, d):# 显示边长交点cv2.circle(img, (a[0], a[1]), 3, (0, 0, 255), -1)cv2.circle(img, (b[0], b[1]), 3, (0, 0, 255), -1)cv2.circle(img, (c[0], c[1]), 3, (0, 0, 255), -1)cv2.circle(img, (d[0], d[1]), 3, (0, 0, 255), -1)# 画边界线cv2.line(img, (a[0], a[1]), (b[0], b[1]), (0, 255, 0), 1)cv2.line(img, (b[0], b[1]), (c[0], c[1]), (0, 255, 0), 1)cv2.line(img, (a[0], a[1]), (d[0], d[1]), (0, 255, 0), 1)cv2.line(img, (c[0], c[1]), (d[0], d[1]), (0, 255, 0), 1)xCenter = (a[0] + b[0] + c[0] + d[0])//4yCenter = (a[1] + b[1] + c[1] + d[1])//4cv2.circle(img, (xCenter, yCenter), 4, (0, 0, 255), -1)return xCenter, yCenterdef dengfen(point, k):dfpoint = np.zeros((k+1,2))dfpoint[0,:] = point[0,:]for i in range(k):dfpoint[i+1,0] = point[0,0] + int((i+1)*(point[1,0]-point[0,0])/k)dfpoint[i+1,1] = point[0,1] + int((i+1)*(point[1,1]-point[0,1])/k)return dfpointif __name__ == "__main__":downsetup()upsetup()upstop()downstop()lightBuzerSetup()off()cap = cv2.VideoCapture(0)cap.set(3,1280)cap.set(4,720)  #设置窗口的大小for i in range(6):for j in range(6):ret, img = cap.read()# 需要裁剪的区域###################################hStart, hEnd, wStart, wEnd = 180, 630, 300, 900################################################cropImg = img[hStart:hEnd, wStart:wEnd]# 找激光点 ->xc1, yc1, xc2, yc2 = findLaser(cropImg)xc1b, yc1b, xc2b, yc2b = xc1, yc1, xc2, yc2 if xc1 != None or xc2 != None:# 对应到原点中的激光坐标xc1, yc1 = xc1 + wStart, yc1 + hStartprint('绿色激光点坐标:', xc1, yc1)xc2, yc2 = xc2 + wStart, yc2 + hStartprint('红色激光点坐标:', xc2, yc2)        #         cv2.imshow('cropImg', cropImg)           
#         cv2.waitKey(0)
#         cv2.destroyAllWindows()points = np.array([[xc1, yc1],[xc2, yc2]])  print('points', points)runtwopoints(points)beep(0.2)beepdestroy()

发挥2部分代码不稳定,就不拿出来丢人了哈!

​​​​​​​祝大家学业有成,在竞赛都能拿个好奖项!最重要的一点是祝我的学姐学长,考研顺利!🚀​​​​​​​

个人思路或者代码有问题,欢迎大家指正!

这篇关于2023年电赛E题-运动目标控制与自动追踪系统(OpenCv)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/359287

相关文章

vcpkg安装opencv中的特殊问题记录(无法找到opencv_corexd.dll)

我是按照网上的vcpkg安装opencv方法进行的(比如这篇:从0开始在visual studio上安装opencv(超详细,针对小白)),但是中间出现了一些别人没有遇到的问题,虽然原因没有找到,但是本人给出一些暂时的解决办法: 问题1: 我在安装库命令行使用的是 .\vcpkg.exe install opencv 我的电脑是x64,vcpkg在这条命令后默认下载的也是opencv2:x6

通信系统网络架构_2.广域网网络架构

1.概述          通俗来讲,广域网是将分布于相比局域网络更广区域的计算机设备联接起来的网络。广域网由通信子网于资源子网组成。通信子网可以利用公用分组交换网、卫星通信网和无线分组交换网构建,将分布在不同地区的局域网或计算机系统互连起来,实现资源子网的共享。 2.网络组成          广域网属于多级网络,通常由骨干网、分布网、接入网组成。在网络规模较小时,可仅由骨干网和接入网组成

据阿谱尔APO Research调研显示,2023年全球髓内钉市场销售额约为4.7亿美元

根据阿谱尔 (APO Research)的统计及预测,2023年全球髓内钉市场销售额约为4.7亿美元,预计在2024-2030年预测期内将以超过3.82%的CAGR(年复合增长率)增长。 髓内钉市场是指涉及髓内钉制造、分销和销售的行业。髓内钉是一种用于整形外科手术的医疗器械,用于稳定长骨骨折,特别是股骨、胫骨和肱骨。髓内钉通常由不銹钢或钛等材料制成,并插入骨的髓管中,以在愈合过程中提供结构支

Linux系统稳定性的奥秘:探究其背后的机制与哲学

在计算机操作系统的世界里,Linux以其卓越的稳定性和可靠性著称,成为服务器、嵌入式系统乃至个人电脑用户的首选。那么,是什么造就了Linux如此之高的稳定性呢?本文将深入解析Linux系统稳定性的几个关键因素,揭示其背后的技术哲学与实践。 1. 开源协作的力量Linux是一个开源项目,意味着任何人都可以查看、修改和贡献其源代码。这种开放性吸引了全球成千上万的开发者参与到内核的维护与优化中,形成了

Java研学-RBAC权限控制(八)

九 登录登出 1 登录作用   判断员工是否有权限访问,首先得知道现在操作的人是谁,所以必须先实现登录功能 2 登录流程   ① 提供登录页面,可输入用户名与密码信息,并添加执行登录的按钮。(登录页面不能被拦截)   ② 给按钮绑定点击事件(异步操作,POST请求)   ③ 事件中发送登录请求,使用 AJAX 方式提交。(使用 AJAX 原因:用户体验更好,既可保留用户刚输入的用户名和密码

brew install opencv@2 时报错 Error: Can't create update lock in /usr/local/var/homebrew/locks!

解决方案,报错里已经说明了: 我的解决方案: sudo chown -R "$USER":admin /usr/local   stackoverflow上的答案 I was able to solve the problem by using chown on the folder: sudo chown -R "$USER":admin /usr/local Also you'

WordPress网创自动采集并发布插件

网创教程:WordPress插件网创自动采集并发布 阅读更新:随机添加文章的阅读数量,购买数量,喜欢数量。 使用插件注意事项 如果遇到404错误,请先检查并调整网站的伪静态设置,这是最常见的问题。需要定制化服务,请随时联系我。 本次更新内容 我们进行了多项更新和优化,主要包括: 界面设置:用户现在可以更便捷地设置文章分类和发布金额。代码优化:改进了采集和发布代码,提高了插件的稳定

【青龙面板辅助】JD商品自动给好评获取京豆脚本

1.打开链接 开下面的链接进入待评价商品页面 https://club.jd.com/myJdcomments/myJdcomments.action?sort=0 2.登陆后执行脚本 登陆后,按F12键,选择console,复制粘贴以下代码,先运行脚本1,再运行脚本2 脚本1代码 可以自行修改评价内容。 var content = '材质很好,质量也不错,到货也很快物流满分,包装快递满

3月份目标——刷完乙级真题

https://www.patest.cn/contests/pat-b-practisePAT (Basic Level) Practice (中文) 标号标题通过提交通过率1001害死人不偿命的(3n+1)猜想 (15)31858792260.41002写出这个数 (20)21702664840.331003我要通过!(20)11071447060.251004成绩排名 (20)159644

PS系统教程25

介绍软件 BR(bridge) PS 配套软件,方便素材整理、管理素材 作用:起到桥梁作用 注意:PS和BR尽量保持版本一致 下载和安装可通过CSDN社区搜索,有免费安装指导。 安装之后,我们打开照片只需双击照片,就自动在Ps软件中打开。 前提:电脑上有PS软件 三种预览格式 全屏预览 评星级 直接按数字键就可以 方向键可以更换图片 esc退出 幻灯片放