赞
踩
STM32 的代码其实就是简单的UART串口通讯代码,但实质上是有很多坑!!!作者发现大部分博主并没有说明出坑所在,这部分作者将会为读者朋友把可能出现的坑给解决掉!
我们利用 Python 代码写一个循环发送数字的代码程序,具体代码如下:
import serial import time ser = serial.Serial('/dev/ttyAMA0',115200) # 串口初始化,根据实际情况修改串口号和波特率 # 定义要输出的数字 num = 196 while True: ser.write(str(int(num)).encode()) # 发送数字到串口 num += 1 if num > 205: num = 196 time.sleep(0.2) # 等待1秒钟
可以看出代码是非常简单的,但是这里读者朋友需要注意的是树莓派4B的端口发送至STM32的其实都是字符串流!在STM32端我们对发送过来的字符串流进行解码(很多博主其实都没有说明该点,导致很多朋友解码失败)!
1、RCC配置外部高速晶振(精度更高)——HSE;
2、SYS配置:Debug设置成Serial Wire(否则可能导致芯片自锁);
3、I2C配置:
4、USART1配置:设置UART1串口;波特率:115200;开启UART串口中断;
5、时钟树配置
6、工程配置
OLED模块主要是方便显示树莓派4B发送给STM32的数据信息!考虑到实际情况,我们一般需要根据树莓派4B发送过来的数字信息,所以,我们这里利用OLED进行数字显示!
小数显示API函数:
//z_len为整数显示位数,f_len为小数显示位数,size2为字体大小 void OLED_Showdecimal(u8 x,u8 y,float num,u8 z_len,u8 f_len,u8 size2) { u8 t,temp; u8 enshow; int z_temp,f_temp; z_temp=(int)num; //整数部分 for(t=0;t<z_len;t++) { temp=(z_temp/oled_pow(10,z_len-t-1))%10; if(enshow==0 && t<(z_len-1)) { if(temp==0) { OLED_ShowChar(x+(size2/2)*t,y,' ',size2); continue; } else enshow=1; } OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2); } //小数点 OLED_ShowChar(x+(size2/2)*(z_len),y,'.',size2); f_temp=(int)((num-z_temp)*(oled_pow(10,f_len))); //小数部分 for(t=0;t<f_len;t++) { temp=(f_temp/oled_pow(10,f_len-t-1))%10; OLED_ShowChar(x+(size2/2)*(t+z_len)+5,y,temp+'0',size2); } }
篇幅有限,OLED参考博客:http://t.csdn.cn/kydg4
这部分代码是比较核心的,上述博客作者已经说明了,其实树莓派4B发送给STM32的数据都是以字符串流的格式发送来得。所以,即使发送过来的是数字数据也会变成字符,这就需要我们进行解码!
uart.h:
#ifndef __UART_H #define __UART_H #include "stm32f1xx_hal.h" extern UART_HandleTypeDef huart1; #define USART1_REC_LEN 600 extern int USART1_RX_BUF[USART1_REC_LEN]; extern uint16_t USART1_RX_STA; extern int USART1_NewData; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); #endif
uart.c:
#include "uart.h" #include "oled.h" int USART1_RX_BUF[USART1_REC_LEN]; //目标数据 uint16_t USART1_RX_STA=2; int USART1_NewData; extern int num; //百位 extern int num2; //十位 extern int num3; //个位 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart ==&huart1) { USART1_RX_BUF[USART1_RX_STA&0X7FFF]=USART1_NewData; USART1_RX_STA++; if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0; //num = USART1_RX_BUF[USART1_RX_STA]; HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1); num = USART1_RX_BUF[USART1_RX_STA-1]; num2 = USART1_RX_BUF[USART1_RX_STA-2]; num3 = USART1_RX_BUF[USART1_RX_STA-3]; } }
上述UART代码,利用 UART 中断函数进行读取 USART1_NewData 的数值,因为我们最大传输的数字为3位数,所以我们分别读取缓存数组中最近的 3 个字节数据(树莓派4B发送过来的是字符串,也就是被转换成了对应数字的ASCII数值,例如:发送过来100,则STM32端接收到的为49 49 48 这3个字节)。
control.c:
#include "control.h" #include "uart.h" #include "tim.h" #include "oled.h" int num; int num2; int num3; int value; int flag; int last; void TargetTracking() { flag = USART1_RX_STA - last; last = USART1_RX_STA; value = (num3-48) * 100 + (num2-48) * 10 + (num-48) * 1; OLED_ShowStr(10,2,"Object Center",2); OLED_Showdecimal(45,4,value,3,1,16); if(flag == 2) { value = (num2-48) * 10 + (num-48) * 1; OLED_Showdecimal(40,4,value,3,1,16); } }
这里默认都是 3 位数,所以之间按照 UART 数据传输高低位的数据将数据-48变为实际的数字,在×对应的比例即可得到实际值!flag 则是通过 USART1_RX_STA 变量的变化值判断一次传输过来几个字节的数据,可以判断是几位数!
读者朋友可以根据自己实际情况更改上述代码为己所用!
代码运行之后:
智能小车的目标追踪
树莓派4B通常会出现在基于视觉技术进行规定目标的追踪,这个时候往往需要树莓派4B读取到目标框数据,然后发送给STM32下位机,之后STM32根据这些目标框信息来进行逻辑上的控制。
作者这里以上一篇博客的网络模型检测结果为例,代码如下:
import cv2 import numpy as np import onnxruntime as ort import serial import time ser = serial.Serial('/dev/ttyAMA0',115200) def plot_one_box(x, img, color=None, label=None, line_thickness=None): """ description: Plots one bounding box on image img, this function comes from YoLov5 project. param: x: a box likes [x1,y1,x2,y2] img: a opencv image object color: color to draw rectangle, such as (0,255,0) label: str line_thickness: int return: no return """ tl = ( line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 ) # line/font thickness color = color or [random.randint(0, 255) for _ in range(3)] c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3])) ser.write(str(int((int(x[2])-int(x[0]))/2+int(x[0]))).encode()) print(int((int(x[2])-int(x[0]))/2+int(x[0]))) cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA) if label: tf = max(tl - 1, 1) # font thickness t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0] c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3 cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled cv2.putText( img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA, ) def _make_grid( nx, ny): xv, yv = np.meshgrid(np.arange(ny), np.arange(nx)) return np.stack((xv, yv), 2).reshape((-1, 2)).astype(np.float32) def cal_outputs(outs,nl,na,model_w,model_h,anchor_grid,stride): row_ind = 0 grid = [np.zeros(1)] * nl for i in range(nl): h, w = int(model_w/ stride[i]), int(model_h / stride[i]) length = int(na * h * w) if grid[i].shape[2:4] != (h, w): grid[i] = _make_grid(w, h) outs[row_ind:row_ind + length, 0:2] = (outs[row_ind:row_ind + length, 0:2] * 2. - 0.5 + np.tile( grid[i], (na, 1))) * int(stride[i]) outs[row_ind:row_ind + length, 2:4] = (outs[row_ind:row_ind + length, 2:4] * 2) ** 2 * np.repeat( anchor_grid[i], h * w, axis=0) row_ind += length return outs def post_process_opencv(outputs,model_h,model_w,img_h,img_w,thred_nms,thred_cond): conf = outputs[:,4].tolist() c_x = outputs[:,0]/model_w*img_w c_y = outputs[:,1]/model_h*img_h w = outputs[:,2]/model_w*img_w h = outputs[:,3]/model_h*img_h p_cls = outputs[:,5:] if len(p_cls.shape)==1: p_cls = np.expand_dims(p_cls,1) cls_id = np.argmax(p_cls,axis=1) p_x1 = np.expand_dims(c_x-w/2,-1) p_y1 = np.expand_dims(c_y-h/2,-1) p_x2 = np.expand_dims(c_x+w/2,-1) p_y2 = np.expand_dims(c_y+h/2,-1) areas = np.concatenate((p_x1,p_y1,p_x2,p_y2),axis=-1) areas = areas.tolist() ids = cv2.dnn.NMSBoxes(areas,conf,thred_cond,thred_nms) if len(ids)>0: return np.array(areas)[ids],np.array(conf)[ids],cls_id[ids] else: return [],[],[] def infer_img(img0,net,model_h,model_w,nl,na,stride,anchor_grid,thred_nms=0.4,thred_cond=0.5): # 图像预处理 img = cv2.resize(img0, [model_w,model_h], interpolation=cv2.INTER_AREA) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = img.astype(np.float32) / 255.0 blob = np.expand_dims(np.transpose(img, (2, 0, 1)), axis=0) # 模型推理 outs = net.run(None, {net.get_inputs()[0].name: blob})[0].squeeze(axis=0) # 输出坐标矫正 outs = cal_outputs(outs,nl,na,model_w,model_h,anchor_grid,stride) # 检测框计算 img_h,img_w,_ = np.shape(img0) boxes,confs,ids = post_process_opencv(outs,model_h,model_w,img_h,img_w,thred_nms,thred_cond) return boxes,confs,ids if __name__ == "__main__": # 模型加载 model_pb_path = "best.onnx" so = ort.SessionOptions() net = ort.InferenceSession(model_pb_path, so) # 标签字典 dic_labels= {0:'drug', 1:'glue', 2:'prime'} # 模型参数 model_h = 320 model_w = 320 nl = 3 na = 3 stride=[8.,16.,32.] anchors = [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]] anchor_grid = np.asarray(anchors, dtype=np.float32).reshape(nl, -1, 2) video = 0 cap = cv2.VideoCapture(video) flag_det = False while True: success, img0 = cap.read() if success: if flag_det: t1 = time.time() det_boxes,scores,ids = infer_img(img0,net,model_h,model_w,nl,na,stride,anchor_grid,thred_nms=0.4,thred_cond=0.5) t2 = time.time() for box,score,id in zip(det_boxes,scores,ids): label = '%s:%.2f'%(dic_labels[id],score) plot_one_box(box.astype(np.int16), img0, color=(255,0,0), label=label, line_thickness=None) str_FPS = "FPS: %.2f"%(1./(t2-t1)) cv2.putText(img0,str_FPS,(50,50),cv2.FONT_HERSHEY_COMPLEX,1,(0,255,0),3) cv2.imshow("video",img0) key=cv2.waitKey(1) & 0xFF if key == ord('q'): break elif key & 0xFF == ord('s'): flag_det = not flag_det print(flag_det) cap.release()
作者上述代码仅以目标物体中心的 x 轴坐标为例,控制机理是这样的:当 x 数值大于320(图片为640×640)时候控制小车向便宜右偏移,反之则向左偏移。目标框的w,h则可以根据实际情况设置阈值,控制小车的前进速度。当目标框的宽度 w 太小时候,加速小车前进,反之则减速!
作者有话:
目前的 YOLOv-Lite 网络模型的 FPS 还是太低了,在该 FPS 下去进行目标追踪的控制是非常困难的。此外,实际情况下搭载在小车亦或是航行器的 Camera 在拍摄过程中避免不了强烈的高斯白噪,需要加卡尔曼滤波进行优化目标框信息!
所以,后续作者将推出可以基于视觉追踪实战的网络模型和目标追踪代码,感兴趣的读者朋友可以期待一下!
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数嵌入式工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)
资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!
-feSQJTB3-1712386408878)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!
[外链图片转存中…(img-YcM1gvgh-1712386408879)]
[外链图片转存中…(img-jyr9ecfv-1712386408879)]
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)
资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。