0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

【聆思CSK6 LNN工具體驗】自定義人臉檢測模型

冬至配餃子 ? 來源:聆思科技AI芯片 ? 作者:mj_QNmuf ? 2023-10-18 11:46 ? 次閱讀

背景

在微信偶然發(fā)現(xiàn)聆思科技的CSK6開發(fā)板的評估活動,因為經(jīng)常在各種硬件平臺上測試模型,因此申請了測評。很榮幸能被選中。

官方提供了開源分類的模型轉(zhuǎn)換,但平常使用分類模型較少因此嘗試了目標檢測模型的轉(zhuǎn)換。

模型架構

模型的思路采自centernet,基干是修改過的普通VGG塊,F(xiàn)PN是簡單的自頂向下結(jié)構,head輸出了一個hm(中心點)和wh,但截至于寫文章時,官方提供的燒錄板子接口只能輸出一個head,所以又將hm和wh cat在一起。網(wǎng)絡結(jié)構如下

title=

過程

環(huán)境搭建

linger與thinker 環(huán)境搭建

linger是用于量化訓練的,thinker是用來轉(zhuǎn)換模型的。我使用的是wsl中Ubuntu18環(huán)境。

linger配置

conda create -n linger-env python==3.7.0
conda activate linger-env
git clone https://github.com/LISTENAI/linger.git
cd linger && sh  install.sh
pip -U pip
cat requirements.txt |xargs -n 1 pip install

thinker配置

conda create -n thinker-env python==3.7.0
conda activate thinker-env
git clone https://github.com/LISTENAI/thinker.git
cd thinker
bash ./scripts/x86_linux.sh
pip -U pip
cat requirements.txt |xargs -n 1 pip install

兩個環(huán)境分開搭建,搭建好后我們就可以進行訓練了。

模型訓練及轉(zhuǎn)換

模型訓練過程中需要先進行浮點訓練,再進行定點訓練,然后再轉(zhuǎn)換成.bin格式。

linger不支持tensorboard,所以要把相關代碼注釋掉。其余的就是添加幾行代碼就ok了。
原始代碼

model = create_model()
  model = model.to(cfg.device)
  optimizer = torch.optim.Adam(model.parameters(), cfg.lr, betas=(0.9, 0.999), eps=1e-08, weight_decay=1e-4)

修改后代碼

import linger
  model = create_model()
  model = model.to(cfg.device)
  dummy_input = torch.randn(1, 3, 128, 128,requires_grad=True).cuda()
  linger.trace_layers(model, model, dummy_input, fuse_bn=True)
  type_modules = (nn.Conv2d,nn.BatchNorm2d,nn.ConvTranspose2d)
  normalize_modules =(nn.Conv2d,nn.BatchNorm2d,nn.ConvTranspose2d)
  linger.normalize_module(model, type_modules=type_modules, normalize_weight_value=16, normalize_bias_value=16,
                          normalize_output_value=16)
  optimizer = torch.optim.Adam(model.parameters(), cfg.lr, betas=(0.9, 0.999), eps=1e-08, weight_decay=1e-4)

再訓練完浮點模型后需要加載保存的浮點模型進行定點訓練,注意需要使用更小的學習率。

import linger
  model = create_model(arch=cfg.arch, num_classes=train_dataset.num_classes, inference_mode=True, onnx_flag=False)
  model = model.to(cfg.device)
  dummy_input = torch.randn(1, 3, 128, 128, requires_grad=True).cuda()
  type_modules = (nn.Conv2d,nn.BatchNorm2d,nn.ConvTranspose2d)
  normalize_modules = (nn.Conv2d,nn.BatchNorm2d,nn.ConvTranspose2d)
  linger.normalize_module(model, type_modules=type_modules, normalize_weight_value=16, normalize_bias_value=16,
                          normalize_output_value=16)
  model = linger.normalize_layers(model, normalize_modules=normalize_modules, normalize_weight_value=8,
                                normalize_bias_value=8, normalize_output_value=8)
  quant_modules = (nn.Conv2d,nn.BatchNorm2d,nn.ConvTranspose2d)
  model = linger.init(model, quant_modules=quant_modules)
  model.load_state_dict(torch.load(cfg.load_model)['state_dict'])
  optimizer = torch.optim.Adam(model.parameters(), cfg.lr, betas=(0.9, 0.999), eps=1e-08, weight_decay=1e-4)

