找回密码
 立即注册
查看: 952|回复: 24

国二,2023电赛 【G题】空地协同智能消防系

[复制链接]

该用户从未签到

1

主题

33

回帖

113

积分

注册会员

积分
113
发表于 2023-8-28 16:32:56 | 显示全部楼层 |阅读模式
简介:
小车上和无人机都使用STC32G12K128,其中四轴使用STC32做信息采集和下位机控制板,小车采用STC32G12K128作为主控,最终获得国二
所有硬件信息大概会开源到嘉立创上(本人考研党,也可能在考完研后整理)。
---河南科技大学
先讲故事:
大学很精彩,大一上来到实验室,跟着学长们学习51单片机,大一下32单片机,然后是21年电赛,老师把无人机交到了我们手中,我们满怀热情的扑街了(省三),但是我们不甘心啊,于是开始沉淀。大二下的17界智能车竞赛中,每天高强度备赛,但是由于已经险些无法参赛,最终在一处山村里和河南另一所学校组成赛点(我们那届特殊,疫情原因)勉强完成比赛冲进国赛,但是在外租房成本真的太高了,我们也没有得到任何学校的支持,于是无奈告别线下决赛,遗憾国二。
正巧的是当时STC赞助的比赛,所以我们使用的芯片是STC16,从此和STC32结下不解之缘~
在完成智能车竞赛后,我们得到一段难得的清闲时光,在这段事件里,我开发了一个小游戏机(也用的是STC32,有机会也会开源,图片在最后),并准备22年河南赛的电赛,虽然我们在小车题中获得过国奖,但是我们组毅然选择飞控题,不为别的,主打一个弥补遗憾!
但是事与愿违,疫情又来了。河南省电赛本来说是延迟到3月份过完年返校后举办,然后我们就把飞机留到学校回家了,突然赛委会通知要在一周内完成所有评比工作(这都没东西咋比啊),遗憾止步省二(还是凭借ppt获得的)。
真的太难受了,当时这个飞控的板子就画出来了,不过族中还是在23年电赛用上了。
然后就是最后一次比赛--23年电赛,我们这时候其实已经没有大一大二那么多经历放在电赛上了,因为都要考研。但是飞控题真的给我留下太多遗憾了,所以我们还是打算搏一把,我几乎整个暑假每天都用半天时间在弄飞机(太痛了),这对考研党来说有点致命,但是没办法,真的很想拿国一。
唉,最后我们奋战4天3夜,但是由于测试的时候没识别到火源,可能摄像头算法的问题也可能是K210没读到SD卡,总之发挥不是很理想,回来的时候我们几个心情都非常沉重,国一肯定没戏了,有点遗憾。
结果出来还是国二。
没办法,竞赛总是伴随遗憾,这大概率是我大学生涯最后一个比赛了,虽然没能达到巅峰,但总归要记录一下,一路来也踩过不少坑,写下此篇,方便大家学习入门。
有些细节方面可能会在有时间以后详细展开,现在先大致介绍一下,也算给初学者点思路。

~1I0S}V`6Y5VQZFT{UHCU`B.png                             002DC48E00F340E21C3255AE44BE81D1.jpg
先上图吧
其中小车因为我们参加过17界智能车(也是国二),所以直接用了以前的车模。但是因为我们的车车都传给学弟了,所以搭车的时候也是慌手慌脚,最终没能达到很好的效果。主板方面是抄的逐飞的板子,就不再过多展示,下面着重说明飞机STC32G12K128主控的设计和使用。

主板设计:
2024电赛 【G题】空地协同智能消防系统-1.png

主板原理图并不是非常复杂,只是提供了很多接口方便连接传感器和数据采集,毕竟主要工作是做下位机嘛,所以画的并不是很复杂。


