为您推荐:
精华内容
最热下载
问答
  • 5星
    159KB weixin_44020886 2021-02-17 08:48:00
  • 3星
    1KB qq_43001582 2018-08-18 10:09:04
  • 1.28MB weixin_43116606 2019-08-03 15:11:47
  • 3KB qq_45385612 2021-07-24 22:07:49
  • 水下机器人使用openmv巡线使用色块进行巡线使用findblobs进行颜色识别使用快速线性回归循迹 使用色块进行巡线 使用findblobs进行颜色识别 GeometryFeature.py class GeometryFeature: def __init__(self, img): ...

    使用色块进行巡线

    使用findblobs进行颜色识别

    GeometryFeature.py

    class GeometryFeature:
    
        def __init__(self, img):
            self.img = img
    
        @staticmethod
        def trans_line_format(line):
            '''
            将原来由两点坐标确定的直线,转换为 y = ax + b 的格式
            '''
            x1 = line.x1()
            y1 = line.y1()
            x2 = line.x2()
            y2 = line.y2()
    
            if x1 == x2:
                # 避免完全垂直,x坐标相等的情况
                x1 += 0.1
            # 计算斜率 a
            a = (y2 - y1) / (x2 - x1)
            # 计算常数项 b
            # y = a*x + b -> b = y - a*x
            b = y1 - a * x1
            return a,b
    
        @staticmethod
        def calculate_angle(line1, line2):
            '''
            利用四边形的角公式, 计算出直线夹角
            '''
            angle  = (180 - abs(line1.theta() - line2.theta()))
            if angle > 90:
                angle = 180 - angle
            return angle
    
        @staticmethod
        def find_verticle_lines(lines, angle_threshold=(70, 90)):
            '''
            寻找相互垂直的两条线
            '''
            return GeometryFeature.find_interserct_lines(lines, angle_threshold=angle_threshold)
    
        @staticmethod
        def find_interserct_lines(lines, angle_threshold=(10,90), window_size=None):
            '''
            根据夹角阈值寻找两个相互交叉的直线, 且交点需要存在于画面中
            '''
            line_num = len(lines)
            for i in range(line_num -1):
                for j in range(i, line_num):
                    # 判断两个直线之间的夹角是否为直角
                    angle = GeometryFeature.calculate_angle(lines[i], lines[j])
                    # 判断角度是否在阈值范围内
                    if not(angle >= angle_threshold[0] and angle <=  angle_threshold[1]):
                        continue
    
                    # 判断交点是否在画面内
                    if window_size is not None:
                        # 获取串口的尺寸 宽度跟高度
                        win_width, win_height = window_size
                        # 获取直线交点
                        intersect_pt = GeometryFeature.calculate_intersection(lines[i], lines[j])
                        if intersect_pt is None:
                            # 没有交点
                            continue
                        x, y = intersect_pt
                        if not(x >= 0 and x < win_width and y >= 0 and y < win_height):
                            # 交点如果没有在画面中
                            continue
                    return (lines[i], lines[j])
            return None
    
        @staticmethod
        def calculate_intersection(line1, line2):
            '''
            计算两条线的交点
            '''
            a1 = line1.y2() - line1.y1()
            b1 = line1.x1() - line1.x2()
            c1 = line1.x2()*line1.y1() - line1.x1()*line1.y2()
    
            a2 = line2.y2() - line2.y1()
            b2 = line2.x1() - line2.x2()
            c2 = line2.x2() * line2.y1() - line2.x1()*line2.y2()
    
            if (a1 * b2 - a2 * b1) != 0 and (a2 * b1 - a1 * b2) != 0:
                cross_x = int((b1*c2-b2*c1)/(a1*b2-a2*b1))
                cross_y = int((c1*a2-c2*a1)/(a1*b2-a2*b1))
                return (cross_x, cross_y)
            return None
    
    

    main.py

    '''
    原理介绍
        算法的主要核心在于,讲整个画面分割出来5个ROI区域
        * 上方横向采样
        * 中间横向采样
        * 下方横向采样
        * 左侧垂直采样
        * 右侧垂直采样
        通过判断5个图片的组合关系给出路口类型的判断
    '''
    import sensor
    import image
    import time
    import math
    import pyb
    from pyb import Pin, Timer,LED
    from GeometryFeature import GeometryFeature
    LED(4).on()
    is_debug = True
    DISTORTION_FACTOR = 1.5#畸变矫正因子
    IMG_WIDTH  = 64#像素点宽度
    IMG_HEIGHT = 64#像素点高度
    def init_sensor():#初始化感光器
        sensor.reset()
        sensor.set_pixformat(sensor.GRAYSCALE)#设置为灰度图
        sensor.set_framesize(sensor.B64X64)  #设置像素大小
        sensor.skip_frames(time=2000)        #最大像素点个数
        sensor.set_auto_gain(False)          #颜色追踪关闭自动增益
        sensor.set_auto_whitebal(False)      #颜色追踪关闭自动白平衡
    init_sensor()
    INTERSERCT_ANGLE_THRESHOLD = (45,90)     #设置角度阈值
    LINE_COLOR_THRESHOLD = [(0, 120)]        #设置巡线的颜色阈值
    ROIS = {                                 #ROIS将镜头的画面分割为5个区域分别找寻色块
        'down': (0, 55, 64, 8),
        'middle': (0, 28, 64, 8),
        'up': (0, 0, 64, 8),
        'left': (0, 0, 8, 64),
        'right': (56, 0, 8, 64)
    }
    def find_blobs_in_rois(img):
        '''
        在ROIS中寻找色块,获取ROI中色块的中心区域与是否有色块的信息
        '''
        global ROIS
        global is_debug
        roi_blobs_result = {}
        for roi_direct in ROIS.keys():
            roi_blobs_result[roi_direct] = {
                'cx': -1,
                'cy': -1,
                'blob_flag': False
            }
        for roi_direct, roi in ROIS.items():
            blobs=img.find_blobs(LINE_COLOR_THRESHOLD, roi=roi, merge=True, pixels_area=10)
            if len(blobs) == 0:
                continue
            largest_blob = max(blobs, key=lambda b: b.pixels())
            x,y,width,height = largest_blob[:4]
            if not(width >=5 and width <= 15 and height >= 5 and height <= 15):
                continue
            roi_blobs_result[roi_direct]['cx'] = largest_blob.cx()
            roi_blobs_result[roi_direct]['cy'] = largest_blob.cy()
            roi_blobs_result[roi_direct]['blob_flag'] = True
            if is_debug:
                img.draw_rectangle((x,y,width, height), color=(255))
        return roi_blobs_result
    def visualize_result(canvas, cx_mean, cx, cy, is_turn_left, is_turn_right, is_t, is_cross):
        '''
        可视化结果
        '''
        if not(is_turn_left or is_turn_right or is_t or is_cross):
            mid_x = int(canvas.width()/2)
            mid_y = int(canvas.height()/2)
            canvas.draw_circle(int(cx_mean), mid_y, 5, color=(255))
            canvas.draw_circle(mid_x, mid_y, 8, color=(0))
            canvas.draw_line((mid_x, mid_y, int(cx_mean), mid_y), color=(255))
        turn_type = 'N'   #判断为直线
        if is_t or is_cross:
            canvas.draw_cross(int(cx), int(cy), size=10, color=(255))
            canvas.draw_circle(int(cx), int(cy), 5, color=(255))
        if is_t:
            turn_type = 'T'  #判断为T字路口
        elif is_cross:
            turn_type = 'C'  #判断为十字路口
        elif is_turn_left:
            turn_type = 'L'  #判断为左转
        elif is_turn_right:
            turn_type = 'R'  #判断为右转
        canvas.draw_string(0, 0, turn_type, color=(0))
    last_cx = 0
    last_cy = 0
    while True:
        img = sensor.snapshot()     #拍取一张图片
        img.lens_corr(DISTORTION_FACTOR)  #进行镜头畸形矫正,里面的参数是进行鱼眼矫正的程度
        lines = img.find_lines(threshold=1000, theta_margin = 50, rho_margin = 50)
        intersect_pt = GeometryFeature.find_interserct_lines(lines, angle_threshold=(45,90), window_size=(IMG_WIDTH, IMG_HEIGHT))
        if intersect_pt is None:
            intersect_x = 0
            intersect_y = 0
        else:
            intersect_x, intersect_y = intersect_pt
        reslut = find_blobs_in_rois(img)
        is_turn_left = False
        is_turn_right = False
        if (not reslut['up']['blob_flag'] ) and reslut['down']['blob_flag']:
            if reslut['left']['blob_flag']:
                is_turn_left = True
            if reslut['right']['blob_flag']:
                is_turn_right = True
        is_t = False
        is_cross = False
        cnt = 0
        for roi_direct in ['up', 'down', 'left', 'right']:
            if reslut[roi_direct]['blob_flag']:
                cnt += 1
        is_t = cnt == 3
        is_cross = cnt == 4
        cx_mean = 0
        for roi_direct in ['up', 'down', 'middle']:
            if reslut[roi_direct]['blob_flag']:
                cx_mean += reslut[roi_direct]['cx']
            else:
                cx_mean += IMG_WIDTH / 2
        cx_mean /= 3  #表示为直线时区域的中心x坐标
        cx = 0        #cx,cy表示当测到为T型或者十字型的时候计算的交叉点的坐标
        cy = 0
        if is_cross or is_t:
            cnt = 0
            for roi_direct in ['up', 'down']:
                if reslut[roi_direct]['blob_flag']:
                    cnt += 1
                    cx += reslut[roi_direct]['cx']
            if cnt == 0:
                cx = last_cx
            else:
                cx /= cnt
            cnt = 0
            for roi_direct in ['left', 'right']:
                if reslut[roi_direct]['blob_flag']:
                    cnt += 1
                    cy += reslut[roi_direct]['cy']
            if cnt == 0:
                cy = last_cy
            else:
                cy /= cnt
        last_cx = cx
        last_cy = cy
        if is_debug:
            visualize_result(img, cx_mean, cx, cy, is_turn_left, is_turn_right, is_t, is_cross)
    
    

    使用快速线性回归循迹

    THRESHOLD = (5, 70, -23, 15, -57, 0) # Grayscale threshold for dark things...
    import sensor, image, time
    from pyb import LED
    
    #开启LED灯进行补光
    LED(1).on()
    LED(2).on()
    LED(3).on()
    
    sensor.reset()
    sensor.set_pixformat(sensor.RGB565)#设置图像为彩模式
    sensor.set_framesize(sensor.QQQVGA) # 80x60 (4,800 pixels) - O(N^2) max = 2,3040,000.
    #sensor.set_windowing([0,20,80,40])
    sensor.skip_frames(time = 2000)     # WARNING: If you use QQVGA it may take seconds
    clock = time.clock()                # to process a frame sometimes.
    
    while(True):
        clock.tick()
        img = sensor.snapshot().binary([THRESHOLD])
        line = img.get_regression([(100,100,0,0,0,0)], robust = True)
        if (line):
            rho_err = abs(line.rho())-img.width()/2
            if line.theta()>90:
                theta_err = line.theta()-180
            else:
                theta_err = line.theta()
            img.draw_line(line.line(), color = 127)
            print(rho_err,line.magnitude(),rho_err)
        #print(clock.fps())
    
    

    自动颜色跟踪

    由于每次开启小车之前都需要手动调试阈值,因此,我们可以使用自动颜色追踪,只需要在每次开始时将小车放到正中央进行阈值矫正。

    # 自动灰度颜色追踪例程
    #
    # 这个例子展示了使用OpenMV的单色自动灰度色彩跟踪。
    
    import sensor, image, time
    print("Letting auto algorithms run. Don't put anything in front of the camera!")
    
    sensor.reset()
    sensor.set_pixformat(sensor.GRAYSCALE)
    sensor.set_framesize(sensor.QVGA)
    sensor.skip_frames(time = 2000)
    sensor.set_auto_gain(False) # 颜色跟踪必须自动增益
    sensor.set_auto_whitebal(False) # 颜色跟踪必须关闭白平衡
    clock = time.clock()
    
    # 捕捉图像中心的颜色阈值。
    r = [(320//2)-(50//2), (240//2)-(50//2), 50, 50] # 50x50 center of QVGA.
    
    print("Auto algorithms done. Hold the object you want to track in front of the camera in the box.")
    print("MAKE SURE THE COLOR OF THE OBJECT YOU WANT TO TRACK IS FULLY ENCLOSED BY THE BOX!")
    for i in range(60):
        img = sensor.snapshot()
        img.draw_rectangle(r)
    
    print("Learning thresholds...")
    threshold = [128, 128] # Middle grayscale values.中间灰度值。
    for i in range(60):
        img = sensor.snapshot()
        hist = img.get_histogram(roi=r)
        lo = hist.get_percentile(0.01) # 获取1%范围的直方图的CDF(根据需要调整)!
        hi = hist.get_percentile(0.99) # 获取99%范围的直方图的CDF(根据需要调整)!
        # 平均百分位值。
        threshold[0] = (threshold[0] + lo.value()) // 2
        threshold[1] = (threshold[1] + hi.value()) // 2
        for blob in img.find_blobs([threshold], pixels_threshold=100, area_threshold=100, merge=True, margin=10):
            img.draw_rectangle(blob.rect())
            img.draw_cross(blob.cx(), blob.cy())
            img.draw_rectangle(r)
    
    print("Thresholds learned...")
    print("Tracking colors...")
    
    while(True):
        clock.tick()
        img = sensor.snapshot()
        for blob in img.find_blobs([threshold], pixels_threshold=100, area_threshold=100, merge=True, margin=10):
            img.draw_rectangle(blob.rect())
            img.draw_cross(blob.cx(), blob.cy())
        print(clock.fps())
    
    
    展开全文
    weixin_45123009 2021-04-06 21:12:26
  • STM32各类小车工作原理及学习 ...简介:循迹小车由三轮或四轮小车和摄像头两大部分组成,通过...寻迹小车核心组成:openMV和STM3F1。 涉及主要知识:STM32相关知识(学习视频 https://ke.qq.com/course/279403); openM...

    此篇文章是自己在学习制作寻迹小车中遇到的问题以及解决方法,写出来供同样的人参考参考…
    -------------->直接开始------------>
    STM32各类小车工作原理及学习
    http://www.yahboom.com/study/bc-32

    简介:循迹小车由三轮或四轮小车和摄像头两大部分组成,通过镜头识别路径,将其得到的图像做处
    理并发送给小车,小车对应做出动作。
    寻迹小车核心组成:openMV和STM3F1。
    涉及主要知识:STM32相关知识(学习视频 https://ke.qq.com/course/279403);
    openMV相关知识(学习网站 http://book.myopenmv.com/quick-start/ide-tutorial.html);
    USART通信(运用在摄像头与小车之间通信);
    SPI通信(用在OLED显示);
    直流电机有关资料;
    直流电机驱动TB6612FNG(http://tech.hqew.com/circuit_1476430);
    等等。

    掌握具体知识后便可搭建循迹小车了。
    然而并不是一帆风顺,肯定会漏洞百出…所以在搭建时候就细心耐心,一点一点解决问题。搭建时候的注意事项:
    1、在用杜邦线连接小车各部分模块时候,首先确保杜邦线没有坏;
    2、杜邦线也不能有松动或者没有插紧的情况,不然很容易造成接触不良,在调试陈旭时候也会被误认为程序问题而浪费时间;
    3、接的电压要按照各器件额定值接入。

    到现在小车基本上是搭建好了(除了摄像头部分),先启动小车,让它能够跑起来,还要能够跑直线,特别直!
    同时还要时不时地检查一下接线是否有松动的情况!!
    一切都OK了,那就装上openMV进行数据对接。

    有几点软件部分需要注意的事项:
    1、注意使用USART通信要明确自己所用的协议,否则会出现不是自己预想的数据;
    2、在我使用openMV时候,左右的偏移量为0到45 和 -45到0,但是将该数据传输回STM32时候负数段数据不正常。解决方法为将左右偏移量的数据范围加上相应的数字,使其左右偏移量范围里没有负数数据。

    搞好以后迫不及待的要跑一下了,但是刚按下开关,小车按照轨迹过弯道过到一半时候乱跑了,然后又是瞎跑。。。。做到这一步又有几点需要注意,可耐心读完:
    1、 注意,这几点很重要!!!
    2、小车能够简单寻迹,但又不能完全寻迹时候,可以把摄像头从小车上拿下来,固定在一个摄像头倾斜角和高度相同的简易支架上,连接到电脑上观看小车视野,,在OPENMV IDE里面查看画面,是否在过弯到一半时候找不到视野。若是这个问题则根据自己的需求配合程序进行相应的调整;
    3、 小车向左转很顺利,向右转会“出轨”,或是左转到一半会瞎跑。首先要确保摄像头摆放位置是正的,接线是否有松动而影响数据传输,然后确保硬件没问题时候检查偏移量的数据。通过查看偏移时候openMV处理出来的数据,发现左边过弯很顺利是其偏移量正常,而右边偏移量不足,导致其过弯一半“出轨”。适当增加右转偏移量即可解决。
    4、 有时会出现数据传输错乱,但是硬件又没有问题,这时候应该把数据传输有关的模块单独接电源或是原理接线群,其他的线路可能会有些许的影响(没有固然最好,但是值得一试)。
    5、 测试时候-务-必-一-定-必-须-要在光线好的地方测试,很重要!!!!因为光线弱会影响到识别路径,从而导致乱跑、跑到旁边的路径上,此时又会误以为是程序出了问题,又去调试程序浪费时间。程序调试N多次,不如灯光照一次!!

    好了,到这里你的小车应该会跑的很顺利了
    最后附上openMV的Python程序

    # Black Grayscale Line Following Example
    #
    # Making a line following robot requires a lot of effort. This example script
    # shows how to do the machine vision part of the line following robot. You
    # can use the output from this script to drive a differential drive robot to
    # follow a line. This script just generates a single turn value that tells
    # your robot to go left or right.
    #
    # For this script to work properly you should point the camera at a line at a
    # 45 or so degree angle. Please make sure that only the line is within the
    # camera's field of view.
    
    import sensor, image, time, math#调用声明
    from pyb import UART
    
    # Tracks a black line. Use [(128, 255)] for a tracking a white line.
    GRAYSCALE_THRESHOLD = [(0, 64)]
    #设置阈值,如果是黑线,GRAYSCALE_THRESHOLD = [(0, 64)];
    #如果是白线,GRAYSCALE_THRESHOLD = [(128,255)]
    
    
    # Each roi is (x, y, w, h). The line detection algorithm will try to find the
    # centroid of the largest blob in each roi. The x position of the centroids
    # will then be averaged with different weights where the most weight is assigned
    # to the roi near the bottom of the image and less to the next roi and so on.
    ROIS = [ # [ROI, weight]
            (0, 100, 160, 20, 0.7), # You'll need to tweak the weights for you app
            (0, 050, 160, 20, 0.3), # depending on how your robot is setup.
            (0, 000, 160, 20, 0.1)
           ]
    #roi代表三个取样区域,(x,y,w,h,weight),代表左上顶点(x,y)宽高分别为w和h的矩形,
    #weight为当前矩形的权值。注意本例程采用的QQVGA图像大小为160x120,roi即把图像横分成三个矩形。
    #三个矩形的阈值要根据实际情况进行调整,离机器人视野最近的矩形权值要最大,
    #如上图的最下方的矩形,即(0, 100, 160, 20, 0.7)
    
    # Compute the weight divisor (we're computing this so you don't have to make weights add to 1).
    weight_sum = 0 #权值和初始化
    for r in ROIS: weight_sum += r[4] # r[4] is the roi weight.
    #计算权值和。遍历上面的三个矩形,r[4]即每个矩形的权值。
    
    # Camera setup...
    sensor.reset() # Initialize the camera sensor.
    sensor.set_pixformat(sensor.GRAYSCALE) # use grayscale.
    sensor.set_framesize(sensor.QQVGA) # use QQVGA for speed.
    sensor.skip_frames(30) # Let new settings take affect.
    sensor.set_auto_gain(False) # must be turned off for color tracking
    sensor.set_auto_whitebal(False) # must be turned off for color tracking
    #关闭白平衡
    clock = time.clock() # Tracks FPS.
    
    while(True):
        clock.tick() # Track elapsed milliseconds between snapshots().
        img = sensor.snapshot() # Take a picture and return the image.
        uart = UART(3,19200)
        uart.init(19200,bits=8,parity=None,stop=1)#init with given parameters
    
        centroid_sum = 0
        #利用颜色识别分别寻找三个矩形区域内的线段
        for r in ROIS:
            blobs = img.find_blobs(GRAYSCALE_THRESHOLD, roi=r[0:4], merge=True)
            # r[0:4] is roi tuple.
            #找到视野中的线,merge=true,将找到的图像区域合并成一个
    
            #目标区域找到直线
            if blobs:
                # Find the index of the blob with the most pixels.
                most_pixels = 0
                largest_blob = 0
                for i in range(len(blobs)):
                #目标区域找到的颜色块(线段块)可能不止一个,找到最大的一个,作为本区域内的目标直线
                    if blobs[i].pixels() > most_pixels:
                        most_pixels = blobs[i].pixels()
                        #merged_blobs[i][4]是这个颜色块的像素总数,如果此颜色块像素总数大于                     #most_pixels,则把本区域作为像素总数最大的颜色块。更新most_pixels和largest_blob
                        largest_blob = i
    
                # Draw a rect around the blob.
                img.draw_rectangle(blobs[largest_blob].rect())
                img.draw_rectangle((0,0,30, 30))
                #将此区域的像素数最大的颜色块画矩形和十字形标记出来
                img.draw_cross(blobs[largest_blob].cx(),
                               blobs[largest_blob].cy())
    
                centroid_sum += blobs[largest_blob].cx() * r[4] # r[4] is the roi weight.
                #计算centroid_sum,centroid_sum等于每个区域的最大颜色块的中心点的x坐标值乘本区域的权值
    
        center_pos = (centroid_sum / weight_sum) # Determine center of line.
        #中间公式
    
        # Convert the center_pos to a deflection angle. We're using a non-linear
        # operation so that the response gets stronger the farther off the line we
        # are. Non-linear operations are good to use on the output of algorithms
        # like this to cause a response "trigger".
        deflection_angle = 0
        #机器人应该转的角度
    
        # The 80 is from half the X res, the 60 is from half the Y res. The
        # equation below is just computing the angle of a triangle where the
        # opposite side of the triangle is the deviation of the center position
        # from the center and the adjacent side is half the Y res. This limits
        # the angle output to around -45 to 45. (It's not quite -45 and 45).
        deflection_angle = -math.atan((center_pos-80)/60)
        #角度计算.80 60 分别为图像宽和高的一半,图像大小为QQVGA 160x120.
        #注意计算得到的是弧度值
    
        # Convert angle in radians to degrees.
        deflection_angle = math.degrees(deflection_angle)
        #将计算结果的弧度值转化为角度值
        A=deflection_angle
        print("Turn Angle: %d" % int (A))#输出时强制转换类型为int
        #print("Turn Angle: %d" % char (A))
        # Now you have an angle telling you how much to turn the robot by which
        # incorporates the part of the line nearest to the robot and parts of
        # the line farther away from the robot for a better prediction.
        print("Turn Angle: %f" % deflection_angle)
        #将结果打印在terminal中
        uart_buf = bytearray([int (A)])
        #uart_buf = bytearray([char (A)])
        #uart.write(uart_buf)#区别于uart.writechar是输出字符型,这个函数可以输出int型
        uart.write(uart_buf)
        uart.writechar(0x41)#通信协议帧尾
        uart.writechar(0x42)
    
        time.sleep(10)#延时
        print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while
        # connected to your computer. The FPS should increase once disconnected.
    
    
    展开全文
    weixin_43679759 2019-03-06 18:31:40
  • 2KB qq_44742927 2019-04-03 11:21:09
  • 5星
    4KB qq_21563141 2018-08-27 21:35:00
  • 将从openmv串口所得的数据包进行解析,之后将解析的数据传出去,Openmv的掉线检测,Openmv的掉线检测复位 / 1.函数:static void OpenMV_Data_Analysis(u8 *buf_data,u8 len) 名称:Openmv数据解析函数 功能:...

    /************************************************************************
    作者:不会写代码的菜鸟
    时间:2019.7.30
    源码:匿名TI板飞控源码+openmvH4
    说明:限于本人水平有限,并不能解读的很详细,还望各位大佬能够补充
    /
    //
    文件 Drv_OpenMV.c
    功能
    将从openmv串口所得的数据包进行解析,之后将解析的数据传出去,Openmv的掉线检测,Openmv的掉线检测复位
    /

    1.函数:static void OpenMV_Data_Analysis(u8 *buf_data,u8 len)
    名称:Openmv数据解析函数
    功能:解析数据包,并将数据传出去

    if(*(buf_data+3)==0x41)  //色块追踪数据包的功能字
    {
    	opmv.cb.color_flag = *(buf_data+5);
    	opmv.cb.sta = *(buf_data+6);
    	opmv.cb.pos_x = (s16)((*(buf_data+7)<<8)|*(buf_data+8));
    	opmv.cb.pos_y = (s16)((*(buf_data+9)<<8)|*(buf_data+10));
    	opmv.mode_sta = 1;  //色块追踪模式
    }
    else if(*(buf_data+3)==0x42) //飞控循迹的功能字,传递方式可以对照Openmv的数据打包方式更好理解
    {
    	opmv.lt.sta = *(buf_data+5);  //巡线检测到的标志位
    	opmv.lt.angle = (s16)((*(buf_data+6)<<8)|*(buf_data+7));  //偏航角度
    	opmv.lt.deviation = (s16)((*(buf_data+8)<<8)|*(buf_data+9));  //偏移距离
    	opmv.lt.p_flag = *(buf_data+10);  //是否是悬停的标志位
    	opmv.lt.pos_x = (s16)((*(buf_data+11)<<8)|*(buf_data+12));  //中心点的x坐标
    	opmv.lt.pos_y = (s16)((*(buf_data+13)<<8)|*(buf_data+14));  //中心点的Y坐标
    	opmv.mode_sta = 2;  //巡模式
    }
    

    ************************************************************************/
    //

    //
    文件 Ano_OPMV_Ctrl.c
    功能
    openmv检测到的数据经过一系列处理后,在这个文件夹里面被使用,实现飞机的自动巡线
    /************************************************************************
    1.函数:void ANO_OPMV_Ctrl_Task(u8 dT_ms)
    名称:Openmv控制任务
    功能:实现一系列的标志位的置位以及判断执行那种程控函数

    *opmv_ct_sta.height_flag =1(飞机高于四十厘米的高度标记置位)
    *opmv_ct_sta.en = 1(当光流,openmv,高度标记置位以及AUX2通道值在范围内允许Openmv正常巡线)
    *opmv.mode_sta(当数据包解析到这个为1的时候是色块追踪,解析到为2的时候为巡线)

    if(opmv.mode_sta==1)  //为色块追踪只进行pit,rol程控
    {
    	opmv_ct_sta.reset_flag = 0;
    	ANO_CBTracking_Ctrl(&dT_ms,opmv_ct_sta.en);
    	//调用用户程控函数赋值控制量
    Program_Ctrl_User_Set_HXYcmps(ano_opmv_cbt_ctrl.exp_velocity_h_cmps[0],ano_opmv_cbt_ctrl.exp_velocity_h_cmps[1]);
    }
    else if(opmv.mode_sta == 2)  //为巡线是pit,rol,yaw同时进行程控
    {
    	opmv_ct_sta.reset_flag = 0;
    	ANO_LTracking_Ctrl(&dT_ms,opmv_ct_sta.en);
    	//调用用户程控函数赋值控制量
    	
    Program_Ctrl_User_Set_HXYcmps(ano_opmv_lt_ctrl.exp_velocity_h_cmps[0],ano_opmv_lt_ctrl.exp_velocity_h_cmps[1]);		
    Program_Ctrl_User_Set_YAWdps(ano_opmv_lt_ctrl.exp_yaw_pal_dps);
    }
    

    ************************************************************************/

    //
    文件 Ano_OPMV_LineTracking_Ctrl.c
    功能
    对openmv的数据进行处理后给到程控去使用,总之在这里就是将得到的数据进行合适的数学处理,以及一些状态位的判断置位
    /************************************************************************
    1.函数:void ANO_LTracking_Task(u8 dT_ms)
    名称:巡线任务函数
    功能:放到伪线程里面进行执行,再执行里面的子函数
    ************************************************************************/

    /************************************************************************
    2.函数:static void ANO_LTracking_Decoupling(u8 *dT_ms,float rol_degs,float pit_degs)
    名称:巡线解耦和
    功能:解除因机体横滚旋转而造成寻线目标位置变化的耦合,也称作“旋转解耦”或“旋转补偿”。并耦合高度得到地面偏差,用解算的地面偏差给到期望速度,以修正偏差。
    *opmv.lt.sta(openmv识别标志位,即flag,识别到直线或者转角的标志位
    *ano_opmv_lt_ctrl.opmv_pos = -opmv.lt.deviation(用这个值最终得到这个反馈值ano_opmv_lt_ctrl.decou_pos_pixel)
    ************************************************************************/

    /************************************************************************
    3.函数:static void ANO_LTracking_Calcu(u8 *dT_ms,s32 relative_height_cm)
    名称:巡线计算处理
    功能:求出位置偏差以及位置偏差的微分,以便用到下面的void ANO_LTracking_Ctrl(u8 *dT_ms,u8 en)函数,执行PD+前馈控制
    *大致方法和前面的色块追踪大致相同
    ************************************************************************/

    /************************************************************************
    4.函数:void ANO_LT_StepProcedure(u8 *dT_ms)
    名称:巡线循迹分布控制函数
    功能:就是分步实现飞机的前进,左右转弯等等

    step_pro_sta(分步控制的标志位)
    ano_opmv_lt_ctrl.exp_yaw_pal_dps(将航角速度赋给这个)
    ano_opmv_lt_ctrl.exp_velocity_h_cmps[0]=0(飞机循迹过程中的飞行速度)
    8ano_opmv_lt_ctrl.exp_velocity_h_cmps[1]=0(左右位置的偏差距离)
    *confirm_cnt(确认次数,相当于一个延时的作用,当确定捕捉到后才往下执行转角的动作)
    

    *左转角度为负,右转角度为正
    这一部分的代码比较容易看懂,稍微有c语言基础的估计都能看懂,就不过多解读
    ************************************************************************/

    /************************************************************************
    5.函数:void ANO_LTracking_Ctrl(u8 *dT_ms,u8 en)
    名称:巡线执行任务
    功能:将前面滤波得到后的数据进行PD处理,内容也不多

        ano_opmv_lt_ctrl.exp_velocity_h_cmps[1]   
    	= LT_KP *ano_opmv_lt_ctrl.ground_pos_err_h_cm\
    	+ LT_KD *ano_opmv_lt_ctrl.ground_pos_err_d_h_cmps//这个左右距中心位置的偏差  
        ano_opmv_lt_ctrl.exp_yaw_pal_dps = -opmv.lt.angle *6;
        ano_opmv_lt_ctrl.exp_yaw_pal_dps = LIMIT(ano_opmv_lt_ctrl.exp_yaw_pal_dps,-100,100);//这个是对YAW角的偏航修正
    

    *en(这里面的修正当然要在这个标志位为1的时候才能起作用,否则都是面谈)


    这里面所有处理过的数据之后都要到程控函数里面去使用,才能实现循迹的功能,Ano_ProgramCtrl_User.c(程控函数)
    //

    展开全文
    qq_42772967 2019-07-30 17:08:58
  • 1、寻线:OpenMV4摄像头将获得的轨迹进行处理,得到轨迹的一个偏离角度,然后通过偏转角判断给STM32发送左转或右转的信号。 2、停止:MV4通过模板匹配和识别圆双重判断是否目标为圆,如果是则给STM32发出信号来停止...

    要完成功能

    主要完成循迹部分图像数据采集:
    1、寻线:OpenMV4摄像头将获得的轨迹进行处理,得到轨迹的一个偏离角度,然后通过偏转角判断给STM32发送左转或右转的信号。
    2、停止:MV4通过模板匹配和识别圆双重判断是否目标为圆,如果是则给STM32发出信号来停止小车运动并开始树莓派识别。

    1、寻线部分:

    通过获得摄像头中线条进行几个块区域的数学运算,计算出轨迹偏离角度。
    例程:

    # 机器人巡线例程
    #
    # 跟随机器人做一条线需要很多努力。 本示例脚本显示了如何执行跟随机器人的
    # 机器视觉部分。 您可以使用此脚本的输出来驱动差分驱动机器人遵循一条线。
    # 这个脚本只是产生一个转动的值,告诉你的机器人向左或向右。
    #
    # 为了使这个脚本正常工作,你应该把摄像机指向45度左右的一条线。请确保只有线在相机的视野内。
    import sensor, image, time, math
    
    # 跟踪一条黑线。使用[(128,255)]来跟踪白线。
    GRAYSCALE_THRESHOLD = [(0, 64)]
    #设置阈值,如果是黑线,GRAYSCALE_THRESHOLD = [(0, 64)];
    #如果是白线,GRAYSCALE_THRESHOLD = [(128,255)]
    
    
    # 每个roi为(x, y, w, h),线检测算法将尝试找到每个roi中最大的blob的质心。
    # 然后用不同的权重对质心的x位置求平均值,其中最大的权重分配给靠近图像底部的roi,
    # 较小的权重分配给下一个roi,以此类推。
    ROIS = [ # [ROI, weight]
            (0, 100, 160, 20, 0.7), # 你需要为你的应用程序调整权重
            (0, 050, 160, 20, 0.3), # 取决于你的机器人是如何设置的。
            (0, 000, 160, 20, 0.1)
           ]
    #roi代表三个取样区域,(x,y,w,h,weight),代表左上顶点(x,y)宽高分别为w和h的矩形,
    #weight为当前矩形的权值。注意本例程采用的QQVGA图像大小为160x120,roi即把图像横分成三个矩形。
    #三个矩形的阈值要根据实际情况进行调整,离机器人视野最近的矩形权值要最大,
    #如上图的最下方的矩形,即(0, 100, 160, 20, 0.7)
    
    # Compute the weight divisor (we're computing this so you don't have to make weights add to 1).
    weight_sum = 0 #权值和初始化
    for r in ROIS: weight_sum += r[4] # r[4] is the roi weight.
    #计算权值和。遍历上面的三个矩形,r[4]即每个矩形的权值。
    
    sensor.reset() # 初始化sensor
    
    sensor.set_pixformat(sensor.GRAYSCALE) # use grayscale.
    #设置图像色彩格式,有RGB565色彩图和GRAYSCALE灰度图两种
    
    sensor.set_framesize(sensor.QQVGA) # 使用QQVGA的速度。
    #设置图像像素大小
    
    sensor.skip_frames(30) # 让新的设置生效。
    sensor.set_auto_gain(False) # 颜色跟踪必须关闭自动增益
    sensor.set_auto_whitebal(False) # 颜色跟踪必须关闭白平衡
    clock = time.clock() # 跟踪FPS帧率
    
    while(True):
        clock.tick() # 追踪两个snapshots()之间经过的毫秒数.
        img = sensor.snapshot() # 拍一张照片并返回图像。
    
        centroid_sum = 0
        #利用颜色识别分别寻找三个矩形区域内的线段
        for r in ROIS:
            blobs = img.find_blobs(GRAYSCALE_THRESHOLD, roi=r[0:4], merge=True) 
            # r[0:4] is roi tuple.
            #找到视野中的线,merge=true,将找到的图像区域合并成一个
    
            #目标区域找到直线
            if blobs:
                # 查找像素最多的blob的索引。
                largest_blob = 0
                for i in range(len(blobs)):
                #目标区域找到的颜色块(线段块)可能不止一个,找到最大的一个,作为本区域内的目标直线
                    if blobs[i].pixels() > most_pixels:
                        most_pixels = blobs[i].pixels()
                        #merged_blobs[i][4]是这个颜色块的像素总数,如果此颜色块像素总数大于                     
                        #most_pixels,则把本区域作为像素总数最大的颜色块。更新most_pixels和largest_blob
                        largest_blob = i
    
                # 在色块周围画一个矩形。
                img.draw_rectangle(blobs[largest_blob].rect())
                # 将此区域的像素数最大的颜色块画矩形和十字形标记出来
                img.draw_cross(blobs[largest_blob].cx(),
                               blobs[largest_blob].cy())
    
                centroid_sum += blobs[largest_blob].cx() * r[4] # r[4] is the roi weight.
                #计算centroid_sum,centroid_sum等于每个区域的最大颜色块的中心点的x坐标值乘本区域的权值
    
        center_pos = (centroid_sum / weight_sum) # Determine center of line.
        #中间公式
    
        # 将center_pos转换为一个偏角。我们用的是非线性运算,所以越偏离直线,响应越强。
        # 非线性操作很适合用于这样的算法的输出,以引起响应“触发器”。
        deflection_angle = 0
        #机器人应该转的角度
    
        # 80是X的一半,60是Y的一半。
        # 下面的等式只是计算三角形的角度,其中三角形的另一边是中心位置与中心的偏差,相邻边是Y的一半。
        # 这样会将角度输出限制在-45至45度左右。(不完全是-45至45度)。
    
        deflection_angle = -math.atan((center_pos-80)/60)
        #角度计算.80 60 分别为图像宽和高的一半,图像大小为QQVGA 160x120.    
        #注意计算得到的是弧度值
    
        deflection_angle = math.degrees(deflection_angle)
        #将计算结果的弧度值转化为角度值
    
        # 现在你有一个角度来告诉你该如何转动机器人。
        # 通过该角度可以合并最靠近机器人的部分直线和远离机器人的部分直线,以实现更好的预测。
    
        print("Turn Angle: %f" % deflection_angle)
        #将结果打印在terminal中
    
            print(clock.fps())              
        # 注意: 当连接电脑后,OpenMV会变成一半的速度。当不连接电脑,帧率会增加。
        # 打印当前的帧率。
    
    

    2、串口通信:

    import time
    from pyb import UART
    
    uart = UART(3, 19200)
    
    while(True):
        uart.write("Hello World!\r")
        time.sleep_ms(1000)
    

    先实例化一个19200波特率的串口3,然后调用write方法就可以了。
    注意:必须是串口3,因为OpenMV2只引出了这个串口,pyb的串口有好多个的。OpenMV3又增加了串口1。

    3、停止部分:

    模板匹配:https://blog.csdn.net/weixin_48267104/article/details/109139208

    颜色识别:https://blog.csdn.net/weixin_48267104/article/details/109020200

    程序:

    #-------(1)识别圆形部分--------------
    
            for c in img.find_circles(threshold = 3500, x_margin = 10, y_margin = 10, r_margin = 10,r_min = 2, r_max = 100, r_step = 2): # 如果发现圆
                img.draw_circle(c.x(), c.y(), c.r(), color = (0, 255, 0)) # 框出圆
                Discern_look_circle = 1
                # print(c) # 打印出{"x":.., "y":.., "r":.., "magnitude":..} magnitude是检测圆的强度。越高越好
            # print("FPS %f" % clock.fps())
    
    #-------(2)模板匹配部分--------------
    
            for t in templates1:  #如果与模板匹配
                template = image.Image(t) #template获取图片
                r = img.find_template(template, 0.70, step=4, search=SEARCH_EX) #进行相关设置,可以设置roi缩小区域
                if r: #如果有目标
                    img.draw_rectangle(r) #画矩形,框出匹配的目标
                    Discern_template_circle = 1
                    # print("圆形")
    
            Discern = Discern_look_circle + Discern_template_circle
    

    循迹总程序:

    #
    # 机器人巡线
    #
    
    import sensor, image, time, math, lcd
    from pyb import UART
    from image import SEARCH_EX, SEARCH_DS
    #从imgae模块引入SEARCH_EX和SEARCH_DS。使用from import仅仅引入SEARCH_EX,
    #SEARCH_DS两个需要的部分,而不把image模块全部引入。
    
    # OpenMV上P4,P5对应的串口3
    # 第二个参数是波特率。用来更精细的控制
    uart = UART(3, 9600, timeout_char=1000)
    
    
    EXPOSURE = 33333 #设置曝光,需要更改
    GRAYSCALE_THRESHOLD = [(0, 64)] # 跟踪黑线。 如果是白线[(128,255)]
    S_PIXEL = sensor.QQVGA #设置图像像素模式:QQVGA/LCD/VGA
    
    # 每个roi为(x, y, w, h),线检测算法将尝试找到每个roi中最大的blob的质心。
    # 然后用不同的权重对质心的x位置求平均值,其中最大的权重分配给靠近图像底部的roi,
    # 较小的权重分配给下一个roi,以此类推。
    ROIS_QQVGA = [ # [ROI, weight]  # QQVGA分辨率: 160x120
            (0, 000, 160, 10, 0.1),
            (0, 025, 160, 10, 0.3),
            (0, 055, 160, 10, 0.5),
            (0, 085, 160, 10, 0.7),
            (0, 110, 160, 10, 0.9)
           ]
    ROIS_LCD = [ # [ROI, weight]  # LCD分辨率128x160
            (0, 000, 128, 10, 0.1),
            (0, 035, 128, 10, 0.3),
            (0, 075, 128, 10, 0.5),
            (0, 115, 128, 10, 0.7),
            (0, 150, 128, 10, 0.9)
           ]
    ROIS_VGA = [ # [ROI, weight]  # VGA分辨率640x480
            (0, 000, 640, 40, 0.1),
            (0, 110, 640, 40, 0.3),
            (0, 230, 640, 40, 0.5),
            (0, 350, 640, 40, 0.7),
            (0, 460, 640, 40, 0.9)
           ]
    
    if S_PIXEL == sensor.LCD:
        ROIS = ROIS_LCD
    elif S_PIXEL == sensor.QQVGA:
        ROIS = ROIS_QQVGA
    elif S_PIXEL == sensor.VGA:
        ROIS = ROIS_VGA
    
    
    weight_sum = 0 # 权值和的初始化
    
    
    for r in ROIS: weight_sum += r[4] # 计算权值和。遍历上面的三个矩形,r[4]即每个矩形的weight值。
    
    
    sensor.reset() #初始化摄像头
    sensor.set_contrast(1)
    sensor.set_gainceiling(16)
    sensor.set_pixformat(sensor.GRAYSCALE) #尽量用灰度方式 # RGB565色彩图和GRAYSCALE灰度图
    sensor.set_framesize(S_PIXEL) #设置图像像素模式  # 尽量使用QQVGA
    sensor.skip_frames(time = 2000) # 设置一定时间让新的设置生效。在更改设置后,跳过一些帧,等待感光元件变稳定。
    sensor.set_auto_gain(False) # 颜色跟踪必须关闭自动增益
    sensor.set_auto_whitebal(False) #关闭白平衡。
    sensor.set_auto_exposure(False,EXPOSURE) #设置曝光,需要更改
    #sensor.set_windowing(((640-80)//2, (480-60)//2, 80, 60)) #子分辨率。可设置windowing窗口来减少搜索图片
    lcd.init() # 初始化lcd屏幕。
    
    # 模板照片
    # templates1 = ["/100.pgm","/101.pgm","/102.pgm","/103.pgm","/110.pgm","/111.pgm","/112.pgm","/113.pgm","/120.pgm","/121.pgm","/122.pgm","/123.pgm"] #保存正三角形多个模板
    # templates2 = ["/200.pgm","/201.pgm","/202.pgm","/203.pgm","/210.pgm","/211.pgm","/212.pgm","/213.pgm","/220.pgm","/221.pgm","/222.pgm","/223.pgm"] #保存倒三角形多个模板
    templates1 = ["/003.pgm"] #保存正三角形多个模板
    templates2 = ["/003.pgm"] #保存倒三角形多个模板
    
    
    
    clock = time.clock() # 跟踪FPS帧率
    flag = 1
    
    while(flag):
            clock.tick() # 追踪两个snapshots()之间经过的毫秒数.
            # img = sensor.snapshot() # 拍一张照片并返回图像。
            img = sensor.snapshot().lens_corr(strength = 1.8, zoom = 1.0)  # 畸变校正
    
    #----------------------------------------检查起点终点-----------------------------------------------
    
            #初始化计数常量
            Discern = 0  # Discern = Discern_look_circle + Discern_template_circle
            Discern_look_circle = 0  # 识别圆常数
            Discern_template_circle = 0  # 模板匹配常数
    
    #-------(1)识别圆形部分--------------
    
            for c in img.find_circles(threshold = 3500, x_margin = 10, y_margin = 10, r_margin = 10,r_min = 2, r_max = 100, r_step = 2): # 如果发现圆
                img.draw_circle(c.x(), c.y(), c.r(), color = (0, 255, 0)) # 框出圆
                Discern_look_circle = 1
                # print(c) # 打印出{"x":.., "y":.., "r":.., "magnitude":..} magnitude是检测圆的强度。越高越好
            # print("FPS %f" % clock.fps())
    
    #-------(2)模板匹配部分--------------
    
            for t in templates1:  #如果与模板匹配
                template = image.Image(t) #template获取图片
                r = img.find_template(template, 0.70, step=4, search=SEARCH_EX) #进行相关设置,可以设置roi缩小区域
                if r: #如果有目标
                    img.draw_rectangle(r) #画矩形,框出匹配的目标
                    Discern_template_circle = 1
                    # print("圆形")
    
            Discern = Discern_look_circle + Discern_template_circle
    
            # print("识别圆形部分:%d" % Discern_look_circle)
            # print("模板匹配部分:%d" % Discern_template_circle)
            # print("识别起点终点结果:%d" % Discern)
    #---------------------------------------------------------------------------------------------------
            if Discern == 2:
                time.sleep(10)#必要延时
                if uart.any():  # 串口接收到数据
                    a = uart.readline().decode().strip()
                    if a == 'MVwirt':
                         print("stopGr")
                         uart.write('stopGr\r\n')  #给stm32发数据,停止,开始抓取
                         time.sleep(10) #必要延时
                         flag = 0  # 退出循环
    
            else:
    #---------------------------------判断偏转角度------------------------------------------------------
                centroid_sum = 0
                #利用颜色识别分别寻找三个矩形区域内的线段
                for r in ROIS:
                    blobs = img.find_blobs(GRAYSCALE_THRESHOLD, roi=r[0:4], merge=True) # r[0:4] is roi tuple.
                    #找到视野中的线,merge=true,将找到的图像区域合并成一个
    
                    if blobs:
                        # Find the blob with the most pixels.
                        largest_blob = max(blobs, key=lambda b: b.pixels()) # 返回最大值像素点
    
    
                        # 在色块周围画一个矩形。
                        img.draw_rectangle(largest_blob.rect())
                        # 将此区域的像素数最大的颜色块画矩形和十字形标记出来
                        img.draw_cross(largest_blob.cx(),
                                       largest_blob.cy())
    
                        centroid_sum += largest_blob.cx() * r[4] # r[4] is the roi weight.
                        #计算centroid_sum,centroid_sum等于每个区域的最大颜色块的中心点的x坐标值乘本区域的权值
    
                lcd.display(img) #lcd显示图像
                center_pos = (centroid_sum / weight_sum)
                #中间公式  确定线心
    
                # 将center_pos转换为一个偏角。我们用的是非线性运算,所以越偏离直线,响应越强。
                # 非线性操作很适合用于这样的算法的输出,以引起响应“触发器”。
    
                deflection_angle = 0
                #机器人应该转的角度
    
                # 80是X的一半,60是Y的一半。
                # 下面的等式只是计算三角形的角度,其中三角形的另一边是中心位置与中心的偏差,相邻边是Y的一半。
                # 这样会将角度输出限制在-45至45度左右。(不完全是-45至45度)。
    
                n = 0
                n_qqvga = (center_pos-80)/60  # QQVGA 160x120.
                n_lcd = (center_pos-64)/80  # LCD  128x160
                n_vga = (center_pos-320)/240  # VGA  640x480
    
                if S_PIXEL == sensor.LCD:
                    n = n_lcd
                elif S_PIXEL == sensor.QQVGA:
                    n = n_qqvga
                elif S_PIXEL == sensor.VGA:
                    n = n_vga
    
                deflection_angle = -math.atan(n)
                # 角度计算.80 60 分别为图像宽和高的一半,图像大小为QQVGA 160x120.
                #注意计算得到的是弧度值
    
                deflection_angle = math.degrees(deflection_angle)
                # 将计算结果的弧度值转化为角度值
    
                # 现在你有一个角度来告诉你该如何转动机器人。
                # 通过该角度可以合并最靠近机器人的部分直线和远离机器人的部分直线,以实现更好的预测。
                print("Turn Angle: %f" % deflection_angle)
    
    #-----------------------------串口发数据------------------------------------------------------------
                time.sleep(10)#必要延时
                if uart.any():  # 串口接收到数据
                    a = uart.readline().decode().strip()
                    if a == 'MVwirt':
                        time.sleep(10)
                        #print("Turn Angle: %f" % deflection_angle)
                        #将结果打印在terminal中
    
                        k1 = 4
                        k2 = 28
    
                        if k2 >= deflection_angle > k1:
                            print("left01")
                            uart.write('left01\r\n')  #给stm32发数据,左转
                        elif -k2 <= deflection_angle < -k1:
                            print("right1")
                            uart.write('right1\r\n')  #给stm32发数据,右转
    
                        elif deflection_angle > k2:
                            print("left0S")
                            uart.write('left0S\r\n')  #给stm32发数据,停下来左转
                        elif deflection_angle < -k2:
                            print("rightS")
                            uart.write('rightS\r\n')  #给stm32发数据,停下来右转
    

    资源链接:

    参考链接:链接1(OpenMV4官网)链接2(OpenMV4函数库)链接3(OpenMV4环境下载)

    总项目文件:下载链接

    上一篇文章:循迹识别小车:(三)树莓派部分

    下一篇文章:循迹识别小车:(五)电机驱动和OLED屏

    展开全文
    weixin_48267104 2021-01-22 16:14:39
  • m0_51247005 2021-10-26 08:32:59
  • visual_eagle 2019-10-29 17:11:50
  • 22KB q1175412626 2011-07-28 12:17:57
  • 4星
    36.45MB qq_39492932 2018-12-08 20:26:08
  • 6.02MB qq_40840461 2018-07-03 18:51:12
  • 34.25MB qq_45385612 2021-08-04 14:58:18
  • weixin_30932183 2021-02-12 05:41:34
  • qingchedeyongqi 2021-06-12 21:42:45
  • 4.93MB qq_42703329 2021-07-26 23:26:45
  • qq_36955622 2021-02-22 15:27:51
  • weixin_43852911 2019-10-10 21:23:35
  • weixin_52336696 2021-05-06 16:12:26
  • qq_41462923 2019-08-21 13:14:28
  • handsomeswp 2020-10-16 10:28:27
  • weixin_46291457 2021-10-17 15:21:23
  • xiangkezhi167810 2017-11-23 20:42:15
  • JIE15164031299 2021-08-11 21:42:11
  • qq_41462923 2019-07-27 15:17:47
  • qq_42580947 2020-04-29 00:00:05
  • weixin_36295500 2021-01-12 09:16:44
  • weixin_45984029 2019-12-08 13:24:47

空空如也

空空如也

1 2 3 4
收藏数 74
精华内容 29
关键字:

openmv寻迹