定點模型訓練完畢后,需要轉(zhuǎn)換成onnx格式

import linger
  model = create_model()
  model = model.to(cfg.device)
  dummy_input = torch.randn(1, 3, 128, 128, requires_grad=True).cuda()
  linger.SetIQTensorCat(True)
    type_modules = (nn.Conv2d,nn.BatchNorm2d,nn.ConvTranspose2d)
  normalize_modules = (nn.Conv2d,nn.BatchNorm2d,nn.ConvTranspose2d)
  linger.normalize_module(model, type_modules=type_modules, normalize_weight_value=16, normalize_bias_value=16,
                          normalize_output_value=16)
  model = linger.normalize_layers(model, normalize_modules=normalize_modules, normalize_weight_value=8,
                                normalize_bias_value=8, normalize_output_value=8)
  quant_modules = (nn.Conv2d,nn.BatchNorm2d,nn.ConvTranspose2d)
  model = linger.init(model, quant_modules=quant_modules)

  model.load_state_dict(torch.load(cfg.load_model)['state_dict'])
  model.eval()
  dummy_input = torch.ones(1, 3, 128, 128).cuda()
  with torch.no_grad():
    torch.onnx.export(model, dummy_input, 'lnn.onnx',input_names=['input'], output_names=['hm'],
                      export_params=True,opset_version=12,operator_export_type=torch.onnx.OperatorExportTypes.ONNX_ATEN_FALLBACK)

模型轉(zhuǎn)成onnx后需要在thinker環(huán)境中轉(zhuǎn)成.bin格式

conda activate thinker-env
tpacker -g net.onnx -d True -o model.bin

如果最后一步報錯,可能是因為不符合轉(zhuǎn)換要求導致,按照官方要求來就行。

燒錄模型到板子

首先需要安裝lisa環(huán)境,這里我選擇了wsl中的ubuntu22.04環(huán)境
先安裝lisa zep 命令行工具,wasi-sdk,wasm-sdk,

編譯程序

配置環(huán)境變量

export WASM_THINKER_SDK="/path_to_sdk/wasm-sdk"
export WASI_TOOLCHAIN_PATH="/path_to_sdk/wasi-sdk-17.0"

安裝完成后,下載官方提供的demo

lisa zep create --from-git https://cloud.listenai.com/listenai/samples/camera_image_detect.git

修改wasm 應用

cd app_wasm/
vi main.c

將文件修改為如下

#include < stdio.h >

#include "thinker/thinker.h"

static tModelHandle model_hdl;
static tExecHandle hdl;

int
main(int argc, char **argv)
{
    printf("BOOT: WAMRn");

    tStatus ret;

    char version[30];
    tGetVersion(0, version, sizeof(version));
    printf("[WASM] tGetVersion: %sn", version);

    ret = tInitialize();
    printf("[WASM] tInitialize: %dn", ret);
    if (ret != T_SUCCESS) return 1;
    return 0;
}

