这里主要还是感觉上一期说的一些对于人体姿态估计模型的前期调研,进行后期的部署的一些尝试。下面主要针对尝试的集中模型进行分享。
1、movenet
首先还是上次提到的谷歌的轻量级人体姿态估计模型MoveNet,虽然没有论文,也没有官方代码,笔者这里主要根据开源的项目进行训练:
git clone https://github.com/fire717/movenet.pytorch.git
上次已经提到训练效果不太好,模型不仅只能对单人识别,并且效果远逊于openpose。后面又尝试炼了几次丹,发现对于关键点的捕捉还是不太准确。
后面发现movenet有谷歌官方开源的模型,movenet | Kaggle上面可以下载tflite格式的模型,并且是已经量化好为8bit的。兴冲冲的准备直接转为onnx格式,再把onnx模型转为axmodel模型,结果发现存在一个问题:tflite无法直接转化为onnx模型,存在一定麻烦。最明显的问题是数据布局问题—— TFLite 模型是 NHWC 格式,而 ONNX 是NCHW,因此内部的算子和张量等等分布都不一样。目前最常用的方式是用tflite2onnx这个库进行转换,但是有这个库目前还不支持的算子:NotImplementedError: Unsupported TFLite OP: 53 CAST!
因此对于movenet的尝试只能暂时告一段落。
2、openpose/lightweight openpose
在上次的分享里面提到了openpose,使用了下面的开源项目:
git clone https://github.com/Hzzone/pytorch-openpose.git
后面笔者又尝试了lightweight openpose的模型,发现同样有很好用的开源模型,并基于此的基础进行尝试:
git clone https://github.com/Daniil-Osokin/lightweight-human-pose-estimation.pytorch.git
简单介绍一下openpose和lightweight openpose模型的差别:
lightweight openpose模型主要是把openpose backbone网络的VGG网络换成了轻量级CNN网络中的mobilenet,此外还将openpose的两个branch合并成一个branch并且用带空洞卷积的block结构代替7*7卷积。lightweight openpose能大幅度减少模型参数,并且达到几乎一样的精度
对于lightweight openpose模型,在生成onnx时首先遇到的第一个问题就是模型输入的参数。这个模型可以接受不同的输入大小,也就是可以接受动态输入大小,若输入1_3_H_W的图片,会用到两个输出,一个是1_38_H/4_W/4的pafs图,一个是1_19_H/4_W/4的heatmaps图。我们知道onnx可以接受动态输入和静态输入,笔者也最开始使用如下代码导出动态的模型,然后试着转化为axmodel模型:
from models.with_mobilenet import PoseEstimationWithMobileNet
from modules.load_state import load_state
import torch
import argparse
def convert_to_onnx(net, output_name):
# 动态尺寸的输入
input = torch.randn(1, 3, 256, 456)
dynamic_axes = {
\'data\': {2: \'height\', 3: \'width\'},# 动态批处理大小和宽度
\'stage_0_output_1_heatmaps\': {2: \'height_out\', 3: \'width_out\'},
\'stage_0_output_0_pafs\': {2: \'height_out\', 3: \'width_out\'},
\'stage_1_output_1_heatmaps\': {2: \'height_out\', 3: \'width_out\'},
\'stage_1_output_0_pafs\': {2: \'height_out\', 3: \'width_out\'}
}
input_names = [\'data\']
output_names = [\'stage_0_output_1_heatmaps\', \'stage_0_output_0_pafs\',
\'stage_1_output_1_heatmaps\', \'stage_1_output_0_pafs\']
torch.onnx.export(net, input, output_name, verbose=True, input_names=input_names, output_names=output_names,
dynamic_axes=dynamic_axes,opset_version=15)
if __name__ == \'__main__\':
parser = argparse.ArgumentParser()
parser.add_argument(\'--checkpoint-path\', type=str, required=True, help=\'path to the checkpoint\')
parser.add_argument(\'--output-name\', type=str, default=\'human-pose-estimation.onnx\',
help=\'name of output model in ONNX format\')
args = parser.parse_args()
net = PoseEstimationWithMobileNet()
checkpoint = torch.load(args.checkpoint_path)
load_state(net, checkpoint)
convert_to_onnx(net, args.output_name)
但是在生成axmodel模型时,出现问题,发现模型不接受动态输入大小,看来必须要接受恒定大小的静态输入:
2024-01-20 21:52:00.043 | WARNING| yamain.command.build:fill_default:320 - ignore data csc config because of src_format is AutoColorSpace or src_format and tensor_format are the same
Traceback (most recent call last):
File \"<frozen yamain.common.error>\", line 11, in wrapper
File \"<frozen yamain.command.build>\", line 631, in optimize_onnx
File \"<frozen yamain.command.load_model>\", line 633, in optimize_onnx_model
File \"<frozen frontend.parsers.onnx_parser>\", line 71, in parse_onnx_model
File \"<frozen frontend.parsers.onnx_parser>\", line 122, in parse_onnx_model_proto
File \"<frozen frontend.parser_utils>\", line 34, in parse_value_info
File \"<frozen frontend.parser_utils>\", line 28, in check_value_info
AssertionError: illegal value_info data: [1, 3, \'height\', \'width\']
于是笔者选择使用1_3_256_456作为恒定的输入大小,这里需要对原项目的代码做一些改动,因为为了实现1_3_256_456的输入大小,我们先要一个scale对图片进行放缩,让图片的height达到256或者width达到456,再进行padding,在达不到的边pad(0,0,0),这里对源代码如下地方做了改动:
`
def infer_fast(net, img, net_input_size, stride, upsample_ratio, cpu,
pad_value=(0, 0, 0), img_mean=np.array([128, 128, 128], np.float32), img_scale=np.float32(1/256)):
height, width, _ = img.shape
scale = min(net_input_size[0]/height,net_input_size[1]/width)
scaled_img = cv2.resize(img, (0, 0), fx=scale, fy=scale, interpolation=cv2.INTER_LINEAR)
scaled_img = normalize(scaled_img, img_mean, img_scale)
min_dims = [net_input_size[0], net_input_size[1]]
padded_img, pad = pad_width(scaled_img, pad_value, min_dims)
tensor_img = torch.from_numpy(padded_img).permute(2, 0, 1).unsqueeze(0).float()
`
def pad_width(img, pad_value, min_dims):
h, w, _ = img.shape
pad = []
pad.append(int(math.floor((min_dims[0] - h) / 2.0)))
pad.append(int(math.floor((min_dims[1] - w) / 2.0)))
pad.append(int(min_dims[0] - h - pad[0]))
pad.append(int(min_dims[1] - w - pad[1]))
padded_img = cv2.copyMakeBorder(img, pad[0], pad[2], pad[1], pad[3],
cv2.BORDER_CONSTANT, value=pad_value)
return padded_img, pad
再通过如下代码,便可以导出来onnx模型,这里笔者用了simplifier库做了简化,发现差别并不大,因此就直接用onnx模型:
`
import argparse
import torch
from models.with_mobilenet import PoseEstimationWithMobileNet
from modules.load_state import load_state
def convert_to_onnx(net, output_name):
input = torch.randn(1, 3, 256, 456)
input_names = [\'data\']
output_names = [\'stage_0_output_1_heatmaps\', \'stage_0_output_0_pafs\',
\'stage_1_output_1_heatmaps\', \'stage_1_output_0_pafs\']
torch.onnx.export(net, input, output_name, verbose=True, input_names=input_names, output_names=output_names)
if __name__ == \'__main__\':
parser = argparse.ArgumentParser()
parser.add_argument(\'--checkpoint-path\', type=str, required=True, help=\'path to the checkpoint\')
parser.add_argument(\'--output-name\', type=str, default=\'human-pose-estimation.onnx\',
help=\'name of output model in ONNX format\')
args = parser.parse_args()
net = PoseEstimationWithMobileNet()
checkpoint = torch.load(args.checkpoint_path)
load_state(net, checkpoint)
convert_to_onnx(net, args.output_name)
导出来的模型后面就需要用pulsar2的工具进行转换为了,但是没有现成的校准数据集,因此只能用openpose训练时的coco数据集中选择图片作为数据集。先从COCO - Common Objects in Context (cocodataset.org)下载coco数据集,然后根据annotation选择类别为person的照片,我这里选择了30张比较完整身躯的人类图片,有一个人的也有多个人的,为了合适先把这些图片按照上述预处理先进行了处理,然后组成了calibration.tar:
之后我们按照之前的方法,先写个config.json,这里写的结果如下:
`
{
\"model_type\": \"ONNX\",
\"npu_mode\": \"NPU1\",
\"quant\": {
\"input_configs\": [
{
\"tensor_name\": \"data\",
\"calibration_dataset\": \"./dataset/calibration_data.tar\",
\"calibration_size\": 30,
\"calibration_mean\": [128,128,128],
\"calibration_std\": [256,256,256]
}
],
\"calibration_method\": \"MinMax\",
\"precision_analysis\": true,
\"precision_analysis_method\":\"EndToEnd\"
},
\"input_processors\": [
{
\"tensor_name\": \"data\",
\"tensor_format\": \"BGR\",
\"src_format\": \"BGR\",
\"src_dtype\": \"U8\",
}
],
\"output_processors\": [
{
\"tensor_name\": \"stage_0_output_1_heatmaps\",
},
{
\"tensor_name\": \"stage_0_output_0_pafs\",
},
{
\"tensor_name\": \"stage_1_output_1_heatmaps\",
},
{
\"tensor_name\": \"stage_1_output_0_pafs\",
}
],
\"compiler\": {
\"check\": 0
}
}
然后再按照如下的指令生成axmodel:
pulsar2 build --input model/human-pose-estimation.onnx --output_dir output --config config/lightweight_openpose_config.json
可以看一下结果:
2024-01-21 00:23:17.469 | WARNING| yamain.command.build:fill_default:320 - ignore data csc config because of src_format is AutoColorSpace or src_format and tensor_format are the same
Building onnx ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
2024-01-21 00:23:19.247 | INFO| yamain.command.build:build:444 - save optimized onnx to [output/frontend/optimized.onnx]
2024-01-21 00:23:19.254 | INFO| yamain.common.util:extract_archive:21 - extract [dataset/calibration_data.tar] to [output/quant/dataset/data]...
Quant Config Table
┏━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓
┃ Input ┃ Shape┃ Dataset Directory ┃ Data Format ┃ Tensor Format ┃ Mean┃ Std ┃
┡━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩
│ data│ [1, 3, 256, 456] │ data│ Image │ BGR│ [128.0, 128.0,│ [128.0, 128.0,│
│ ││ │ ││ 128.0] │ 128.0] │
└───────┴──────────────────┴───────────────────┴─────────────┴───────────────┴────────────────────┴────────────────────┘
Transformer optimize level: 0
30 File(s) Loaded.
[00:23:24] AX LSTM Operation Format Pass Running ...Finished.
[00:23:24] AX Set MixPrecision Pass Running ...Finished.
[00:23:24] AX Refine Operation Config Pass Running ... Finished.
[00:23:24] AX Reset Mul Config Pass Running ...Finished.
[00:23:24] AX Tanh Operation Format Pass Running ...Finished.
[00:23:24] AX Confused Op Refine Pass Running ...Finished.
[00:23:24] AX Quantization Fusion Pass Running ...Finished.
[00:23:24] AX Quantization Simplify Pass Running ...Finished.
[00:23:24] AX Parameter Quantization Pass Running ...Finished.
Calibration Progress(Phase 1): 100%|████████████████████████████████████████████████████| 30/30 [00:05<00:00,5.03it/s]
Finished.
[00:23:31] AX Passive Parameter Quantization Running ...Finished.
[00:23:31] AX Parameter Baking Pass Running ...Finished.
[00:23:31] AX Refine Int Parameter Pass Running ... Finished.
[00:23:31] AX Refine Weight Parameter Pass Running ... Finished.
--------- Network Snapshot ---------
Num of Op:[117]
Num of Quantized Op: [117]
Num of Variable:[226]
Num of Quantized Var:[226]
------- Quantization Snapshot ------
Num of Quant Config: [350]
BAKED:[54]
OVERLAPPED: [171]
ACTIVATED:[74]
PASSIVE_BAKED: [51]
Network Quantization Finished.
quant.axmodel export success: output/quant/quant_axmodel.onnx
===>export per layer debug_data(float data) to folder: output/quant/debug/float
Writing npy... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
===>export input/output data to folder: output/quant/debug/test_data_set_0
Building native ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
/usr/local/lib/python3.9/site-packages/scipy/spatial/distance.py:620: RuntimeWarning: invalid value encountered in float_scalars
dist = 1.0 - uv / np.sqrt(uu * vv)
Building native ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
2024-01-21 00:23:37.654 | WARNING| yamain.command.load_model:pre_process:454 - preprocess tensor [data]
2024-01-21 00:23:37.654 | INFO| yamain.command.load_model:pre_process:456 - tensor: data, (1, 3, 256, 456), U8
2024-01-21 00:23:37.654 | INFO| yamain.command.load_model:pre_process:456 - op: op:pre_dequant_1, AxDequantizeLinear, {\'const_inputs\': {\'x_zeropoint\': array(0, dtype=int32), \'x_scale\': array(1., dtype=float32)}, \'output_dtype\': <class \'numpy.float32\'>, \'quant_method\': 0}
2024-01-21 00:23:37.654 | INFO| yamain.command.load_model:pre_process:456 - tensor: tensor:pre_norm_1, (1, 3, 256, 456), FP32
2024-01-21 00:23:37.654 | INFO| yamain.command.load_model:pre_process:456 - op: op:pre_norm_1, AxNormalize, {\'dim\': 1, \'mean\': [128.0, 128.0, 128.0], \'std\': [128.0, 128.0, 128.0]}
2024-01-21 00:23:37.654 | WARNING| yamain.command.load_model:post_process:475 - postprocess tensor [stage_0_output_1_heatmaps]
2024-01-21 00:23:37.654 | WARNING| yamain.command.load_model:post_process:475 - postprocess tensor [stage_0_output_0_pafs]
2024-01-21 00:23:37.654 | WARNING| yamain.command.load_model:post_process:475 - postprocess tensor [stage_1_output_1_heatmaps]
2024-01-21 00:23:37.654 | WARNING| yamain.command.load_model:post_process:475 - postprocess tensor [stage_1_output_0_pafs]
tiling op...━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 92/92 0:00:00
new_ddr_tensor = []
build op...━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 389/389 0:00:00
add ddr swap...━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 738/738 0:00:00
calc input dependencies...━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1039/1039 0:00:00
calc output dependencies...━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1039/1039 0:00:00
assign eu heuristic━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1039/1039 0:00:00
assign eu onepass━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1039/1039 0:00:00
assign eu greedy━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1039/1039 0:00:00
2024-01-21 00:23:39.899 | INFO| yasched.test_onepass:results2model:2004 - max_cycle = 4,752,230
2024-01-21 00:23:40.217 | INFO| yamain.command.build:compile_npu_subgraph:1076 - QuantAxModel macs: 7,656,013,824
2024-01-21 00:23:40.218 | INFO| yamain.command.build:compile_npu_subgraph:1084 - use random data as gt input: data, uint8, (1, 3, 256, 456)
2024-01-21 00:23:42.095 | INFO| yamain.command.build:compile_ptq_model:1003 - fuse 1 subgraph(s)
虽然成功生成了,但是当我们把这个model放到板子上时,发现虽然能生成结果,但是无法生成和之前一样的结果,为了查找原因,笔者选择在电脑上先跑一下结果看一下:
我们执行如下的指令
python3
pose_detection.py --pre_processing --image_path sim_images/cxk.jpg
--axmodel_path models/compiled.axmodel --intermediate_path sim_inputs/0
post_detecion.py的pre_preocessing函数如下:
def pre_processing(args):
image_path = Path(args.image_path)
if not image_path.exists():
raise FileNotFoundError(f\"Not found image file at \'{image_path}\'\")
axmodel_path = Path(args.axmodel_path)
if not axmodel_path.exists():
raise FileNotFoundError(f\"Not found compiled axmodel at \'{axmodel_path}\'\")
pad_value=(0, 0, 0)
img = cv2.imread(str(image_path), cv2.IMREAD_COLOR)
height, width, _ = img.shape
net_input_size = np.array([256,456])
scale = min(net_input_size[0]/height,net_input_size[1]/width)
scaled_img = cv2.resize(img, (0, 0), fx=scale, fy=scale, interpolation=cv2.INTER_LINEAR)
min_dims = [net_input_size[0], net_input_size[1]]
padded_img, pad = pad_width(scaled_img, pad_value, min_dims)
input_names = get_input_info(axmodel_path)
if len(input_names) != 1:
raise NotImplementedError(f\"Currently only supports length 1, but got {input_names}\")
intermediate_path = Path(args.intermediate_path)
intermediate_path.mkdir(exist_ok=True, parents=True)
output_path = intermediate_path / f\"{sanitize(input_names[0])}.bin\"
output_path.write_bytes(padded_img.tobytes())
LOGGER.info(f\"Write [{input_names[0]}] to \'{output_path}\' successfully.\")
结果是:
[I]Write [data] to \'sim_inputs/0/data.bin\' successfully.
然后再执行如下指令:
pulsar2 run --model models/compiled.axmodel --input_dir sim_inputs --output_dir sim_outputs --list list.txt
可以看到输出的四个矩阵分别作为四个bin文件存储在output的文件夹里面,这里后处理可以接着修改post_detecion.py的post_preocessing函数,其实主要功能也就是读取bin文件的数据,笔者这里自己写了一个代码进行output:
`
from pathlib import Path
from typing import Dict, List, Tuple
import cv2
import numpy as np
import onnx
from pulsar2_run_helper.utils import get_tensor_value_info, sanitize
from torch import Tensor
def get_output_info(model_path: str):
\"\"\"
Returns the shape and tensor type of all outputs.
\"\"\"
model_obj = onnx.load(model_path)
model_graph = model_obj.graph
output_info = {}
for tensor_info in model_graph.output:
output_info.update({tensor_info.name: get_tensor_value_info(tensor_info)})
return output_info
output_info = get_output_info(\'pulsar2-run-helper\\\\\\\\\\\\\\\\models\\\\\\\\\\\\\\\\compiled.axmodel\')
output_data: Dict[str, np.ndarray] = {}
for k, v in output_info.items():
data_path = Path(f\"pulsar2-run-helper/sim_outputs/0/{sanitize(k)}.bin\")
if not data_path.exists():
raise FileNotFoundError(
f\"Could not find the expected key \'{k}\', please double check your pulsar run output directory.\",
)
data = data_path.read_bytes()
output_data[k] = np.frombuffer(data, dtype=v[\"tensor_type\"]).reshape(v[\"shape\"]).copy()
stage2_heatmaps = np.array(output_data[\'stage_1_output_1_heatmaps\'])
stage2_pafs = np.array(output_data[\'stage_1_output_0_pafs\'])
upsample_ratio = 4
heatmaps = np.transpose(stage2_heatmaps.squeeze().cpu().data.numpy(), (1, 2, 0))
heatmaps = cv2.resize(heatmaps, (0, 0), fx=upsample_ratio, fy=upsample_ratio, interpolation=cv2.INTER_CUBIC)
pafs = np.transpose(stage2_pafs.squeeze().cpu().data.numpy(), (1, 2, 0))
pafs = cv2.resize(pafs, (0, 0), fx=upsample_ratio, fy=upsample_ratio, interpolation=cv2.INTER_CUBIC)
作者把需要用到的两个输出,stage_1_output_0_pafs和stage_1_output_1_heatmaps,把本来的onnx模型运行结果,和这里axmodel运行结果进行对比,来比较下输出有什么不同。因为代码后续操作就是根据heatmaps使用nvs筛选关节点,再利用pafs进行配对,所以只要pafs和heatmaps输出一致,那么后续就应该没有问题,但是比较后发现差别还是挺大的:
原onnx的输出:
[[[-1.52968132e-054.39210387e-044.67733509e-04 ...1.89101411e-04
2.96855404e-049.99360263e-01]
[-2.03267518e-054.37346724e-044.73105902e-04 ...1.85395606e-04
2.94736557e-049.99273658e-01]
[-4.39256692e-054.27683495e-044.98184003e-04 ...1.69247418e-04
2.85554648e-049.98842716e-01]
...
[ 1.48566623e-056.13198732e-041.83847776e-04 ... -1.04615065e-04
8.77983111e-059.97699857e-01]
[ 2.27145174e-056.29885879e-042.23268275e-04 ... -8.43701637e-05
1.05829211e-049.97494936e-01]
[ 2.40934714e-056.33706921e-042.31671875e-04 ... -8.03298753e-05
1.09530112e-049.97451067e-01]]
[[-1.54063455e-054.41880926e-044.65514458e-04 ...1.83200624e-04
2.94604048e-049.99336004e-01]
[-2.04233584e-054.39986703e-044.70740197e-04 ...1.79557086e-04
2.92459794e-049.99250889e-01]
[-4.39912328e-054.30239772e-044.95205459e-04 ...1.63641351e-04
2.83144385e-049.98827040e-01]
...
[ 1.50406322e-056.05818816e-041.78435293e-04 ... -1.01274185e-04
8.60481086e-059.97708738e-01]
[ 2.18633922e-056.21417770e-042.17492474e-04 ... -8.17274704e-05
1.03465252e-049.97506917e-01]
[ 2.30273308e-056.25000568e-042.25825745e-04 ... -7.78278918e-05
1.07039421e-049.97463822e-01]]
[[-1.56860224e-054.57458053e-044.53745743e-04 ...1.56425449e-04
2.84618087e-049.99214590e-01]
[-2.06957775e-054.55345347e-044.58224851e-04 ...1.53032437e-04
2.82332650e-049.99136090e-01]
[-4.43626486e-054.44885925e-044.79539827e-04 ...1.38012576e-04
2.72285804e-049.98743176e-01]
...
[ 1.64749854e-055.74149657e-041.52241788e-04 ... -8.50291181e-05
7.78756585e-059.97737229e-01]
[ 1.84111286e-055.84639900e-041.89980768e-04 ... -6.85549094e-05
9.25216373e-059.97549713e-01]
[ 1.85594690e-055.87106333e-041.98066118e-04 ... -6.52728268e-05
9.55235009e-059.97509658e-01]]
...
[[ 1.60911601e-047.16453709e-041.12063228e-03 ...1.95305420e-05
2.95104983e-049.93238151e-01]
[ 1.55670175e-047.07174186e-041.10480236e-03 ...1.61740845e-05
2.93474703e-049.93373156e-01]
[ 1.32725501e-046.63083512e-041.03514048e-03 ...1.66020197e-06
2.86501076e-049.93972063e-01]
...
[-1.28306638e-043.12105549e-04 -1.59716117e-04 ...3.75063137e-05
3.05537433e-059.99176919e-01]
[-1.35803712e-043.12552031e-04 -1.45055514e-04 ...1.80985189e-05
3.50762166e-059.99347508e-01]
[-1.37439521e-043.12695687e-04 -1.42450066e-04 ...1.42308554e-05
3.60244339e-059.99381363e-01]]
[[ 2.10002720e-048.25065654e-041.23704271e-03 ...4.70072628e-05
3.27726681e-049.93095756e-01]
[ 2.03197109e-048.12154845e-041.21757423e-03 ...4.28054227e-05
3.24971421e-049.93218422e-01]
[ 1.73336040e-047.51663989e-041.13101187e-03 ...2.43920495e-05
3.12652002e-049.93755341e-01]
...
[-1.51157365e-042.43631308e-04 -1.81165044e-04 ...5.54859871e-05
2.44969706e-059.99618769e-01]
[-1.55363508e-042.51566060e-04 -1.59615520e-04 ...3.67974717e-05
3.21547923e-059.99756753e-01]
[-1.56319831e-042.53219740e-04 -1.55611167e-04 ...3.31435440e-05
3.37603196e-059.99783993e-01]]
[[ 2.20244023e-048.48500291e-041.26282580e-03 ...5.30014295e-05
3.34725075e-049.93052840e-01]
[ 2.13104737e-048.34821782e-041.24257524e-03 ...4.86175632e-05
3.31732503e-049.93173242e-01]
[ 1.81768570e-047.70858896e-041.15238572e-03 ...2.93666471e-05
3.18286417e-049.93698537e-01]
...
[-1.56104667e-042.29996047e-04 -1.85998841e-04 ...5.89654264e-05
2.30604492e-059.99710977e-01]
[-1.59631090e-042.39599918e-04 -1.62955912e-04 ...4.04056809e-05
3.13900709e-059.99843001e-01]
[-1.60447336e-042.41591828e-04 -1.58648603e-04 ...3.67916145e-05
3.31362789e-059.99869108e-01]]]
但是转换后axmodel的输出:
[[[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9400777e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9417037e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9492270e-01]
...
[ 0.0000000e+000.0000000e+004.6832468e-03 ...0.0000000e+00
0.0000000e+009.9824995e-01]
[ 0.0000000e+000.0000000e+004.7707101e-03 ...0.0000000e+00
0.0000000e+009.9908477e-01]
[ 0.0000000e+000.0000000e+004.7885687e-03 ...0.0000000e+00
0.0000000e+009.9926531e-01]]
[[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9400777e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9417037e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9492270e-01]
...
[ 0.0000000e+000.0000000e+004.5393431e-03 ...0.0000000e+00
0.0000000e+009.9826699e-01]
[ 0.0000000e+000.0000000e+004.5964858e-03 ...0.0000000e+00
0.0000000e+009.9907321e-01]
[ 0.0000000e+000.0000000e+004.6081534e-03 ...0.0000000e+00
0.0000000e+009.9924749e-01]]
[[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9395919e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9412346e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9488354e-01]
...
[ 0.0000000e+000.0000000e+003.8737776e-03 ...0.0000000e+00
0.0000000e+009.9838972e-01]
[ 0.0000000e+000.0000000e+003.7901415e-03 ...0.0000000e+00
0.0000000e+009.9906290e-01]
[ 0.0000000e+000.0000000e+003.7731556e-03 ...0.0000000e+00
0.0000000e+009.9920863e-01]]
...
[[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9916482e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9901927e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9830627e-01]
...
[ 0.0000000e+000.0000000e+00 -3.1781812e-05 ...0.0000000e+00
0.0000000e+009.9830580e-01]
[ 0.0000000e+000.0000000e+00 -7.8918245e-05 ...0.0000000e+00
0.0000000e+009.9901927e-01]
[ 0.0000000e+000.0000000e+00 -9.7570926e-05 ...0.0000000e+00
0.0000000e+009.9916482e-01]]
[[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9924749e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9907321e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9821997e-01]
...
[ 0.0000000e+000.0000000e+00 -7.8918245e-05 ...0.0000000e+00
0.0000000e+009.9821991e-01]
[ 0.0000000e+000.0000000e+002.1989405e-05 ...0.0000000e+00
0.0000000e+009.9907327e-01]
[ 0.0000000e+000.0000000e+003.3657252e-05 ...0.0000000e+00
0.0000000e+009.9924749e-01]]
[[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9926525e-01]
[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9908489e-01]
[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9820137e-01]
...
[ 0.0000000e+000.0000000e+00 -9.7570926e-05 ...0.0000000e+00
0.0000000e+009.9820119e-01]
[ 0.0000000e+000.0000000e+003.3657252e-05 ...0.0000000e+00
0.0000000e+009.9908483e-01]
[ 0.0000000e+000.0000000e+005.1516203e-05 ...0.0000000e+00
0.0000000e+009.9926525e-01]]]
发现差别还是很大的,这里一个简单的推测是由于量化时的离群值,数据分布差距比较大,导致较小的数据(~1e-4/1e-5)的数据量化时直接被忽略为0了,导致量化后出现很多的0值。
后续笔者尝试了一些方法,但都不太行:
1、直接用FP精度的模型,但是不知道为什么pulsar2好像无法用FP32/FP16精度的模型,必须要用量化量化为int精度的模型。
2、在官方文档里说明了可以Quantized ONNX 模型导入,但是这一块需要先进行QAT,作者对这一块可能不太会,也就放弃了。
作者把资料整理一下放在这里,有需要的朋友可以后续进行研究:
https://drive.google.com/file/d/1ON6SWXVpFrJBKrn9OXREepWn4q0raNbi/view?usp=drive_link
2024-01-22 10:46:20
本文将介绍如何使用爱芯派Pro开发板、USB摄像头和个人电脑搭建一个简单的视频直播系统。本文搭建的简单视频直播系统主要由三个软件构成,分别是运行在爱芯派Pro 开发板上的ffmpeg和nginix
2024-01-21 16:36:14
这里主要还是感觉上一期说的一些对于人体姿态估计模型的前期调研,进行后期的部署的一些尝试。下面主要针对尝试的集中模型进行分享。
1、movenet
首先还是上次提到的谷歌的轻量级人体姿态估计模型MoveNet,虽然没有论文,也没有官方代码,笔者这里主要根据开源的项目进行训练:
git clone https://github.com/fire717/movenet.pytorch.git
上次已经提到训练效果不太好,模型不仅只能对单人识别,并且效果远逊于openpose。后面又尝试炼了几次丹,发现对于关键点的捕捉还是不太准确。
后面发现movenet有谷歌官方开源的模型,movenet | Kaggle上面可以下载tflite格式的模型,并且是已经量化好为8bit的。兴冲冲的准备直接转为onnx格式,再把onnx模型转为axmodel模型,结果发现存在一个问题:tflite无法直接转化为onnx模型,存在一定麻烦。最明显的问题是数据布局问题—— TFLite 模型是 NHWC 格式,而 ONNX 是NCHW,因此内部的算子和张量等等分布都不一样。目前最常用的方式是用tflite2onnx这个库进行转换,但是有这个库目前还不支持的算子:NotImplementedError: Unsupported TFLite OP: 53 CAST!
因此对于movenet的尝试只能暂时告一段落。
2、openpose/lightweight openpose
在上次的分享里面提到了openpose,使用了下面的开源项目:
git clone https://github.com/Hzzone/pytorch-openpose.git
后面笔者又尝试了lightweight openpose的模型,发现同样有很好用的开源模型,并基于此的基础进行尝试:
git clone https://github.com/Daniil-Osokin/lightweight-human-pose-estimation.pytorch.git
简单介绍一下openpose和lightweight openpose模型的差别:
lightweight openpose模型主要是把openpose backbone网络的VGG网络换成了轻量级CNN网络中的mobilenet,此外还将openpose的两个branch合并成一个branch并且用带空洞卷积的block结构代替7*7卷积。lightweight openpose能大幅度减少模型参数,并且达到几乎一样的精度
对于lightweight openpose模型,在生成onnx时首先遇到的第一个问题就是模型输入的参数。这个模型可以接受不同的输入大小,也就是可以接受动态输入大小,若输入1_3_H_W的图片,会用到两个输出,一个是1_38_H/4_W/4的pafs图,一个是1_19_H/4_W/4的heatmaps图。我们知道onnx可以接受动态输入和静态输入,笔者也最开始使用如下代码导出动态的模型,然后试着转化为axmodel模型:
from models.with_mobilenet import PoseEstimationWithMobileNet
from modules.load_state import load_state
import torch
import argparse
def convert_to_onnx(net, output_name):
# 动态尺寸的输入
input = torch.randn(1, 3, 256, 456)
dynamic_axes = {
\'data\': {2: \'height\', 3: \'width\'},# 动态批处理大小和宽度
\'stage_0_output_1_heatmaps\': {2: \'height_out\', 3: \'width_out\'},
\'stage_0_output_0_pafs\': {2: \'height_out\', 3: \'width_out\'},
\'stage_1_output_1_heatmaps\': {2: \'height_out\', 3: \'width_out\'},
\'stage_1_output_0_pafs\': {2: \'height_out\', 3: \'width_out\'}
}
input_names = [\'data\']
output_names = [\'stage_0_output_1_heatmaps\', \'stage_0_output_0_pafs\',
\'stage_1_output_1_heatmaps\', \'stage_1_output_0_pafs\']
torch.onnx.export(net, input, output_name, verbose=True, input_names=input_names, output_names=output_names,
dynamic_axes=dynamic_axes,opset_version=15)
if __name__ == \'__main__\':
parser = argparse.ArgumentParser()
parser.add_argument(\'--checkpoint-path\', type=str, required=True, help=\'path to the checkpoint\')
parser.add_argument(\'--output-name\', type=str, default=\'human-pose-estimation.onnx\',
help=\'name of output model in ONNX format\')
args = parser.parse_args()
net = PoseEstimationWithMobileNet()
checkpoint = torch.load(args.checkpoint_path)
load_state(net, checkpoint)
convert_to_onnx(net, args.output_name)
但是在生成axmodel模型时,出现问题,发现模型不接受动态输入大小,看来必须要接受恒定大小的静态输入:
2024-01-20 21:52:00.043 | WARNING| yamain.command.build:fill_default:320 - ignore data csc config because of src_format is AutoColorSpace or src_format and tensor_format are the same
Traceback (most recent call last):
File \"<frozen yamain.common.error>\", line 11, in wrapper
File \"<frozen yamain.command.build>\", line 631, in optimize_onnx
File \"<frozen yamain.command.load_model>\", line 633, in optimize_onnx_model
File \"<frozen frontend.parsers.onnx_parser>\", line 71, in parse_onnx_model
File \"<frozen frontend.parsers.onnx_parser>\", line 122, in parse_onnx_model_proto
File \"<frozen frontend.parser_utils>\", line 34, in parse_value_info
File \"<frozen frontend.parser_utils>\", line 28, in check_value_info
AssertionError: illegal value_info data: [1, 3, \'height\', \'width\']
于是笔者选择使用1_3_256_456作为恒定的输入大小,这里需要对原项目的代码做一些改动,因为为了实现1_3_256_456的输入大小,我们先要一个scale对图片进行放缩,让图片的height达到256或者width达到456,再进行padding,在达不到的边pad(0,0,0),这里对源代码如下地方做了改动:
`
def infer_fast(net, img, net_input_size, stride, upsample_ratio, cpu,
pad_value=(0, 0, 0), img_mean=np.array([128, 128, 128], np.float32), img_scale=np.float32(1/256)):
height, width, _ = img.shape
scale = min(net_input_size[0]/height,net_input_size[1]/width)
scaled_img = cv2.resize(img, (0, 0), fx=scale, fy=scale, interpolation=cv2.INTER_LINEAR)
scaled_img = normalize(scaled_img, img_mean, img_scale)
min_dims = [net_input_size[0], net_input_size[1]]
padded_img, pad = pad_width(scaled_img, pad_value, min_dims)
tensor_img = torch.from_numpy(padded_img).permute(2, 0, 1).unsqueeze(0).float()
`
def pad_width(img, pad_value, min_dims):
h, w, _ = img.shape
pad = []
pad.append(int(math.floor((min_dims[0] - h) / 2.0)))
pad.append(int(math.floor((min_dims[1] - w) / 2.0)))
pad.append(int(min_dims[0] - h - pad[0]))
pad.append(int(min_dims[1] - w - pad[1]))
padded_img = cv2.copyMakeBorder(img, pad[0], pad[2], pad[1], pad[3],
cv2.BORDER_CONSTANT, value=pad_value)
return padded_img, pad
再通过如下代码,便可以导出来onnx模型,这里笔者用了simplifier库做了简化,发现差别并不大,因此就直接用onnx模型:
`
import argparse
import torch
from models.with_mobilenet import PoseEstimationWithMobileNet
from modules.load_state import load_state
def convert_to_onnx(net, output_name):
input = torch.randn(1, 3, 256, 456)
input_names = [\'data\']
output_names = [\'stage_0_output_1_heatmaps\', \'stage_0_output_0_pafs\',
\'stage_1_output_1_heatmaps\', \'stage_1_output_0_pafs\']
torch.onnx.export(net, input, output_name, verbose=True, input_names=input_names, output_names=output_names)
if __name__ == \'__main__\':
parser = argparse.ArgumentParser()
parser.add_argument(\'--checkpoint-path\', type=str, required=True, help=\'path to the checkpoint\')
parser.add_argument(\'--output-name\', type=str, default=\'human-pose-estimation.onnx\',
help=\'name of output model in ONNX format\')
args = parser.parse_args()
net = PoseEstimationWithMobileNet()
checkpoint = torch.load(args.checkpoint_path)
load_state(net, checkpoint)
convert_to_onnx(net, args.output_name)
导出来的模型后面就需要用pulsar2的工具进行转换为了,但是没有现成的校准数据集,因此只能用openpose训练时的coco数据集中选择图片作为数据集。先从COCO - Common Objects in Context (cocodataset.org)下载coco数据集,然后根据annotation选择类别为person的照片,我这里选择了30张比较完整身躯的人类图片,有一个人的也有多个人的,为了合适先把这些图片按照上述预处理先进行了处理,然后组成了calibration.tar:
之后我们按照之前的方法,先写个config.json,这里写的结果如下:
`
{
\"model_type\": \"ONNX\",
\"npu_mode\": \"NPU1\",
\"quant\": {
\"input_configs\": [
{
\"tensor_name\": \"data\",
\"calibration_dataset\": \"./dataset/calibration_data.tar\",
\"calibration_size\": 30,
\"calibration_mean\": [128,128,128],
\"calibration_std\": [256,256,256]
}
],
\"calibration_method\": \"MinMax\",
\"precision_analysis\": true,
\"precision_analysis_method\":\"EndToEnd\"
},
\"input_processors\": [
{
\"tensor_name\": \"data\",
\"tensor_format\": \"BGR\",
\"src_format\": \"BGR\",
\"src_dtype\": \"U8\",
}
],
\"output_processors\": [
{
\"tensor_name\": \"stage_0_output_1_heatmaps\",
},
{
\"tensor_name\": \"stage_0_output_0_pafs\",
},
{
\"tensor_name\": \"stage_1_output_1_heatmaps\",
},
{
\"tensor_name\": \"stage_1_output_0_pafs\",
}
],
\"compiler\": {
\"check\": 0
}
}
然后再按照如下的指令生成axmodel:
pulsar2 build --input model/human-pose-estimation.onnx --output_dir output --config config/lightweight_openpose_config.json
可以看一下结果:
2024-01-21 00:23:17.469 | WARNING| yamain.command.build:fill_default:320 - ignore data csc config because of src_format is AutoColorSpace or src_format and tensor_format are the same
Building onnx ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
2024-01-21 00:23:19.247 | INFO| yamain.command.build:build:444 - save optimized onnx to [output/frontend/optimized.onnx]
2024-01-21 00:23:19.254 | INFO| yamain.common.util:extract_archive:21 - extract [dataset/calibration_data.tar] to [output/quant/dataset/data]...
Quant Config Table
┏━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓
┃ Input ┃ Shape┃ Dataset Directory ┃ Data Format ┃ Tensor Format ┃ Mean┃ Std ┃
┡━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩
│ data│ [1, 3, 256, 456] │ data│ Image │ BGR│ [128.0, 128.0,│ [128.0, 128.0,│
│ ││ │ ││ 128.0] │ 128.0] │
└───────┴──────────────────┴───────────────────┴─────────────┴───────────────┴────────────────────┴────────────────────┘
Transformer optimize level: 0
30 File(s) Loaded.
[00:23:24] AX LSTM Operation Format Pass Running ...Finished.
[00:23:24] AX Set MixPrecision Pass Running ...Finished.
[00:23:24] AX Refine Operation Config Pass Running ... Finished.
[00:23:24] AX Reset Mul Config Pass Running ...Finished.
[00:23:24] AX Tanh Operation Format Pass Running ...Finished.
[00:23:24] AX Confused Op Refine Pass Running ...Finished.
[00:23:24] AX Quantization Fusion Pass Running ...Finished.
[00:23:24] AX Quantization Simplify Pass Running ...Finished.
[00:23:24] AX Parameter Quantization Pass Running ...Finished.
Calibration Progress(Phase 1): 100%|████████████████████████████████████████████████████| 30/30 [00:05<00:00,5.03it/s]
Finished.
[00:23:31] AX Passive Parameter Quantization Running ...Finished.
[00:23:31] AX Parameter Baking Pass Running ...Finished.
[00:23:31] AX Refine Int Parameter Pass Running ... Finished.
[00:23:31] AX Refine Weight Parameter Pass Running ... Finished.
--------- Network Snapshot ---------
Num of Op:[117]
Num of Quantized Op: [117]
Num of Variable:[226]
Num of Quantized Var:[226]
------- Quantization Snapshot ------
Num of Quant Config: [350]
BAKED:[54]
OVERLAPPED: [171]
ACTIVATED:[74]
PASSIVE_BAKED: [51]
Network Quantization Finished.
quant.axmodel export success: output/quant/quant_axmodel.onnx
===>export per layer debug_data(float data) to folder: output/quant/debug/float
Writing npy... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
===>export input/output data to folder: output/quant/debug/test_data_set_0
Building native ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
/usr/local/lib/python3.9/site-packages/scipy/spatial/distance.py:620: RuntimeWarning: invalid value encountered in float_scalars
dist = 1.0 - uv / np.sqrt(uu * vv)
Quant Precision Table【EndToEnd Reference】
┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓
┃ Operator ┃ Type┃ Output Tensor┃ Data Type ┃ Shape ┃ Cosin Distance┃
┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩
│ /model/model.0/mode… │ AxQuantizedConv│ /model/model.0/mode… │ FP32│ (1, 32, 128, 228) │ 0.9999498724937439 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.1/mode… │ AxQuantizedConv│ /model/model.1/mode… │ FP32│ (1, 32, 128, 228) │ 0.9999966621398926 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.1/mode… │ AxQuantizedConv│ /model/model.1/mode… │ FP32│ (1, 64, 128, 228) │ 0.9569284915924072 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.2/mode… │ AxQuantizedConv│ /model/model.2/mode… │ FP32│ (1, 64, 64, 114)│ 0.9795299172401428 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.2/mode… │ AxQuantizedConv│ /model/model.2/mode… │ FP32│ (1, 128, 64, 114) │ 0.9590328931808472 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.3/mode… │ AxQuantizedConv│ /model/model.3/mode… │ FP32│ (1, 128, 64, 114) │ 0.9650511741638184 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.3/mode… │ AxQuantizedConv│ /model/model.3/mode… │ FP32│ (1, 128, 64, 114) │ 0.9283918142318726 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.4/mode… │ AxQuantizedConv│ /model/model.4/mode… │ FP32│ (1, 128, 32, 57)│ 0.9993457794189453 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.4/mode… │ AxQuantizedConv│ /model/model.4/mode… │ FP32│ (1, 256, 32, 57)│ 0.9516056776046753 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.5/mode… │ AxQuantizedConv│ /model/model.5/mode… │ FP32│ (1, 256, 32, 57)│ 0.9471707940101624 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.5/mode… │ AxQuantizedConv│ /model/model.5/mode… │ FP32│ (1, 256, 32, 57)│ 0.9344280958175659 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.6/mode… │ AxQuantizedConv│ /model/model.6/mode… │ FP32│ (1, 256, 32, 57)│ 0.9990376830101013 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.6/mode… │ AxQuantizedConv│ /model/model.6/mode… │ FP32│ (1, 512, 32, 57)│ 0.9555284976959229 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.7/mode… │ AxQuantizedConv│ /model/model.7/mode… │ FP32│ (1, 512, 32, 57)│ 0.9700952768325806 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.7/mode… │ AxQuantizedConv│ /model/model.7/mode… │ FP32│ (1, 512, 32, 57)│ 0.9461223483085632 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.8/mode… │ AxQuantizedConv│ /model/model.8/mode… │ FP32│ (1, 512, 32, 57)│ 0.9950165748596191 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.8/mode… │ AxQuantizedConv│ /model/model.8/mode… │ FP32│ (1, 512, 32, 57)│ 0.9367055892944336 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.9/mode… │ AxQuantizedConv│ /model/model.9/mode… │ FP32│ (1, 512, 32, 57)│ 0.980312168598175│
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.9/mode… │ AxQuantizedConv│ /model/model.9/mode… │ FP32│ (1, 512, 32, 57)│ 0.9332307577133179 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.10/mod… │ AxQuantizedConv│ /model/model.10/mod… │ FP32│ (1, 512, 32, 57)│ 0.9839740991592407 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.10/mod… │ AxQuantizedConv│ /model/model.10/mod… │ FP32│ (1, 512, 32, 57)│ 0.9234165549278259 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.11/mod… │ AxQuantizedConv│ /model/model.11/mod… │ FP32│ (1, 512, 32, 57)│ 0.9968637824058533 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /model/model.11/mod… │ AxQuantizedConv│ /model/model.11/mod… │ FP32│ (1, 512, 32, 57)│ 0.8376449346542358 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/align/align.0/… │ AxQuantizedConv│ /cpm/align/align.0/… │ FP32│ (1, 128, 32, 57)│ 0.8724657297134399 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/trunk/trunk.0/… │ AxQuantizedConv│ /cpm/trunk/trunk.0/… │ FP32│ (1, 128, 32, 57)│ 0.8473747968673706 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/trunk/trunk.0/… │ AxQuantizedElu │ /cpm/trunk/trunk.0/… │ FP32│ (1, 128, 32, 57)│ 0.8474124670028687 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/trunk/trunk.0/… │ AxQuantizedConv│ /cpm/trunk/trunk.0/… │ FP32│ (1, 128, 32, 57)│ 1│
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/trunk/trunk.0/… │ AxQuantizedElu │ /cpm/trunk/trunk.0/… │ FP32│ (1, 128, 32, 57)│ 1│
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/trunk/trunk.1/… │ AxQuantizedConv│ /cpm/trunk/trunk.1/… │ FP32│ (1, 128, 32, 57)│ 1│
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/trunk/trunk.1/… │ AxQuantizedElu │ /cpm/trunk/trunk.1/… │ FP32│ (1, 128, 32, 57)│ 1│
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/trunk/trunk.1/… │ AxQuantizedConv│ /cpm/trunk/trunk.1/… │ FP32│ (1, 128, 32, 57)│ 1│
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/trunk/trunk.1/… │ AxQuantizedElu │ /cpm/trunk/trunk.1/… │ FP32│ (1, 128, 32, 57)│ 1│
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/trunk/trunk.2/… │ AxQuantizedConv│ /cpm/trunk/trunk.2/… │ FP32│ (1, 128, 32, 57)│ 1│
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/trunk/trunk.2/… │ AxQuantizedElu │ /cpm/trunk/trunk.2/… │ FP32│ (1, 128, 32, 57)│ 1│
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/trunk/trunk.2/… │ AxQuantizedConv│ /cpm/trunk/trunk.2/… │ FP32│ (1, 128, 32, 57)│ 1│
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/trunk/trunk.2/… │ AxQuantizedElu │ /cpm/trunk/trunk.2/… │ FP32│ (1, 128, 32, 57)│ 1│
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/Add │ AxQuantizedAdd │ /cpm/Add_output_0 │ FP32│ (1, 128, 32, 57)│ 0.8724657297134399 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /cpm/conv/conv.0/Co… │ AxQuantizedConv│ /cpm/conv/conv.0/Co… │ FP32│ (1, 128, 32, 57)│ 0.8918052315711975 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /initial_stage/trun… │ AxQuantizedConv│ /initial_stage/trun… │ FP32│ (1, 128, 32, 57)│ 0.8402097225189209 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /initial_stage/trun… │ AxQuantizedConv│ /initial_stage/trun… │ FP32│ (1, 128, 32, 57)│ 0.8613110780715942 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /initial_stage/trun… │ AxQuantizedConv│ /initial_stage/trun… │ FP32│ (1, 128, 32, 57)│ 0.9579821228981018 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /initial_stage/heat… │ AxQuantizedConv│ /initial_stage/heat… │ FP32│ (1, 512, 32, 57)│ 0.9848107099533081 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /initial_stage/heat… │ AxQuantizedConv│ stage_0_output_1_he… │ FP32│ (1, 19, 32, 57)│ 0.9975641965866089 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /initial_stage/pafs… │ AxQuantizedConv│ /initial_stage/pafs… │ FP32│ (1, 512, 32, 57)│ 0.9518132209777832 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /initial_stage/pafs… │ AxQuantizedConv│ stage_0_output_0_pa… │ FP32│ (1, 38, 32, 57)│ 0.8149770498275757 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /Concat│ AxQuantizedConcat │ /Concat_output_0│ FP32│ (1, 185, 32, 57)│ 0.9521203637123108 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.9810469150543213 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.8794618248939514 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.8622568845748901 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedAdd │ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.9406654238700867 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.9486998915672302 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.8384159207344055 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.8343141674995422 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedAdd │ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.9094435572624207 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.9495641589164734 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.8413822054862976 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.8205063939094543 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedAdd │ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.8983384370803833 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.9512494206428528 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.8841379880905151 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.8782262802124023 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedAdd │ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.9195815920829773 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.9604532122612 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.9433491230010986 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.8501253128051758 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedAdd │ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.891287624835968│
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.9831163287162781 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ /refinement_stages.… │ FP32│ (1, 128, 32, 57)│ 0.9556347131729126 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ stage_1_output_1_he… │ FP32│ (1, 19, 32, 57)│ 0.9990192651748657 │
├──────────────────────┼───────────────────┼──────────────────────┼───────────┼───────────────────┼────────────────────┤
│ /refinement_stages.… │ AxQuantizedConv│ stage_1_output_0_pa… │ FP32│ (1, 38, 32, 57)│ 0.9560971260070801 │
└──────────────────────┴───────────────────┴──────────────────────┴───────────┴───────────────────┴────────────────────┘
Building native ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
2024-01-21 00:23:37.654 | WARNING| yamain.command.load_model:pre_process:454 - preprocess tensor [data]
2024-01-21 00:23:37.654 | INFO| yamain.command.load_model:pre_process:456 - tensor: data, (1, 3, 256, 456), U8
2024-01-21 00:23:37.654 | INFO| yamain.command.load_model:pre_process:456 - op: op:pre_dequant_1, AxDequantizeLinear, {\'const_inputs\': {\'x_zeropoint\': array(0, dtype=int32), \'x_scale\': array(1., dtype=float32)}, \'output_dtype\': <class \'numpy.float32\'>, \'quant_method\': 0}
2024-01-21 00:23:37.654 | INFO| yamain.command.load_model:pre_process:456 - tensor: tensor:pre_norm_1, (1, 3, 256, 456), FP32
2024-01-21 00:23:37.654 | INFO| yamain.command.load_model:pre_process:456 - op: op:pre_norm_1, AxNormalize, {\'dim\': 1, \'mean\': [128.0, 128.0, 128.0], \'std\': [128.0, 128.0, 128.0]}
2024-01-21 00:23:37.654 | WARNING| yamain.command.load_model:post_process:475 - postprocess tensor [stage_0_output_1_heatmaps]
2024-01-21 00:23:37.654 | WARNING| yamain.command.load_model:post_process:475 - postprocess tensor [stage_0_output_0_pafs]
2024-01-21 00:23:37.654 | WARNING| yamain.command.load_model:post_process:475 - postprocess tensor [stage_1_output_1_heatmaps]
2024-01-21 00:23:37.654 | WARNING| yamain.command.load_model:post_process:475 - postprocess tensor [stage_1_output_0_pafs]
tiling op...━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 92/92 0:00:00
new_ddr_tensor = []
build op...━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 389/389 0:00:00
add ddr swap...━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 738/738 0:00:00
calc input dependencies...━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1039/1039 0:00:00
calc output dependencies...━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1039/1039 0:00:00
assign eu heuristic━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1039/1039 0:00:00
assign eu onepass━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1039/1039 0:00:00
assign eu greedy━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1039/1039 0:00:00
2024-01-21 00:23:39.899 | INFO| yasched.test_onepass:results2model:2004 - max_cycle = 4,752,230
2024-01-21 00:23:40.217 | INFO| yamain.command.build:compile_npu_subgraph:1076 - QuantAxModel macs: 7,656,013,824
2024-01-21 00:23:40.218 | INFO| yamain.command.build:compile_npu_subgraph:1084 - use random data as gt input: data, uint8, (1, 3, 256, 456)
2024-01-21 00:23:42.095 | INFO| yamain.command.build:compile_ptq_model:1003 - fuse 1 subgraph(s)
虽然成功生成了,但是当我们把这个model放到板子上时,发现虽然能生成结果,但是无法生成和之前一样的结果,为了查找原因,笔者选择在电脑上先跑一下结果看一下:
我们执行如下的指令
python3
pose_detection.py --pre_processing --image_path sim_images/cxk.jpg
--axmodel_path models/compiled.axmodel --intermediate_path sim_inputs/0
post_detecion.py的pre_preocessing函数如下:
def pre_processing(args):
image_path = Path(args.image_path)
if not image_path.exists():
raise FileNotFoundError(f\"Not found image file at \'{image_path}\'\")
axmodel_path = Path(args.axmodel_path)
if not axmodel_path.exists():
raise FileNotFoundError(f\"Not found compiled axmodel at \'{axmodel_path}\'\")
pad_value=(0, 0, 0)
img = cv2.imread(str(image_path), cv2.IMREAD_COLOR)
height, width, _ = img.shape
net_input_size = np.array([256,456])
scale = min(net_input_size[0]/height,net_input_size[1]/width)
scaled_img = cv2.resize(img, (0, 0), fx=scale, fy=scale, interpolation=cv2.INTER_LINEAR)
min_dims = [net_input_size[0], net_input_size[1]]
padded_img, pad = pad_width(scaled_img, pad_value, min_dims)
input_names = get_input_info(axmodel_path)
if len(input_names) != 1:
raise NotImplementedError(f\"Currently only supports length 1, but got {input_names}\")
intermediate_path = Path(args.intermediate_path)
intermediate_path.mkdir(exist_ok=True, parents=True)
output_path = intermediate_path / f\"{sanitize(input_names[0])}.bin\"
output_path.write_bytes(padded_img.tobytes())
LOGGER.info(f\"Write [{input_names[0]}] to \'{output_path}\' successfully.\")
结果是:
[I]Write [data] to \'sim_inputs/0/data.bin\' successfully.
然后再执行如下指令:
pulsar2 run --model models/compiled.axmodel --input_dir sim_inputs --output_dir sim_outputs --list list.txt
可以看到输出的四个矩阵分别作为四个bin文件存储在output的文件夹里面,这里后处理可以接着修改post_detecion.py的post_preocessing函数,其实主要功能也就是读取bin文件的数据,笔者这里自己写了一个代码进行output:
`
from pathlib import Path
from typing import Dict, List, Tuple
import cv2
import numpy as np
import onnx
from pulsar2_run_helper.utils import get_tensor_value_info, sanitize
from torch import Tensor
def get_output_info(model_path: str):
\"\"\"
Returns the shape and tensor type of all outputs.
\"\"\"
model_obj = onnx.load(model_path)
model_graph = model_obj.graph
output_info = {}
for tensor_info in model_graph.output:
output_info.update({tensor_info.name: get_tensor_value_info(tensor_info)})
return output_info
output_info = get_output_info(\'pulsar2-run-helper\\\\\\\\\\\\\\\\models\\\\\\\\\\\\\\\\compiled.axmodel\')
output_data: Dict[str, np.ndarray] = {}
for k, v in output_info.items():
data_path = Path(f\"pulsar2-run-helper/sim_outputs/0/{sanitize(k)}.bin\")
if not data_path.exists():
raise FileNotFoundError(
f\"Could not find the expected key \'{k}\', please double check your pulsar run output directory.\",
)
data = data_path.read_bytes()
output_data[k] = np.frombuffer(data, dtype=v[\"tensor_type\"]).reshape(v[\"shape\"]).copy()
stage2_heatmaps = np.array(output_data[\'stage_1_output_1_heatmaps\'])
stage2_pafs = np.array(output_data[\'stage_1_output_0_pafs\'])
upsample_ratio = 4
heatmaps = np.transpose(stage2_heatmaps.squeeze().cpu().data.numpy(), (1, 2, 0))
heatmaps = cv2.resize(heatmaps, (0, 0), fx=upsample_ratio, fy=upsample_ratio, interpolation=cv2.INTER_CUBIC)
pafs = np.transpose(stage2_pafs.squeeze().cpu().data.numpy(), (1, 2, 0))
pafs = cv2.resize(pafs, (0, 0), fx=upsample_ratio, fy=upsample_ratio, interpolation=cv2.INTER_CUBIC)
作者把需要用到的两个输出,stage_1_output_0_pafs和stage_1_output_1_heatmaps,把本来的onnx模型运行结果,和这里axmodel运行结果进行对比,来比较下输出有什么不同。因为代码后续操作就是根据heatmaps使用nvs筛选关节点,再利用pafs进行配对,所以只要pafs和heatmaps输出一致,那么后续就应该没有问题,但是比较后发现差别还是挺大的:
原onnx的输出:
[[[-1.52968132e-054.39210387e-044.67733509e-04 ...1.89101411e-04
2.96855404e-049.99360263e-01]
[-2.03267518e-054.37346724e-044.73105902e-04 ...1.85395606e-04
2.94736557e-049.99273658e-01]
[-4.39256692e-054.27683495e-044.98184003e-04 ...1.69247418e-04
2.85554648e-049.98842716e-01]
...
[ 1.48566623e-056.13198732e-041.83847776e-04 ... -1.04615065e-04
8.77983111e-059.97699857e-01]
[ 2.27145174e-056.29885879e-042.23268275e-04 ... -8.43701637e-05
1.05829211e-049.97494936e-01]
[ 2.40934714e-056.33706921e-042.31671875e-04 ... -8.03298753e-05
1.09530112e-049.97451067e-01]]
[[-1.54063455e-054.41880926e-044.65514458e-04 ...1.83200624e-04
2.94604048e-049.99336004e-01]
[-2.04233584e-054.39986703e-044.70740197e-04 ...1.79557086e-04
2.92459794e-049.99250889e-01]
[-4.39912328e-054.30239772e-044.95205459e-04 ...1.63641351e-04
2.83144385e-049.98827040e-01]
...
[ 1.50406322e-056.05818816e-041.78435293e-04 ... -1.01274185e-04
8.60481086e-059.97708738e-01]
[ 2.18633922e-056.21417770e-042.17492474e-04 ... -8.17274704e-05
1.03465252e-049.97506917e-01]
[ 2.30273308e-056.25000568e-042.25825745e-04 ... -7.78278918e-05
1.07039421e-049.97463822e-01]]
[[-1.56860224e-054.57458053e-044.53745743e-04 ...1.56425449e-04
2.84618087e-049.99214590e-01]
[-2.06957775e-054.55345347e-044.58224851e-04 ...1.53032437e-04
2.82332650e-049.99136090e-01]
[-4.43626486e-054.44885925e-044.79539827e-04 ...1.38012576e-04
2.72285804e-049.98743176e-01]
...
[ 1.64749854e-055.74149657e-041.52241788e-04 ... -8.50291181e-05
7.78756585e-059.97737229e-01]
[ 1.84111286e-055.84639900e-041.89980768e-04 ... -6.85549094e-05
9.25216373e-059.97549713e-01]
[ 1.85594690e-055.87106333e-041.98066118e-04 ... -6.52728268e-05
9.55235009e-059.97509658e-01]]
...
[[ 1.60911601e-047.16453709e-041.12063228e-03 ...1.95305420e-05
2.95104983e-049.93238151e-01]
[ 1.55670175e-047.07174186e-041.10480236e-03 ...1.61740845e-05
2.93474703e-049.93373156e-01]
[ 1.32725501e-046.63083512e-041.03514048e-03 ...1.66020197e-06
2.86501076e-049.93972063e-01]
...
[-1.28306638e-043.12105549e-04 -1.59716117e-04 ...3.75063137e-05
3.05537433e-059.99176919e-01]
[-1.35803712e-043.12552031e-04 -1.45055514e-04 ...1.80985189e-05
3.50762166e-059.99347508e-01]
[-1.37439521e-043.12695687e-04 -1.42450066e-04 ...1.42308554e-05
3.60244339e-059.99381363e-01]]
[[ 2.10002720e-048.25065654e-041.23704271e-03 ...4.70072628e-05
3.27726681e-049.93095756e-01]
[ 2.03197109e-048.12154845e-041.21757423e-03 ...4.28054227e-05
3.24971421e-049.93218422e-01]
[ 1.73336040e-047.51663989e-041.13101187e-03 ...2.43920495e-05
3.12652002e-049.93755341e-01]
...
[-1.51157365e-042.43631308e-04 -1.81165044e-04 ...5.54859871e-05
2.44969706e-059.99618769e-01]
[-1.55363508e-042.51566060e-04 -1.59615520e-04 ...3.67974717e-05
3.21547923e-059.99756753e-01]
[-1.56319831e-042.53219740e-04 -1.55611167e-04 ...3.31435440e-05
3.37603196e-059.99783993e-01]]
[[ 2.20244023e-048.48500291e-041.26282580e-03 ...5.30014295e-05
3.34725075e-049.93052840e-01]
[ 2.13104737e-048.34821782e-041.24257524e-03 ...4.86175632e-05
3.31732503e-049.93173242e-01]
[ 1.81768570e-047.70858896e-041.15238572e-03 ...2.93666471e-05
3.18286417e-049.93698537e-01]
...
[-1.56104667e-042.29996047e-04 -1.85998841e-04 ...5.89654264e-05
2.30604492e-059.99710977e-01]
[-1.59631090e-042.39599918e-04 -1.62955912e-04 ...4.04056809e-05
3.13900709e-059.99843001e-01]
[-1.60447336e-042.41591828e-04 -1.58648603e-04 ...3.67916145e-05
3.31362789e-059.99869108e-01]]]
但是转换后axmodel的输出:
[[[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9400777e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9417037e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9492270e-01]
...
[ 0.0000000e+000.0000000e+004.6832468e-03 ...0.0000000e+00
0.0000000e+009.9824995e-01]
[ 0.0000000e+000.0000000e+004.7707101e-03 ...0.0000000e+00
0.0000000e+009.9908477e-01]
[ 0.0000000e+000.0000000e+004.7885687e-03 ...0.0000000e+00
0.0000000e+009.9926531e-01]]
[[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9400777e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9417037e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9492270e-01]
...
[ 0.0000000e+000.0000000e+004.5393431e-03 ...0.0000000e+00
0.0000000e+009.9826699e-01]
[ 0.0000000e+000.0000000e+004.5964858e-03 ...0.0000000e+00
0.0000000e+009.9907321e-01]
[ 0.0000000e+000.0000000e+004.6081534e-03 ...0.0000000e+00
0.0000000e+009.9924749e-01]]
[[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9395919e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9412346e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9488354e-01]
...
[ 0.0000000e+000.0000000e+003.8737776e-03 ...0.0000000e+00
0.0000000e+009.9838972e-01]
[ 0.0000000e+000.0000000e+003.7901415e-03 ...0.0000000e+00
0.0000000e+009.9906290e-01]
[ 0.0000000e+000.0000000e+003.7731556e-03 ...0.0000000e+00
0.0000000e+009.9920863e-01]]
...
[[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9916482e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9901927e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9830627e-01]
...
[ 0.0000000e+000.0000000e+00 -3.1781812e-05 ...0.0000000e+00
0.0000000e+009.9830580e-01]
[ 0.0000000e+000.0000000e+00 -7.8918245e-05 ...0.0000000e+00
0.0000000e+009.9901927e-01]
[ 0.0000000e+000.0000000e+00 -9.7570926e-05 ...0.0000000e+00
0.0000000e+009.9916482e-01]]
[[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9924749e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9907321e-01]
[ 0.0000000e+000.0000000e+004.2681405e-03 ...0.0000000e+00
0.0000000e+009.9821997e-01]
...
[ 0.0000000e+000.0000000e+00 -7.8918245e-05 ...0.0000000e+00
0.0000000e+009.9821991e-01]
[ 0.0000000e+000.0000000e+002.1989405e-05 ...0.0000000e+00
0.0000000e+009.9907327e-01]
[ 0.0000000e+000.0000000e+003.3657252e-05 ...0.0000000e+00
0.0000000e+009.9924749e-01]]
[[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9926525e-01]
[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9908489e-01]
[ 0.0000000e+000.0000000e+004.2681401e-03 ...0.0000000e+00
0.0000000e+009.9820137e-01]
...
[ 0.0000000e+000.0000000e+00 -9.7570926e-05 ...0.0000000e+00
0.0000000e+009.9820119e-01]
[ 0.0000000e+000.0000000e+003.3657252e-05 ...0.0000000e+00
0.0000000e+009.9908483e-01]
[ 0.0000000e+000.0000000e+005.1516203e-05 ...0.0000000e+00
0.0000000e+009.9926525e-01]]]
发现差别还是很大的,这里一个简单的推测是由于量化时的离群值,数据分布差距比较大,导致较小的数据(~1e-4/1e-5)的数据量化时直接被忽略为0了,导致量化后出现很多的0值。
后续笔者尝试了一些方法,但都不太行:
1、直接用FP精度的模型,但是不知道为什么pulsar2好像无法用FP32/FP16精度的模型,必须要用量化量化为int精度的模型。
2、在官方文档里说明了可以Quantized ONNX 模型导入,但是这一块需要先进行QAT,作者对这一块可能不太会,也就放弃了。
作者把资料整理一下放在这里,有需要的朋友可以后续进行研究:
https://drive.google.com/file/d/1ON6SWXVpFrJBKrn9OXREepWn4q0raNbi/view?usp=drive_link
2024-01-21 00:49:11
一、基础知识
YOLOv8-pose关键点模型输出,每个框输出51个信息,即17个关键点以及每个关键点的得分。
17个关节点分别是:“nose”,“left_eye”, “right_eye”,“left_ear”, “right_ear”,“left_shoulder”, “right_shoulder”,“left_elbow”, “right_elbow”,“left_wrist”, “right_wrist”,“left_hip”, “right_hip”,“left_knee”, “right_knee”,“left_ankle”, “right_ankle”。
通过判断关键点不同的角度,可以实现各种姿势检测,如弯腰、躺卧……
二、准备
原本计划自己搭建环境,单独训练模型,然后评估模型…后来发现时间不够了,加上最近训练的机器申请不到。直接拉取ax-samples及其子模块吧。考虑到AX650N强大的能力,直接在板上编译。
首先,git clone https://github.com/AXERA-TECH/ax-samples.git下载源码到本地。
然后,指定芯片为AX650,cmake生成makefile。
cd ax-samples
mkdir build && cd build
cmake -DBSP_MSP_DIR=/soc/ -DAXERA_TARGET_CHIP=ax650 ..
之后,便是make -j6 和make install,可以看到生成的可执行示例存放在build/install/ax650/ 路径下。
因为要做坐姿检测,所以选择了ax_yolov8_pose。
可以看看源码,注释的非常清晰:
// 1. init engine
#ifdef AXERA_TARGET_CHIP_AX620E
auto ret = AX_ENGINE_Init();
#else
AX_ENGINE_NPU_ATTR_T npu_attr;
memset(&npu_attr, 0, sizeof(npu_attr));
npu_attr.eHardMode = AX_ENGINE_VIRTUAL_NPU_DISABLE;
auto ret = AX_ENGINE_Init(&npu_attr);
#endif
if (0 != ret)
{
return ret;
}
// 2. load model
std::vector<char> model_buffer;
if (!utilities::read_file(model, model_buffer))
{
fprintf(stderr, \"Read Run-Joint model(%s) file failed.\\\\n\", model.c_str());
return false;
}
// 3. create handle
AX_ENGINE_HANDLE handle;
ret = AX_ENGINE_CreateHandle(&handle, model_buffer.data(), model_buffer.size());
SAMPLE_AX_ENGINE_DEAL_HANDLE
fprintf(stdout, \"Engine creating handle is done.\\\\n\");
// 4. create context
ret = AX_ENGINE_CreateContext(handle);
SAMPLE_AX_ENGINE_DEAL_HANDLE
fprintf(stdout, \"Engine creating context is done.\\\\n\");
// 5. set io
AX_ENGINE_IO_INFO_T* io_info;
ret = AX_ENGINE_GetIOInfo(handle, &io_info);
SAMPLE_AX_ENGINE_DEAL_HANDLE
fprintf(stdout, \"Engine get io info is done. \\\\n\");
// 6. alloc io
AX_ENGINE_IO_T io_data;
ret = middleware::prepare_io(io_info, &io_data, std::make_pair(AX_ENGINE_ABST_DEFAULT, AX_ENGINE_ABST_CACHED));
SAMPLE_AX_ENGINE_DEAL_HANDLE
fprintf(stdout, \"Engine alloc io is done. \\\\n\");
// 7. insert input
ret = middleware::push_input(data, &io_data, io_info);
SAMPLE_AX_ENGINE_DEAL_HANDLE_IO
fprintf(stdout, \"Engine push input is done. \\\\n\");
fprintf(stdout, \"--------------------------------------\\\\n\");
// 8. warn up
for (int i = 0; i < 5; ++i)
{
AX_ENGINE_RunSync(handle, &io_data);
}
// 9. run model
std::vector<float> time_costs(repeat, 0);
for (int i = 0; i < repeat; ++i)
{
timer tick;
ret = AX_ENGINE_RunSync(handle, &io_data);
time_costs[i] = tick.cost();
SAMPLE_AX_ENGINE_DEAL_HANDLE_IO
}
// 10. get result
post_process(io_info, &io_data, mat, input_w, input_h, time_costs);
fprintf(stdout, \"--------------------------------------\\\\n\");
middleware::free_io(&io_data);
return AX_ENGINE_DestroyHandle(handle);
最后,在官方的ModelZoo上下载已经准备好的YOLOV8S-POSE模型并cp到开发板,URL为https://pan.baidu.com/s/1CCu-oKw8jUEg2s3PEhTa4g?pwd=xq9f。
三、运行
测试图片如下,包括典型的坐姿:
运行
root@maixbox:~/ax-samples/build/install/ax650# ./ax_yolov8_pose -m /root/yolov8s-pose.axmodel -i /root/SIT.png
--------------------------------------
model file : /root/yolov8s-pose.axmodel
image file : /root/SIT.png
img_h, img_w : 640 640
--------------------------------------
Engine creating handle is done.
Engine creating context is done.
Engine get io info is done.
Engine alloc io is done.
Engine push input is done.
--------------------------------------
post process cost time:0.25 ms
--------------------------------------
Repeat 1 times, avg time 12.81 ms, max_time 12.81 ms, min_time 12.81 ms
--------------------------------------
detection num: 2
0:94%, [48,27,232,442], person
0:93%, [ 298,33,440,439], person
--------------------------------------
输出的结果如下,pose识别效果不错:
2024-01-16 22:41:10
本文主要在爱芯派 Pro上连接USB摄像头,为下一篇的人体姿势AI识别提供图像。
一、软件环境
(一)操作系统
官方的Debian镜像,也没用去update了,反正也挺
2024-01-15 22:09:32
技术的可用性。 基于基于爱芯派 Pro 开发板的警用安防智能机器人设计:
AX650N是一款兼具高算力与高能效比的SoC芯片,集成了八核A55 CPU,43.2TOPs@INT4或
2024-01-14 21:56:23
的API编程也是需要一定的步骤的,但是爱芯派的SDK已经集成了这部分的内容,我看了一下是因为DEMO的需要,不管怎样,对于我们的下一步开始是件非常不错的事情。
在x86上搭建FFMPEG编程环境
其实
2024-01-09 13:07:38
新通信技术比如像第三代移动通信(IMT2000),微波数字通信,高速多通信和卫星通信服务等都需要在频谱、调制上提高能力,而且新的功能必须比以前更快地推广。R3264/3267/3273正是为了这些需要而设计的高性能频谱分析仪。R3264/3267/3273 在2GHz的带宽下,特性有频宽精度±0.2%(typ.)和动态范围-145dBc/Hz(typ.),能
2024-01-04 09:31:01
感谢电子发烧友和爱芯元智公司提供的测试机会。
前面介绍了分割图像的SAM框架在爱芯派 Pro (AXera-Pi Pro)开发板的测试结果,今天来展示一下对SAM程序的修改,使它成为一个交互式抠图
2024-01-02 22:04:55
coremark源码下载到爱芯派Pro上:
git clone https://github.com/eembc/coremark.git
下载完成后,可以看到如下文件:
三、CoreMark性能测试
3.1
2024-01-01 16:36:48
在经过之前对于开发板的使用,以及通过几个爱芯派官方给出的示例demo(mobilenet/yolov5)在开发板上的部署之后,笔者也逐渐了解了爱芯派这块开发板利用其官方的推理引擎和pipeline
2024-01-01 01:04:09
感谢电子发烧友和爱芯元智公司提供的测试机会。
前面在爱芯派 Pro
(AXera-Pi Pro)部署了SAM,本来想继续研究一下如何修改SAM,结果第二天无法开机了,整体黑屏,只在屏幕下方有点杂乱
2023-12-28 19:33:14
完毕就可以了,然后打开AXP下载软件,爱芯官方的文档说的非常详细,如果想要深入理解我们可以进行详细阅读。
但是在本次使用当中,我们只需要先点击第一个按钮加载AXP文件,选择完毕后,加载的时间会很长,因为
2023-12-27 15:04:11
感谢电子发烧友和爱芯元智公司提供的测试机会。
自从开箱爱芯派 Pro (AXera-Pi Pro)开发板之后一直没有更新,电子发烧友的小姐姐已经来催更了。一个是年底的事情确实多,单位的各部门都在冲刺
2023-12-26 11:22:49
爱芯派pro通过无线网卡rtl8188eu连接热点
爱芯派pro目前的底板的pcie的复位有问题,所以pcie接口无法挂载上去,所以自己购买的rtl8822网卡也用不了,然后想起来自己还有正点原子
2023-12-20 18:02:13
ax650使用ax-pipeline进行推理
搭建交叉编译环境
拉取ax-pipeline源码及子模块
git clone --recursive https://github.com/AXERA-TECH/ax-pipeline.git
下载sdk
cd ax-pipeline
./download_ax_bsp.sh ax650
cd ax650n_bsp_sdk
wget https://github.com/ZHEQIUSHUI/assets/releases/download/ax650/drm.zip
mkdir third-party
unzip drm.zip -d third-party
cd ..
下载opencv
mkdir 3rdparty
cd 3rdparty
wget https://github.com/ZHEQIUSHUI/assets/releases/download/ax650/libopencv-4.5.5-aarch64.zip
unzip libopencv-4.5.5-aarch64.zip
编译环境
wget https://developer.arm.com/-/media/Files/downloads/gnu-a/9.2-2019.12/binrel/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz
tar -xvf gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz
export PATH=$PATH:$PWD/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin/
源码编译
cd ax-pipeline
mkdir build
cd build
cmake -DAXERA_TARGET_CHIP=AX650 -DBSP_MSP_DIR=$PWD/../ax650n_bsp_sdk/msp/out -DOpenCV_DIR=$PWD/../3rdparty/libopencv-4.5.5-aarch64/lib/cmake/opencv4 -DSIPY_BUILD=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../toolchains/aarch64-none-linux-gnu.toolchain.cmake -DCMAKE_INSTALL_PREFIX=install ..
make -j12
make install
获得bin文件到开发板上
bin
├── config
│├── custom_model.json
│├── dinov2.json
│├── dinov2_depth.json
│├── glpdepth.json
│├── ppyoloe.json
│├── scrfd.json
│├── scrfd_recognition.json
│├── yolo_nas.json
│├── yolov5_seg.json
│├── yolov5s.json
│├── yolov5s_face.json
│├── yolov5s_face_recognition.json
│├── yolov6.json
│├── yolov7.json
│├── yolov7_face.json
│├── yolov8.json
│├── yolov8_pose.json
│└── yolox.json
├── sample_demux_ivps_npu_hdmi_vo
├── sample_demux_ivps_npu_rtsp
├── sample_demux_ivps_npu_rtsp_hdmi_vo
├── sample_multi_demux_ivps_npu_hdmi_vo
├── sample_multi_demux_ivps_npu_multi_rtsp
├── sample_multi_demux_ivps_npu_multi_rtsp_hdmi_vo
├── sample_vin_ivps_npu_hdmi_vo
└── sample_vin_ivps_npu_venc_rtsp
开发板运行
修改yolov5s.json文件
博主的
{
\"MODEL_TYPE\": \"MT_DET_YOLOV5\",
\"MODEL_PATH\": \"/root/Desktop/install/bin/config/models/yolov5s_hat.axmodel\",
\"TRACK_ENABLE\": true,
\"STRIDES\": [8, 16, 32],
\"ANCHORS\": [
10.0,
13.0,
16.0,
30.0,
33.0,
23.0,
30.0,
61.0,
62.0,
45.0,
59.0,
119.0,
116.0,
90.0,
156.0,
198.0,
373.0,
326.0
],
\"CLASS_NAMES\": [
\"hat\",
\"person\"
],
\"CLASS_NUM\": 2,
\"NMS_THRESHOLD\": 0.44999998807907104,
\"PROB_THRESHOLD\": 0.4000000059604645
}
需要修改标签和标签数量,anchors可以不改,如果你是官方模型pt训练的,然后修改一下模型路径
要显示推理到hdmi上,先杀掉 fb_vo 这个进程
ps aux|grep fb_vo
找到进程号后kill -9 实际pid杀掉即可
将hdmi插入hdmi0(远离网口的那个)
如果需要恢复运行/root/runVoHook.sh即可恢复原样
本例子使用读取视频MP4,调用npu推理到hdmi显示
./sample_demux_ivps_npu_hdmi_vo -p config/yolov5s.json -f test.mp4
开发板运行显示hdmi上的视频:
2023-12-19 17:36:29
解释器Code Interpreter
爱芯派自带32G eMMC 5.1内存,八核心的处理器 ,内置 AI 算力 43.2TOPS@INT4 或 10.8TOPS@INT8可以加载下
2023-12-17 22:54:49
现在来了解一下视频输入输出的相关API,如何通过rust接Axera的视频输入输出,以便将内容读取,然后给模型做分析,后面做结果输出。
主要API以及调用流程,请参考文档《24 - AX VO API 文档.pdf》和《16 - AX VIN 开发参考.pdf》,主要用到了libax_proton.so,libax_vo.so和libax_ivps动态库,提供的函数具体参考文档。
以下是部分参考代码:
视频输入部分代码:
extern \"C\" {
// 声明需要调用的函数签名
pub fn AX_VIN_Init() -> i32; //初始化vin模块
pub fn AX_VIN_Deinit() -> i32; //释放vin模块
pub fn AX_VIN_CreateDev(devID: u8) -> i32;//创建dev, dev取值范围[0, AX_VIN_MAX_DEV_NUM]
pub fn AX_VIN_DestroyDev(devID: u8) -> i32; //销毁对应的dev
pub fn AX_VIN_EnableDev(devID: u8) -> i32;//使能dev
pub fn AX_VIN_DisableDev(devID: u8) -> i32; //禁止dev
}
视频输出部分代码,结构体和枚举定义内容会很多,这里贴一部分样例:
extern \"C\" {
// 声明需要调用的函数签名
pub fn AX_VO_Init() -> i32; //初始化vo模块的软硬件资源
pub fn AX_VO_Deinit() -> i32; //释放o模块的软硬件资源
pub fn AX_VO_SetPubAttr(devID: u8, pub_attr: &AxVoPubAttrT) -> i32;//设置指定显示设备的公共属性
pub fn AX_VO_SetCSC(devID: u8, csc: &AxVoCscT) -> i32; // 设置显示设备 CSC
pub fn AX_VO_Enable(devID: u8) -> i32;//使能dev
pub fn AX_VO_Disable(devID: u8) -> i32; //禁止dev
}
#[repr(u8)]// Use u8 so it matches with C enum size
pub enum AxVoModeE {
AxVoModelOffline = 0,
AxVoModelOnline,
AxVoModelButt,
}
#[repr(u8)]
pub enum AxVoIntfTypeE {
AxVoIntfDpt,
AxVoIntfBt601,
AxVoIntfBt656,
AxVoIntfBt1120,
AxVoIntfDsi,
AxVoIntfHdmi,
AxVoIntfButt
}
#[repr(u8)]
pub enum AxVoOutFmtE {
AxVoOutFmtUnused = 0,
AxVoOutFmtRgb565,
AxVoOutFmtRgb666,
AxVoOutFmtRgb666lp,
AxVoOutFmtRgb888,
AxVoOutFmtRgb101010,
AxVoOutFmtYuv422,
AxVoOutFmtYuv422_10,
AxVoOutFmtButt
}
以下是简单的测试,输入模块的初始化,创建设备,销毁设备,以及使能和禁止设备等简单操作的样例。
2023-12-17 21:42:07
在上次进行了开箱以后,我在接下来的日子里继续研究了爱芯派Pro,在我研究其他人的试用报告时,我发现大家都在玩人工智能的模型,虽然板子的确强项是人工智能,但是在我看来,首先要编译好DEMO,然后研究
2023-12-14 22:28:05
想着升级到1.45的bsp版本好能使用硬件接口还有跟爱芯元智github开源的sdk等仓库能对得上以方便正常使用的,结果现在升级了之后翻车了。
升级到1.45的之后出现的问题是网口无法
2023-12-13 20:36:51
继上文开箱后,本文主要依托爱芯元智官方的实例,进行官方YOLOV5模型的部署和测试。
一、环境搭建
由于8核A55的SoC,加上目前Debian OS的工具齐全,所以决定直接在板上编译程序
2023-12-12 22:58:48
本文首先介绍Redis是什么,然后介绍如何在爱芯派上编译Redis源码,以及从源码安装Redis,最后介绍如何在爱芯派上运行Redis基准测试,并在树莓派4B上运行同一版本的Redis服务
2023-12-10 22:18:16
啊抱歉,上周得了一周的流感,人才活过来。。本来还想研究下sam它是怎么部署的,但是时间好像有点来不急,就讲个最简单的efficientNet吧。但是会仔细讲下使用模型部署,实际也就是推理引擎的全过程,以及pulsar2作为推理引擎的一些细节。
1、完整流程概括
可以将模型部署细分为以下几个步骤,其实就是和用onnxruntime\\\\openvino\\\\tensorRT这些部署步骤是一样的,只不过主角这次换成了pulsar2:
1、先在服务器上训练好网络模型,并以一个通用的中间形式导出(通常是onnx)
2、根据你要使用的推理引擎进行离线转换,把onnx转换成你的推理引擎能部署的模型格式,如这里就是axmodel
3、在移动端的linux系统安装好推理引擎
4、使用推理引擎运行
下面一步步的细说,并会挑一些觉得有意思的细节说说。以一个简单的mobilenetV2为例.
2、训练模型,导出onnx
这里就取了个巧,直接用torch官方库里面预训练好的模型导出来,得到onnx模型。后续还可以进一步转化为-sim.onnx文件,做了一定的简化,不过差别不大。
import torch
model = torch.hub.load(\'pytorch/vision:v0.10.0\', \'mobilenet_v2\', pretrained=True)
model.eval()
x = torch.randn(1, 3, 224, 224)
# 指定输入输出节点名称
input_names = [\"input\"]# 例如,将默认的\"input.1\"更改为\"input\"
output_names = [\"output\"]# 指定输出节点的名称
# 导出模型
torch.onnx.export(model, x, \"mobilenetv2.onnx\", export_params = True, verbose=True, opset_version=11,
input_names=input_names,
output_names=output_names)
3、模型转换
一句话概括:
安装pulsar2,这里就是用docker安装,他要干的是在你的电脑上,把onnx模型转换为他能跑的模型,也就是axmodel模型
这里面的docker安装什么的具体可以参照这个网站,他们都讲得很详细,我不赘述了:部署模型到 Maix-III(M3) 系列 AXera-Pi 开发板 - Sipeed Wiki
在docker里面,根据这个指令进行转换:
pulsar2 build --input model/mobilenetv2-sim.onnx --output_dir output --config config/mobilenet_config.js
其实就是根据config.json进行转换,可以具体看看config.json干了什么:
config.json:
{
\"model_type\": \"ONNX\",
\"npu_mode\": \"NPU1\",
\"quant\": {
\"input_configs\": [
{
\"tensor_name\": \"input\",
\"calibration_dataset\": \"./dataset/imagenet-1k-images-rgb.tar\",
\"calibration_size\": 256,
\"calibration_mean\": [103.939, 116.779, 123.68],
\"calibration_std\": [58.0, 58.0, 58.0]
}
],
\"calibration_method\": \"MinMax\",
\"precision_analysis\": false
},
\"input_processors\": [
{
\"tensor_name\": \"input\",
\"tensor_format\": \"BGR\",
\"src_format\": \"BGR\",
\"src_dtype\": \"U8\",
\"src_layout\": \"NHWC\",
\"csc_mode\": \"NoCSC\"
}
],
\"compiler\": {
\"check\": 0
}
}
主要两点:
1、进行了训练后量化的方式进行量化(PTQ),你要提供一个校准数据集
2、你要指定输入、输出数据tensor
这里对量化方式做一个简要的介绍,量化方式现在主要分为两种,分别是感知量化训练(QAT)、训练后量化(PTQ),其中量化后训练又分为静态和动态两种。一般来说感知量化训练效果最好,但同样算力开销不小,量化后训练中静态效果比动态好,因为动态是在推理时去估计算子的数据范围,误差较大。
这里采用的是静态的训练后量化方式,通过提供一个和训练集同分布的校准数据集,将算子的权重从FP32量化到INT8
一些细节:仔细看看axmodel量化的模型结构和onnx有什么区别,这里可以用netron网站打开:
在最开头,有一个quantize和dequantize,把输入量化,输出反量化,从float转为uint8,再在结束从uint8转为float
在转化时还会自动的进行算子的融合,最常见的就是conv+relu融合在一起(这里是relu6),conv+bn+relu融合在一起,详细远离可以看量化的白皮书论文
4、在移动端的linux系统安装好推理引擎
在板子上面的debian系统中执行如下指令:
git clone [https://github.com/AXERA-TECH/ax-samples.git](https://github.com/AXERA-TECH/ax-samples.git)
cd ax-samples
mkdir build && cd build
cmake -DBSP_MSP_DIR=/soc/ -DAXERA_TARGET_CHIP=ax650 ..
make -j6
make install
这个代码本质上就是对cmakelists.txt进行编译运行
其实就是用cmakelists.txt进行编译。真正有用的是这一句:
# src files
add_subdirectory(examples)
它编译 examples子目录下的示例代码
examples的cmakelist:其实就是把这些代码编译成可执行文件:
比如classification.cc编译成一个可执行的classification文件,就能在命令行调用了。
仔细看一下这些文件,发现这些文件通常只用了很少的代码调用了模型,大部分是用于pre_process和post_process,也就是定义了怎么调用模型,以及对模型的输出怎么处理的。
在后续如果要调用新的模型,需要自己编写代码文件,这里先简单学习一下。
浅浅记录一下运行的结果:
root@maixbox:~/Desktop/ax-samples/build/install/ax650#
./ax_classification -m mobilenetv2.axmodel -i cat.jpg
--------------------------------------
model file :
mobilenetv2.axmodel
image file : cat.jpg
img_h, img_w : 224 224
--------------------------------------
WARN,Func(__is_valid_file),NOT find file = \'/etc/ax_syslog.conf\'
ERROR,Func(__syslog_parma_cfg_get),NOT find = \'/etc/ax_syslog.conf\'
Engine creating handle is done.
Engine creating context is done.
Engine get io info is done.
Engine alloc io is done.
Engine push input is done.
--------------------------------------
topk cost time:0.07 ms
9.0340, 285
9.0340, 283
8.6931, 282
8.5227, 281
7.6704, 463
--------------------------------------
Repeat 1 times, avg time 0.57 ms, max_time 0.57 ms, min_time 0.57 ms
--------------------------------------
2023-12-10 16:34:43
U3771爱德万|Advantest U3771 30G频谱分析仪9KHz–31.8GHz新的便携式频谱分析仪具有体积小,重量轻的特点,可以在微波和毫米波范围内测量无线信号日本株式会社爱德万测试
2023-12-04 10:02:57
之前学习用的都是官网写好的模型runner,这里尝试用rust自己写一个runner,主要是为了熟悉Axera的一些API以便后期扩展。这里主要用到AxEngine,他是神经网络模型芯片侧推理计算库,能够完成模型加载到执行的全部推理任务。这次主要以获取版本为例,介绍一下如何用rust接入以及相关API,完整的runner待后期完善,本例中仅仅涉及三个API,AX_ENGINE_Init,AX_ENGINE_Deinit和AX_ENGINE_GetVersion。
模型runner主要API以及调用流程,请参考文档《51 - AX ENGINE API 使用说明.pdf》,特别是调用流程,也可以参考一些C实现的example。主要用到了libax_engine.so动态库,提供的函数不多。
主要参考代码如下:
extern crate libc;
use libc::c_char;
use std::ffi::CStr;
// 指定动态库文件名
#[link(name = \"ax_engine\")]
extern \"C\" {
// 声明需要调用的函数签名
fn AX_ENGINE_Init(npu_attr: &AxEngineNpuAttrT) -> i32;
fn AX_ENGINE_Deinit() -> i32;
fn AX_ENGINE_GetVersion() -> *const c_char;
}
#[repr(C)]
pub struct AxEngineNpuAttrT {
e_hard_mode: AxEngineNpuModeT,
reserve: [u32; 8],
}
#[repr(u8)]// Use u8 so it matches with C enum size
pub enum AxEngineNpuModeT {
AxEngineVirtualNpuDisable = 0, // virtual npu disable
AxEngineVirtualNpuStd=1,
AxEngineVirtualNpuBigLittle = 2,
AxEngineVirtualNpuButt = 3,
}
fn main() {
let init_param = AxEngineNpuAttrT {
e_hard_mode: AxEngineNpuModeT::AxEngineVirtualNpuDisable,
reserve: Default::default(),
};
// 初始化推理引擎资源
let ret = unsafe { AX_ENGINE_Init(&init_param) };
println!(\"init engine: {}\", ret);
// 获取Engine版本号
let c_buf: *const c_char = unsafe {AX_ENGINE_GetVersion()};
let c_str: &CStr = unsafe {CStr::from_ptr(c_buf) };
let ver: &str = c_str.to_str().unwrap();
println!(\"{}\", ver);
// 释放推理引擎资源
let ret = unsafe { AX_ENGINE_Deinit() };
println!(\"deinit engine:{}\", ret);
}
下面的截图是最后运行结果:
2023-12-02 22:00:47
首先还是要感谢发烧友论坛、爱芯元智以及SIPEED开展的本次活动,在看到板子强大的编解码能力后,马上决定参与本次活动,本来以为没有成功,没想到过了几天之后,论坛的小姐姐就联系我了,实在是太开心了
2023-12-02 17:50:49
本帖最后由 邓云秀 于 2023-11-29 10:59 编辑
主板在这里
经过标准 对比视频 处理后猪只背部加红点方式标注
点数、估重、体尺测量、评级准确率高,多维度数据信息全面精准。
本地算法、本地缓存,网络依赖性低。
实际2
实际3
基于目前的测试 还是会有1%左右的偏差
特别异常---把赶猪板识别成猪了
设备无人化操作预期目标 全智能化无人值守,自动开始任务,自动停止休眠,自动关联封装售猪业务及车辆、人员、地点等信息。
2023-11-27 08:57:18
非常感谢电子发烧友平台组织本次试用活动,感谢爱芯元智为我们的爱芯派 Pro (AXera-Pi Pro)开发板。
一、简单开箱
拆开快递包装,看到的是这样的:
爱芯派Pro包装盒看起来像一个“饭盒
2023-11-26 14:38:52
爱芯元智AX650N部署yolov8s 自定义模型
本博客将向你展示零基础一步步的部署好自己的yolov8s模型(博主展示的是自己训练的手写数字识别模型),本博客教你从训练模型到转化成利于
2023-11-24 20:40:25
因为调试某些功能的时候没有,问了群友说有新的镜像了,所以就来试试烧录新的镜像,而新的镜像是axp格式也就是爱心元智自己封装过的了,只能用它们提供的工具进行烧录,接这个机会来说一篇文章。
工欲善其事必先利其器,首先要准备好工具
1、《00 - AXDL 工具使用指南.pdf》,在M4N-Dock\\\\07_Software_Doc\\\\pc目录下
2、Driver_V1.20.46.1.7z
3、AXDL_V1.22.48.1.7z(2和3 可以在QQ群里找 139953715)
4、M4NDock_release(1.45)_231117_sha256_6372f2721aa0d87072bfd5fb213f52a8cd03d5d10b46835a9b671e47cf3fb40f.axp ,在M4N-Dock\\\\09_Image目录下,没有的话可以去网盘里面重新下载
可以参考AXDL工具使用指南来进行操作,这里我简单说一下我的操作流程。
1、安装驱动,解压驱动包,DriverSetup.exe 打开后点点点安装即可。
2、打开上位机,选择axp镜像,我这里是选择usb烧录快一点,里面的配置就不用去选择了,按照默认就好了,如果是串口或者要分区更新的话请参考官方文档。
3、点击开始按钮,然后连接板端的烧录口与电脑,板子上电,然后再同时按下boot、rst,松开rst等一会电脑识别到烧录口后松开boot键,就可以看到板子开始烧录,等待烧录完成即可。
好了,去看看新镜像能不能解决我的问题了。
2023-11-23 10:44:27
本帖最后由 邓云秀 于 2023-11-29 10:58 编辑
对比用户需求,除了猪只点数之外还有猪只重点估重.
开发目标:
不限宽通道、通用支架,不影响赶猪进度,视觉点数识别率达99%以上,估重误差小于2%,RFID盘点率达99.9%。
最终产品适用场景多样、养殖场、家庭农场、屠宰厂.
目前只是与摄像头打通,现实的画面
现场太差 高温高湿,还会不定期消毒[次氯酸 或烧碱水]
因此对设备的密封需要三防[防水防尘\\耐酸碱]
很多工作还是要做图片标注---现场环境都不一样
2023-11-22 15:00:59
很荣幸能收获这次爱芯派 Pro 开发板的试用机会,这次报告主要针对对于开发板的开箱测试以及网络连接配置等方面做一个自己的分享。
1、开箱体验
想起来之前用的都是从实验室薅的板子,买的板子也是自己即开
2023-11-21 15:12:38
感谢电子爱好者和爱芯元智公司提供的测试机会。
爱芯派 Pro (AXera-Pi Pro)搭载爱芯元智第三代高算力、高能效比智能视觉芯片 AX650N,内置高算力和超强编解码能力,满足行业对高性能
2023-11-20 22:09:06
首先介绍一下什么是PSDK:
DJI 为支持开发者开发出可挂载在DJI 无人机上的负载设备,提供了开发工具包Payload SDK(即PSDK)以及开发配件X-Port 标准云台open in new window、SkyPort V2转接环open in new window和SDK 同轴线open in new window等,方便开发者利用DJI 无人机上如电源、通讯链路及状态信息等 资源 。开发者能够根据行业的应用需求,基于PSDK提供的功能接口,结合具体的结构设计、硬件设计、软件逻辑实现和算法优化,开发出如 自动巡检系统 、 红外相机 、 测绘相机 、 多光谱相机 、 喊话器 、探照灯等满足不同细分领域的负载设备。
简单来说就是我们可以通过大疆提供的硬件接口和软件接口来开发自己的云台设备去挂载在大疆的行业级无人机上使用。
大疆PSDK 网站:DJI Developer
github仓库:dji-sdk/Payload-SDK: DJI Payload SDK Official Repository (github.com)
我的硬件平台是 M300 + Skyport V2 + AX650,把它们连接起来就可以了,PSDK的通讯是需要一个ttl串口和网口,我使用的串口是板子的/dev/ttyS1 ( 232转ttl ) 和 end0 网口。
然后是软件平台的准备,先去github拉取PSDK仓库,因为板子的性能足够强大,编译一个PSDK问题不大,我们直接在板子上来编译即可,先准备一些软件包。
sudo apt update
sudo apt install cmake
sudo apt install libusb-1.0-0-dev
sudo apt install ffmpeg
拉到仓库后,我们需要对里面的配置文件进行一个修改,主要是这三个文件
Payload-SDK/samples/sample_c/platform/linux/manifold2/application/dji_sdk_app_info.h//文件里面的信息需要去大疆开发者平台申请
Payload-SDK/samples/sample_c/platform/linux/manifold2/hal/hal_network.h // 修改 LINUX_NETWORK_DEV 为对应的网口,我使用的是end0
Payload-SDK/samples/sample_c/platform/linux/manifold2/hal/hal_uart.h// 修改 LINUX_UART_DEV1 为对应的串口,我使用的是/dev/ttyS1
准备好一切,我们直接编译执行。
cdPayload-SDK/samples/sample_c/platform/linux/manifold2
mkdir build;cd build
cmake ..
make -j6
sudo ./bin/dji_sdk_demo_linux
这样下来就可以实现在大疆飞机上进行自己的应用开发啦。
2023-11-20 12:39:38
尝试将最新的yolov8模型转换为爱芯派的模型。
环境准备
准备Docker环境
首先自己在任意机器上准备好docker环境,详细步骤见官网。
Docker 镜像文件
准备 yolo8 模型,并转
2023-11-20 12:19:32
爱芯元智AX650N部署yolov5s 自定义模型
本博客将向你展示零基础一步步的部署好自己的yolov5s模型(博主展示的是安全帽模型),利用yolov5 官方的代码工具导出onnx模型,并通过
2023-11-16 19:34:46
通过HDMI链接显示器:
1, uname -a :显示系统名、节点名称、操作系统的发行版号、内核版本等等。
Linux maixbox 5.15.73 #1 SMP PREEMPT Wed Aug 16 15:38:30 CST 2023 aarch64 GNU/Linux
2,Top 命令显示使用 CPU 核心数top ->1
3,通过USB链接电脑,这样不需要12V供电就能跑了。
运行一下指令更新系统:
apt update
apt install build-essential
apt install libopencv-dev
apt install cmake
apt-get install sysstat
dmesg | tail
显示最新的20个系统信息,可以查看导致性能问题的错误信息。
ping 命令:
在测试几个好玩的命令
screenfetch:显示系统、主题信息
sudo apt install screenfetch
screenfetch
查看内置的logo列表:
2023-11-15 14:29:13
爱芯派 Pro (AXera-Pi Pro)M4N Dock 是一款集成了高算力、高能效 AI SOC 的开发板。它采用了爱芯 AX650N 作为主控芯片。AX650N 集成了 8 核 A55
2023-11-15 11:32:06
很高兴获得了这次试用爱心派Pro开发板的试用资格,之前也有接触过爱芯元智家的AX620开发板,是因为他们的家的AI ISP慕名而来的,之前深圳安博会的时候也有去现场体验过他们的产品,确实做的不错
2023-11-14 20:59:51
开发板介绍
收到了爱芯派 Pro (AXera-Pi Pro)开发板,一个高能效比智能视觉芯片 AX650N,处理器是8核64位Cortex-A55架构,主频达1.7Ghz。内置高算力视频,图片处理
2023-11-13 13:18:38
【爱芯派 Pro 开发板试用体验】+使用yolov5s模型(官方)
配置好基本环境
如果没有连接上网络的,可以看博主写的上一篇怎么连接网络
apt update
apt install
2023-11-13 11:04:14
本帖最后由 邓云秀 于 2023-11-29 13:44 编辑
规模化猪场生猪出栏时,农场售猪短则一天长则几天,管理耗费人力;同时由于买卖中间环节的监管不到位,可能存在不少人为干预的情况,影响猪企经济收益。
项目背景:
1.猪场卖猪前的动员会,卖猪后的犒劳餐,仍觉得累。
2.卖猪累的要命的凌晨/大半夜,饲养员、主管、统计员的数据对不上,挨老板一顿批是常有的事。
3.买卖双方还担心地磅/称,被卖/买猪人做手脚。
4.猪装上车了,买/卖家对数据仍有异议,猪进了车里,是点不清了的,考虑生物安全,也不可能卸车重装,咋办?
基于上述现场问题做解决方案。
结合算法引擎,通过双目摄像头和传感器,对出猪数量进行AI盘点、猪只估重、猪只回流监测、行为异常预警,实现卖猪的智能化监控。AI智能采集、分析数据,回放售猪视频,进行核验,买卖双方都放心。
项目目标:
1.基于人工智能AI机器视觉技、大数据云计算技术而研发的智能化产品。
2.服务于智能养殖、数字化生产、生物资产安全及养殖大数据运用的鹰瞳产品。
3.通过非接触远距离,无人值守智能化自主完成:集盘点、估重、体尺测量、身份标识、等级评定等多功能为一体的智能化设备。
4.产品场景适应性强,算法精准,网络依赖度低,点数准确率达到100%,估重准确率≥98%以上。
AI数猪估重在整个大系统的中结构。
一、低功耗:
采集设备均处于休眠状态以降低功耗、减少衰减,当通道出现猪只时自动转为正常工作模式。
二.智能无人值守
1.系统平台产生收售猪业务时,当有车辆进入到车牌相机视觉范围,车牌相机根据视觉范围内的场景变化,抓拍车尾整体照片,并标记车牌在车尾的位置,通过算法解析出车牌牌号(前提在清洗消毒时尽量将车牌冲洗干净)。车牌相机将识别到的
2.车牌号及场地位置等信息,发送给平台系统,并给出唤醒信号(或由平台唤醒),唤醒设备主体执行盘点任务。
系统平台根据车牌及场地信息,在系统中查询与之关联的合法收售猪业务单,并将关键业务信息(收售单位、入场时间、离场时间、装车数量、均重等)填充完善,现场信息(执行人员、赶猪人员等)由执行业务单位在功能端(web、H5等)执行完善。
3.在装卸赶猪过程中,通过设备系统AI视觉,实时执行对通过猪只的:盘点、估重、评级,并通过网络将动态视频流媒体上传至云端媒体库,作为历史数据保存,以便供财务、生产、审计、监察等部门执行审核、复核。
三.网络依赖低
赶猪或一车装卸完后,设备系统在一定的时间内,算法执行单车或整体业务停止,并执行系统待机,并将本地存储数据通过网络与系统平台执行同步。如本地无有线网络条件,可通过4G/NB运营商网络传输,如无运营商信号,可执行本地算法、数据缓存,有网续传,或存储介质导出。
2023-11-13 09:40:18
【爱芯派 Pro 开发板试用体验】+开箱初次体验
开箱内容
打开包装,你可以看到以下物品
12v 2A电源适配器
typec的数据线
一个螺母头(应该为了给固定插12 2A的适配器的插头
2023-11-12 10:58:19
首先感谢爱芯元智 & 电子发烧友给与的试用机会!
首先介绍下爱芯元智公司(AXERA):
爱芯元智半导体(宁波)有限公司成立于2019年5月,致力于打造世界领先的人工智能感知与边缘
2023-11-12 00:31:59
【爱芯派 Pro 开发板试用体验】+基本使用环境配置
登录系统
系统默认为debian系统,插入网线,插入显示器,鼠标、键盘,介绍基础系统使用操作。
将 M4N-Dock 连接上显示器后,可以看到
2023-11-11 20:44:11
本帖最后由 邓云秀 于 2023-11-29 13:44 编辑
感谢电子发烧友论坛。
感谢爱芯元智半导体(宁波)有限公司。
效率高。昨天发货今天就收到了。
铁盒包装
2023-11-11 10:00:59
1 评估套件硬件介绍
全爱科技QA200A2 Altas200I A2开发套件
Xilinx FPGA 开发套件-VC709
实物测试组成主要如下:
图 1-1 QA200A2
2023-09-05 14:39:57
爱德万直流电源ADCMT6240A产品概述爱德万直流电源ADCMT6240A是一个直流电压和电流源/监视器的基本精度±0.025%的高准确度在4 1/2位发生,5 1/2位的测量
2023-09-02 16:45:54
七夕情人节到了,为了Ta,
你打算用什么方式表达爱?
是包包、鲜花、美酒、巧克力,
还是一封充满爱意的短信?
在HarmonyOS,有一群精致又机智的开发者们,他们#以代码之名,表达爱#
比起鲜花
2023-08-22 17:18:12
有一个爱浪X3,中置没声音,其他都正常,求指导
2023-04-08 17:29:12
本帖最后由 我爱方案网 于 2023-4-3 14:17 编辑
电动扳手是指拧紧和旋松螺栓及螺母的电动工具,是一种拧紧螺栓的工具。用于钢结构桥梁、厂房建筑、化工、发电设备安装大六角头螺栓施工
2023-04-03 14:16:30
评论
查看更多