本篇测评由电子发烧友的优秀测评者“alset”提供。
什么是ncnn
set(cmake_system_name linux)
set(cmake_system_processor arm)
set(cmake_c_compiler "arm-poky-linux-gnueabi-gcc")
set(cmake_cxx_compiler "arm-poky-linux-gnueabi-g ")
set(cmake_find_root_path_mode_program never)
set(cmake_find_root_path_mode_library only)
set(cmake_find_root_path_mode_include only)
set(cmake_c_flags "-march=armv7-a -mfloat-abi=hard -mfpu=neon --sysroot=/home/lutherluo/workspace/fsl-imx-fb/5.10-gatesgarth/sysroots/cortexa7t2hf-neon-poky-linux-gnueabi")
set(cmake_cxx_flags "-march=armv7-a -mfloat-abi=hard -mfpu=neon --sysroot=/home/lutherluo/workspace/fsl-imx-fb/5.10-gatesgarth/sysroots/cortexa7t2hf-neon-poky-linux-gnueabi")
# cache flags
set(cmake_c_flags "${cmake_c_flags}" cache string "c flags")
set(cmake_cxx_flags "${cmake_cxx_flags}" cache string "c flags")
板上运行测试ncnn
测试基于ncnn的应用
ncnn移植测试总结
由测评者honestqiao提供
这篇分享,内容较长,且涉及到的知识点较多,需要耐心阅读,下面为具体内容的目录:
前言:
如果有心查看米尔myc-j1028x核心板及开发板的官方界面:
今天,我们就在myc-j1028x开发板上,搭建node-red环境,将开发板变身为工业物联网控制网关。
很多人了解或者知道node-red,是从智能家居或者小型物联网控制开始的,有的人认为只是一个类似的图形界面编程的工具。
其实,在工业互联网中,node-red可以用于数据收集、关联性触发控制、仪表呈现等,在低成本投入的情况下,也能获得较好的效果。
一、安装node-red
安装node-red,可以参考官方的文档:
虽然这篇文档,是针对树莓派的,但是对于debian系的系统,都是可以参考的。
-
bash <(curl -sl https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
执行上述命令后,将自动下载安装脚本,启动安装进程,安装过程中的问题,一律选y即可。
安装过程可能耗时比较长,先沏一杯茶或者咖啡,等着吧:
首次安装,可能会遇到莫名其妙的node-red core没装上,核心都没装上,怎么玩?
不管,再次执行安装命令,就能好了:
如果提示那个shortcut有问题,先不管,不影响使用。
因为是首次安装,所以执行 node-red admin init 进行初始化:
初始化完成后,就可以执行 node-red start 启动node-red了。
正常启动后,就可以通过网址 http://开发板ip:1880/ 进行访问了:
输入初始化的时候设置的账户密码,登录后,按照下面的设置,即可切换到中文界面:
设置完成后,需要刷新页面,才能正式生效。
二、系统命令控制led
现在,我们就可以开始在node-red中,开始进行外设控制了。
第一个控制逻辑,还是点灯,能点灯,我们就成功一大半了。
通过查看官方的手册,可以了解到:
我们可以先使用上面的指令,测试能否控制系统的d22-led。
-
echo 0 | sudo tee /sys/class/leds/d22/brightness
-
echo 1 | sudo tee /sys/class/leds/d22/brightness
三、led权限设置:
通过查看node-red的文档资料,可以了解到,有一个exec node,可以用于执行系统的命令,那我们正好可以使用其来执行上面手册中,控制d22的指令。
要使得普通用户运行的node-red能够控制系统led,需要在udev中,添加对应的权限处理规则,否则操作时将会提示没有权限。
-
sudo groupadd -f -r leds
-
sudo usermod -a -g leds $user
-
sudo vim /etc/udev/rules.d/99-leds.rules
-
subsystem=="leds", action=="add", run ="/bin/chgrp -r leds /sys%p", run ="/bin/chmod -r g=u /sys%p"
-
subsystem=="leds", action=="change", env{trigger}!="none", run ="/bin/chgrp -r leds /sys%p", run ="/bin/chmod -r g=u /sys%p"
将上面的最后两行设置,添加到 /etc/udev/rules.d/99-leds.rules 后,需要重启系统才能生效。
友情提醒:类似在udev中进行普通用户权限的设置,后续还有很多次,一定要仔细设置。
重启后(sudo reboot),可以测试使用普通用户权限,来操作led:
-
echo 0 | tee /sys/class/leds/d22/brightness
-
echo 1 | tee /sys/class/leds/d22/brightness
四、node-red控制系统led
先在node-red界面中,参考下图,添加两个inject node,再添加两个exec node。
exec node的命令部分,参考下图填写,记住一个为echo 0,一个为echo 1,对应熄灭和点亮led
两个injection node的设置,分别如下:
上面的逻辑表示:
五、系统命令控制gpio:
在官方手册上,有说明如何操作gpio:
结合开发板和原理图上我们可以了解到,j18是我们可以方便使用的引脚区域:
j18上可供使用的gpio对应关系如下:
类似系统led,如果要在普通用户下控制gpio,也需要在udev中进行一些设置,具体设置如下:
-
sudo groupadd -f -r gpio
-
sudo usermod -a -g gpio $user
-
sudo vim /etc/udev/rules.d/99-gpio.rules
-
subsystem=="gpio", kernel=="gpiochip*", action=="add", run ="/bin/chgrp -r gpio /sys/class/gpio/export /sys/class/gpio/unexport", run ="/bin/chmod -r g=u /sys/class/gpio/export /sys/class/gpio/unexport"
-
subsystem=="gpio*", kernel=="gpio*", action=="add", run ="/bin/chgrp -r gpio /sys%p", run ="/bin/chmod -r g=u /sys%p"
-
subsystem=="gpio*", kernel=="gpio*", action=="change", env{trigger}!="none", run ="/bin/chgrp -r gpio /sys%p", run ="/bin/chmod -r g=u /sys%p"
将上述最后三行内容写入/etc/udev/rules.d/99-gpio.rules,然后重启开发板即可生效。
重启完成后,我们就可以现在命令行下进行测试。
首先按照原理图,将可以用3.3v驱动的led,连接到gpio3_9引脚上。
然后使用下面的命令进行测试:
-
echo 425 > /sys/class/gpio/export
-
echo out > /sys/class/gpio/gpio425/direction
-
for i in {1..100};do
-
echo $((i%2)) > /sys/class/gpio/gpio425/value
-
sleep 1
-
done
执行完命令后,连接到gpio3_9引脚上的led,将每秒闪烁一次。
测试控制成功后,我们就可以到node-red中进行控制了。
六、node-red控制gpio
要在node-red中控制gpio,可以安装opi-gpio插件,按照以下步骤安装即可:
安装完成后,在右边组件选择栏,就会出现orange pi,选择其中的output pin,然后将之前设置的injection 0和1连接到新添加的控件上,再按照下面的步骤,设置对应的gpio端口号。
因为gpio3_9对应425,所以pin填写425即可。但orange pi在填写后,再次打开的时候,界面会变成初始状态,所以务必仔细填写。
填写完成,点击右上角的部署,连接到gpio3_9上的led,就会开始闪烁了,和板子上的d22-led同步。
七、开发板上的s3按键使用:
在开发板上,有一个可供用户编程使用的按键s3:
s3按键,使用系统/dev/input/event0来监听接收,普通用户使用,需要做如下设置:
-
sudo usermod -a -g input $user
-
sudo vim /etc/udev/rules.d/99-event.rules
-
kernel=="event[0-9]*", subsystem=="input", tag ="uaccess"
设置好以后,重启才能生效。
重启后,使用evtest指令测试:
s3按键后,就能够收到按键的消息了。
为了能够更好的接收到按键信息,我们需要使用python去进行检测,这需要使用到python3-evdev库,先安装:
-
pip3 install evdev
然后,使用下面的python脚本进行测试:
-
from evdev import inputdevice
-
from select import select
-
dev = inputdevice('/dev/input/event0')
-
while true:
-
r,w,x=select([dev],[],[])
-
for event in dev.read():
-
print(event)
按下s3按键然后释放,就会收到信息了:
需要注意的是,s3按键使用系统event接收,按下去和释放,都会触发一次。
按下去对应:
释放对应:
我们可以将程序完善一下,直接按键输出1,释放输出0:
-
from evdev import inputdevice
-
from select import select
-
dev = inputdevice('/dev/input/event0')
-
while true:
-
r,w,x=select([dev],[],[])
-
for event in dev.read():
-
if event.code == 2 and event.type == 1:
-
print("value: %d" % event.value)
按下s3按键然后释放,就会收到下面的信息了:
这样,我们就准备好将按键值提供给node-red使用了。
八、在node-red中使用s3按键控制led:
要在node-red中,获取到python提供的按键值,然后控制led,需要使用下面的逻辑:
在node-red中的控制逻辑如下:
下面进行详细的说明。
首先,我们再次调整上面的监控脚本,使得监控按键的逻辑,符合实际需要,即:
具体代码如下:
-
from evdev import inputdevice
-
from select import select
-
dev = inputdevice('/dev/input/event0')
-
keypress_times = 0
-
print(keypress_times % 2)
-
while true:
-
r,w,x=select([dev],[],[])
-
for event in dev.read():
-
if event.value == 1:
-
keypress_times = keypress_times 1
-
print(keypress_times % 2)
将上述代码保存到 /home/honestqiao/projects/event/evdev_key.py (具体路径,请根据你的实际情况设定),然后运行测试:
要在node-red中,启动运行上面的监听脚本,需要使用到node-red-node-daemon插件,安装即可:
因为evdev_key.py输出的内容,为【0或者1 并附带回车】,所以node-red收到后,需要进行一次转换,才能使用。
按照下面的步骤,添加转换控件,并设置好转换逻辑即可:
然后,添加gpio3_10对应426引脚控制,并与转换为数字控件进行关联:
然后,在添加一个debug控件,以便能够查看到中间过程的数据信息:
点击部署后,在右边,就能看到调试输出信息了。
尝试一下按键,就能够看到脚本输出的字符串信息,已经转换为数字好的信息,并且gpio3_10连接的黄色led,也能够被正常控制了。
九、node-red使用i2c接口读取sht30温湿度传感器数据:
前面的部分,都是led控制、gpio控制、按键读取,相对比较简单。
下面,再来一个感觉稍微提高一点点的实例,就是使用i2c接口读取sht30温湿度传感器。
在之前看过的电路原理图中,j18接口上,就有i2c接口。
将i2c接口的sht30,连接到j18对应的接口上:
要在普通用户权限下,使用i2c接口,我们又需要在udev中做一些设置:
-
sudo vim /etc/udev/rules.d/99-i2c.rules
-
kernel=="i2c-[0-9]*", group="i2c"
将这行内容,写入到/etc/udev/rules.d/99-i2c.rules,然后重启生效。
重启完成后,在命令下下,测试是否能够找到sht30:
先使用 i2cdetect -l 命令查看一下可供使用的i2c接口:
从上面可以看到,i2c-0、i2c-1可供使用。
然后,在使用 i2cdetect -y 0 和 i2cdetect -y 1 查看i2c挂载的设备:
在i2c-1中,有一个44的设备,这个正是 sht30的默认i2c通讯地址。
可以尝试,把sht30连线断开,再使用 i2cdetect -y 1 查看,有什么不同。
在node-red中,要读取sht3x的数据,可以使用 node-red-contrib-sht 插件:
逻辑设置完成,点击右上角的部署后,调试区域,就会打印出来,实际读取到的温湿度信息了。
对着温湿度传感器哈哈气,就会发现读取到的数值,发生了变化。
十:node-red使用仪表盘显示温湿度传感器数据:
node-red还有一个迷人又强大的地方,那就是它不仅能够方便你用图形界面设计物联网设备的控制交互流程,还能够很方便的使用仪表盘,提供数据查看界面。
下面,我就在上一步读取到温湿度传感器的基础上,再做一个,可以在手机界面上查看访问的仪表盘。
首先,安装一个dashboard仪表盘控件:
然后,在sht3x空间上,挂两个change,用于转换温湿度值:
而温湿度值,各挂一个gauge,用于显示最终的数据,其内容,参考如下设置即可:
温度gauge:
在第一次设置gauge的时候,如上面的温度gauge,要点 (4) 进入,设置group节点和tab节点,具体如下:
经过以上设置,就能够将温湿度值,输出到仪表盘上了。
为了效果更好,我再添加一个当前日期时间转换,并使用文本控件显示。
先按照下面的步骤,添加一个function控件,别编写转换代码:
-
function formatdate(value) {
-
var date = new date(value);
-
var y = date.getfullyear(),
-
m = date.getmonth() 1,
-
d = date.getdate(),
-
h = date.gethours(),
-
i = date.getminutes(),
-
s = date.getseconds();
-
if (m < 10) { m = '0' m; }
-
if (d < 10) { d = '0' d; }
-
if (h < 10) { h = '0' h; }
-
if (i < 10) { i = '0' i; }
-
if (s < 10) { s = '0' s; }
-
var t = y '-' m '-' d ' ' h ':' i ':' s;
-
return t;
-
}
-
return { payload: formatdate(msg.payload) };
其中的 formatdate() 函数,就是一个js中标准的把当前时间戳转换为yyyy-mm-dd hh:mm:ss格式的函数。
这也是node-red的一个强大之处,除了编写好的控件,还可以使用脚本进行中间过程的处理。
设置好了日期时间转换,再在其后挂一个text控件,用作显示即可:
全部设计完成后,点击右上角的部署,就可以使用手机进行查看了。
在手机上,访问网址 http://开发板ip:1880/ui ,就能看到如下的界面了:
十一、总结:
这篇分享,基于米尔myc-j1028x开发板,结合node-red,将其变身为工业控制网关。
看到这里,看似分享了不少内容,但涉及到node-red的部分,只有node-red全部功能的万分之一都不到,而且都是非常基础的使用。
对于米尔myc-j1028x开发板的使用,也是用到了实际功能的百分之一都不到。
node-red还有很多很多很多强大的功能,可以方便我们快速的获取外部设备的数据,并编写合适的逻辑处理流程来规整数据,并进行数据的呈现,或者对外部设备进行控制。
而这一切,并不需要特别专业的编程技能,就能够完成。
基于米尔myc-j1028x开发板强劲的运算能力,以及丰富的外设接口,结合node-red所构建的工业控制网关,完全可以应用在实际场合中,发挥重大的作用。
将其部署在工业现场,用于收集、存储、处理和分析网络边缘的数据,能够减轻对云和数据中心的压力。
这套系统,可以通过灵活的i/o简化通信和控制,对数据进行主动采集、解析及过滤、汇聚,来可视化现场数据和控制逻辑,既能方便预测性维护,又能开展实时数据处理与决策。
此次的板卡测试,是米尔myd-yt507h开发板的行车记录仪测试体验。
为了又快又好的开发行车记录仪的实际界面,以及后续进行各移动平台的app开发,选择了flutter。事实证明,坑太多了。不过,跨平台特性,确实好。
# http服务器请求处理:网页、mjpeg数据流
class camhandler(basehttprequesthandler):
def do_get(self):
# mjpeg推流
if self.path.endswith('.mjpg'):
self.send_response(200)
self.send_header('content-type','multipart/x-mixed-replace; boundary=--jpgboundary')
self.end_headers()
while true:
if is_stop:
break
try:
# rc,img = cameracapture.read()
rc,img = success,frame
if not rc:
continue
if true:
imgrgb=cv2.cvtcolor(img,cv2.color_bgr2rgb)
jpg = image.fromarray(imgrgb)
tmpfile = bytesio()
jpg.save(tmpfile,'jpeg')
self.wfile.write(b"--jpgboundary")
self.send_header(b'content-type','image/jpeg')
self.send_header(b'content-length',str(tmpfile.getbuffer().nbytes))
self.end_headers()
jpg.save(self.wfile,'jpeg')
else:
img_fps = jpeg_quality_value
img_param = [int(cv2.imwrite_jpeg_quality), img_fps]
img_str = cv2.imencode('.jpg', img, img_param)[1].tobytes() # change image to jpeg format
self.send_header('content-type','image/jpeg')
self.end_headers()
self.wfile.write(img_str)
self.wfile.write(b"
--jpgboundary
") # end of this part
time.sleep(0.033)
except keyboardinterrupt:
self.wfile.write(b"
--jpgboundary--
")
break
except brokenpipeerror:
continue
return
# 网页
if self.path == '/' or self.path.endswith('.html'):
self.send_response(200)
self.send_header('content-type','text/html')
self.end_headers()
self.wfile.write(b'
live video ')self.wfile.write(('' % self.headers.get('host')).encode())
self.wfile.write(b'')
return
# websocket服务请求处理
async def camtransmithandler(websocket, path):
print("client connected !")
try :
while true:
# rc,img = cameracapture.read()
rc,img = success,frame
if not rc:
continue
img_fps = jpeg_quality_value
img_param = [int(cv2.imwrite_jpeg_quality), img_fps]
encoded = cv2.imencode('.jpg', img, img_param)[1]
data = str(base64.b64encode(encoded))
data = data[2:len(data)-1]
await websocket.send(data)
# cv2.imshow("transimission", frame)
# if cv2.waitkey(1) & 0xff == ord('q'):
# break
# cap.release()
except exception_connection_close as e:
print("client disconnected !")
# cap.release()
except:
print("someting went wrong !")
cameracapture = cv2.videocapture(camera_no)
cameracapture.set(cv2.cap_prop_frame_width, 320)
cameracapture.set(cv2.cap_prop_frame_width, 240)
cameracapture.set(cv2.cap_prop_saturation, 135)
fps = 30
size=(int(cameracapture.get(cv2.cap_prop_frame_width)),int(cameracapture.get(cv2.cap_prop_frame_height)))
success,frame = cameracapture.read()
...
while true:
if is_stop:
success = false
break;
success,frame = cameracapture.read()
if not success:
continue
time_now = get_current_time()
if time_now["time"] - time_record["time"] >= rotate_time:
if time_record_prev:
thubm_file = get_file_name(time_record_prev, 'thumbs', 'jpg')
print("[info] write to thumb: %s" % thubm_file)
if not os.path.isfile(thubm_file):
cv2.imwrite(thubm_file, frame)
time_record = time_now
time_record_prev = get_current_time()
video_file = get_file_name(time_record_prev, 'videos', media_ext)
print("[info] write to video: %s" % video_file)
# encode = cv2.videowriter_fourcc(*"mp4v")
encode = cv2.videowriter_fourcc(*'x264')
# encode = cv2.videowriter_fourcc(*'avc1')
# encode = cv2.videowriter_fourcc(*'xvid')
# encode = cv2.videowriter_fourcc(*'h264')
videowriter=cv2.videowriter(video_file, encode,fps,size) # mp4
numframeremaining = rotate_time * fps #摄像头捕获持续时间
while success and numframeremaining > 0:
videowriter.write(frame)
success,frame = cameracapture.read()
numframeremaining -= 1
cameracapture.release()
encode = cv2.videowriter_fourcc(*'x264')
,在不同的环境下面,提供的编码方式不完全相同。# -*- coding: utf-8 -*-
import signal
import cv2
import time
from pil import image
from threading import thread
from http.server import basehttprequesthandler,httpserver
from socketserver import threadingmixin
from io import bytesio
import os
import sys
import websockets
import asyncio
import base64
import ctypes
import inspect
camera_no = 2
rotate_time = 120
mjpeg_enable = 1
websocket_enable = 1
mjpeg_server_port = 28888
websocket_port = 28889
jpeg_quality_value = 65
store_dir = "./data/" if os.uname()[0] == 'darwin' else "/sdcard/data/"
media_ext = "mkv"
exception_connection_close = websockets.exceptions.connectionclosed if sys.version[:3] == '3.6' else websockets.connectionclosed
def _async_raise(tid, exctype):
"""raises the exception, performs cleanup if needed"""
try:
tid = ctypes.c_long(tid)
if not inspect.isclass(exctype):
exctype = type(exctype)
res = ctypes.pythonapi.pythreadstate_setasyncexc(tid, ctypes.py_object(exctype))
if res == 0:
# pass
raise valueerror("invalid thread id")
elif res != 1:
# """if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=null to revert the effect"""
ctypes.pythonapi.pythreadstate_setasyncexc(tid, none)
raise systemerror("pythreadstate_setasyncexc failed")
except exception as err:
print(err)
def stop_thread(thread):
"""终止线程"""
_async_raise(thread.ident, systemexit)
# 信号处理回调
def signal_handler(signum, frame):
# global cameracapture
# global thread
# global server
# global is_stop
# global success
print('signal_handler: caught signal ' str(signum))
if signum == signal.sigint.value:
print('stop server:')
is_stop = true
success = false
print("mjpeg server.socket.close...")
server.socket.close()
print("mjpeg server.shutdown...")
server.shutdown()
print("ws server.socket.close...")
server_ws.ws_server.close()
time.sleep(1)
# print("ws server.shutdown...")
# await server_ws.ws_server.wait_closed()
print("mjpeg thread.shutdown...")
thread_mjpeg.join()
print("ws loop.shutdown...")
# event_loop_ws.stop()
event_loop_ws.call_soon_threadsafe(event_loop_ws.stop)
time.sleep(1)
# print("ws thread.shutdown...")
# stop_thread(thread_ws)
# time.sleep(1)
# print(server)
# print(server_ws)
print(thread_mjpeg.is_alive())
print(thread_ws.is_alive())
print(event_loop_ws.is_running())
# thread_ws.join()
print("cameracapture.release...")
cameracapture.release()
print("quit...")
# print(server_ws)
sys.exit(0)
# http服务器请求处理:网页、mjpeg数据流
class camhandler(basehttprequesthandler):
def do_get(self):
# mjpeg推流
if self.path.endswith('.mjpg'):
self.send_response(200)
self.send_header('content-type','multipart/x-mixed-replace; boundary=--jpgboundary')
self.end_headers()
while true:
if is_stop:
break
try:
# rc,img = cameracapture.read()
rc,img = success,frame
if not rc:
continue
if true:
imgrgb=cv2.cvtcolor(img,cv2.color_bgr2rgb)
jpg = image.fromarray(imgrgb)
tmpfile = bytesio()
jpg.save(tmpfile,'jpeg')
self.wfile.write(b"--jpgboundary")
self.send_header(b'content-type','image/jpeg')
self.send_header(b'content-length',str(tmpfile.getbuffer().nbytes))
self.end_headers()
jpg.save(self.wfile,'jpeg')
else:
img_fps = jpeg_quality_value
img_param = [int(cv2.imwrite_jpeg_quality), img_fps]
img_str = cv2.imencode('.jpg', img, img_param)[1].tobytes() # change image to jpeg format
self.send_header('content-type','image/jpeg')
self.end_headers()
self.wfile.write(img_str)
self.wfile.write(b"
--jpgboundary
") # end of this part
time.sleep(0.033)
except keyboardinterrupt:
self.wfile.write(b"
--jpgboundary--
")
break
except brokenpipeerror:
continue
return
# 网页
if self.path == '/' or self.path.endswith('.html'):
self.send_response(200)
self.send_header('content-type','text/html')
self.end_headers()
self.wfile.write(b'
live video ')self.wfile.write(('' % self.headers.get('host')).encode())
self.wfile.write(b'')
return
class threadedhttpserver(threadingmixin, httpserver):
"""handle requests in a separate thread."""
# 启动mjpeg服务
def mjpeg_server_star():
global success
global server
global thread_mjpeg
try:
server = threadedhttpserver(('0.0.0.0', mjpeg_server_port), camhandler)
print("mjpeg server started: http://0.0.0.0:%d" % mjpeg_server_port)
# server.serve_forever()
thread_mjpeg = thread(target=server.serve_forever);
thread_mjpeg.start()
except keyboardinterrupt:
print("mjpeg server stoping...")
server.socket.close()
server.shutdown()
print("mjpeg server stoped")
# websocket服务请求处理
async def camtransmithandler(websocket, path):
print("client connected !")
try :
while true:
# rc,img = cameracapture.read()
rc,img = success,frame
if not rc:
continue
img_fps = jpeg_quality_value
img_param = [int(cv2.imwrite_jpeg_quality), img_fps]
encoded = cv2.imencode('.jpg', img, img_param)[1]
data = str(base64.b64encode(encoded))
data = data[2:len(data)-1]
await websocket.send(data)
# cv2.imshow("transimission", frame)
# if cv2.waitkey(1) & 0xff == ord('q'):
# break
# cap.release()
except exception_connection_close as e:
print("client disconnected !")
# cap.release()
except:
print("someting went wrong !")
# websocket服务器启动
def websocket_server_start():
global thread_ws
global server_ws
global event_loop_ws
event_loop_ws = asyncio.new_event_loop()
def run_server():
global server_ws
print("websocket server started: ws://0.0.0.0:%d" % websocket_port)
server_ws = websockets.serve(camtransmithandler, port=websocket_port, loop=event_loop_ws)
event_loop_ws.run_until_complete(server_ws)
event_loop_ws.run_forever()
thread_ws = thread(target=run_server)
thread_ws.start()
# try:
# yield
# except e:
# print("an exception occurred")
# finally:
# event_loop.call_soon_threadsafe(event_loop.stop)
# 获取存储的文件名
def get_file_name(time_obj, path, ext):
file_name_time = "d-d-d_d-d-d" % (time_obj["year"], time_obj["month"], time_obj["day"], time_obj["hour"], time_obj["min"], 0)
return '%s/%s/%s.%s' % (store_dir, path, file_name_time, ext)
# 获取当前整分时间
def get_current_time():
time_now = time.localtime()
time_int = int(time.time())
return {
"year": time_now.tm_year,
"month": time_now.tm_mon,
"day": time_now.tm_mday,
"hour": time_now.tm_hour,
"min": time_now.tm_min,
"sec": time_now.tm_sec,
"time": time_int - time_now.tm_sec
}
# 设置信号回调
signal.signal(signal.sigint, signal_handler)
signal.signal(signal.sigterm, signal_handler)
# 捕获摄像头
cameracapture = cv2.videocapture(camera_no)
# 摄像头参数设置
cameracapture.set(cv2.cap_prop_frame_width, 320)
cameracapture.set(cv2.cap_prop_frame_width, 240)
cameracapture.set(cv2.cap_prop_saturation, 135)
fps = 30
size=(int(cameracapture.get(cv2.cap_prop_frame_width)),int(cameracapture.get(cv2.cap_prop_frame_height)))
# 读取捕获的数据
success,frame = cameracapture.read()
if not success:
print("camera start failed.")
quit()
is_stop = false
server = none
server_ws = none
event_loop_ws = none
thread_mjpeg = none
thread_ws = none
mjpeg_server_star()
websocket_server_start()
print("record server star:")
thubm_file = none
video_file = none
time_start = int(time.time())
time_record = {"time":0}
time_record_prev = none
while true:
if is_stop:
success = false
break;
success,frame = cameracapture.read()
if not success:
continue
time_now = get_current_time()
if time_now["time"] - time_record["time"] >= rotate_time:
if time_record_prev:
thubm_file = get_file_name(time_record_prev, 'thumbs', 'jpg')
print("[info] write to thumb: %s" % thubm_file)
if not os.path.isfile(thubm_file):
cv2.imwrite(thubm_file, frame)
time_record = time_now
time_record_prev = get_current_time()
video_file = get_file_name(time_record_prev, 'videos', media_ext)
print("[info] write to video: %s" % video_file)
# encode = cv2.videowriter_fourcc(*"mp4v")
encode = cv2.videowriter_fourcc(*'x264')
# encode = cv2.videowriter_fourcc(*'avc1')
# encode = cv2.videowriter_fourcc(*'xvid')
# encode = cv2.videowriter_fourcc(*'h264')
videowriter=cv2.videowriter(video_file, encode,fps,size) # mp4
numframeremaining = rotate_time * fps #摄像头捕获持续时间
while success and numframeremaining > 0:
videowriter.write(frame)
success,frame = cameracapture.read()
numframeremaining -= 1
cameracapture.release()
static_url = 'static/'
staticfiles_dirs = [
base_dir / "static"
]
# 媒体文件存放目录,以及缩略图和视频文件的后缀
thumb_home_dir = "%s/%s/data/thumbs/" % (base_dir, static_url)
video_home_dir = "%s/%s/data/videos/" % (base_dir, static_url)
img_filter = [".jpg"]
media_filter = [ ".mkv"]
import json
from django.shortcuts import render, httpresponse
from rest_framework.response import response
from rest_framework.permissions import allowany
from rest_framework.decorators import api_view, permission_classes
import os
from django.conf import settings
thumb_home_dir = settings.thumb_home_dir
video_home_dir = settings.video_home_dir
img_filter = settings.img_filter
media_filter = settings.media_filter
# create your views here.
def hello_django(request):
str = '''[
{
"id": 1,
"time": "2022-07-28 21:00",
"title": "2022-07-28 21:00",
"body": "videos/2022-07-28_2100.mp4"
},
{
"id": 2,
"time": "2022-07-28 23:00",
"title": "2022-07-28 23:00",
"body": "videos/2022-07-28_2300.mp4"
},
{
"id": 3,
"time": "2022-07-28 25:00",
"title": "2022-07-28 25:00",
"body": "videos/2022-07-28_2500.mp4"
}
]'''
_json = json.loads(str)
return httpresponse(json.dumps(_json), content_type='application/json')
def history_list(request):
next = request.get.get("next", '')
print(f"thumb next = {next}")
path = "/".join(request.path.split("/")[3:])
print(f"thumb request.path= {request.path}")
print(f"thumb path = {path}")
#print os.listdir(file_home_dir ".none/")
data = {"files":[], "dirs":[]}
print(data)
child_path = thumb_home_dir next
print(f"child_path = {child_path}")
data['cur_dir'] = path next
print(data)
for dir in os.listdir(child_path):
if os.path.isfile(child_path "/" dir):
if os.path.splitext(dir)[1] in img_filter:
data['files'].append(dir)
else:
data['dirs'].append(dir)
print(data)
data['files']=sorted(data['files'])
data['files'].reverse()
data['infos'] = []
for i in range(0,len(data['files'])):
thumb_name = data['files'][i]
video_name = thumb_name.replace('.jpg', media_filter[0])
file_time = thumb_name.replace('.jpg', '').replace('_', ' ')
data['infos'].append(
{
"id": i,
"time": file_time,
"title": file_time,
"body": thumb_name,
'thumb': thumb_name,
'video': video_name
}
)
return response(data['infos'], status = 200)
hello_django
是最开始学习使用的,返回写死的json数据。history_list
,则是自动遍历缩略图文件夹,获取缩略图文件信息,并生成所需要的json数据格式。下载代码,进入manage.py所在的目录后,执行下面的命令即可启动:
scaffold
来模拟手机/pad的操作界面,具体界面如下:实时画面:
历史记录列表:
fetch
is widely supported enough to use · issue #595 · dart-lang/http (github.com)最主要的,对米尔myd-yt507开发板有了深入的了解,进行了实际的应用。作为一款车规级处理器t507的开发板,名不虚传!
此次的板卡测试,是米尔myd-yt507h开发板的各项性能测试。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
从coremark的基础性能测试,包括emmc、ram的存储性能测试,以及qt的显示测试来看,米尔myd-yt507h开发板的相关性能都是非常高的,可以满足边缘计算,人机交互,智能终端等各类高性能应用场景需求。
此次板卡的测试,是如何在ubuntu建立xfce桌面环境,以及如何远程穿透访问家中的米尔myd-yt507h开发板。
sudo apt update
sudo apt upgrade
2.安装locales:
# 安装
sudo apt install locales
# 设置:安装过程中,按照提示,选择 488. zh_cn.utf-8 utf-8、3. zh_cn.utf-8
sudo dpkg-reconfigure locales
# 设置系统全局locale,按照如下信息进行设置
sudo vim /etc/environment
language="zh_cn.utf-8:zh:en_us.utf-8:en"
lang="zh_cn.utf-8"
# 设置当前用户locale,按照如下信息进行设置
vim ~/.bashrc
export lc_all="zh_cn.utf-8"
export lang="zh_cn.utf-8"
export language="zh_cn.utf-8:zh:en_us.utf-8:en"
# 测试是否生效:设置生效后,执行data指令应返回中文
source ~/.bashrc
date
4.安装文泉驿中文字体:
sudo apt install -y fonts-arphic-ukai fonts-arphic-gkai00mp fonts-arphic-bkai00mp
sudo apt install -y xfonts-wqy ttf-wqy-microhei ttf-wqy-zenhei
5.安装xfce4:
# 安装过程中,按照提示,选择:56. 汉语、1. 汉语
sudo apt install xfce4 xfce4-terminal
6.修改xorg配置:
vim /etc/x11/xwrapper.config
# 修改为如下行
allowed_users = anybody
7.启动xfce4:
首先将开发板用hdmi线连接到显示器;我没有独立的显示器,所以连接到了电视;然后执行:
startxfce4
运行后,xfce4桌面会启动;没想到,电视分辨率太低,显示效果不佳:
既然本地显示效果不行,那就想办法弄成远程访问桌面。
我的电脑是macbook pro,分辨率够用,所以我安装了vnc,来提供远程桌面服务。
8.安装并配置vnc:
# 安装
sudo apt install vnc4server
# 设置密码
vncserver # 启动后,根据提示,输入后续通过vnc访问远程桌面,需要的密码
# 修改配置,使得vnc可以启动xfce4
vim ~/.vnc/xstartup
####添加以下内容
unset dbus_session_bus_address
# 下面两个任选其一
# xfce4-session &
startxfce4 &
####添加以上内容
vncserver -kill :1
vncserver :1
最后一条命令执行后,就能够使用vnc工具,远程连接了。
该命令后,可以添加 -geometry 1600x900
参数,表示设置分辨率。
9.在其他电脑上,使用vnc viewer远程连接桌面:
vnc viewer是一个免费工具,可以在网上搜索下载realvnc提供版本。
使用前面设置的vnc密码登录后,就能够正常进入访问了。
10.设置xfce4字体:
通过系统设置菜单,进入外观设置,选择对应的文泉驿微米黑字体即可。
到这一步,我们已经建立好了xfce4桌面环境,并且能够在内网访问该桌面环境了。
另外,我也在我的ipad pro上,安装了vnc工具,通过ipad远程访问桌面,效果如下:
11.通过autossh,建立内网穿透,在公司也能够访问家里内网的米尔myd-yt507h开发板:
要建立这个内网穿透,你首先需要一台可以在外网访问的linux服务器,例如在云上的服务器,需要具有公网ip地址。autossh能够帮助你,米尔myd-yt507h开发板连接到该服务器,并且可以让你通过该服务器,再来访问米尔myd-yt507h开发板。
云上的linux服务器设置:
# linux服务器设置
sudo vim /etc/ssh/sshd_config
# 添加如下行,千万别修改错了
gatewayports yes
# 重启sshd
/etc/init.d/sshd restart
米尔myd-yt507h开发板设置:
sudo apt install autossh
autossh -m 5911 -fnr 5901:127.0.0.1:5901 user@linux服务器ip
# 检查连接情况:出现一个autossh、一个ssh的执行行,表示正确
ps -ef | grep ssh | grep 5901
云上的linux服务器检查:
netstat -nlap | grep listen | grep 5901
# 执行后,应该可以看到5901对应的信息,否则没有设置正确,请重新设置
ssh -n -l 127.0.0.1:5901:127.0.0.1:5901 user@linux服务器ip
127.0.0.1:5901
,从而连到家里的米尔myd-yt507h开发板上的5901端口,也就是vnc服务,并打开xfce4桌面环境了。使用同样的方法,也可以将米尔myd-yt507h开发板的ssh的22端口,做内网穿透,在公司也能访问了。
想要了解优秀测评者“qinyunti”关于myd-yt507h开发板测评原文的可以复制下方链接查看:
https://bbs.elecfans.com/jishu_2293739_1_1.html
想要了解米尔t507-h处理器开发板可以去米尔pg电子娱乐试玩官网查看具体的产品介绍:
http://www.myir-tech.com/product/myc-yt507h.htm
需要购买米尔myd-yt507h开发板的可以复制下方链接购买:
https://detail.tmall.com/item.htm?id=673629085661
此次板卡的测试,是linux c开发环境下按键控制led的操作实录。
这里使用win11 wsl2 ubuntu 20.04进行开发。ubuntu单独安装在e盘,而不是直接从应用商店下载安装(具体安装方法可以网络搜索,这里不再赘述),因为开发环境需要的空间较大,直接应用商店安装默认位于c盘会导致c盘空间不够,所以独立安装在空间较大的盘。
米尔的资料是比较友好和全面的,手册,开发环境等都比较详尽,能方便用户快速熟悉开发环境进行二次开发。
另外参考文档中《myd-yt507h_linux软件开发指南v1.1.pdf》中相关信息有误,比如:
myd-yt507h_linux软件开发指南v1.1.pdf
本篇测评由电子发烧友的优秀测评者“honestqiao”提供。
此次板卡的测试,是用macos将ubuntu系统烧录到emmc的操作实录。
米尔myd-yt507h开发板,官方提供了hmi系统和ubuntu18.04镜像,体验过默认的hmi系统后,我就换上了我喜欢的ubuntu系统了。
一、系统烧录
参考官方的文档,使用全志的图形界面烧录工具,在windows下烧录简单又方便:
因为我使用的是macos系统,所以我是在macos下进行烧录的,这需要使用命令行来操作。
首先,我们看一下板子上的接口:
在上图中,红色的tv接口上面,有两个type-c的接口,标注了otg接口,可以用来烧录固件。标注了debug的,可以用于串口终端,来进行设备调试。
所以,要进行系h,连接到电脑,就可以准备烧录了。
从官方资料库中,下载ubuntu镜像和phoneixsuit的macos版本:
然后,进入命令行执行:
此时,会等待按键,具体的按键如下:
当 ----enter sem_wait-------
出现后,应先按住fel按键
不放,再按一下rst按键
,然后烧录就会开始进行,中途会提示百分比信息,此时就可以松开fel按键了。
如果按键不及时,可以再次运行命令,并按键进入烧录。
烧录完成后,重启即可开始体验新的系统了。
二、串口终端连接
默认的ubuntu系统,是18.04的,开发板相关的驱动都自带了,但是没有图形界面,所以启动的时候,只能进到命令行界面。即使hdmi连接了显示器,也只会显示myir的logo,不会显示命令行界面。
参考前面发的开发板的整体图,用type-c数据线,把debug接口对应的type-c口接上,连接到电脑,就可以在电脑上使用串口终端工具,直接连接操作了。
windows上的串口终端工具,可以使用putty或者mobaxterm。
linux或者macos上,可以使用screen命令或者minicom进行连接。
串口终端连接上以后,就像是在本地操作ubuntu系统的命令行界面。
登录串口终端,默认的用户如下:
管理员: root / root
普通用户:myir / 123456
串口终端连接后,先简单查看了一下系统的基本信息:
从上图中,我们可以看到:
linux内核:4.9.170版本
cpu:四核,arm64/aaarch64(aarch64是armv8 架构的一种执行状态)
内存:1g
三、远程ssh连接
通过串口终端操作,还是有一些不太方便,那么下一步,就是联网,并能够使用ssh远程登录。
#先切换到root用户 $su root #将网络配置文件,修改为如下的内容 $vim/etc/network/interfaces source-directory/etc/network/interfaces.d auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp auto eth1 iface eth1 inet dhcp
设置完成后,参考之前开发板的全图,使用网线连接到设备,重启设备,就能够自动联网了。
启动的时候,需要注意的是,要等全部启动过程完成,提示用户login的时候,才可以使用ssh远程登录。
在ssh远程登录以前,我们需要获得开发板取得的ip地址,这可以通过在路由器管理界面查看来获取,并且最好做mac地址绑定,以防发生变化。
我们也可以在串口终端中登录,然后使用ifconfig来查看。
其中,lo为系统本地回环网络设备,eth0、eth1对应两个有线网口。
ifconfig命令的结果如上图所示,eth0接了网线,所以其获得了ip地址,后续我将使用该地址,进行远程操作。
要进行远程ssh连接,windows系统可以使用putty或者mobaxterm,linux或者macos系统,则可以直接使用ssh命令来进行连接:
#请注意替换为实际获得的ip地址 ssh myir@192.168.1.239 #登录后,切换到root用户 su root
远程登录成功后,我们开始进行一些基础的设置,让系统更好使用。
注意:在sudo安装配置完成以前,以下的命令,使用root用户执行。
如果图简单省事,可以试用unminimize
命令,一步操作,自动安装成一个完整版本的ubuntu系统。
但我更喜欢自己来一手调教系统,这样更加的适合自己使用,而不会让系统变得臃肿。
1.将默认的shell,设置为bash:
#执行后,按照提示,选no即 dpkg-reconfigure dash
设置完成,退出ssh,重新连接即可
2.更换ubuntu apt更新源为国内源:
cp/etc/apt/sources.list/etc/apt/sources.list.bak #换源 echo"#中科大源 deb http://mirrors.ustc.edu.cn/ubuntu-ports/bionic-updates main restricted universe multiverse deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic-updates main restricted universe multiverse deb http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic-security main restricted universe multiverse deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic-security main restricted universe multiverse deb http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic-backports main restricted universe multiverse deb-src http://mirrors.ustc.edu.cn