int
set_model(void *ptr, uint32_t size)
{
    tStatus ret;

    uint32_t use_psram_size = 0;
    uint32_t use_share_size = 0;

    int num_memory = 0;
    tMemory memory_list[7];
    ret = tGetMemoryPlan(
        memory_list, &num_memory, (int8_t *)ptr, size, &use_psram_size, &use_share_size);
    printf("[WASM] tGetMemoryPlan: %dn", ret);
    if (ret != T_SUCCESS) return 1;

    printf("[WASM] * num_memory=%dn", num_memory);
    printf("[WASM] * use_psram_size=%dn", use_psram_size);
    printf("[WASM] * use_share_size=%dn", use_share_size);
    for (int i = 0; i < num_memory; i++) {
        printf("[WASM] * memory_list[%d].dev_type=%dn", i, memory_list[i].dev_type_);
        printf("[WASM] * memory_list[%d].mem_type=%dn", i, memory_list[i].mem_type_);
        printf("[WASM] * memory_list[%d].size=%dn", i, memory_list[i].size_);
        printf("[WASM] * memory_list[%d].addr=0x%08llxn", i, memory_list[i].dptr_);
    }

    ret = tModelInit(&model_hdl, (int8_t *)ptr, size, memory_list, num_memory);
    printf("[WASM] tModelInit: %d, model=0x%llxn", ret, model_hdl);
    if (ret != T_SUCCESS) return 1;

    ret = tCreateExecutor(model_hdl, &hdl, memory_list, num_memory);
    printf("[WASM] tCreateExecutor: %d, hdl=0x%llxn", ret, hdl);
    if (ret != T_SUCCESS) return 1;

    return 0;
}

int
set_input(void *ptr, uint32_t size)
{
    printf("[WASM] set_input(%p, %d)n", ptr, size);

    tStatus ret;

    int32_t in_c = 3;
    int32_t in_h = 128;
    int32_t in_w = 128;

    tData input;
    input.dtype_ = Int8;
    input.scale_ = 5;
    input.shape_.ndim_ = 4;
    input.shape_.dims_[0] = 1;
    input.shape_.dims_[1] = in_c;
    input.shape_.dims_[2] = in_h;
    input.shape_.dims_[3] = in_w;
    input.dptr_ = ptr;

    ret = tSetInput(hdl, 0, &input);
    printf("[WASM] tSetInput: %dn", ret);

    return ret;
}

int
get_output(void **ptr, uint32_t *size)
{
    printf("[WASM] get_outputn");

    tStatus ret;

    ret = tForward(hdl);
    printf("[WASM] tForward: %dn", ret);
    if (ret != T_SUCCESS) return 1;

    tData output;
    ret = tGetOutput(hdl, 0, &output);
    printf("[WASM] tGetOutput: %dn", ret);
    if (ret != T_SUCCESS) return 1;

    printf("[WASM] * output.dtype=%un", output.dtype_);
    printf("[WASM] * output.shape.ndim=%un", output.shape_.ndim_);
    printf("[WASM] * output.shape.ndim=%un", output.shape_.ndim_);
    printf("[WASM] * output.dptr=0x%pn", output.dptr_);

    int shape_size = (output.dtype_ & 0xF);
    for (int i = 0; i < output.shape_.ndim_; i++) {
        shape_size *= output.shape_.dims_[i];
    }

    printf("[WASM] * shape_size=%dn", shape_size);

    *ptr = output.dptr_;
    *size = shape_size;

    return ret;
}

主要是修改set_input中in_h和in_w為自己模型的輸入輸出,input.scale_的值修改為自己模型的值,這個如何查看開源通過onnx中的input quant中的scale_x, scale_x=pow(2,input.scale_)

title=

修改主程序

cd ..
cd camera_image_detect
vi main.c

修改后的代碼為

#include < zephyr/kernel.h >
#include < zephyr/device.h >
#include < zephyr/drivers/gpio.h >
#include < zephyr/drivers/video.h >
#include < zephyr/storage/flash_map.h >

#include < math.h >

#include < csk_malloc.h >

#include < lsf/services/thinker.h >

#include "lib_image.h"
#include "venus_ap.h"

#define THINKER_MODEL_ADDR (FLASH_BASE + FLASH_AREA_OFFSET(thinker_model))

double CIFAR100_TRAIN_MEAN[] = {0.5070751592371323, 0.48654887331495095, 0.4409178433670343};
double CIFAR100_TRAIN_STD[] = {0.2673342858792401, 0.2564384629170883, 0.27615047132568404};