该主控板采用STC宏晶官方最新推出单片机STC32G12K128作为主控芯片,上有两个K210深度学习摄像头嵌槽,用于固定k210以及进行数据通讯,并且外引4GPIO3AD、两路串口、两个可控LED灯、三个可编程按键以及一个IIC接口方便后续扩展模块等资源。其中IIC扩展部分可以直接排线相连接16PWM舵机控制模块,对于有些需要依靠舵机转向或者抓取的任务也可以快速扩展。右侧K210直插的中间有四个孔位,可以与我们后续介绍的12v5v稳压降压模块依靠铜柱直接相连接。
主板资源概述(来自于报告图片):

2024电赛 【G题】空地协同智能消防系统-2.png




上面有两个K210的卡槽,可以插K210模块,因为近些年的无人机题都有识别任务,并且可能牵扯到前方和下方两个方向,所以设计两个卡槽来进行通讯。

P30728-160836.jpg


电源设计:
2024电赛 【G题】空地协同智能消防系统-3.png 2024电赛 【G题】空地协同智能消防系统-4.png

与上述主控搭配使用,可对3C锂电池进行降压稳压处理,输出5V稳定电源,并且连接方式采用铜柱代替导线,无线化设计减少飞行扰动。

2024电赛 【G题】空地协同智能消防系统-5.png

使用螺丝代替导线进行连接,这样可能会有一些问题(大佬轻喷),但是我们目前用了这么久是没遇到问题的,而且非常方便更换。

飞控方面
采用匿名飞控,懂得都懂,非常好用,方便二次开发,与飞控采用串口连接通讯,在本次设计中,重物的抛洒工作(舵机完成)、摄像头的信息反馈与控制、蜂鸣器的控制、LED的控制信号等都由飞控下达指令,然后由STC32进行传感器的控制,为了保持通讯的实时性,使用921600的波特率(之前都一直用的115200,没想到升高后数据依然稳定)。
2024电赛 【G题】空地协同智能消防系统-1.png

还有就是为了方便图像的调试,自制了上位机方便观察图像,配合K210上面镶嵌的ESP32实现组网通讯(K210处理完图像直接通过ESP32发送至电脑)
2024电赛 【G题】空地协同智能消防系统-2.png
通过图中信息可看的,灯点被识别后被涂满,在识别区密度满足要求后,返回数传信息给上位机,飞机通过调试信息完成对点位的矫正工作。
假设目标值为 Setpoint,当前系统输出值为 ProcessVariable,定义以下变量:
偏差: Error = Setpoint – ProcessVariable
积分误差: Integral = Integral + Error * Δt,其中 Δt 为采样时间间隔
导数: Derivative = (Error - PreviousError) / Δt
通过公式
ControlOutput = Kp * Error + Ki * Integral + Kd * Derivative     (1)
计算位置式PID偏差,对位置环进行校准。

消防车”路径规划
在系统中设计每个街区每个边都一一对应一条路径,首先无人机向小车发送火情位置,小车在接受到信息后计算火情所在街区,由于小车无法驶入街区,小车会将火情坐标位置转化成离街区“火情”最近的位置、需要到达“火情”街区的边。
每个路径数据包括包括数个转向操作其中包括方向dir,进行拐弯位置的四元列表[x,y,w,h],其中x、y表示需要转向的区域矩形的左下角坐标,w、h表示需要转向的区域矩形的宽和高。这样利用车载“UWB”就可以实现小车在指定位置转向。

消防车”对无人机轨迹刻画与里程计算
本系统中使用UWB进行室内定位,无人机将UWB信息每秒通过蓝牙发送给“消防车”,“消防车”在接受到数据后将实际坐标转化为屏幕中的像素坐标,之后计算本次接收到的坐标值与上次接收到的坐标值之间的距离,累加进总里程中并显示。

我们的定位采用的是UWB的方案,UWB建系PID校准,然后状态机逐点搜索,大概就是这么个流程。

