项目介绍
本项目我通过使用硬禾学堂提供的m1s dock完成基于sipeed m1s cock综合应用的网络项目任务,该项目作品需要板卡与电脑端两边都运行程序互相配合,效果为,在电脑端开始拍摄后,会不断接收板卡发来的图像数据,并将其保存在特定目录下,在电脑端点下停止拍摄后,停止接收板卡发来的图像数据,并把已经保存的图片转换为视频
流程图
主要硬件介绍
本次板卡的硬件资源较为丰富,主芯片 BL808 RISC-V 480Mhz + NPU BLAI-100,板载 USB 转 UART 调试器,1.69 寸 240x280 电容触摸屏,200W 像素摄像头,板载 1 个模拟麦克风、1 个 LED、1 个 TF 卡座。在我的项目中,我主要是用到了摄像头这个模块。
主要软件介绍
在软件实现上,在板卡与电脑上都需要运行程序才可。
板卡我们可以从官方的SDK及示例网站上进行下载使用https://gitee.com/sipeed/M1s_BL808_example,下载下来后,需要我们先对环境进行配置。
cd ~/bl808 && mkdir M1s_BL808_SDK/toolchain
mv {your_download_path}/Xuantie-900-gcc-elf-newlib-x86_64-V2.2.4-20220715.tar.gz ~/bl808
tar -zxvf Xuantie-900-gcc-elf-newlib-x86_64-V2.2.4-20220715.tar.gz -C M1s_BL808_SDK/toolchain/
cd M1s_BL808_SDK/toolchain && mv Xuantie-900-gcc-elf-newlib-x86_64-V2.2.4/ Linux_x86_64 && cd -
cd ~/bl808/M1s_BL808_example/c906_app/
# Configure environment variables (Executed only once per working environment)
export BL_SDK_PATH=$(pwd)/../M1s_BL808_SDK
# Compile the hello_world project
./build.sh hello_world
最后的build.sh执行程序,后面加上相应的文件名,就可对相应的示例程序进行编译了。而我的项目里是直接对示例程序camera_streaming_through_wifi进行修改并使用的,该程序是官方在2月份刚更新,如果没有,重新拉库进行了。
void main()
{
vTaskDelay(1);
bl_cam_mipi_mjpeg_init();
m1s_xram_wifi_init();
m1s_xram_wifi_connect("WIFI有毒", "zhan1234");//对这里进行修改
m1s_xram_wifi_upload_stream("192.168.1.112", 8889);//对这里进行修改
}
将main.c中的两处进行修改,分别是wifi名wifi密码,电脑端的ip地址,电脑端程序的端口。这些函数都是现实了什么功能,感兴趣的话可以自己深入研究。
便遗完成后,便可烧录进板卡中,至于烧录方式,https://wiki.sipeed.com/hardware/zh/maix/m1s/other/start.html官方介绍两种,一种是通过电脑上的烧录工具,一种是使用板卡的OTG功能,我使用的是第二种,因为比较快捷方便,按住上下两个键,再按rst复位键即可,电脑上会跳出盘符,直接拖动文件进板卡在电脑上显示的盘符中即可
烧录成功后,连接上uart口子,串口工具选择较小的串口号,复位后,会显示一下信息
Starting bl808 now....
Heap Info: 63458 KB @ [0x0x0000000050207510 ~ 0x0x0000000054000000]
[OS] Starting aos_loop_proc task...
[OS] Start c906 xram handle...
[OS] Starting OS Scheduler...
init ring:0,tx:0x0000000022020140,rx:0x0000000000000000
init ring:2,tx:0x0000000022021340,rx:0x0000000022020340
init ring:3,tx:0x0000000022022540,rx:0x0000000022022340
init ring:4,tx:0x0000000022022840,rx:0x0000000022022740
init ring:5,tx:0x0000000000000000,rx:0x0000000000000000
Init CLI with event Driven
-------------------scan_i2c_bus--------------------
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
slv_addr[0x3c]
try matching the sensor module [BF2013]
try matching the sensor module [C2399]
try matching the sensor module [C2496]
try matching the sensor module [GC0308]
try matching the sensor module [GC0328]
try matching the sensor module [GC1054]
try matching the sensor module [GC2053]
try matching the sensor module [GC2093]
try matching the sensor module [OV2685]
-----------camera id 2685
ID matched
Initialize sensor module [OV2685]
说明,板卡上的程序已经准备完毕了
至于电脑上的程序,则是使用python脚本制作而成,主要功能代码如下
def generate_video():
for root, dirs, files in os.walk(path+'./img/'):
for file in files:
list.append(file) # 获取目录下文件名列表
list.sort(key=lambda x: int(x[:-4])) #文件名按数字排序,不然会卡帧
video = cv.VideoWriter('myvideo01.avi', cv.VideoWriter_fourcc(*'MJPG'),10,(800,600))
print(len(list))
for i in range(1, len(list)):
img = cv.imread(path +'./img/' + list[i - 1])
img = cv.resize(img, (800, 600))
video.write(img)
#釋放资源
print("视频生成成功" + path + '\\myvideo01.avi')#给个弹策提示吧
showinfo('网络相机' , '图像转换视频 完成')
video.release()
def start_transfer():
global flag
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,让程序退出端口号立即释放,否则的话在30秒-2分钟之内这个端口是不会被释放的,这是TCP的为了保证传输可靠性的机制。
tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 给客户端绑定端口号,客户端需要知道服务器的端口号才能进行建立连接。IP地址不用设置,默认就为本机的IP地址。
tcp_server.bind(("", 8889))
# 设置监听
# 128:最大等待建立连接的个数, 提示: 目前是单任务的服务端,同一时刻只能服务与一个客户端,后续使用多任务能够让服务端同时服务与多个客户端
# 不需要让客户端进行等待建立连接
# listen后的这个套接字只负责接收客户端连接请求,不能收发消息,收发消息使用返回的这个新套接字tcp_client来完成
tcp_server.listen(128)
# 等待客户端建立连接的请求, 只有客户端和服务端建立连接成功代码才会解阻塞,代码才能继续往下执行
# 1. 专门和客户端通信的套接字: tcp_client
# 2. 客户端的ip地址和端口号: tcp_client_address
tcp_client, tcp_client_address = tcp_server.accept()
# 代码执行到此说明连接建立成功
print("摄像头连接成功,IP端口号为:", tcp_client_address)
pic_num = 1
while True:
if flag == 1:
# 接收客户端发送的数据, 这次接收数据的最大字节数是4
recv_data = tcp_client.recv(4)
mjpeg_len = int.from_bytes(recv_data, 'little')
print("recv len: ", mjpeg_len)
tcp_client.send(recv_data)
recv_data_mjpeg = b''
remained_bytes = mjpeg_len
while remained_bytes > 0:
recv_data_mjpeg += tcp_client.recv(remained_bytes)
remained_bytes = mjpeg_len - len(recv_data_mjpeg)
print("recv stream success")
if recv_data_mjpeg[:2] != b'\xff\xd8' \
or recv_data_mjpeg[-2:] != b'\xff\xd9':
continue
mjpeg_data = np.frombuffer(recv_data_mjpeg, 'uint8')
img = cv.imdecode(mjpeg_data, cv.IMREAD_COLOR)
#cv.imshow('stream', img)
# 保存图片
cv.imwrite(path + './img/' + str(pic_num) + '.jpg', img)
print("已将" + str(pic_num) + ".jpg图片成功保存!")
pic_num += 1
elif flag == 0:
print("停止获取")
generate_video()
break
一个是接收板卡发来的图片数据并保存在相应目录下,其中接收图片数据的部分是直接使用官方的示例。另一个则是停止接收图片数据,并将已经收集到的图片数据转换成视频,得到的视频文件名myvideo01.avi,保存路径可以自己设置。
其中用到到了opencv,tkinter,threading库,可以自己一一导入使用。
具体的效果,可以看上方视频链接进行观看。
主要的收获和遇到的困难
通过本次的实践练习,我对摄像头和网络传输的了解更加深入,编程能力得到一定程度的提升,尤其是对原理图的使用有了更深刻的理解。在平时的学习生活中曾接触过网络通信的使用,故对本次的活动项目产生了极为浓厚的兴趣。但在编程的过程中,也意识到了自己的不足。首先是在电子方向上仍然存在着很多还未了解的专业知识,其次是代码的编写略显累赘,期望在下次的活动中有所改善。