void main(void)
{
    int ret;

    printk("Hello World! %sn", CONFIG_BOARD);

    /* 加載 Thinker 模型,注意傳入模型的實際字節(jié)數(shù) */
    lsf_thinker_set_model((void *)THINKER_MODEL_ADDR, 421520);

    const struct device *video = device_get_binding(DT_LABEL(DT_NODELABEL(dvp)));
    if (video == NULL) {
        printk("Video device not foundn");
        return;
    }

    struct video_format fmt;
    fmt.pixelformat = VIDEO_PIX_FMT_VYUY;
    fmt.width = 640;
    fmt.height = 480;
    fmt.pitch = fmt.width * 2;
    if (video_set_format(video, VIDEO_EP_OUT, &fmt)) {
        printk("Unable to set video formatn");
        return;
    }

    // 圖像輸入?yún)^(qū)域
    float box[4] = {0, 0, 0, 0};
    box[2] = fmt.width;
    box[3] = fmt.height;

    struct video_buffer *buffers[2];

    /* Size to allocate for each buffer */
    int bsize = fmt.width * fmt.height * 2;

    /* Alloc video buffers and enqueue for capture */
    for (int i = 0; i < ARRAY_SIZE(buffers); i++) {
        printk("#%d: Alloc video buffer: %dn", i, bsize);
        buffers[i] = video_buffer_alloc(bsize);
        if (buffers[i] == NULL) {
            csk_heap_info();
            printk("Unable to alloc video buffern");
            return;
        }

        video_enqueue(video, VIDEO_EP_OUT, buffers[i]);
    }

    ret = video_stream_start(video);
    if (ret != 0) {
        printk("Unable to start video streamn");
        return;
    }

    size_t result_size = 3 * 128 * 128;
    size_t pixel_count = fmt.width * fmt.height;
    uint8_t *result = csk_malloc(result_size); // 縮放后的圖像
    assert(result != NULL);
    uint8_t *rgb_buffer = csk_malloc(pixel_count * 3); // 存 RGB 數(shù)組
    assert(rgb_buffer != NULL);

    double *input_data = csk_malloc(result_size * sizeof(double)); // 特征換算
    assert(input_data != NULL);
    uint8_t *final_input = csk_malloc(result_size); // 最終輸入
    assert(final_input != NULL);

    size_t one_third_result = result_size / 3;

    int8_t *output;
    uint32_t output_size;

    // Start process
    struct video_buffer *vbuf;
    ret = video_dequeue(video, VIDEO_EP_OUT, &vbuf, K_MSEC(1000));
    if (ret != 0) {
        printk("Video buffer dequeued failed: %dn", ret);
        return;
    }

    uint8_t *buffer = vbuf- >buffer;

    printk("Processing...n");
    vyuy_to_rgb24(buffer, rgb_buffer, pixel_count);

    video_enqueue(video, VIDEO_EP_OUT, vbuf);

    printk("Resizing...n");
    ImagingResample(rgb_buffer, fmt.width, fmt.height, result, 128, 128, box);

    // 特征換算
    printk("Feature extraction...n");
    for (int i = 0; i < result_size; i++) {
        int index = i % 3;
        uint8_t value = result[i];
        input_data[i] = (double)(value / 255.0 - CIFAR100_TRAIN_MEAN[index]) /
                CIFAR100_TRAIN_STD[index];
    }

    // 輸入數(shù)據(jù)
    printk("Input data to Thinker...n");
    for (int i = 0; i < result_size; i++) {
        // final_input[i] = (int8_t)floor(input_data[i] * 64 + 0.5);

        if (i < one_third_result) {
            final_input[i] = (int8_t)floor(input_data[i * 3] * 64 + 0.5);
        } else if (i < one_third_result * 2) {
            final_input[i] = (int8_t)floor(
                input_data[(i - one_third_result) * 3 + 1] * 64 + 0.5);
        } else {
            final_input[i] = (int8_t)floor(
                input_data[(i - one_third_result) * 3 + 2] * 64 + 0.5);
        }
    }

    // input data 給 Thinker
    lsf_thinker_set_input(final_input, result_size);

    // 獲取 Output
    lsf_thinker_get_output((void **)&output, &output_size);

  int max_value = -129;
  int cur_index = 0;
  int xs = 0;
  int ys = 0;
  float score = 0;
  int w = 32;
  int h =32;
  for (int c = 0; c < 1; c++) {
    for (int h1 = 0; h1 < h; h1++) {
      for (int w1 = 0; w1 < w; w1++) {

        int value = output[cur_index];

        if (value > max_value) {
          max_value = value;
          xs = w1;
          ys = h1;
          score = value / pow(2.0, 3);
        }


          cur_index++;
      
      }
    }
  }


  


    int32_t idx_lx = w * h + ys * w + xs;
    int32_t idx_ty = w * h + idx_lx;
    int32_t idx_rx = w * h * 2 + idx_lx;
    int32_t idx_by = w * h * 3 + idx_lx;
    float a1 = xs - (float)(output[idx_lx]) / pow(2.0, 3);
    float b1 = ys - (float)(output[idx_ty]) / pow(2.0, 3);
    float c1 = xs + (float)(output[idx_rx]) / pow(2.0, 3);
    float d1 = ys + (float)(output[idx_by]) / pow(2.0, 3);


    float x1 = a1 * 4 * (640.0 /128.0);
    float y1 = b1 * 4* (480.0 /128.0);
    float x2 = c1 * 4* (640.0 /128.0);
    float y2 = d1 * 4* (480.0 /128.0);



    printk("facebox:x1:%f,y1:%f,x2:%f,y2:%f,score:%fn",x1,y1,x2,y2,score);

    for (int j = 0; j < 128; j++) {
        for (int i = 0; i < 128; i++) {
            uint8_t pixe_r = result[j * 128 * 3 + i * 3];
            uint8_t pixe_g = result[j * 128 * 3 + i * 3 + 1];
            uint8_t pixe_b = result[j * 128 * 3 + i * 3 + 2];
            printk("?33[0;38;2;%d;%d;%dm#", pixe_r, pixe_g, pixe_b);
        }
        printk("?33[00mn");
    }
}

