a一级爱做片免费观看欧美,久久国产一区二区,日本一二三区免费,久草视频手机在线观看

博客專欄

EEPW首頁 > 博客 > 【征程 6】工具鏈 VP 示例為什么能運(yùn)行

【征程 6】工具鏈 VP 示例為什么能運(yùn)行

發(fā)布人:地平線開發(fā)者 時間:2025-04-01 來源:工程師 發(fā)布文章

    1.引言

    在上一篇文章【征程 6】VP 簡介與單算子實(shí)操 中,介紹了 VP 是什么,并以單算子 rotate 為例,介紹了 VP API 使用方法,但對于對 C++不那么熟悉的伙伴,可能會有這樣的疑問:一個 main 函數(shù)就讓 VP 示例跑起來了?沒有什么依賴嗎?CMakeLists.txt 沒看到,xxx.h 頭文件也沒有,甚至連怎么編譯的都沒寫,只有 main 文件中的 C++代碼,還是讓人有點(diǎn)迷迷瞪瞪。由于本人就是屬于對 C++不那么熟悉的同學(xué),所以下面會從我的視角來介紹上一篇文章遺留的問題,如果其中有錯誤或表述不當(dāng)?shù)牡胤剑瑲g迎評論指正。


      2.代碼解讀

      OE/samples/ucp_tutorial/目錄下的結(jié)構(gòu)如下:

      .
      ├── deps_aarch64
      │   ├── appsdk
      │   ├── eigen
      │   ├── fmt
      │   ├── gflags
      │   ├── glog
      │   ├── hlog
      │   ├── nlohmann
      │   ├── opencv
      │   ├── openssl
      │   ├── protobuf
      │   ├── rapidjson
      │   ├── ucp
      │   ├── uWS
      │   └── zlib
      ├── deps_x86
      │   ├── eigen
      │   ├── fmt
      │   ├── gflags
      │   ├── hlog
      │   ├── nlohmann
      │   ├── opencv
      │   └── ucp
      └── vp
          ├── code
          └── vp_samples

      在 vp/code/07_single_rotate 目錄如下:

      .
      ├── CMakeLists.txt
      ├── log_util.h
      ├── main.cpp
      ├── rotate.cpp
      └── rotate.h

      有個感覺即可,后面會細(xì)致的解讀運(yùn)行起一個 VP 示例所依賴的文件。


      2.1 main.cpp

      從 main.cpp 看過去,內(nèi)容以及解讀如下:

      #include <iostream>    // 引入標(biāo)準(zhǔn)輸入輸出庫
      #include "rotate.h"    // 引入自定義頭文件 rotate.h
      
      int32_t main(int32_t argc, char **argv) {  // 主函數(shù),接受命令行參數(shù)
        single_rotate();   // 調(diào)用 single_rotate() 函數(shù)
        return 0;          // 返回 0,表示程序正常結(jié)束,符合 C++ 規(guī)范
      }

      有兩處拿出來解釋下:#include “rotate.h”:該文件是一個自定義頭文件,用于聲明函數(shù)或類,代碼中調(diào)用的 single_rotate() 在 rotate.h 中被聲明,并在相應(yīng)的 rotate.cpp 中被實(shí)現(xiàn)。int32_t main(int32_t argc, char **argv):入口函數(shù) main(),接收兩個參數(shù):argc:命令行參數(shù)個數(shù)(包括程序本身)。argv:存儲命令行參數(shù)的字符串?dāng)?shù)組(char*)。

      2.2 rotate.h

      下面看一下頭文件 rotate.h,代碼作用:聲明 single_rotate() 函數(shù),并防止頭文件被重復(fù)包含。

      #ifndef VP_CODE_07_ROTATE_IMAGE_PROCESS_H_
      #define VP_CODE_07_ROTATE_IMAGE_PROCESS_H_
      
      #include "hobot/vp/hb_vp.h"   // 包含的頭文件,不在當(dāng)前目錄下,為什么能包含?
      #include "log_util.h"         // 包含的頭文件,就在當(dāng)前目錄下
      
      int32_t single_rotate();
      
      #endif  // VP_CODE_07_ROTATE_IMAGE_PROCESS_H_

      來了解一下這段代碼能防止頭文件被重復(fù)包含。

      #ifndef VP_CODE_07_ROTATE_IMAGE_PROCESS_H_
      #define VP_CODE_07_ROTATE_IMAGE_PROCESS_H_
      ...
      #endif  // VP_CODE_07_ROTATE_IMAGE_PROCESS_H_

      這種方式稱為 頭文件保護(hù)(Header Guard),用于防止頭文件的 重復(fù)包含,避免 編譯錯誤。

      還是有些不太理解?詳細(xì)解釋一下:

      1.什么是頭文件保護(hù):在 C/C++ 語言中,頭文件(。h)是用于聲明變量、函數(shù)、類等的文件。為了防止頭文件被 重復(fù)包含(multiple inclusion),通常使用 頭文件保護(hù)(Header Guard),其基本結(jié)構(gòu)是:

        #ifndef HEADER_NAME_H    // 如果 HEADER_NAME_H 未定義
        #define HEADER_NAME_H    // 定義 HEADER_NAME_H
        
        // 頭文件內(nèi)容
        // 變量、函數(shù)、類的聲明等
        
        #endif  // 結(jié)束頭文件保護(hù)

          A.h 頭文件

          // A.h
          #ifndef A_H
          #define A_H
          
          void foo();
          
          #endif  // A_H

          B.h 頭文件

          // B.h
          #ifndef B_H
          #define B_H
          
          #include "A.h"
          
          #endif  // B_H

          main.cpp 中

          // main.cpp
          #include "A.h"
          #include "B.h"

          當(dāng) main.cpp 被編譯時,它會展開 #include:在 A.h 中直接包含 void foo();,B.h 也包含 A.h,再次引入 void foo();這會導(dǎo)致重復(fù)聲明,如果沒有 頭文件保護(hù),編譯器可能會報錯:

          error: redefinition of ‘void foo()’

          當(dāng)使用了上面#ifndef / #define / #endif,就可以避免這個問題,原理如下:

          第一次 包含 A.h 時:

          第二次 再次包含 A.h:

          編譯器在處理頭文件時會進(jìn)行優(yōu)化,所以頭文件保護(hù)不會影響性能,為了避免 重復(fù)包含頭文件 導(dǎo)致的編譯錯誤,提高代碼可維護(hù)性,推薦大家使用頭文件保護(hù)。

          最后,#ifndef 保護(hù)多個頭文件需要不同的宏名,因為宏名重復(fù),也可能導(dǎo)致錯誤,建議使用 文件名相關(guān)的宏 方便記憶排查。

          2.3 log_util.h

          在 rotate.h 中包含了 log_util.h,定義了一些用于日志打印的宏,具體的代碼解讀可見文章:【征程 6】工具鏈 VP 示例中日志打印解讀

          2.4 rotate.cpp

          該文件解讀可見文章:【征程 6】VP 簡介與單算子實(shí)操

          2.5 CMakeLists.txt

          想了解 VP 示例中 CMakeLists.txt 的嵌套以及運(yùn)行邏輯,可見文章:【征程 6】工具鏈 VP 示例中 Cmakelists 解讀。

          2.6 build.sh

          該 Bash 腳本 用于 構(gòu)建 aarch64(ARM64)或 x86(PC 端)架構(gòu)的項目,并支持 自動檢測 gcc 版本,確保編譯環(huán)境正確。

          # 默認(rèn)編譯 ARM64 版本、 Release 模式
          arch=aarch64
          build_type=release
          
          # 顯示幫助信息
          function show_usage() {
          cat <<EOF
          
          Usage: bash -e $0 <options>    # 第一個參數(shù)是目標(biāo)架構(gòu),后面參數(shù)可選
          available options:
            -a|--arch: set arch ([aarch64|x86]), default is aarch64
            -h|--help
          EOF
          exit
          }
          
          # 檢查gcc版本
          function check_gcc() {
            export compiler=$(which gcc)
            ### get version code
            MAJOR=$(echo __GNUC__ | $compiler -E -xc - | tail -n 1)    # 獲取 gcc 主版本號
            MINOR=$(echo __GNUC_MINOR__ | $compiler -E -xc - | tail -n 1)    # 獲取 次版本號
            PATCHLEVEL=$(echo __GNUC_PATCHLEVEL__ | $compiler -E -xc - | tail -n 1)    # 獲取 修訂號
            gcc_version=${MAJOR}.${MINOR}.${PATCHLEVEL}
            # 檢查 gcc 是否 >= 5.4.0
            if ((${MAJOR} < 5)) || ((${MAJOR} == 5 && ${MINOR} < 4)) || ((${MAJOR} == 5 && ${MINOR} == 4 && ${PATCHLEVEL} < 0)); then
              echo "Your gcc version is ${gcc_version}"
              echo "x86 GCC version should be >= 5.4.0, please unpack ddk/package/host/gcc-5.4.0.tar.gz to install then re-execute the install.sh."
              exit    # 版本低于 5.4.0,則終止并提示安裝
            else
              echo "GCC version check success. GCC version is ${gcc_version}."
            fi
          }
          
          
          function build_arm() {
              # 刪除舊的 build_arm 目錄
              rm -rf build_arm
              rm -rf outputs
              mkdir build_arm
              cd build_arm
          
              # check environment for arm64
              # 檢查 LINARO_GCC_ROOT 環(huán)境變量
              if [ ! $LINARO_GCC_ROOT ];then
                  echo "Please set environment LINARO_GCC_ROOT correctly"
                  # 若未設(shè)置,則使用默認(rèn)路徑
                  export LINARO_GCC_ROOT=/arm-gnu-toolchain-12.2.rel1-x86_64-aarch64-none-linux-gnu
              else
                  export LINARO_GCC_ROOT=${LINARO_GCC_ROOT}
              fi
              # 設(shè)置 gcc/g++ 交叉編譯器
              export CC="${LINARO_GCC_ROOT}/bin/aarch64-none-linux-gnu-gcc"
              export CXX="${LINARO_GCC_ROOT}/bin/aarch64-none-linux-gnu-g++"
              # 執(zhí)行 CMake 和 Make,選項 PLATFORM_AARCH64=ON
              cmake .. -Dbuild_type=${build_type} -DPLATFORM_AARCH64=ON
              make -j8
              make install
          }
          
          function build_x86() {
              rm -rf build_x86
              rm -rf outputs
              mkdir build_x86
              cd build_x86
              # 調(diào)用 check_gcc,確保 gcc 版本合格
              check_gcc
              # 不使用交叉編譯器,直接使用本機(jī) gcc/g++
              export CC=gcc
              export CXX=g++
              # cmake 選項 PLATFORM_AARCH64=OFF
              cmake .. -Dbuild_type=${build_type} -DPLATFORM_AARCH64=OFF
              make -j8
              make install
          }
          
          # 支持的架構(gòu) aarch64、x86
          ARCH_OPTS=(aarch64 x86)
          # getopt 命令行選項解析工具,用于處理命令行中的選項(如 -a、--arch 等),詳解見下文
          # -o a:h:定義短選項,a:接收一個參數(shù),表示 --arch 選項,h:表示 --help 選項,不接參數(shù)
          # -al arch:,help:定義長選項,arch:接收一個參數(shù),--arch 后需跟一個值,help:不需要參數(shù)
          # -- "$@":"$@" 是傳遞給腳本的所有命令行參數(shù),-- 用于標(biāo)識選項結(jié)束,防止后續(xù)的命令行參數(shù)被當(dāng)作選項解析
          GETOPT_ARGS=`getopt -o a:h -al arch:,help -- "$@"`
          # 通過 eval 命令將 getopt 解析后的選項參數(shù)設(shè)置為當(dāng)前腳本的命令行參數(shù)。確??梢允褂?nbsp;$1, $2 等變量訪問解析后的命令行選項
          eval set -- "$GETOPT_ARGS"
          
          # 當(dāng) $1 不為空時,進(jìn)入循環(huán)
          # $1 是第一個命令行參數(shù),循環(huán)會遍歷所有傳入的參數(shù),直到所有參數(shù)都被處理完。
          while [ -n "$1" ]
          do
            case "$1" in
              -a|--arch)    # 匹配 -a 或 --arch 選項
                arch=$2    # 將第二個參數(shù)(即 --arch 后的值)賦值給變量 arch
                shift 2    # shift 命令會將位置參數(shù)左移 2 位,意味著處理過的選項被移除,接下來可以處理下一個參數(shù)
                # 檢查 arch 是否是有效的選項之一。"${ARCH_OPTS[*]}" 是一個數(shù)組,包含所有有效的架構(gòu)選項。
                # [[ ! "${ARCH_OPTS[*]}" =~ $arch ]]:使用正則表達(dá)式檢查 $arch 是否在 ARCH_OPTS 數(shù)組中。
                # 如果無效,則打印錯誤信息并調(diào)用 show_usage 顯示幫助
                if [[ ! "${ARCH_OPTS[*]}" =~ $arch ]] ; then
                  echo "invalid arch: $arch"
                  show_usage
                fi
                ;;
              # 匹配 -h 或 --help 選項,如果用戶請求幫助,則調(diào)用 show_usage 函數(shù)顯示幫助信息,之后使用 break 跳出循環(huán)。
              -h|--help) show_usage; break;;
              # 當(dāng)遇到 -- 時,停止解析選項,后面的參數(shù)被視為位置參數(shù)
              --) break ;;
              # *用于匹配其他任何不符合上述選項的參數(shù)
              *) 
                echo $1,$2 
                show_usage; 
                break;;
            esac
          done
          
          # 根據(jù) arch 選擇 build_arm 或 build_x86
          if [[ $arch == "aarch64" ]]; then
            build_arm
          else
            build_x86
          fi

          getopt 會返回一個規(guī)范化的、已排序的選項和參數(shù)字符串,存儲在 GETOPT_ARGS 變量中。例如,輸入:

          ./build.sh -a x86 --help

          則 GETOPT_ARGS 可能會被解析成:

          --arch x86 --help

          希望把 build.sh 腳本運(yùn)行起來:

          開啟調(diào)試模式,打印執(zhí)行的每一條命令及其參數(shù)
          set -x
          如果任何命令執(zhí)行失敗(返回非零退出狀態(tài)),則立即終止腳本執(zhí)行
          set -e
          運(yùn)行 build.sh,并傳遞參數(shù) -a x86
          bash build.sh -a x86

          到這兒,項目構(gòu)建編譯就完成了。


            3.程序執(zhí)行

            項目構(gòu)建完成后,會在 vp/vp_samples 下準(zhǔn)備好程序可執(zhí)行的相關(guān)依賴文件

            vp_samples
            .
            ├── data
            └── script_x86
                ├── 07_single_rotate
                │   ├── rotate.jpg
                │   └── run_single_rotate.sh
                └── x86
                    ├── bin
                    │   └── single_rotate
                    └── lib
                        ├── libalog.so.1
                        ├── libarm_model_gdc.so
                        ├── libhb_arm_rpc.so
                        ├── libhbmem.so.1
                        ├── libhbucp.so
                        ├── libhbvp.so
                        ├── libhlog.so -> libhlog.so.1
                        ├── libhlog.so.1 -> libhlog.so.1.14.3
                        ├── libhlog.so.1.14.3
                        ├── libhlog_wrapper.so
                        ├── libopencv_world.so.3.4
                        └── libperfetto_sdk.so

            運(yùn)行 vp_samples/script_x86/07_single_rotate/run_single_rotate.sh 腳本即可。

            # bin可執(zhí)行文件路徑
            bin=../x86/bin/single_rotate
            # 二進(jìn)制文件目錄 ../x86/bin/
            root=../x86/bin/
            # 共享庫目錄 ../x86/lib
            lib=../x86/lib
            
            # 指定運(yùn)行時動態(tài)鏈接庫路徑,確保執(zhí)行 single_rotate 時能找到 ../x86/lib 里的共享庫(.so)
            # ${LD_LIBRARY_PATH} 可能已經(jīng)有其他路徑,: 號保證新路徑追加,不會覆蓋原有路徑
            export LD_LIBRARY_PATH=${lib}:${LD_LIBRARY_PATH}
            # 將 root 和 bin 目錄添加到 PATH 變量,使 single_rotate 可直接運(yùn)行,而無需寫完整路徑。
            export PATH=${root}:${bin}:${PATH}
            
            export HB_DSP_ENABLE_CONFIG_VDSP=true    # 開啟 DSP 配置
            # 設(shè)置 DSP 日志級別
            export HB_DSP_LOG_LEVEL=3    
            export HB_DSP_VDSP_LOG_LEVEL=3
            export HB_UCP_ENABLE_RELAY_MODE=false    # 禁用 UCP 透傳模式
            export HB_DSP_CMODEL_IMAGE=${root}/image/vdsp0    # 指定 DSP 仿真鏡像路徑
            export HB_DSP_CONNECT_RETRY_TIMES=0    # DSP 連接失敗時,不進(jìn)行重試
            
            # ${XTENSA_ROOT} is the root directory where the xtensa compilation environment is installed. 
            # The user needs to install the compilation environment by himself.
            # For details, please see the dsp development document-Linux development environment installation chapter in the oe document
            # For example, in oe document, need to set 'export XTENSA_ROOT=/opt/xtensa/XtDevTools/install/tools/RI-2021.7-linux/XtensaTools/'
            export XTENSA_CORE=Vision_Q8    # 指定使用 Vision_Q8 處理器
            export XTENSA_VERSION=RI-2023.11-linux    # 指定 Xtensa 版本
            # 設(shè)定 Xtensa 處理器的配置文件路徑
            # Xtensa 開發(fā)工具的安裝目錄,用戶需自行安裝并正確設(shè)置該變量
            export XTENSA_SYSTEM=${XTENSA_ROOT}/../../../builds/${XTENSA_VERSION}/${XTENSA_CORE}/config
            export XTENSA_CONFIG=${XTENSA_ROOT}/../../../builds/${XTENSA_VERSION}/${XTENSA_CORE}/config
            
            # 執(zhí)行 single_rotate,并傳遞所有命令行參數(shù) $*
            # ${bin} 解析為 ../x86/bin/single_rotate,所以實(shí)際執(zhí)行:../x86/bin/single_rotate $*
            ${bin} $*

            至此,程序完成運(yùn)行。


            *博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點(diǎn),如有侵權(quán)請聯(lián)系工作人員刪除。




            相關(guān)推薦

            技術(shù)專區(qū)

            關(guān)閉