一 整体结构下面给出了这个开发板的整体的一个接口示意图,固定孔位、尺寸和整体结构的布局与树莓派是极为相似的。个人感觉,旭日3派最想拉过来树莓派用户,希望吸引更多jetson nano用户。
如何去理解这个开发板,最直观的比方,可以说是:旭日3派≈树莓派+神经计算棒。它不像nvidia那样有nvcc来开发cuda相关程序,没有那么高的灵活度。但是它相比于树莓派,多了5tops的深度学习计算能力。这对jetson用户来说可能有点幼稚,但对树莓派用户来说刚刚好(✿◡‿◡)。
旭日x3派开发板,为soc系统级芯片,中心处理器采用的是arm a53,其他isp(图像信号处理器)和bpu(人工智能处理器单元)均为自研,2gb的内存,存储使用tf卡,其他的一些参数去官网查看具体配置即可。
前面已介绍,该开发板三点大优势算力/功耗比高、重量轻、价格低。对于算力/功耗比,2.5w的基础功耗提供了5tops的算力,下表给出一些同类型的一些边缘计算设备的一些配置。值得注意的是,神经计算棒必须搭配其他开发板使用。
这个表有几点需要说明下:
jetson nano查不到tops数据,考虑到nano的核心数是nx的1/3,因此根据nx的数据除以3作为nano的tops数据。功耗一般为基础功耗,非峰值功耗。价格为参考价。具体参数细节以个人使用为主哈,这里只是参考。在项目应用中,如果其中检测识别等相关处理算法,对实时要求不高,且对cpu要求不大的话,非常可以试一下旭日开发板。下面给出主要接口的说明:
type c电源口:该开发板可接入usb type c线来供电,实际使用时可使用5v/2a的手机充电头来用(充电头功率高点能稳定使用,低功率头似乎无法提供稳定的5v电压)。tf卡:开发板系统需要从tf卡加载并运行,用户在使用开发板前,需要先完成tf卡镜像制作。推荐使用至少8gb容量的tf存储卡,以便满足ubuntu操作系统及相关功能包的安装要求。hdmi接口:这个接口主要用于查看可视化结果,无鼠标,因为系统不具有桌面可视化能力,系统自己开发了个功能,可以通过代码将图像数据输出到hdmi方便查看。串口(用于登录):接入串口线,即可直接用远程的方式进行登录。usb2.0/3.0:开发板通过usb hub、硬件开关电路扩展了多路usb接口,满足用户多路usb设备接入的需求。无法确定是否全部来自于一个usb,对于高吞吐量的设备比如intel realsense深度相机,可能存在丢帧问题,有待后续适配测试。mipi接口:开发板提供1路mipi csi接口,可实现mipi摄像头的接入有线网口:千兆以太网接口,需要用命令行配置40pin标准接口:开发板提供40pin标准接口,方便进行外围扩展,其中数字io采用3.3v电平设计。40pin接口定义如下,有相关需求的可以测试一下。
二 启动系统下面,将完成系统的配置,完成系统的启动。
2.1 制作系统镜像开发板目前支持ubuntu 20.04 server、desktop两个版本。注意,由于x3芯片不支持gpu硬件加速,因此使用ubuntu desktop版本时,可能会因cpu渲染图形桌面而造成系统负载过大,如对系统性能有较高要求,推荐使用不带图形桌面的ubuntu server版本,下面我就带各位来使用server版本的系统。
首先,准备好一个tf卡(16g以上),和一个读卡器,插到自己的笔记本上。我试用时候,提供了一个镜像包x3pi_ubuntu_disk.tar.gz,解压它得到一组文件,其中system_sdcard.img就是我们要刷的系统文件镜像。
镜像刷录我使用的是balenaetcher工具,它是一款支持windows/mac/linux等多平台的启动盘制作工具,下载安装好就可以进行刷录啦。
※第一步:选择镜像
※第二步:选择tf卡。
※第三步:刷机!!
这几步完成后,我们就完成了刷机工作,相当简单吧φ(゜▽゜*)♪。
2.2 进入系统刷完系统后,我们就可以把这张tf卡插回开发板里面了。下面,我们要尝试进入系统。如果是刷机后第一次进入系统,一定要利用开发板自带的串口连接开发板,配置好网络后,后续可以不再利用串口登录(如果网络ip变了,那就重新用串口连接,查看下ip地址)。
※第零步:基础准备.
将hdmi连接到一个1080p显示器上(仅用于显示图像,无用户操作)。我在实验中,使用了一个hdmi采集卡,可通过usb接到笔记本上,利用vlc来查看输出图像信息。将mipi相机连接到开发板上。将串口线的一段插在开发板上。
※第一步:串口连接系统
将串口线的另一端连接到笔记本上,看下设备管理器,若提示未知usb设备时,说明pc机未安装串口驱动,驱动程序可从地平线开发者社区发布页面https://developer.horizon.ai/resource获取。驱动安装完成后,设备管理器可正常识别串口板端口。
使用 mobaxterm 工具按照如下方式进行配置,打开后,由于设备没插电,所以空白。
下面,将usb type c电源线,插入开发板。这时候,控制台就会输出一堆文件,到最后,会需要输入用户名和密码,默认账户和密码均为sunrise。如果开发板hdmi正常显示开机画面(server系统显示地平线logo、desktop版本显示系统桌面),说明tf卡系统制作正确。
※第二步:bpu测试
开发板已经连接了一个mipi相机,下面使用官方示例来测试bpu模块是否有效。先进入示例程序文件夹cd /app/ai_inference/03_mipi_camera_sample/,然后输入sudo python3 mipi_camera.py,注意一定要加sudo,调用bpu模块需要管理员权限。
这时候,控制台就会输出一些检测信息,对应可视化效果由显示器显示。
※后话:功耗分析
我分别对开机时,bpu检测中,和检测后的功耗进行了分析。开机后的功率在2.3w左右,利用bpu执行了一个检测示例,功耗升到3.6左右,结束后,功耗降为2.0w,这个原因比较诡异,可能是中止项目后,关闭了显示输出使得功耗下降。
至此,开发板启动起来了,我们happy的进行使用了。
2.3 网络配置用串口来操作开发板的话,有几个致命问题:无法传文件、命令过长有bug、vim使用不方便。因此,非常有必要把网络配置好来进行后续的调试开发。
开发板本身自带无线模块,同时也可以插网线以获得更快的速度。下面给出这两种模式的一个配置。
2.3.1 以太网配置① 利用sudo nmcli dev查看网络设备。输入ifconfig发现ip地址是192.168.1.10,翻阅手册发现开发板以太网默认采用静态ip地址(192.168.1.10),以方便固定网络环境下的使用,例如开发板与pc机直连场景。但是,对于我来说,我需要动态分配,因为校园网整体就是局域网,不需要网络环境仅局限在电脑、开发板之间。
以下的配置,是想让开发板的ip地址由学校路由器分配得到,不需要静态ip
② 创建一个新的以太网链接
考虑到串口连接,输入的命令不能过长,则先利用sudo -i切换为root(操作完后利用su sunrise切换回去),然后在命令行中输入nmcli con add con-name ethdhcp type ethernet ifname eth0,这样可以使用dhcp获取网络,其中ethdhcp为网络名,用户可以自定义。这时候,我们发现,connection部分已经变了,且ip地址变为自动分配的了。
以太网有个大问题,就是连接校园网时候,由于没有用户界面,因此账号的登录,可能需要利用python脚本去完成,或者让校园网插在路由器上完成中转,如果是个人路由器的话,这种问题一般不存在。
2.3.2 无线网配置无线网络的连接参考博客《linux命令行连接wifi(全网最简单的方法)》。
① 利用sudo nmcli radio wifi on开启wifi。
② 利用sudo nmcli dev wifi扫描wifi。其中,nova 9 pro 为个人用手机开的热点
③ 利用sudo nmcli dev wifi connect wifi名 password 密码连接wifi。将wifi的账号密码套在这个命令里,即可成功连接上wifi。
无线网最大的问题就是它的速度真的太慢了,我手上的这个版本速度约为300kb/s,自己外加个天线能够减低远程操作的延迟,这个问题已反馈,据说发布后的板子不存在这个问题。
三 cpu项目测试无论什么开发板,基于cpu相关的程序的稳定性至关重要。因此,非常有必要去测试usb、串口、wifi等相关的有效性以及稳定性。该部分的测试,一是测试项目的一些基本功能,其次是测试自己做项目中使用的一些算法,来分析整体系统的一个性能。
由于刷机时选择的是ubuntu server,所以带有界面相关的程序均无法使用。如果想在server上部署界面端,40pin接口上有spi接口,可以购置spi液晶屏来开发。
3.1 使用注意事项第一次使用记得要sudo apt-get update,默认清华源速度还是可以的。vscode可以使用但不建议(占用600m左右的内存,总共内存在2g)。通过arch指令,可查得当前系统的架构为aarch64。系统自带了个轻量版opencv但不够用,还是得通过指令sudo apt-get install libopencv-dev安装个完整的opencv。通过指令sudo apt-get install mlocate安装locate,方便用来查找某些文件的地址。系统默认没有git,通过sudo apt-get install git来方便下载代码利用htop可以查看cpu和内存的利用情况3.2 hdmi可视化图像数据由于系统里面并不包含图形界面,因此如果动态地看算法的检测效果的话,就需要将图像数据输出到hdmi来显示,系统自带的python包里面有个类libsrcampy.display就是来完成这样的工作的。
为了方便各位后续可视化自己的算法,我将这个功能封装为一个class
class imageshow(object): # 初始化,screen_w和screen_h表示hdmi输出支持的显示器分辨率 def __init__(self, screen_w = 1920, screen_h = 1080): super().__init__() self.screen_w = screen_w self.screen_h = screen_h self.disp = srcampy.display() self.disp.display(0, screen_w, screen_h) # 结束显示 def close(self): self.disp.close() # 显示图像,输入image即可 def show(self, image, wait_time=0): imgshow = self.putimage(image, self.screen_w, self.screen_h) imgshow_nv12 = self.bgr2nv12_opencv(imgshow) self.disp.set_img(imgshow_nv12.tobytes()) # 私有函数,将图像数据转换为用于hdmi输出的数据 @classmethod def bgr2nv12_opencv(cls, image): height, width = image.shape[0], image.shape[1] area = height * width yuv420p = cv2.cvtcolor(image, cv2.color_bgr2yuv_i420).reshape((area * 3 // 2,)) y = yuv420p[:area] uv_planar = yuv420p[area:].reshape((2, area // 4)) uv_packed = uv_planar.transpose((1, 0)).reshape((area // 2,)) nv12 = np.zeros_like(yuv420p) nv12[:height * width] = y nv12[height * width:] = uv_packed return nv12 # 图像数据在显示器最大化居中 @classmethod def putimage(cls, img, screen_width, screen_height): if len(img.shape) == 2: imgt = cv2.cvtcolor(img, cv2.color_gray2bgr) else: imgt = img irows, icols = imgt.shape[0:2] scale_w = screen_width * 1.0/ icols scale_h = screen_height * 1.0/ irows final_scale = min([scale_h, scale_w]) final_rows = int(irows * final_scale) final_cols = int(icols * final_scale) print(final_rows, final_cols) imgt = cv2.resize(imgt, (final_cols, final_rows)) diff_rows = screen_height - final_rows diff_cols = screen_width - final_cols img_show = np.zeros((screen_height, screen_width, 3), dtype=np.uint8) img_show[diff_rows//2:(diff_rows//2+final_rows), diff_cols//2:(diff_cols//2+final_cols), :] = imgt return img_show下面给出视频和图像的测试方法,相机用的是mipi相机
def test_show_image(): img = cv2.imread('00000160.png') # 加载本地图片 im_show = imageshow() # 初始化显示 im_show.show(img) # 显示图像 time.sleep(1) im_show.close() def test_mipi_camera(): im_show = imageshow() cam = srcampy.camera() # 定义相机类型 cam.open_cam(0, 1, 30, 1920, 1080) # 设置相机采集所用的参数 while true: origin_image = cam.get_img(2, 1920, 1080) # 获取相机数据流 origin_nv12 = np.frombuffer(origin_image, dtype=np.uint8).reshape(1620, 1920) # origin_bgr = cv2.cvtcolor(origin_nv12, cv2.color_yuv420sp2bgr) # 图像虽然是rgb实际上是bgr,问题已反馈 origin_bgr = cv2.cvtcolor(origin_nv12, cv2.color_yuv420sp2rgb) im_show.show(origin_bgr) im_show.close()对这两个函数分别测试,这时候显示屏就会出现对应可视化结果,这样就完成数据可视化所需的一些工作(视频流图像显示这部分,功耗占了1w,cpu占用30%左右,显示这部分的代码还是有点冗余)。
3.3 qt项目测试——串口转wifi之前有个项目,要求无人机与地面站直接的通信由之前的数传改为wifi,搜了一圈,很多都属于手工调试,而且包含复杂的界面。然而实际需求要求稳定,自动化。因此为了满足这个需求只能是自己开发一个小工具。该项目的细节部分参考博客串口转wifi —— 两个串口之间通过网络进行通信。
该工具可测试板子的串口模块和wifi模块的稳定性。
① 利用minicom测试串口。第一次使用可利用命令sudo apt-get install minicom安装相关工具,将开发板的4-5针头,也就是串口的收发接头短接。这样接收到的信息又会发送出去。
② 编译uart2net工具。按顺序执行以下相关代码:
sudo apt-get install qt5-defaultsudo apt-get install libqt5serialport5-devgit clone https://github.com/li-zhaoxi/uart2netcd uart2netqmake uart2net.promake all -j4③ 配置uart2net.ini文件。注意串口和ip地址的相关配置。
④ 启动uart2net。
服务端为个人笔记本,使用了串口调试助手+虚拟串口模拟串口数据的输入。客户端为旭日3派板子,由于/dev/ttys3收发已短接,因此,接收到什么数据就会发送出去。最后测试数据的输出与博客串口转wifi —— 两个串口之间通过网络进行通信是一样的这里就不再进行叙述了。
测试时候发现个问题,由于wifi传输有一定的延迟,如果每10ms就发送几十个字节的数据的话,会造成大量数据的阻塞,后续在应用时候注意数据传输不要过快,过多,否则会造成几秒的延迟。
3.4 c++项目测试——圆形结构检测部分项目应用中需要检测场景中的椭圆目标,因此将算法移植到这个板子上,以方便测试检测效果与实时性,通过命令git clone https://github.com/li-zhaoxi/aamed下载椭圆检测代码。
① 编译python代码模块aamed.so。按照下面的方式配置好setup.py文件之后,cd python进入python文件夹,执行python3 setup.py build_ext --inplace编译算法模块。需要修改代码的部分如下所示,直接替换即可。
opencv_include = /usr/include/opencv4/opencv_lib_dirs = /usr/lib/aarch64-linux-gnu/ext_modules = [ extension( pyaamed, [../src/adaptapproximatecontours.cpp, ../src/adaptapproxpolydp.cpp, ../src/contours.cpp, ../src/ellipsenonmaximumsuppression.cpp, ../src/fled.cpp, ../src/fled_drawandwritefunctions.cpp, ../src/fled_initialization.cpp, ../src/fled_privatefunctions.cpp, ../src/group.cpp, ../src/linkmatrix.cpp, ../src/node_fc.cpp, ../src/segmentation.cpp, ../src/validation.cpp, aamed.pyx], include_dirs = [numpy_include,'fled', opencv_include], language='c++', libraries=['opencv_core', 'opencv_highgui', 'opencv_imgproc', 'opencv_imgcodecs', 'opencv_flann'], library_dirs=[opencv_lib_dirs] ), ]② 调用mipi摄像头实现检测。我在测试时候出现了一个错误cannot allocate memory in static tls block python,把python头文件的顺序调整了下就ok了。下面给出我的测试代码。
我在显示屏上放上了待检测的照片,让mipi相机去拍显示器完成检测过程
from hobot_vio import libsrcampy as srcampyimport cv2import numpy as npimport timefrom pyaamed import pyaamed# 这里把前面的hdmi可视化部分的代码贴上# 复制类class imageshow(object)# 检测主程序def test_mipi_camera(): im_show = imageshow() cam = srcampy.camera() cam.open_cam(0, 1, 30, 1920, 1080) aamed = pyaamed(550, 970) aamed.setparameters(3.1415926/3, 3.4,0.77) # 阈值设置,如果假椭圆过多,可适当调高0.77 while true: origin_image = cam.get_img(2, 1920, 1080) origin_nv12 = np.frombuffer(origin_image, dtype=np.uint8).reshape(1620, 1920) origin_bgr = cv2.cvtcolor(origin_nv12, cv2.color_yuv420sp2rgb) imgg = cv2.resize(cv2.cvtcolor(origin_bgr, cv2.color_bgr2gray), (960, 540)) imggc = cv2.cvtcolor(imgg, cv2.color_gray2bgr) t1 = cv2.gettickcount() res = aamed.run_aamed(imgg) # 检测部分代码 t2 = cv2.gettickcount() print('time consumption(ms):', (t2 - t1) * 1000 / cv2.gettickfrequency()) for each_elp in res: cv2.ellipse(imggc, ((each_elp[1], each_elp[0]), (each_elp[3], each_elp[2]), -each_elp[4]), (0, 0, 255), 2) im_show.show(imggc) im_show.close()test_mipi_camera()下面是检测耗时和效果图,检测的图像分辨率为960×540,这个时耗几乎在37ms左右,也能满足一些基本的算法需求。
time consumption(ms): 36.989471time consumption(ms): 37.507962time consumption(ms): 36.99551time consumption(ms): 43.346158time consumption(ms): 37.378966time consumption(ms): 38.764665time consumption(ms): 38.915905time consumption(ms): 25.642136time consumption(ms): 49.384246
3.5 小结至此,开发板cpu的部分相关所需功能均已测试完毕,总体来说,基本能满足大部分轻量型算法的需求,除了wifi部分延迟较高,其余我觉得均已经足够适应大部分的任务了。我个人非常喜欢操作hdmi显示图像的方式,降低带有桌面系统带来的性能损耗,极大的给算法留出更多的计算量。
四 bpu项目测试开发板中的bpu部分为自研芯片,部分深度学习网络层从硬件的角度进行了加速。因此,这个开发板核心在于部署。在前文进入系统部分中,通过cd /app/ai_inference/03_mipi_camera_sample/和sudo python3 mipi_camera.py已经展示了系统自带的检测效果。
4.1 基本操作查看bpu使用率。使用sudo watch -n 1 hrut_somstatus命令可以查看当前开发板的bpu使用率,在运行mipi_camera.py的时候,可执行该命令获得bpu利用情况。
查看cpu使用率。尽管hrut_somstatus已经提供了cpu的利用率情况,但我还是觉得htop效果更直观ծㅂծ。
这些操作,主要是用来查看算法的资源占用率的,初级功能。后续非常期待官方出一个类似jetson的jtop工具,jtop的参考链接为jetson_stats。
4.2 已有模型测试由于开发板的特殊性,利用pytorch训练好的模型,是无法直接用在这个板子上的,官方将一堆常见的模型参数进行了转换。在装好的系统中,有两个可直接使用的模型。fcos用于目标检测,mobilenetv1用于目标分类。
在开发板的/app/ai_inference/01_basic_sample/路径下,提供了一个示例test_mobilenetv1.py,下面对其中的主函数部分进行一个介绍,部分核心功能的解释写在代码注释里面了。这部分通过opencv的cv2.gettickcount()和cv2.gettickfrequency()可测出,耗时约为9ms!!
# 主函数代码前面还包含如下子函数,用于数据转换,参数输出等。# bgr2nv12_opencv、print_properties、get_hwif __name__ == '__main__': # 1. 加载模型,用于bpu加速计算的模型为一个*.bin文件,里面包含了模型的所有信息 models = dnn.load('../models/mobilenetv1_224x224_nv12.bin') # 2. 输出模型的input和output信息 # ========== inputs[0] properties ========== print(= * 10, inputs[0] properties, = * 10) # 输出模型的输入信息,输出信息如下 # tensor type: nv12_separate # data type: uint8 # layout: nchw # shape: (1, 3, 224, 224) # inputs[0] name is: data print_properties(models[0].inputs[0].properties) print(inputs[0] name is:, models[0].inputs[0].name) # ========== outputs[0] properties ========== print(= * 10, outputs[0] properties, = * 10) # 这里输出模型的输出信息,输出内容如下: # tensor type: float32 # data type: float32 # layout: nchw # shape: (1, 1000, 1, 1) # outputs[0] name is: prob print_properties(models[0].outputs[0].properties) print(outputs[0] name is:, models[0].outputs[0].name) # 3. 加载图像数据,前面已经输出了模型需要的输入数据尺寸和数据类型 # 因此,先利用cv2.resize将图像转换为目标大小尺寸 # 再利用bgr2nv12_opencv将图像数据转为nv12形式(个人理解是压缩了图像数据,减少了传输时间)。 img_file = cv2.imread('./zebra_cls.jpg') h, w = get_hw(models[0].inputs[0].properties) des_dim = (w, h) resized_data = cv2.resize(img_file, des_dim, interpolation=cv2.inter_area) nv12_data = bgr2nv12_opencv(resized_data) # 4. 将图像的nv12数据传入模型中,完成了整体的推理过程 outputs = models[0].forward(nv12_data) # 下面将模型预测信息打印输出 # ========== get output[0] numpy data ========== # output[0] buffer numpy info: # shape: (1, 1000, 1, 1) # dtype: float32 # ========== classification result ========== # cls id: 340 confidence: 0.991851 print(= * 10, get output[0] numpy data, = * 10) print(output[0] buffer numpy info: ) print(shape: , outputs[0].buffer.shape) print(dtype: , outputs[0].buffer.dtype) # print(first 10 results:, outputs[0].buffer[0][:10]) print(= * 10, classification result, = * 10) assert np.argmax(outputs[0].buffer) == 340 print(cls id: %d confidence: %f % (np.argmax(outputs[0].buffer), outputs[0].buffer[0][np.argmax(outputs[0].buffer)]))其实要把模型装板里,拢共分三步:
加载模型。模型格式为*.bin文件,需要利用地平线的天工开物平台转换得到。加载图像数据。图像的加载利用opencv即可完成。获取数据之后,转换为目标尺寸、nv12数据即可直接输入到加载好的模型中。直接推理。outputs = models[0].forward(nv12_data)即可完成推理,这部分很简单。
4.3 个人模型部署概述上面介绍了如何将 大象 模型装进bpu里→_→, 其实对个人来说,最难的就是如何获得*.bin文件。这里我其实无法一步步的引导各位如何部署自己的模型,因为这里的部署过程需要利用地平线开发的天工开物工具链,部署教程参考文档:horizon ai toolchain user guide。对我来说,这部分东西太多,学习成本太大了,装进这个博客里有点太多了(多写个博客可以白嫖更多浏览量 )。
深度学习用的比较多的还是pytorch,模型可以转换为onnx模型文件,这个模型文件我觉得还是非常通用的,tensorrt和intel的神经计算棒都利用了这个文件。
模型转换的目的就是检查模型文件中的网络层是否包含在bpu支持的层中(bpu本质上是从硬件的角度加速模型的计算,是一个专用工具),如果某些层不存在,这些层就需要利用cpu完成推理。
实际上,为了保证模型迁移的可靠性,整个上有以下几个关键过程:
① 模型准备。这些模型一般都是基于公开深度学习训练框架得到的, 需要将模型导出为开发板支持的格式,目前转换工具支持的深度学习框架如下。caffe导出的caffemodel是直接支持的(caffe是基于c++的,代码相当优美,非常适合硬件转换); pytorch、tensorflow和mxnet是通过转到onnx实现间接支持。
② 模型验证。用来确保提出的算法模型是符合bpu要求的,开发平台提供了hb_mapper checker来完成模型的检查。 不满足迁移的层,就需要手动调整,最简单的办法就是这部分转到cpu来跑(数据传输上存在大量时间浪费),因此还是尽可能将这部分转到bpu上( ,科研和落地还是有很大差距的)。
③ 模型转换。这个阶段会将浮点模型转为可用bpu使用的模型,利用函数hb_mapper makertbin完成转换,转换成功后,得到的模型就可以运行在开发板上了。
模型转换,不一定就能保证一定就能跑起来,精度和性能都不敢说保证与开发中的结果是一样的,因此需要进行验证和调试。nvidia其实也有类似的工作,就是tensorrt,加速必有一定程度的损失,这些不可避免,这些其实涉及到数值分析的内容。这部分有需要的话,可以参考模型性能分析与调优和模型精度分析与调优。
原作者:小玺玺
原链接:原文详见地平线开发者社区
安卓手机企业持续提升手机售价
恒流电路原理图设计方案介绍
居民供暖室温无线监测系统
移动机器人中的控制学问题
薄膜表面缺陷在线检测仪器实现更高效率的生产线
多方位玩转“地平线新发布AIoT开发板——旭日X3派(Sunrise X3 Pi)” 插电!开机!轻松秒杀!
离线式LED灯对LED驱动器IC要求
iPhone 12/Pro “信号门”,国内运营商:跟基站无关
努比亚异形柔性屏设计专利曝光采用了双刘海
CPU核心数是不是越多越好
一款基于DSP内核处理器的FPGA验证实现设计
光电式液位传感器可以实现无水提醒功能吗?
安森美半导体FL7740应用于智能照明,实现调光、节能两不误
IBM加强Power8系统拟定,助推FPGA
泰克支持电赛2019,为未来工程师培养赞助“仪”臂之力
线电流和相电流的关系
PCB多层印制板层压工艺技术解析
物联网技术成为企业纷纷选择的工作手段
机器学习如今大获成功的原因有哪些?如何才能取得进一步的突破?
海信洗衣机高效除菌不伤衣 助力打赢疫情战斗