以下是STC32中关键代码的局部展示(4天3夜写的有点乱~)
  1. /**********************************
  2. **函数名:Isr_timerInit
  3. **作用 :定时器ISR总初始化
  4. ************************************/        
  5. void Isr_timerInit()
  6. {
  7. //        //最多定时20ms左右,多了会炸!!!!!
  8. //        pit_timer_ms(TIM_0, 5);                                                                                                                                        //使用TIMER作为系统时钟,时间5ms一次,最高优先级
  9. //        PT0 = 1;    //定时器0优先级中断低位
  10. //        PT0H =1;        //定时器0优先级中断高位
  11. //        
  12. //        pit_timer_ms(TIM_1, 20);                                                                                                                                //使用TIMER作为系统时钟,时间20ms一次,次高优先级
  13. //        PT1 = 0;    //定时器1优先级中断低位
  14. //        PT1H =1;        //定时器1优先级中断高位
  15. //        
  16.         pit_timer_ms(TIM_4, 1);                                                                                                                //使用TIMER作为系统时钟,时间1ms一次
  17. }
  18. /*1ms线程*/
  19. void task_1ms()
  20. {
  21.                
  22. }
  23. /*5ms线程*/
  24. void task_5ms()
  25. {
  26.         
  27.         LED_control();        
  28. }
  29. /*20ms线程*/
  30. void task_20ms()
  31. {
  32.         Servo_actuator_control();
  33.         Uart1_Sendbuff();        //向两个K210传输数据
  34.         
  35.                         
  36.         
  37.         Uart4_Sendbuff(); //转发k210数据                        
  38.         //
  39.                         
  40. //                        uart_putbuff(UART_1,"561",3);
  41. //                        set_pwm(0,15);
  42. //                        set_pwm(0,10);
  43. //                   delay_ms(1000);               
  44.                                 
  45. }
  46. /*31ms线程*/
  47. void task_31ms()
  48. {
  49.                 Uart3_Sendbuff();        //转发k210数据
  50. }
  51. /*32ms线程*/
  52. void task_32ms()
  53. {
  54.                 Uart2_Sendbuff();        //转发k210数据
  55. }
  56. /*100ms线程*/
  57. void task_100ms()
  58. {
  59. //        UWB_Receive();//uwb接收函数
  60. //        UWB_X =UWB_Y =51231;
  61. //        UWB_NewDate =1;
  62. //        UWB_Send();
  63. }
  64. /*300ms线程*/
  65. void task_300ms()
  66. {
  67.         
  68.         KeyAndLedRecall();
  69. //        BEEP==0?BEEP =1:BEEP=0;
  70. }
  71. /*****************任务管理**************************/
  72. /*****************用户屏蔽**************************/
  73. //创建任务配置
  74. static schedule_task_t schedule_task[] =
  75. {
  76. //task_2ms(函数名) 2 (执行周期)2(初始化计时器) 0(任务执行标志)
  77.         {task_1ms, 1, 1, 0}        ,
  78.         {task_5ms, 5, 5, 0},
  79.         {task_20ms, 20,  20, 0},
  80.         {task_31ms,31 ,31 ,0},
  81.         {task_32ms,32,32,0},
  82.         {task_100ms, 100,  100, 0},
  83.         {task_300ms, 300, 300, 0},
  84. };
  85. //根据数组长度,判断任务数量
  86. #define TASK_NUM (sizeof(schedule_task)/sizeof(schedule_task_t))//记得改定时器值
  87. void task_interrupt(void)
  88. {
  89.         uint8 index = 0;
  90.         // 循环判断
  91.         for (index = 0; index < TASK_NUM; index++)
  92.         {
  93.                 // 判断计时器是否到时间
  94.                 if (schedule_task[index].run_timer)    //不为0
  95.                 {
  96.                         // 计时器减1
  97.                         schedule_task[index].run_timer--;
  98.                         //判断计时器是否到时间
  99.                         if (0 == schedule_task[index].run_timer)
  100.                         {
  101.                                 // 恢复倒计时器的时间
  102.                                 schedule_task[index].run_timer = schedule_task[index].interval_time;  
  103.                                 // 任务标志置1
  104.                                 schedule_task[index].run_sign = 1;
  105.                         }
  106.                 }
  107.         }
  108. }
  109. void task_process(void)
  110. {
  111.         uint8 index = 0;
  112.         
  113.         // 循环判断任务
  114.         for (index = 0; index < TASK_NUM; index++)
  115.         {
  116.                 if (schedule_task[index].run_sign)
  117.                 {
  118.                         // 清除任务标志
  119.                         schedule_task[index].run_sign = 0;
  120.                         // 使用函数指针,执行任务,
  121.                         schedule_task[index].task_func();
  122.                 }
  123.         }
  124.         
  125. }
