Competition Achievement
江苏省大学生机器人大赛"越野"项目 第11名
共 23 支队伍参赛 · 南京航空航天大学代表队

项目背景

本项目为参加江苏省大学生机器人大赛而设计,要求参赛队设计并制作一台能够适应不同地形的自主导航机器人小车(AGV),在规定赛道上完成自主巡线与障碍规避任务。

作为项目负责人,我全面负责视觉识别算法开发、硬件选型与系统集成,带队完成全部技术攻关,最终在"越野"项目中荣获第11名(共23支队伍)。

核心技术贡献

技术栈

OpenMV SolidWorks Python 激光雷达 STM32 舵机控制 多传感器融合

激光雷达环境感知

项目集成激光雷达模块,实现对周围环境的实时感知与障碍物检测。以下为激光雷达在两个不同场景下的可视化输出:

核心代码

以下展示项目中两个核心传感器的驱动程序。

OpenMV 视觉识别程序 视觉识别程序.py · OpenMV MicroPython
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import sensor, image, time, math
from pyb import LED

# 初始化摄像头
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time=2000)
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
clock = time.clock()

# 定义颜色阈值 (LAB格式: L, A, B 最小值与最大值)
red_threshold   = (30, 100, 15, 127, 15, 127)
green_threshold = (30, 100, -128, -15, -128, 15)
blue_threshold  = (30, 100, -128, 127, -128, -15)

# 初始化LED用于状态指示
red_led = LED(1)
green_led = LED(2)
blue_led = LED(3)

while(True):
    clock.tick()
    img = sensor.snapshot()

    # 检测二维码
    for code in img.find_qrcodes():
        img.draw_rectangle(code.rect(), color=(255, 0, 0))
        img.draw_string(code.x(), code.y(), code.payload(), color=(0, 255, 0))
        print("QR Code: ", code.payload())
        red_led.on()
        green_led.off()
        blue_led.off()

    # 检测颜色块
    max_area = 0
    color_flag = 0

    for blob in img.find_blobs([red_threshold], pixels_threshold=100, area_threshold=100):
        if blob.area() > max_area:
            max_area = blob.area()
            color_flag = 1
            img.draw_rectangle(blob.rect(), color=(255, 0, 0))
            img.draw_cross(blob.cx(), blob.cy(), color=(0, 255, 0))

    for blob in img.find_blobs([green_threshold], pixels_threshold=100, area_threshold=100):
        if blob.area() > max_area:
            max_area = blob.area()
            color_flag = 2
            img.draw_rectangle(blob.rect(), color=(0, 255, 0))
            img.draw_cross(blob.cx(), blob.cy(), color=(0, 255, 0))

    for blob in img.find_blobs([blue_threshold], pixels_threshold=100, area_threshold=100):
        if blob.area() > max_area:
            max_area = blob.area()
            color_flag = 3
            img.draw_rectangle(blob.rect(), color=(0, 0, 255))
            img.draw_cross(blob.cx(), blob.cy(), color=(0, 255, 0))

    # 根据识别结果控制LED
    if color_flag == 1:
        red_led.on()
        green_led.off()
        blue_led.off()
    elif color_flag == 2:
        red_led.off()
        green_led.on()
        blue_led.off()
    elif color_flag == 3:
        red_led.off()
        green_led.off()
        blue_led.on()
    else:
        red_led.off()
        green_led.off()
        blue_led.off()
激光雷达探测程序 激光雷达程序.py · Python Serial
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/env python
# -*- coding:UTF-8 -*-
from __future__ import print_function
import  serial

if __name__ == '__main__':
        listdata = []
        lastangle = 0
        # LD19波特率:230400     LD14波特率:115200
        ser = serial.Serial('/dev/wheeltec_lidar', 115200)  # ubuntu,如果未修改串口别名,可通过 ll /dev 查看雷达具体端口再进行更改
        # ser = serial.Serial("COM5", 115200, timeout=5)    # window系统,需要先通过设备管理器确认COM口
        while True:
                 data = ser.read(2)                                 # 读取2个字节数据
                 if ord(data[0]) == 0x54 and ord(data[1]) == 0x2C:  # 判断是否为数据帧头
                    data = ser.read(45)                             # 是数据帧头就读取整一帧,去掉帧头之后为45个字节

                    # for x in range(44):                           # 打印原始数据,默认不打印
                    #  print('%#.2x '%ord(data[x]),end="\t")
                    # print("\n")

                    listdata.insert(0, "转速(度/秒):")
                    listdata.insert(1, ord(data[1])*256+ord(data[0]))    # 转速:高字节在后,低字节在前,复合后再转换成十进制

                    listdata.insert(2, "起始角度(度):")
                    listdata.insert(3, (ord(data[3])*256+ord(data[2]))/100.0)  # 原始角度为方便传输放大了100倍,这里要除回去
                    listdata.insert(4, "距离(mm)|光强 *12个点 :")
                    j = 5
                    for x in range(4, 40, 3):                                 # 2个字节的距离数据,1个信号强度数据,步长为3
                        listdata.insert(j, ord(data[x+1])*256+ord(data[x]))  # 距离
                        j += 1
                        listdata.insert(j, ord(data[x+2]))                   # 信号强度
                        j += 1

                    listdata.insert(29, "结束角度(度):")
                    listdata.insert(30, (ord(data[41])*256+ord(data[40]))/100.0)
                    listdata.insert(31, "时间戳(ms):")
                    listdata.insert(32, ord(data[43])*256+ord(data[42]))
                    j = 33

                    if lastangle - listdata[3] > 100:         # 判断上一帧的起始角度与这一帧的角度差是否大于一定角度即为新的一圈
                      print("*******************************")
                    lastangle = listdata[3]                   # 将此帧角度赋值为上一帧,为下一次的判断做准备
                    for k in range(j):
                        print(listdata[k], end="\t")          # 打印解析之后的数据
                    print("\n")

机械结构设计

六足机器人结构设计采用模块化设计理念,包含以下核心组件:

参赛收获