需要修改的地方為
1.double CIFAR100_TRAIN_MEAN[]和double CIFAR100_TRAIN_STD[]改為自己模型使用的值
2.lsfthinkersetmodel((void )THINKERMODELADDR, 421520)
中的第二個參數(shù)為自己模型的大小
3.resultsize = 3 128 128;改為自己模型的輸入大小
4.ImagingResample(rgbbuffer, fmt.width, fmt.height, result, 128, 128, box);中的倒數(shù)第二個和第三個參數(shù)改為自己模型的輸入大小
5.修改自己的后處理:將講一下我自己的后處理邏輯:我參照的是centernet的邏輯,只不過將hm和wh兩個輸出cat在一起,因此最后需要拆開來看;hm為13232的特征圖,wh為43232的特征圖。hm代表目標中心的score值,wh代表對應中心點的左上右下的距離,組合起來就是目標框的左上和右下角點。因此先計算hm特征圖,為了節(jié)省時間以及單純認為只檢測一個人臉,遍歷hm特征圖后得到最大值就是目標中心點,然后獲取該中心點位置的wh的4個值就是目標框。

燒錄到板子

1.編譯wasm應用

cd app_wasm
lisa zep exec python $WASM_THINKER_SDK/cmake/sdk.py build -p .

此時會在當前目錄下build文件夾中生成:thinker_resnet18.aot

2.編譯主程序

cd camera_image_detect
lisa zep build -b csk6011a_nano

此時會在當前目錄下build文件夾中生成:zephyr/zephyr.bin

3.燒錄到板子