复制代码
stc32游戏机:
W}_6F{VKLL20L_1S4P$Z]MC.png





回复 送花

使用道具 举报

该用户从未签到

550

主题

9412

回帖

1万

积分

管理员

积分
13966
发表于 2023-8-28 17:28:32 | 显示全部楼层
开心,STC32自由飞翔
截图202309081954282363.jpg
回复 支持 反对 送花

使用道具 举报

该用户从未签到

1

主题

33

回帖

113

积分

注册会员

积分
113
 楼主| 发表于 2023-9-19 11:35:33 | 显示全部楼层
再来两张飞机特写,方便大家参考结构
002DC48E00F340E21C3255AE44BE81D1.jpg
B64B8BFDF8980394D28BC4AB239EF055.jpg
回复 支持 反对 送花

使用道具 举报

该用户从未签到

1

主题

33

回帖

113

积分

注册会员

积分
113
 楼主| 发表于 2023-10-9 14:59:15 | 显示全部楼层
图传如下
截图202310091459073899.jpg
回复 送花

使用道具 举报

该用户从未签到

1

主题

33

回帖

113

积分

注册会员

积分
113
 楼主| 发表于 2023-10-9 15:00:29 | 显示全部楼层
测试K210代码块
  1. def ToBytes(num):
  2.     num_H = (num >> 8) & 0xFF
  3.     num_L = num & 0xFF
  4.     return [num_H, num_L]
  5. def JoinBytes(Bytes):
  6.     low_byte = Bytes[1]
  7.     high_byte = Bytes[0]
  8.     return (high_byte << 8) | low_byte
  9. def GetMinDistance(Radar):
  10.     Radar.ExpressGetData(10)
  11.     DataList = Radar.ReadRadarData()
  12.     if DataList:
  13.         min_radar_data = min(DataList, key=lambda radar_data: radar_data.distance)
  14.         return min_radar_data.ToBytes()
  15.     else:
  16.         return [0xFF,0xFF,0xFF,0xFF]
  17. def SetAngleScope(Scope,Radar):
  18.     MinAngle = JoinBytes(Scope[0:2])
  19.     MaxAngle = JoinBytes(Scope[2:4])
  20.     Maxdistance = JoinBytes(Scope[4:6])
  21.     Radar.SetScope(int(MinAngle),(MaxAngle))
  22. def GetAngleScope(Radar):
  23.     minangle,maxangle,maxdistance = Radar.GetScope()
  24.     return ToBytes(minangle)+ToBytes(maxangle)+ToBytes(maxdistance)
  25. def FindfrequentBarCode(List):
  26.     counter = {}
  27.     for s in List:
  28.         if s not in counter:
  29.             counter[s] = 0
  30.         counter[s] += 1
  31.     Frequentstr = max(counter, key=counter.get)
  32.     return int(Frequentstr)
  33. def IdentifyBarCode(Batch,TimeOut):
  34.     timeout = TimeOut
  35.     DataList = []
  36.     while(len(DataList) < Batch and timeout > 0):
  37.         img = sensor.snapshot()
  38.         barcodes = img.find_barcodes()
  39.         if barcodes:
  40.             for barcode in barcodes:
  41.                 barcodeData = barcode.payload()
  42.                 (x, y, w, h) = barcode.rect()
  43.                 #img.draw_rectangle(barcode.rect())
  44.                 #img.draw_string(x, y, barcodeData, color=(0,255,0), scale=1)
  45.                 #lcd.display(img)
  46.                 DataList = DataList + [barcodeData]
  47.         else:
  48.             timeout -= 1
  49.     if len(DataList) > Batch//2:
  50.         return FindfrequentBarCode(DataList)
  51.     return -1
  52. def BarcodeTask(Uart,Radar):
  53.     Data = [0x01]
  54.     #IdentifyResult = IdentifyBarCode(10,5)
  55.     #if IdentifyResult == -1:
  56.     Data = Data + [0x00]
  57.     Result = GetMinDistance(Radar)
  58.     Data = Data + Result
  59.     Scope = GetAngleScope(Radar)
  60.     Data = Data + Scope
  61.     Uart.SendData(Data)
  62. def CalculateCenter(rect):
  63.     x, y, width, height = rect
  64.     center_x = x + width // 2
  65.     center_y = y + height // 2
  66.     return [center_x,center_y]
  67. def AverageCoordinate(List):
  68.     x_List, y_List = zip(*List)
  69.     x_avg = sum(x_List) / len(x_List)
  70.     y_avg = sum(y_List) / len(y_List)
  71.     return [x_avg,y_avg]
  72. #Batch:识别Batch帧的数据平均成一帧 TimeOut:尝试TimeOut次未达到收集不齐Batch帧率,收集多少算多少
  73. def SearchPark(YoloV2Task, Batch, TimeOut):
  74.     ResultList = []
  75.     timeout = TimeOut
  76.     while(len(ResultList) < Batch and timeout > 0):
  77.         img = sensor.snapshot()
  78.         SearchResults = kpu.run_yolo2(YoloV2Task,img)
  79.         if SearchResults:
  80.             timeout = TimeOut
  81.             for Result in SearchResults:
  82.                 img.draw_rectangle(Result.rect())
  83.                 Coordinate = CalculateCenter(Result.rect())
  84.                 ResultList.append(Coordinate)
  85.         #lcd.display(img)
  86.         timeout -= 1
  87.     if ResultList:
  88.         return ResultList
  89.     else:
  90.         return [0xff,0xff]
  91. def SearchParkTask(MyUart):
  92.     Data = [0x02]#发送时带模式号
  93.     ParkingList = SearchPark(ParkingTask, 1, 10)
  94.     if ParkingList[0] != 0xff:
  95.         ParkImgCoordinate = AverageCoordinate(ParkingList)
  96.     else:
  97.         ParkImgCoordinate = ParkingList
  98.     #ParkCoordinate = [(int)(ParkImgCoordinate[0]-112)//1,(int)(112-ParkImgCoordinate[1])//1] # 计算以最中心点为原点的坐标
  99.     ParkCoordinate = [(int)(ParkImgCoordinate[0]),(int)(ParkImgCoordinate[1])]
  100.     Data = Data + ParkCoordinate
  101.     MyUart.SendData(Data)
  102. # Batch:识别多少次 TimeOut:识别不到重试次数
  103. def JudgeColorBlobs(Batch, TimeOut):
  104.     Color = (37, 95, -28, -6, -39, 29)
  105.     ColorRoi = (54,54,112,112)
  106.     timeout = TimeOut
  107.     SuccessCount = 0
  108.     while(SuccessCount < Batch and timeout > 0):
  109.         Picture = sensor.snapshot()
  110.         ColorBlobs = Picture.find_blobs([Color],roi=ColorRoi, x_stride = 5,y_stride = 5,pixels_threshold=50,merge=True)
  111.         if ColorBlobs:
  112.             SuccessCount += 1
  113.         else:
  114.             timeout -= 1
  115.     if SuccessCount == Batch:
  116.         return 1
  117.     return 0
  118. def JudgeColorTask(MyUart):
  119.     Data = [0x03]#发送时带模式号
  120.     Result = JudgeColorBlobs(5,10)
  121.     Data = Data + [Result]
  122.     MyUart.SendData(Data)
  123. def GetCoordinateTask(MyUart):
  124.     Data = [0x04]#发送时带模式号
  125.     Result = CD.FindXY()
  126.     Data = Data + [(int)(Result[0]),(int)(Result[1])]
  127.     MyUart.SendData(Data)
  128. OldRevDatas = [0x00]
  129. Count = 0
  130. while True:
  131.     RevDatas = UartToStc32.ReadData()# 串口读到的模式标志为是持久性的 stc发送一次改变模式 直到下一次收到数据后
  132.     if RevDatas:
  133.         RevCmd = RevDatas[0]
  134.     if RevCmd != 0x00 and RevCmd != 0x11 and RevCmd != 0x01 and RevCmd != 0x02 and RevCmd != 0x03 and RevCmd != 0x04 and RevCmd != 0x0F and RevCmd != 0xF0:
  135.         UartToStc32.RcvData = OldRevDatas
  136.     if RevCmd == 0x01:
  137.         BarcodeTask(UartToStc32,MyRadar)
  138.     elif RevCmd == 0x11:
  139.         if len(RevDatas)>=7:
  140.             SetAngleScope(RevDatas[1:7],MyRadar)
  141.         UartToStc32.RcvData = [0x01]
  142.     elif RevCmd == 0x02:
  143.         MyRadar.PauseScan()
  144.         SearchParkTask(UartToStc32)
  145.     elif RevCmd == 0x03:
  146.         MyRadar.PauseScan()
  147.         JudgeColorTask(UartToStc32)
  148.     elif RevCmd == 0x04:
  149.         GetCoordinateTask(UartToStc32)
  150.     elif RevCmd == 0x0F:
  151.         MyData.TakePhoto()
  152.         UartToStc32.SendData([0x0F]+ToBytes(MyData.datanum-MyData.nownum+1))
  153.         UartToStc32.RcvData = OldRevDatas
  154.     elif RevCmd == 0x0E:
  155.         MapImg = MyRadar.CreatMapping()
  156.         MyDataOrigin.TakePhoto(img = MapImg)
  157.         DataList = MyRadar.ReadRadarData()
  158.         MyDbscan.DataList.clear()
  159.         MyDbscan.DataList = DataList
  160.         ClusterList, NoiseList = MyDbscan.GetCluster()
  161.         MapImg = MyRadar.CreatDbscanMapping(ClusterList, NoiseList)
  162.         MyData.TakePhoto(img = MapImg)
  163.         MapImg = MyRadar.CreatAveragRadarDataMapping(ClusterList, MyDbscan)
  164.         MyDataAfter.TakePhoto(img = MapImg)
  165.         UartToStc32.SendData([0x0E]+ToBytes(MyData.datanum-MyData.nownum+1))
  166.         UartToStc32.RcvData = OldRevDatas
  167.     elif RevCmd == 0x00:
  168.         Count += 1
  169.         MyRadar.PauseScan()
  170.         if Count >= 50:
  171.             Count = 0
  172.             UartToStc32.SendData([0x00,0x00,0x00])# 停机
  173.     OldRevDatas = RevDatas[:]
  174. gc.collect()
复制代码
回复 支持 反对 送花

使用道具 举报

该用户从未签到

1

主题

33

回帖

113

积分

注册会员

积分
113
 楼主| 发表于 2023-10-9 15:01:00 | 显示全部楼层
激光雷达训练照片
截图202310091500532311.jpg
回复 支持 反对 送花

使用道具 举报

该用户从未签到

1

主题

33

回帖

113

积分

注册会员

积分
113
 楼主| 发表于 2023-10-9 15:01:31 | 显示全部楼层
详细照片1
截图202310091501218891.jpg
回复 送花

使用道具 举报

该用户从未签到

1

主题

33

回帖

113

积分

注册会员

积分
113
 楼主| 发表于 2023-10-9 15:02:18 | 显示全部楼层
详细照片2
截图202310091502168602.jpg
回复 送花

使用道具 举报

该用户从未签到

1

主题

33

回帖

113

积分

注册会员

积分
113
 楼主| 发表于 2023-10-9 15:02:39 | 显示全部楼层
雷达检测处理图像
截图202310091502377620.jpg
回复 支持 反对 送花

使用道具 举报

该用户从未签到

1

主题

33

回帖

113

积分

注册会员

积分
113
 楼主| 发表于 2023-10-9 15:03:02 | 显示全部楼层
详细照片1
截图202310091502549633.jpg
回复 送花

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2024-5-13 12:28 , Processed in 0.077394 second(s), 67 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表