lisa zep exec cskburn -s COMx -C 6 0x0 ./build/zephyr/zephyr.bin -b 748800

lisa zep exec cskburn -s COMx -C 6 0x100000 ./resource/cp.bin -b 748800

lisa zep exec cskburn -s COMx -C 6 0x200000 ./app_wasm/build/thinker_resnet18.aot -b 748800

lisa zep exec cskburn -s COMx -C 6 0x300000 ./resource/resnet18_model.bin -b 748800

其中 COMx 為 自己板子的串口,我的是COM3,需要修改對應的串口名字
./resource/resnet18_model.bin 是自己的模型,需要修改成對應的路徑

4.串口查看結(jié)果
使用官方提供的串口工具
去查看模型結(jié)果
連接板子后,按動復位鍵,就會看到結(jié)果

title=

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
收藏 人收藏

    評論

    相關推薦

    【附實操視頻】CSK6模型開發(fā)板接入國內(nèi)主流大模型(星火大模型、文心一言、豆包、kimi、智譜glm、通義千問)

    接入文心一言、通義千問、豆包、智譜glm、kimi等國內(nèi)的大模型,但由于各家接口和數(shù)據(jù)格式不一樣,調(diào)試驗證會浪費很多時間。因此本篇以CSK6
    發(fā)表于 08-22 10:12

    智能硬件接入主流大模型做語音交互(附文心一言、豆包、kimi、智譜glm、通義千問示例)

    本帖最后由 jf_40317719 于 2024-8-21 19:17 編輯 多模態(tài)交互離不開硬件載體,近期有不少開發(fā)者在研究CSK6模型開發(fā)板除了使用出廠示例自帶的星火大
    發(fā)表于 08-21 19:13

    NVIDIA NeMo加速并簡化自定義模型開發(fā)

    如果企業(yè)希望充分發(fā)揮出 AI 的力量,就需要根據(jù)其行業(yè)需求量身定制的自定義模型
    的頭像 發(fā)表于 07-26 11:17 ?576次閱讀
    NVIDIA NeMo加速并簡化<b class='flag-5'>自定義</b><b class='flag-5'>模型</b>開發(fā)

    NVIDIA AI Foundry 為全球企業(yè)打造自定義 Llama 3.1 生成式 AI 模型

    Foundry 提供從數(shù)據(jù)策管、合成數(shù)據(jù)生成、微調(diào)、檢索、防護到評估的全方位生成式 AI 模型服務,以便部署自定義 Llama 3.1 NVIDIA NIM 微服務和新的 NVIDIA NeMo
    發(fā)表于 07-24 09:39 ?623次閱讀
    NVIDIA AI Foundry 為全球企業(yè)打造<b class='flag-5'>自定義</b> Llama 3.1 生成式 AI <b class='flag-5'>模型</b>

    CSK6視覺語音大模型AI開發(fā)板入門資源合集(硬件資料、大模型語音/多模態(tài)交互/英語評測SDK合集)

    IO 芯片 CH32 擴展引出的按鍵,支持輪詢檢測。 15CSK6 功能按鍵區(qū)包括 CSK6 芯片的復位、BOOT、自定義功能按鍵。 16電源LED指示開發(fā)板的供電狀態(tài),供電正常使,
    發(fā)表于 06-18 17:33

    CSK6芯片性能與應用前景分析

    CSK6芯片性能與應用前景分析
    的頭像 發(fā)表于 05-15 09:11 ?478次閱讀

    TSMaster 自定義 LIN 調(diào)度表編程指導

    LIN(LocalInterconnectNetwork)協(xié)議調(diào)度表是用于LIN總線通信中的消息調(diào)度的一種機制,我們收到越來越多來自不同用戶希望能夠通過接口實現(xiàn)自定義LIN調(diào)度表的需求。所以在
    的頭像 發(fā)表于 05-11 08:21 ?424次閱讀
    TSMaster <b class='flag-5'>自定義</b> LIN 調(diào)度表編程指導

    HarmonyOS開發(fā)實例:【自定義Emitter】

    使用[Emitter]實現(xiàn)事件的訂閱和發(fā)布,使用[自定義彈窗]設置廣告信息。
    的頭像 發(fā)表于 04-14 11:37 ?880次閱讀
    HarmonyOS開發(fā)實例:【<b class='flag-5'>自定義</b>Emitter】

    鴻蒙ArkUI實例:【自定義組件】

    組件是 OpenHarmony 頁面最小顯示單元,一個頁面可由多個組件組合而成,也可只由一個組件組合而成,這些組件可以是ArkUI開發(fā)框架自帶系統(tǒng)組件,比如?`Text`?、?`Button`?等,也可以是自定義組件,本節(jié)筆者簡單介紹一下自定義組件的語法規(guī)范。
    的頭像 發(fā)表于 04-08 10:17 ?484次閱讀

    基于YOLOv8實現(xiàn)自定義姿態(tài)評估模型訓練

    Hello大家好,今天給大家分享一下如何基于YOLOv8姿態(tài)評估模型,實現(xiàn)在自定義數(shù)據(jù)集上,完成自定義姿態(tài)評估模型的訓練與推理。
    的頭像 發(fā)表于 12-25 11:29 ?2418次閱讀
    基于YOLOv8實現(xiàn)<b class='flag-5'>自定義</b>姿態(tài)評估<b class='flag-5'>模型</b>訓練

    博途用戶自定義庫的使用

    博途官方提供了很多庫,比如:基本函數(shù)庫、通信庫、安全庫、驅(qū)動庫等等,用戶可以使用庫中的函數(shù)/函數(shù)塊來完成具體的控制任務。除了官方的庫,我們也可以創(chuàng)建自己的庫(用戶自定義庫)。比如,把項目
    的頭像 發(fā)表于 12-25 10:08 ?664次閱讀
    博途用戶<b class='flag-5'>自定義</b>庫的使用

    基于CSK6視覺AI開發(fā)套件實現(xiàn)剪子包袱錘游戲

    本文來自極術社區(qū)CSK6視覺AI開發(fā)套件試用活動文章。作者用CSK6芯片支持的手勢識別能
    的頭像 發(fā)表于 12-05 09:56 ?1057次閱讀
    基于<b class='flag-5'>聆</b><b class='flag-5'>思</b><b class='flag-5'>CSK6</b>視覺AI開發(fā)套件實現(xiàn)剪子包袱錘游戲

    NVIDIA 加快企業(yè)自定義生成式 AI 模型開發(fā)

    的業(yè)務數(shù)據(jù)進行自定義。 如今,免費、開源的大語言模型對企業(yè)來說就像是一頓“自助餐”。但對于構建自定義生成式 AI 應用的開發(fā)者來說,這頓“大餐”可能會讓他們應接不暇,因為他們需要滿足各種不同的項目和業(yè)務
    的頭像 發(fā)表于 11-16 21:15 ?492次閱讀
    NVIDIA 加快企業(yè)<b class='flag-5'>自定義</b>生成式 AI <b class='flag-5'>模型</b>開發(fā)

    如何在Matlab中自定義Message

    for ROS Custom Messages 工具,如果低于這個版本就需要通過鏈接ROS Toolbox Interface for ROS Custom Messages 下載。 自定義 Message
    的頭像 發(fā)表于 11-15 18:12 ?1037次閱讀
    如何在Matlab中<b class='flag-5'>自定義</b>Message

    Android端自定義鈴聲 MobPush對安卓端自定義鈴聲的教程

    如何為APP推送設置獨特的通知鈴聲呢?本次帶來的是MobPush對安卓端自定義鈴聲的教程,快來看看吧~
    的頭像 發(fā)表于 10-21 15:34 ?1020次閱讀
    Android端<b class='flag-5'>自定義</b>鈴聲 MobPush對安卓端<b class='flag-5'>自定義</b>鈴聲的教程