关于舰船检测项目调试记录

# 关于舰船检测项目的调试记录

项目要求:

  1. 通过摄像头图像采集,在海面状态下实现对船只的精准检测
  2. 嵌入式平台的制板尺寸要严格符合要求
  3. 将检测到的结果通过以太网传输到客户端
  4. 通过控制GPIO,PWM,ADC实现对直流电机的角度控制

1、选择目标检测网络(Yolov5、Yolov5_lite)、训练

将训练后的模型已经转换,并使用onnxsim进行简化

python export.py --weights 'weights/best.pt' --batch-size 1 --img_size 320
pip install onnxsimplifier
python -m onnxsim best.onnx e.onnx

2、通过NCNN将Yolov5_lite部署

在树莓派4b、香橙派zero3、香橙派zero2上进行测试、验证可行性

sudo apt-get install git cmake
sudo apt-get install -y gfortran
sudo apt-get install -y libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
sudo apt-get install --no-install-recommends libboost-all-dev
sudo apt-get install -y libgflags-dev libgoogle-glog-dev liblmdb-dev libatlas-base-dev
#!/bin/bash

sudo apt-get install -y g++
sudo apt-get install -y libncurses5-dev
sudo apt-get install -y zlib1g-dev
sudo apt-get install -y bison
sudo apt-get install -y flex
sudo apt-get install -y unzip
sudo apt-get install -y autoconf
sudo apt-get install -y gawk
sudo apt-get install -y make
sudo apt-get install -y gettext
sudo apt-get install -y gcc
sudo apt-get install -y binutils
sudo apt-get install -y patch
sudo apt-get install -y bzip2
sudo apt-get install -y libz-dev
sudo apt-get install -y asciidoc
sudo apt-get install -y subversion
sudo apt-get install -y sphinxsearch
sudo apt-get install -y libtool
sudo apt-get install -y sphinx-common
sudo apt-get install -y libc6:i386 libgcc1:i386 libstdc++6:i386 -y


sudo apt-get install -y git cmake
sudo apt-get install -y gfortran
sudo apt-get install -y libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
sudo apt-get install -y --no-install-recommends libboost-all-dev
sudo apt-get install -y libgflags-dev libgoogle-glog-dev liblmdb-dev libatlas-base-dev

在Ubuntu虚拟机上进行交叉编译

git clone https://github.com/Tencent/ncnn.git
cd ncnn
mkdir build-aarch64
cd  build-aarch64
cmake -DCMAKE_TOOLCHAIN_FILE=../toolchains/aarch64-linux-gnu.toolchain.cmake .. #可选择开启vulkan -DNCNN_VULKAN=ON
make -j8
make install

在这里插入图片描述
之后将build-aarch64文件夹转移到香橙派上,在 CMakeLists.txt 中添加静态库路径如下,就可以编译自己的工程了。

INCLUDE_DIRECTORIES(/home/ds-h616/ncnn/install/include/ncnn/)
LINK_DIRECTORIES(/home/ds-h616/ncnn/install/lib/)
target_link_libraries(${PROJECT_NAME} ncnn ${OpenCV_LIBS} /home/ds-h616/ncnn/install/lib/libncnn.a)

新建工程文件夹boat_detect,并将onnx模型转换为ncnn,并添加到model文件下

cd ncnn/build
./tools/onnx/onnx2ncnn e.onnx e.param e.bin
优化为fp16精度
./tools/onnxoptimize e.param e.bin eopt.param eopt.bin 65536

在这里插入图片描述将代码添加到src中,示例代码如下

// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
 
#include "layer.h"
#include "net.h"
 
#if defined(USE_NCNN_SIMPLEOCV)
#include "simpleocv.h"
#else
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#endif
#include <float.h>
#include <stdio.h>
#include <vector>
#include <sys/time.h>
 
// 0 : FP16
// 1 : INT8
#define USE_INT8 0
 
// 0 : Image
// 1 : Camera
#define USE_CAMERA 1
 
struct Object
{
    cv::Rect_<float> rect;
    int label;
    float prob;
};
 
static inline float intersection_area(const Object& a, const Object& b)
{
    cv::Rect_<float> inter = a.rect & b.rect;
    return inter.area();
}
 
static void qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right)
{
    int i = left;
    int j = right;
    float p = faceobjects[(left + right) / 2].prob;
 
    while (i <= j)
    {
        while (faceobjects[i].prob > p)
            i++;
 
        while (faceobjects[j].prob < p)
            j--;
 
        if (i <= j)
        {
            // swap
            std::swap(faceobjects[i], faceobjects[j]);
 
            i++;
            j--;
        }
    }
 
    #pragma omp parallel sections
    {
        #pragma omp section
        {
            if (left < j) qsort_descent_inplace(faceobjects, left, j);
        }
        #pragma omp section
        {
            if (i < right) qsort_descent_inplace(faceobjects, i, right);
        }
    }
}
 
static void qsort_descent_inplace(std::vector<Object>& faceobjects)
{
    if (faceobjects.empty())
        return;
 
    qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1);
}
 
static void nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold)
{
    picked.clear();
 
    const int n = faceobjects.size();
 
    std::vector<float> areas(n);
    for (int i = 0; i < n; i++)
    {
        areas[i] = faceobjects[i].rect.area();
    }
 
    for (int i = 0; i < n; i++)
    {
        const Object& a = faceobjects[i];
 
        int keep = 1;
        for (int j = 0; j < (int)picked.size(); j++)
        {
            const Object& b = faceobjects[picked[j]];
 
            // intersection over union
            float inter_area = intersection_area(a, b);
            float union_area = areas[i] + areas[picked[j]] - inter_area;
            // float IoU = inter_area / union_area
            if (inter_area / union_area > nms_threshold)
                keep = 0;
        }
 
        if (keep)
            picked.push_back(i);
    }
}
 
static inline float sigmoid(float x)
{
    return static_cast<float>(1.f / (1.f + exp(-x)));
}
 
// unsigmoid
static inline float unsigmoid(float y) {
    return static_cast<float>(-1.0 * (log((1.0 / y) - 1.0)));
}
 
static void generate_proposals(const ncnn::Mat &anchors, int stride, const ncnn::Mat &in_pad,
                               const ncnn::Mat &feat_blob, float prob_threshold,
                               std::vector <Object> &objects) {
    const int num_grid = feat_blob.h;
    float unsig_pro = 0;
    if (prob_threshold > 0.6)
        unsig_pro = unsigmoid(prob_threshold);
 
    int num_grid_x;
    int num_grid_y;
    if (in_pad.w > in_pad.h) {
        num_grid_x = in_pad.w / stride;
        num_grid_y = num_grid / num_grid_x;
    } else {
        num_grid_y = in_pad.h / stride;
        num_grid_x = num_grid / num_grid_y;
    }
 
    const int num_class = feat_blob.w - 5;
 
    const int num_anchors = anchors.w / 2;
 
    for (int q = 0; q < num_anchors; q++) {
        const float anchor_w = anchors[q * 2];
        const float anchor_h = anchors[q * 2 + 1];
 
        const ncnn::Mat feat = feat_blob.channel(q);
 
        for (int i = 0; i < num_grid_y; i++) {
            for (int j = 0; j < num_grid_x; j++) {
                const float *featptr = feat.row(i * num_grid_x + j);
 
                // find class index with max class score
                int class_index = 0;
                float class_score = -FLT_MAX;
                float box_score = featptr[4];
                if (prob_threshold > 0.6) {
                    // while prob_threshold > 0.6, unsigmoid better than sigmoid
                    if (box_score > unsig_pro) {
                        for (int k = 0; k < num_class; k++) {
                            float score = featptr[5 + k];
                            if (score > class_score) {
                                class_index = k;
                                class_score = score;
                            }
                        }
 
                        float confidence = sigmoid(box_score) * sigmoid(class_score);
 
                        if (confidence >= prob_threshold) {
 
                            float dx = sigmoid(featptr[0]);
                            float dy = sigmoid(featptr[1]);
                            float dw = sigmoid(featptr[2]);
                            float dh = sigmoid(featptr[3]);
 
                            float pb_cx = (dx * 2.f - 0.5f + j) * stride;
                            float pb_cy = (dy * 2.f - 0.5f + i) * stride;
 
                            float pb_w = pow(dw * 2.f, 2) * anchor_w;
                            float pb_h = pow(dh * 2.f, 2) * anchor_h;
 
                            float x0 = pb_cx - pb_w * 0.5f;
                            float y0 = pb_cy - pb_h * 0.5f;
                            float x1 = pb_cx + pb_w * 0.5f;
                            float y1 = pb_cy + pb_h * 0.5f;
 
                            Object obj;
                            obj.rect.x = x0;
                            obj.rect.y = y0;
                            obj.rect.width = x1 - x0;
                            obj.rect.height = y1 - y0;
                            obj.label = class_index;
                            obj.prob = confidence;
 
                            objects.push_back(obj);
                        }
                    } else {
                        for (int k = 0; k < num_class; k++) {
                            float score = featptr[5 + k];
                            if (score > class_score) {
                                class_index = k;
                                class_score = score;
                            }
                        }
                        float confidence = sigmoid(box_score) * sigmoid(class_score);
 
                        if (confidence >= prob_threshold) {
                            float dx = sigmoid(featptr[0]);
                            float dy = sigmoid(featptr[1]);
                            float dw = sigmoid(featptr[2]);
                            float dh = sigmoid(featptr[3]);
 
                            float pb_cx = (dx * 2.f - 0.5f + j) * stride;
                            float pb_cy = (dy * 2.f - 0.5f + i) * stride;
 
                            float pb_w = pow(dw * 2.f, 2) * anchor_w;
                            float pb_h = pow(dh * 2.f, 2) * anchor_h;
 
                            float x0 = pb_cx - pb_w * 0.5f;
                            float y0 = pb_cy - pb_h * 0.5f;
                            float x1 = pb_cx + pb_w * 0.5f;
                            float y1 = pb_cy + pb_h * 0.5f;
 
                            Object obj;
                            obj.rect.x = x0;
                            obj.rect.y = y0;
                            obj.rect.width = x1 - x0;
                            obj.rect.height = y1 - y0;
                            obj.label = class_index;
                            obj.prob = confidence;
 
                            objects.push_back(obj);
                        }
                    }
                }
            }
        }
    }
}
 
static int detect_yolov5(const cv::Mat& bgr, std::vector<Object>& objects)
{
    ncnn::Net yolov5;
 
#if USE_INT8
    yolov5.opt.use_int8_inference=true;
#else
    yolov5.opt.use_vulkan_compute = true;
    yolov5.opt.use_bf16_storage = true;
#endif
 
    // original pretrained model from https://github.com/ultralytics/yolov5
    // the ncnn model https://github.com/nihui/ncnn-assets/tree/master/models
 
#if USE_INT8
    yolov5.load_param("../model/eopt.param");
    yolov5.load_model("../model/eopt.bin");
#else
    yolov5.load_param("../model/eopt.param");
    yolov5.load_model("../model/eopt.bin");
#endif
 
    const int target_size = 320;
    const float prob_threshold = 0.60f;
    const float nms_threshold = 0.60f;
 
    int img_w = bgr.cols;
    int img_h = bgr.rows;
 
    // letterbox pad to multiple of 32
    int w = img_w;
    int h = img_h;
    float scale = 1.f;
    if (w > h)
    {
        scale = (float)target_size / w;
        w = target_size;
        h = h * scale;
    }
    else
    {
        scale = (float)target_size / h;
        h = target_size;
        w = w * scale;
    }
 
    ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR2RGB, img_w, img_h, w, h);
 
    // pad to target_size rectangle
    // yolov5/utils/datasets.py letterbox
    int wpad = (w + 31) / 32 * 32 - w;
    int hpad = (h + 31) / 32 * 32 - h;
    ncnn::Mat in_pad;
    ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 114.f);
 
    const float norm_vals[3] = {1 / 255.f, 1 / 255.f, 1 / 255.f};
    in_pad.substract_mean_normalize(0, norm_vals);
 
    ncnn::Extractor ex = yolov5.create_extractor();
 
    ex.input("images", in_pad);
 
    std::vector<Object> proposals;
 
    // stride 8
    {
        ncnn::Mat out;
        ex.extract("451", out);
 
        ncnn::Mat anchors(6);
        anchors[0] = 10.f;
        anchors[1] = 13.f;
        anchors[2] = 16.f;
        anchors[3] = 30.f;
        anchors[4] = 33.f;
        anchors[5] = 23.f;
 
        std::vector<Object> objects8;
        generate_proposals(anchors, 8, in_pad, out, prob_threshold, objects8);
 
        proposals.insert(proposals.end(), objects8.begin(), objects8.end());
    }
    // stride 16
    {
        ncnn::Mat out;
        ex.extract("479", out);
 
 
        ncnn::Mat anchors(6);
        anchors[0] = 30.f;
        anchors[1] = 61.f;
        anchors[2] = 62.f;
        anchors[3] = 45.f;
        anchors[4] = 59.f;
        anchors[5] = 119.f;
 
        std::vector<Object> objects16;
        generate_proposals(anchors, 16, in_pad, out, prob_threshold, objects16);
 
        proposals.insert(proposals.end(), objects16.begin(), objects16.end());
    }
    // stride 32
    {
        ncnn::Mat out;
        ex.extract("507", out);
 
 
        ncnn::Mat anchors(6);
        anchors[0] = 116.f;
        anchors[1] = 90.f;
        anchors[2] = 156.f;
        anchors[3] = 198.f;
        anchors[4] = 373.f;
        anchors[5] = 326.f;
 
        std::vector<Object> objects32;
        generate_proposals(anchors, 32, in_pad, out, prob_threshold, objects32);
 
        proposals.insert(proposals.end(), objects32.begin(), objects32.end());
    }
 
    // sort all proposals by score from highest to lowest
    qsort_descent_inplace(proposals);
 
    // apply nms with nms_threshold
    std::vector<int> picked;
    nms_sorted_bboxes(proposals, picked, nms_threshold);
 
    int count = picked.size();
 
    objects.resize(count);
    for (int i = 0; i < count; i++)
    {
        objects[i] = proposals[picked[i]];
 
        // adjust offset to original unpadded
        float x0 = (objects[i].rect.x - (wpad / 2)) / scale;
        float y0 = (objects[i].rect.y - (hpad / 2)) / scale;
        float x1 = (objects[i].rect.x + objects[i].rect.width - (wpad / 2)) / scale;
        float y1 = (objects[i].rect.y + objects[i].rect.height - (hpad / 2)) / scale;
 
        // clip
        x0 = std::max(std::min(x0, (float)(img_w - 1)), 0.f);
        y0 = std::max(std::min(y0, (float)(img_h - 1)), 0.f);
        x1 = std::max(std::min(x1, (float)(img_w - 1)), 0.f);
        y1 = std::max(std::min(y1, (float)(img_h - 1)), 0.f);
 
        objects[i].rect.x = x0;
        objects[i].rect.y = y0;
        objects[i].rect.width = x1 - x0;
        objects[i].rect.height = y1 - y0;
    }
 
    return 0;
}
 
static void draw_objects(const cv::Mat& bgr, const std::vector<Object>& objects)
{
    static const char* class_names[] = {
        "face","face_mask"
    };
 
    cv::Mat image = bgr.clone();
 
    for (size_t i = 0; i < objects.size(); i++)
    {
        const Object& obj = objects[i];
 
        fprintf(stderr, "%d = %.5f at %.2f %.2f %.2f x %.2f\n", obj.label, obj.prob,
                obj.rect.x, obj.rect.y, obj.rect.width, obj.rect.height);
 
        cv::rectangle(image, obj.rect, cv::Scalar(0, 255, 0));
 
        char text[256];
        sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);
 
        int baseLine = 0;
        cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
 
        int x = obj.rect.x;
        int y = obj.rect.y - label_size.height - baseLine;
        if (y < 0)
            y = 0;
        if (x + label_size.width > image.cols)
            x = image.cols - label_size.width;
 
        cv::rectangle(image, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),
                      cv::Scalar(255, 255, 255), -1);
 
        cv::putText(image, text, cv::Point(x, y + label_size.height),
                    cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
    }
#if USE_CAMERA
    imshow("camera", image);
    cv::waitKey(1);
#else
    cv::imwrite("result.jpg", image);
#endif
}
 
#if USE_CAMERA
int main(int argc, char** argv)
{
    cv::VideoCapture capture;
    capture.open(0, cv::CAP_V4L2); 
    capture.set(cv::CAP_PROP_FRAME_WIDTH,640);
    capture.set(cv::CAP_PROP_FRAME_HEIGHT,640);
    
    cv::Mat frame;
    while (true)
    {
        capture >> frame;
        cv::Mat m = frame;
 
        std::vector<Object> objects;
        detect_yolov5(frame, objects);
 
        draw_objects(m, objects);
        if (cv::waitKey(30) >= 0)
            break;
    }
}
#else
int main(int argc, char** argv)
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s [imagepath]\n", argv[0]);
        return -1;
    }
 
    const char* imagepath = argv[1];
 
    struct timespec begin, end;
    long time;
    clock_gettime(CLOCK_MONOTONIC, &begin);
 
    cv::Mat m = cv::imread(imagepath, 1);
    if (m.empty())
    {
        fprintf(stderr, "cv::imread %s failed\n", imagepath);
        return -1;
    }
 
    std::vector<Object> objects;
    detect_yolov5(m, objects);
 
    clock_gettime(CLOCK_MONOTONIC, &end);
    time = (end.tv_sec - begin.tv_sec) + (end.tv_nsec - begin.tv_nsec);
    printf(">> Time : %lf ms\n", (double)time/1000000);
 
    draw_objects(m, objects);
 
    return 0;
}
#endif
#CMakeList.txt

cmake_minimum_required(VERSION 2.8)

project(boat_detect)

#opencv
find_package(OpenCV REQUIRED)
INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})

#ncnn
find_package(OpenMP REQUIRED)
if(OPENMP_FOUND)
    message("OPENMP_FOUND")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()

INCLUDE_DIRECTORIES(/home/ds-h616/ncnn/install/include/ncnn/)

LINK_DIRECTORIES(/home/ds-h616/ncnn/install/lib/)

add_executable(${PROJECT_NAME}
    src/boat_detect.cpp)

target_link_libraries(
    ${PROJECT_NAME}
    ncnn
    ${OpenCV_LIBS}
    /home/ds-h616/ncnn/install/lib/libncnn.a
    )

cd build
cmake ..
make
./boat_detect

不断更新数据集并产生新的权重(不断测试、缺啥补啥)

降低虚警概率,加入分类网络(ShuffleNet)

通过先分类,分辨是否为海面场景后在选择是否进行目标检测来降低虚警概率

#include "layer.h"
#include "net.h"
 
#if defined(USE_NCNN_SIMPLEOCV)
#include "simpleocv.h"
#else
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#endif
#include <float.h>
#include <stdio.h>
#include <vector>
#include <sys/time.h>
 
#include<fstream>
#include <iostream>  
#include <chrono>  
#include <opencv2/opencv.hpp>  
 

using namespace cv;

static ncnn::UnlockedPoolAllocator g_blob_pool_allocator;
static ncnn::PoolAllocator g_workspace_pool_allocator;


struct Object
{
    cv::Rect_<float> rect;
    int label;
    float prob;
};


static inline float intersection_area(const Object& a, const Object& b)
{
    cv::Rect_<float> inter = a.rect & b.rect;
    return inter.area();
}


static void qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right)
{
    int i = left;
    int j = right;
    float p = faceobjects[(left + right) / 2].prob;
 
    while (i <= j)
    {
        while (faceobjects[i].prob > p)
            i++;
 
        while (faceobjects[j].prob < p)
            j--;
 
        if (i <= j)
        {
            // swap
            std::swap(faceobjects[i], faceobjects[j]);
 
            i++;
            j--;
        }
    }
 
    #pragma omp parallel sections
    {
        #pragma omp section
        {
            if (left < j) qsort_descent_inplace(faceobjects, left, j);
        }
        #pragma omp section
        {
            if (i < right) qsort_descent_inplace(faceobjects, i, right);
        }
    }
}

static void qsort_descent_inplace(std::vector<Object>& faceobjects)
{
    if (faceobjects.empty())
        return;
 
    qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1);
}
//nms
static void nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold)
{
    picked.clear();
 
    const int n = faceobjects.size();
 
    std::vector<float> areas(n);
    for (int i = 0; i < n; i++)
    {
        areas[i] = faceobjects[i].rect.area();
    }
 
    for (int i = 0; i < n; i++)
    {
        const Object& a = faceobjects[i];
 
        int keep = 1;
        for (int j = 0; j < (int)picked.size(); j++)
        {
            const Object& b = faceobjects[picked[j]];
 
            // intersection over union
            float inter_area = intersection_area(a, b);
            float union_area = areas[i] + areas[picked[j]] - inter_area;
            // float IoU = inter_area / union_area
            if (inter_area / union_area > nms_threshold)
                keep = 0;
        }
 
        if (keep)
            picked.push_back(i);
    }
}
 
 static inline float sigmoid(float x)
{
    return static_cast<float>(1.f / (1.f + exp(-x)));
}

static inline float unsigmoid(float y) {
    return static_cast<float>(-1.0 * (log((1.0 / y) - 1.0)));
}

//生成置信�?
static void generate_proposals(const ncnn::Mat &anchors, int stride, const ncnn::Mat &in_pad,
                               const ncnn::Mat &feat_blob, float prob_threshold,
                               std::vector <Object> &objects) {
    const int num_grid = feat_blob.h;
    float unsig_pro = 0;
    if (prob_threshold > 0.6)
        unsig_pro = unsigmoid(prob_threshold);
 
    int num_grid_x;
    int num_grid_y;
    if (in_pad.w > in_pad.h) {
        num_grid_x = in_pad.w / stride;
        num_grid_y = num_grid / num_grid_x;
    } else {
        num_grid_y = in_pad.h / stride;
        num_grid_x = num_grid / num_grid_y;
    }
 
    const int num_class = feat_blob.w - 5;
 
    const int num_anchors = anchors.w / 2;
 
    for (int q = 0; q < num_anchors; q++) {
        const float anchor_w = anchors[q * 2];
        const float anchor_h = anchors[q * 2 + 1];
 
        const ncnn::Mat feat = feat_blob.channel(q);
 
        for (int i = 0; i < num_grid_y; i++) {
            for (int j = 0; j < num_grid_x; j++) {
                const float *featptr = feat.row(i * num_grid_x + j);
 
                // find class index with max class score
                int class_index = 0;
                float class_score = -FLT_MAX;
                float box_score = featptr[4];
                if (prob_threshold > 0.6) {
                    // while prob_threshold > 0.6, unsigmoid better than sigmoid
                    if (box_score > unsig_pro) {
                        for (int k = 0; k < num_class; k++) {
                            float score = featptr[5 + k];
                            if (score > class_score) {
                                class_index = k;
                                class_score = score;
                            }
                        }
 
                        float confidence = sigmoid(box_score) * sigmoid(class_score);
 
                        if (confidence >= prob_threshold) {
 
                            float dx = sigmoid(featptr[0]);
                            float dy = sigmoid(featptr[1]);
                            float dw = sigmoid(featptr[2]);
                            float dh = sigmoid(featptr[3]);
 
                            float pb_cx = (dx * 2.f - 0.5f + j) * stride;
                            float pb_cy = (dy * 2.f - 0.5f + i) * stride;
 
                            float pb_w = pow(dw * 2.f, 2) * anchor_w;
                            float pb_h = pow(dh * 2.f, 2) * anchor_h;
 
                            float x0 = pb_cx - pb_w * 0.5f;
                            float y0 = pb_cy - pb_h * 0.5f;
                            float x1 = pb_cx + pb_w * 0.5f;
                            float y1 = pb_cy + pb_h * 0.5f;
 
                            Object obj;
                            obj.rect.x = x0;
                            obj.rect.y = y0;
                            obj.rect.width = x1 - x0;
                            obj.rect.height = y1 - y0;
                            obj.label = class_index;
                            obj.prob = confidence;
 
                            objects.push_back(obj);
                        }
                    } else {
                        for (int k = 0; k < num_class; k++) {
                            float score = featptr[5 + k];
                            if (score > class_score) {
                                class_index = k;
                                class_score = score;
                            }
                        }
                        float confidence = sigmoid(box_score) * sigmoid(class_score);
 
                        if (confidence >= prob_threshold) {
                            float dx = sigmoid(featptr[0]);
                            float dy = sigmoid(featptr[1]);
                            float dw = sigmoid(featptr[2]);
                            float dh = sigmoid(featptr[3]);
 
                            float pb_cx = (dx * 2.f - 0.5f + j) * stride;
                            float pb_cy = (dy * 2.f - 0.5f + i) * stride;
 
                            float pb_w = pow(dw * 2.f, 2) * anchor_w;
                            float pb_h = pow(dh * 2.f, 2) * anchor_h;
 
                            float x0 = pb_cx - pb_w * 0.5f;
                            float y0 = pb_cy - pb_h * 0.5f;
                            float x1 = pb_cx + pb_w * 0.5f;
                            float y1 = pb_cy + pb_h * 0.5f;
 
                            Object obj;
                            obj.rect.x = x0;
                            obj.rect.y = y0;
                            obj.rect.width = x1 - x0;
                            obj.rect.height = y1 - y0;
                            obj.label = class_index;
                            obj.prob = confidence;
 
                            objects.push_back(obj);
                        }
                    }
                }
            }
        }
    }
}
 

int classify(const cv::Mat& bgr, std::vector<float>& cls_scores)
{

   printf("h:%d,w:%d\n",bgr.rows,bgr.cols);
   cv::Mat image = bgr.clone();
   
   ncnn::Net cls_net;
   
   cls_net.opt.use_vulkan_compute = true;
   cls_net.opt.use_bf16_storage = false;
   
	 cls_net.opt.lightmode = true;
	 cls_net.opt.num_threads = 2;
	 cls_net.opt.blob_allocator = &g_blob_pool_allocator;
	 cls_net.opt.workspace_allocator = &g_workspace_pool_allocator;
   
   cls_net.load_param("/home/ds-h616/boat_detect/model/sea_sim_1710228-sim-opt.param");
   cls_net.load_model("/home/ds-h616/boat_detect/model/sea_sim_1710228-sim-opt.bin");

	ncnn::Mat in = ncnn::Mat::from_pixels_resize(image.data, ncnn::Mat::PIXEL_BGR2RGB, image.cols, image.rows, 224, 224);

	const float norm_255[3] = { 1 / 255.0f, 1 / 255.0f, 1 / 255.0f };
	// in.substract_mean_normalize(mean_vals, std_vals);
	in.substract_mean_normalize(0, norm_255);
	// in.substract_mean_normalize(mean_vals, std_vals);
	// fprintf(stderr, "input shape: %d %d %d %d\n", in.dims, in.h, in.w, in.c);

	ncnn::Extractor ex = cls_net.create_extractor();

	ex.input("input.1", in);//input �?.param鏂囦欢涓緭鍏ヨ妭鐐瑰悕�?

	ncnn::Mat out;
	ex.extract("1491", out);

 {
      ncnn::Layer* softmax = ncnn::create_layer("Softmax");
      ncnn::ParamDict pd;
      softmax->load_param(pd);
      softmax->forward_inplace(out, cls_net.opt);
      delete softmax;
  }  

  out = out.reshape(out.w * out.h * out.c);


  cls_scores.resize(out.w);

  for (int j = 0; j < out.w; j++) {

      cls_scores[j] = out[j];
      printf("cls_scores[%d]=%f\n", j, cls_scores[j]);
      std::string text = "cls_scores[" + std::to_string(j) + "]=" + std::to_string(cls_scores[j]);
      cv::Point textOrg(50, (j + 1) * 30);
      cv::putText(image, text, textOrg, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 255, 255), 1);

  }
  

  	if (cls_scores[0] >= 0.5) {
        return 1;
    }
    else {
        return -1;
    }


	return 0;
}

static int detect_yolov5(const cv::Mat& bgr, std::vector<Object>& objects,float prob_threshold = 0.65f)
{
  
    //printf("yolo\n");
    //printf("prob_threshold:%f\n",prob_threshold);
    
    ncnn::Net yolov5;
    
    //printf("h:%d,w:%d\n",bgr.rows,bgr.cols);
    
    yolov5.opt.use_vulkan_compute = true;
    yolov5.opt.use_bf16_storage = false;
    	
	  yolov5.opt.lightmode = true;
	  yolov5.opt.num_threads = 2;
	  yolov5.opt.blob_allocator = &g_blob_pool_allocator;
	  yolov5.opt.workspace_allocator = &g_workspace_pool_allocator;
 
    yolov5.load_param("/home/ds-h616/boat_detect/model/eopt316.param");
    yolov5.load_model("/home/ds-h616/boat_detect/model/eopt316.bin");

 
    const int target_size = 320;
    //const float prob_threshold = 0.60f;
    const float nms_threshold = 0.60f;
 
    int img_w = bgr.cols;
    int img_h = bgr.rows;
 
    // letterbox pad to multiple of 32
    int w = img_w;
    int h = img_h;
    float scale = 1.f;
    if (w > h)
    {
        scale = (float)target_size / w;
        w = target_size;
        h = h * scale;
    }
    else
    {
        scale = (float)target_size / h;
        h = target_size;
        w = w * scale;
    }
 
    ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR2RGB, img_w, img_h, w, h);
 
    // pad to target_size rectangle
    // yolov5/utils/datasets.py letterbox
    int wpad = (w + 31) / 32 * 32 - w;
    int hpad = (h + 31) / 32 * 32 - h;
    ncnn::Mat in_pad;
    ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 114.f);
 
    const float norm_vals[3] = {1 / 255.f, 1 / 255.f, 1 / 255.f};
    in_pad.substract_mean_normalize(0, norm_vals);
 
    ncnn::Extractor ex = yolov5.create_extractor();
 
    ex.input("images", in_pad);
 
    std::vector<Object> proposals;
 
    // stride 8
    {
        ncnn::Mat out;
        ex.extract("/model.21/Transpose_output_0", out);
 
        ncnn::Mat anchors(6);
        anchors[0] = 10.f;
        anchors[1] = 13.f;
        anchors[2] = 16.f;
        anchors[3] = 30.f;
        anchors[4] = 33.f;
        anchors[5] = 23.f;
 
        std::vector<Object> objects8;
        generate_proposals(anchors, 8, in_pad, out, prob_threshold, objects8);
 
        proposals.insert(proposals.end(), objects8.begin(), objects8.end());
    }
    // stride 16
    {
        ncnn::Mat out;
        ex.extract("/model.21/Transpose_1_output_0", out);
 
 
        ncnn::Mat anchors(6);
        anchors[0] = 30.f;
        anchors[1] = 61.f;
        anchors[2] = 62.f;
        anchors[3] = 45.f;
        anchors[4] = 59.f;
        anchors[5] = 119.f;
 
        std::vector<Object> objects16;
        generate_proposals(anchors, 16, in_pad, out, prob_threshold, objects16);
 
        proposals.insert(proposals.end(), objects16.begin(), objects16.end());
    }
    // stride 32
    {
        ncnn::Mat out;
        ex.extract("/model.21/Transpose_2_output_0", out);
 
 
        ncnn::Mat anchors(6);
        anchors[0] = 116.f;
        anchors[1] = 90.f;
        anchors[2] = 156.f;
        anchors[3] = 198.f;
        anchors[4] = 373.f;
        anchors[5] = 326.f;
 
        std::vector<Object> objects32;
        generate_proposals(anchors, 32, in_pad, out, prob_threshold, objects32);
 
        proposals.insert(proposals.end(), objects32.begin(), objects32.end());
    }
 
    // sort all proposals by score from highest to lowest
    qsort_descent_inplace(proposals);
 
    // apply nms with nms_threshold
    std::vector<int> picked;
    nms_sorted_bboxes(proposals, picked, nms_threshold);
 
    int count = picked.size();
 
    objects.resize(count);
    for (int i = 0; i < count; i++)
    {
        objects[i] = proposals[picked[i]];
 
        // adjust offset to original unpadded
        float x0 = (objects[i].rect.x - (wpad / 2)) / scale;
        float y0 = (objects[i].rect.y - (hpad / 2)) / scale;
        float x1 = (objects[i].rect.x + objects[i].rect.width - (wpad / 2)) / scale;
        float y1 = (objects[i].rect.y + objects[i].rect.height - (hpad / 2)) / scale;
 
        // clip
        x0 = std::max(std::min(x0, (float)(img_w - 1)), 0.f);
        y0 = std::max(std::min(y0, (float)(img_h - 1)), 0.f);
        x1 = std::max(std::min(x1, (float)(img_w - 1)), 0.f);
        y1 = std::max(std::min(y1, (float)(img_h - 1)), 0.f);
 
        objects[i].rect.x = x0;
        objects[i].rect.y = y0;
        objects[i].rect.width = x1 - x0;
        objects[i].rect.height = y1 - y0;
    }
 
    return 0;
}

static void draw_objects(const cv::Mat& bgr, const std::vector<Object>& objects, const int frame_count)
{
    static const char* class_names[] = {
        "boat"
    };
 
    cv::Mat image = bgr.clone();
 
    for (size_t i = 0; i < objects.size(); i++)
    {
        const Object& obj = objects[i];
 
        fprintf(stderr, "%d = %.5f at %.2f %.2f %.2f x %.2f\n", obj.label, obj.prob,
                obj.rect.x, obj.rect.y, obj.rect.width, obj.rect.height);
 
        cv::rectangle(image, obj.rect, cv::Scalar(0, 255, 0));
 
        char text[256];
        sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);
 
        int baseLine = 0;
        cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
 
        int x = obj.rect.x;
        int y = obj.rect.y - label_size.height - baseLine;
        if (y < 0)
            y = 0;
        if (x + label_size.width > image.cols)
            x = image.cols - label_size.width;
 
        cv::rectangle(image, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),
                      cv::Scalar(255, 255, 255), -1);
 
        cv::putText(image, text, cv::Point(x, y + label_size.height),
                    cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0)); 
  

        //std::vector<int> compression_params;
        //compression_params.push_back(cv::IMWRITE_JPEG_QUALITY);
        //compression_params.push_back(90); 

        //std::vector<uchar> jpeg_buffer;
        //cv::imencode(".jpg", image, jpeg_buffer, compression_params);

        //std::ofstream file("/home/orangepi/Desktop/detect/result_" + std::to_string(frame_count) +".dat", std::ios::out | std::ios::binary);
        //file.write(reinterpret_cast<const char*>(jpeg_buffer.data()), jpeg_buffer.size());
        //file.close();
     
		//cv::imwrite(save_path, image);
      cv::imwrite("/home/ds-h616/boat_detect/detect_images/result_" + std::to_string(frame_count) +".jpg", image);
    }

    	//imshow("camera", image);
    	cv::waitKey(1);

}  


int main(int argc, char** argv){

    cv::VideoCapture capture;
    capture.open(0, cv::CAP_V4L2); 
    capture.set(cv::CAP_PROP_FRAME_WIDTH,320);
    capture.set(cv::CAP_PROP_FRAME_HEIGHT,320);
    
    int total_frames = 0;  
    cv::Mat frame;

   while (true)
  {
      
      capture >> frame;
      std::vector<Object> objects;
      cv::Mat m = frame;
     	//imshow("camera", m);
  	  cv::waitKey(1);

    	std::vector<float> cls_score;
      
      /*
      int ret = classify(frame, cls_score);
       if (ret == 1) {
          detect_yolov5(frame, objects,0.5f);
          draw_objects(frame,objects,total_frames);
      }else{
          detect_yolov5(frame, objects,0.7f);
          draw_objects(frame,objects,total_frames);
      }
      */
      int ret = classify(frame, cls_score);
      if (ret == 1) {
          detect_yolov5(frame, objects);
          draw_objects(m,objects,total_frames++);
      }
      
  }
  
  capture.release();
  
  cv::destroyAllWindows();
  
  return 0;

}

在这里插入图片描述

在香橙派上利用wiringpi控制GPIO,PWM,通过tb6612实现电机控制

在这里插入图片描述

利用Socket实现以太网的图片传输

client.cpp(windows)

#include <iostream>
#include <fstream>
#include <cstring>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

#define SERVER_PORT           5678
#define BUFFER_SIZE           1024
#define FILE_NAME_MAX_SIZE    512

using namespace std;

// 函数用于从完整路径中提取文件名
string extractFileName(const string& filePath) {
    // 查找最后一个路径分隔符
    size_t pos = filePath.find_last_of("/\\");
    if (pos != string::npos) {
        // 如果找到,返回分隔符之后的部分
        return filePath.substr(pos + 1);
    } else {
        // 如果没有找到分隔符,假设整个路径就是一个文件名
        return filePath;
    }
}

int main(int argc, char **argv)
{
    if (argc != 2) {
        cout << "Usage: ./" << argv[0] << " ServerIPAddress\n";
        exit(1);
    }

    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        cout << "WSAStartup Failed!" << endl;
        return 1;
    }

    // 设置一个socket地址结构client_addr, 代表客户机的ip地址和端口
    SOCKADDR_IN client_addr;
    memset(&client_addr, 0, sizeof(client_addr));
    client_addr.sin_family = AF_INET; // internet协议族IPv4
    client_addr.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY表示自动获取本机地址
    client_addr.sin_port = htons(0); // auto allocated, 让系统自动分配一个空闲端口

    // 创建用于internet的流协议(TCP)类型socket,用client_socket代表客户端socket
    SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (client_socket == INVALID_SOCKET) {
        cout << "Create Socket Failed!" << endl;
        WSACleanup();
        return 1;
    }
    else
        cout << "Create Socket Success." << endl;

    // 把客户端的socket和客户端的socket地址结构绑定
    if (bind(client_socket, reinterpret_cast<SOCKADDR*>(&client_addr), sizeof(client_addr)) != 0) {
        cout << "Client Bind Port Failed!" << endl;
        closesocket(client_socket);
        WSACleanup();
        return 1;
    }
    else
        cout << "Client Bind Port Success." << endl;

    // 设置一个socket地址结构server_addr,代表服务器的internet地址和端口
    SOCKADDR_IN server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;

    // 服务器的IP地址来自程序的参数
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    if (server_addr.sin_addr.s_addr == INADDR_NONE) {
        cout << "Server IP Address Error!" << endl;
        closesocket(client_socket);
        WSACleanup();
        return 1;
    }

    server_addr.sin_port = htons(SERVER_PORT);
    int  server_addr_length = sizeof(server_addr);

    // 向服务器发起连接请求,连接成功后client_socket代表客户端和服务器端的一个socket连接
    if (connect(client_socket, reinterpret_cast<SOCKADDR*>(&server_addr), server_addr_length) != 0) {
        cout << "Can Not Connect To " << argv[1] << "!" << endl;
        closesocket(client_socket);
        WSACleanup();
        return 1;
    }
    else
        cout << "Already Connected To " << argv[1] << "." << endl;

    char file_name[FILE_NAME_MAX_SIZE + 1];
    memset(file_name, 0, sizeof(file_name));
    cout << "Please Input File Name On Server: ";
    cin >> file_name;

    char buffer[BUFFER_SIZE];//缓存区
    memset(buffer, 0, sizeof(buffer));
    strncpy_s(buffer, sizeof(buffer), file_name, strlen(file_name) > BUFFER_SIZE ? BUFFER_SIZE : strlen(file_name));
    // 向服务器发送buffer中的数据,此时buffer中存放的是客户端需要接收的文件的名字
    send(client_socket, buffer, BUFFER_SIZE, 0);

    string fileName = extractFileName(file_name);
    ofstream file(fileName, ios::binary);
    if (!file.is_open()) {
        cout << "File: " << fileName << " Can Not Open To Write!" << endl;
        closesocket(client_socket);
        WSACleanup();
        return 1;
    }

    // 从服务器端接收数据到buffer中
    memset(buffer, 0, sizeof(buffer));
    int length = 0;
    while ((length = recv(client_socket, buffer, BUFFER_SIZE, 0)) > 0) {
        file.write(buffer, length);
        memset(buffer, 0, sizeof(buffer));
    }

    if (length < 0) {
        cout << "Receive Data From Server " << argv[1] << " Failed!" << endl;
    }

    cout << "Receive File: " << fileName << " From Server[" << argv[1] << "] Finished!" << endl;

    // 传输完毕,关闭socket
    file.close();
    closesocket(client_socket);
    WSACleanup();
    return 0;
}

server.cpp(linux)

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define SERVER_PORT            5678         // 端口号
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE            1024
#define FILE_NAME_MAX_SIZE     512

using namespace std;

int main(int argc, char **argv)
{
    // 设置一个socket地址结构server_addr,代表服务器ip地址和端口
    struct sockaddr_in   server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htons(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);

    // 创建用于流协议(TCP)socket,用server_socket代表服务器向客户端提供服务的接口
    int server_socket = socket(PF_INET, SOCK_STREAM, 0);
    if (server_socket < 0)
    {
        cout << "Create Socket Failed!" << endl;
        exit(1);
    }
    else
        cout << "Create Socket Success." << endl;

    // 把socket和socket地址结构绑定
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))
    {
        cout << "Server Bind Port: " << SERVER_PORT << " Failed!" << endl;
        exit(1);
    }
    else
        cout << "Client Bind Port Success." << endl;

    // server_socket用于监听
    if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE))
    {
        cout << "Server Listen Failed!" << endl;
        exit(1);
    }
    else
        cout << "Listening...." << endl;

    // 服务器始终监听
    while(1)
    {
        // 定义客户端的socket地址结构client_addr,当收到来自客户端的请求后,调用accept
        // 接受此请求,同时将client端的地址和端口等信息写入client_addr中
        struct sockaddr_in client_addr;
        socklen_t          length = sizeof(client_addr);

        // 接受一个从client端到达server端的连接请求,将客户端的信息保存在client_addr中
        // 如果没有连接请求,则一直等待直到有连接请求为止,这是accept函数的特性
        // accpet返回一个新的socket,这个socket用来与此次连接到server的client进行通信
        // 这里的new_server_socket代表了这个通信通道
        int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);
        if (new_server_socket < 0)
        {
            cout << "Server Accept Failed!" << endl;
            break;
        }
        else
            cout << "Server Accept Success." << endl;

        char buffer[BUFFER_SIZE];
        bzero(buffer, sizeof(buffer));
        length = recv(new_server_socket, buffer, BUFFER_SIZE, 0);
        if (length < 0)
        {
            cout << "Server Recieve Data Failed!" << endl;
            break;
        }
        else
            cout << "Server Recieve Data Success." << endl;

        char file_name[FILE_NAME_MAX_SIZE + 1];
        bzero(file_name, sizeof(file_name));
        strncpy(file_name, buffer,
                strlen(buffer) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer));

        ifstream fp(file_name, ios::binary);  //获取文件操作符
        if (!fp.is_open())
        {
            cout << "File:\t" << file_name << " Not Found!" << endl;
        }
        else
        {
            bzero(buffer, BUFFER_SIZE);
            int file_block_length = 0;
            while( (file_block_length = fp.readsome(buffer, BUFFER_SIZE)) > 0)
            {
                // 发送buffer中的字符串到new_server_socket,实际上就是发送给客户端
                if (send(new_server_socket, buffer, file_block_length, 0) < 0)
                {
                    cout << "Send File:\t" << file_name << " Failed!" << endl;
                    break;
                }

                bzero(buffer, sizeof(buffer));
            }
            fp.close();
            cout << "File:\t" << file_name << " Transfer Finished!" << endl;
        }

        close(new_server_socket);
    }

    close(server_socket);

    return 0;
}

芯片选型(H616核心板方案)

在这里插入图片描述

按接口需求绘制底板并焊接_V1.0

烧录系统固件出现问题(USB口速率问题,已解决)

按接口需求绘制底板并焊接_V1.0

在这里插入图片描述
在这里插入图片描述

测试新板子

厂家提供的ubuntu镜像系统未开启摄像头设备支持,按照厂家技术支持的方式,通过在虚拟机上配置系统内核menuconfig、重新编译并烧录。

在这里插入图片描述
ls /dev/video*
显示 /dev/video0,说明摄像头设备正常,检查opencv摄像头图像采集是否正常

在新板卡上配置opencv、NCNN环境

运行程序,出现系统崩溃、segmentation fault的错误

崩溃日志显示
在这里插入图片描述

  • 通过查阅资料,崩溃日志显示是文件系统错误,通过查看磁盘占用,我们怀疑是inodes太小造成的,尝试更改inodes大小,或者重新挂载文件系统,未果。
  • 不断gdb,查看程序中止的位置,发现在程序大概率卡死在读取模型后/推理前,分析大概率是硬件资源的问题。
尝试开启vulkan,在虚拟机上交叉编译打开vulkan的ncnn

编译后,在板子上安装vulkan驱动,vulkaninfo发现只起用了CPU模拟GPU,并没有调用起硬件GPU,查阅资料发现H616仅支持vulkan1.1,但我们并没有找到Vulkan的1.1版本。。。。。

尝试opencv的DNN推理
  • 只能作为备用方案,速度较慢、而且模型推理结果并不正常,dnn已经三年没有更新维护了,可能存在网络的支持问题
#include <iostream>
#include <vector>
using namespace std;
#include <opencv2/opencv.hpp>

using namespace cv;

#define	SIGMOID(x) (float)(1.0f/(1+exp(-x)))

#define	 USE_LETTERBOX	1

typedef struct {
	int left;
	int top;
	int right;
	int bottom;
	float score;
	int type;
} BBox;

class YOLOv5
{
	private:
		int				mInputWidth;
		int				mInputHeight;
		int				mClassNum;
		vector<int>		mScales;
		vector<int>		mOutputWidth;
		vector<int>		mOutputHeight;
		vector<int>		mAnchors;
		dnn::Net		mNet;

	public:
		YOLOv5(char *model, int width, int height, int classNum, int *anchors);
		~YOLOv5();
		Mat preProcess(Mat img);
		vector<BBox> process(Mat img);
		vector<BBox> postProcess(vector<Mat> outputs);
};
std::vector<cv::String> getOutputsNames(const cv::dnn::Net& net)
{
    static std::vector<cv::String> names;
    if (names.empty())
    {
        //Get the indices of the output layers, i.e. the layers with unconnected outputs
        std::vector<int> outLayers = net.getUnconnectedOutLayers();
         
        //get the names of all the layers in the network
        std::vector<cv::String> layersNames = net.getLayerNames();
         
        // Get the names of the output layers in names
        names.resize(outLayers.size());
        for (size_t i = 0; i < outLayers.size(); ++i)
        names[i] = layersNames[outLayers[i] - 1];
    }
//	for (auto a=names.begin();a!=names.end();a++)
//		cout << *a <<endl;
    return names;
}

bool cmp(const BBox& a, const BBox& b)
{
	return a.score > b.score;
}

float iou(BBox a, BBox b)
{
	Rect r1 = Rect(Point(a.left, a.top),Point(a.right, a.bottom));
	Rect r2 = Rect(Point(b.left, b.top),Point(b.right, b.bottom));
	Rect inter = r1 & r2;
	if (inter.area() <= 0)
		return 0.;
	return inter.area()*1. / (r1.area() + r2.area() - inter.area());
}

void doNMS(vector<BBox>& boxes)
{
	for (size_t i=0;i<boxes.size();i++)
		for (size_t j=i+1;j<boxes.size();j++)
			if (iou(boxes[i],boxes[j]) > 0.85)
			{
				boxes.erase(boxes.begin()+j--);
			}
}

void dumpBBoxes(vector<BBox> boxes)
{
	for (size_t i=0;i<boxes.size();i++)
		printf("%d  %d  %d  %d\n", boxes[i].left, boxes[i].top,boxes[i].right,boxes[i].bottom);
}

YOLOv5::YOLOv5(char *model, int width, int height, int classNum, int *anchors)
{
	mNet = dnn::readNetFromONNX(model);
	for (int i=0;i<18;i++)
		mAnchors.push_back(anchors[i]);

	mInputWidth = width;
	mInputHeight = height;
	mClassNum = classNum;
	
	mScales.push_back(8);
	mScales.push_back(16);
	mScales.push_back(32);

	for (int i=0;i<3;i++)
	{
		mOutputWidth.push_back(mInputWidth/mScales[i]);
		mOutputHeight.push_back(mInputHeight/mScales[i]);
	}
}

YOLOv5::~YOLOv5()
{
}

Mat YOLOv5::preProcess(Mat img)
{
	Mat dst = Mat::zeros(mInputHeight,mInputWidth,CV_8UC3);;
#if USE_LETTERBOX
	float scaleSize = min(1.*mInputWidth/img.cols, 1.*mInputHeight/img.rows);
	int dstWidth = img.cols * scaleSize;
	int dstHeight = img.rows*scaleSize;
	int wIndex = (mInputWidth - dstWidth)/2;
	int hIndex = (mInputHeight - dstHeight)/2;
	Mat resized;
	resize(img,resized, Size(dstWidth,dstHeight));
	dst.setTo(Scalar(127,127,127));
	Rect r = Rect(wIndex,hIndex,dstWidth,dstHeight);
	resized.copyTo(dst(r));
#else
	resize(img,dst,Size(mInputWidth,mInputHeight));
#endif

	return dst;
}

vector<BBox> YOLOv5::postProcess(vector<Mat> outputs)
{
	vector<BBox> boxes;
	//第i个输出层
	for (size_t i=0;i<outputs.size();i++)
	{
		float *data = (float*)(outputs[i].data);
		size_t index = 0;
		//第d个维度
		for (size_t d= 0;d<3;d++)
		{
			//列
			for (size_t m=0;m<mOutputHeight[i];m++)
			{
				//行
				for (size_t n=0;n<mOutputWidth[i];n++)
				{
					float objConf = SIGMOID(data[index+4]);
					if (objConf >= 0.01)
					{
					//	printf("%u %u %u %u\n", i,d,m,n);
						float x = (float)(SIGMOID(data[index+0]) * 2 - 0.5 + n) * mScales[i];
						float y = (float)(SIGMOID(data[index+1]) * 2 - 0.5 + m) * mScales[i];
						float w = (float)pow(SIGMOID(data[index+2])*2,2) * mAnchors[6*i+2*d];
						float h = (float)pow(SIGMOID(data[index+3])*2,2) * mAnchors[6*i+2*d+1];
						//类别
						for (size_t c = 0;c<mClassNum;c++)
						{
							float score = SIGMOID(data[index+5+c]) * objConf;
							if (score  >= 0.01)
							{
								BBox box;
								box.left = x-w/2.;
								box.right = x+w/2;
								box.top = y-h/2.;
								box.bottom = y+h/2.;
								box.score = score;
								box.type = c;
								boxes.push_back(box);
							}
						}
					}
					index += (5+mClassNum);
				}
			}
		}
	}
	
	if (boxes.size() > 1)
	{
		std::sort(boxes.begin(),boxes.end(), cmp);
		doNMS(boxes);
	}
	return boxes;
}

vector<BBox> YOLOv5::process(Mat img)
{
	Mat resized = preProcess(img);
	Mat blob;
	dnn::blobFromImage(resized, blob, 1/255., Size(mInputWidth, mInputHeight), Scalar(0.,0.,0.), true, false);
	mNet.setInput(blob);
	vector<Mat> outputs;
	mNet.forward(outputs, getOutputsNames(mNet));
	vector<BBox> boxes = postProcess(outputs);
	
#if USE_LETTERBOX
	float scaleSize = min(1.*mInputWidth/img.cols, 1.*mInputHeight/img.rows);
	int dstWidth = img.cols * scaleSize;
	int dstHeight = img.rows*scaleSize;
	int wIndex = (mInputWidth - dstWidth)/2;
	int hIndex = (mInputHeight - dstHeight)/2;
	for (size_t i=0;i<boxes.size();i++)
	{
		boxes[i].left  = (boxes[i].left - wIndex) * img.cols / dstWidth;
		boxes[i].right  = (boxes[i].right - wIndex) * img.cols / dstWidth;
		boxes[i].top  = (boxes[i].top - hIndex) * img.rows / dstHeight;
		boxes[i].bottom  = (boxes[i].bottom - hIndex) * img.rows / dstHeight;
	}
#else
	for (size_t i=0;i<boxes.size();i++)
	{
		boxes[i].left = boxes[i].left * img.cols/mInputWidth;
		boxes[i].right = boxes[i].right * img.cols/mInputWidth;
		boxes[i].top = boxes[i].top * img.rows / mInputHeight;
		boxes[i].bottom = boxes[i].bottom * img.rows / mInputHeight;
	}
#endif
	return boxes;
}


int main(int argc, char** argv)
{
	int anchors[] = {10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326};

	YOLOv5 *v5 = new YOLOv5(argv[1], 640,480,10,anchors);

    Mat frame = imread(argv[2]);
	vector<BBox> results = v5->process(frame);

	for (size_t i=0;i<results.size();i++)
	{
		int left = results[i].left;
		int right = results[i].right;
		int top = results[i].top;
		int bottom = results[i].bottom;
		rectangle(frame, Point(left,top),Point(right,bottom),Scalar(0,0,255),4,4,0);
	}
	resize(frame,frame,Size(1920,1080));
	imshow("result",frame);
	waitKey(0);
    return 0;
}

尝试直接移植pytorch使用onnx或其他推理框架MNN等
对NCNN进行参数配置,内存优化,规定线程成功解决系统崩溃问题
  • 线程为2时,CPU占用稳定在45%左右
cls_net.opt.use_vulkan_compute = true;
cls_net.opt.use_bf16_storage = false; 
//H616貌似不支持bf16精度计算,如果设为true检测结果不正常,在香橙派zero2(h616)上也如此

cls_net.opt.lightmode = true;
cls_net.opt.num_threads = 2;
cls_net.opt.blob_allocator = &g_blob_pool_allocator;
cls_net.opt.workspace_allocator = &g_workspace_pool_allocator;
	 
cls_net.load_param("/home/ds-h616/boat_detect/model/sea_sim_1710228-sim-opt.param");
cls_net.load_model("/home/ds-h616/boat_detect/model/sea_sim_1710228-sim-opt.bin");

操作GPIO、PWM、LRADC

修改设备树文件,将PI11引脚复用为PWM1,与uart3引脚冲突,注释掉uart3。

vim /root/tina/device/config/chips/h616/configs/p2/board.dts
vim /root/tina/lichee/linux-4.9/arch/arm64/boot/dts/sunxi/sun50iw9p1.dtsi
  • 在sun50iw9p1.dtsi文件中添加或修改如下代码
                pwm: pwm@0300a000 {
                        compatible = "allwinner,sunxi-pwm";
                        reg = <0x0 0x0300a000 0x0 0x3ff>;
                        clocks = <&clk_pwm>;
                        pwm-number = <6>;
                        pwm-base = <0x0>;
                        pwms = <&pwm0>, <&pwm1>,<&pwm2>, <&pwm3>, <&pwm4>, <&pwm5>;
                };

                pwm0: pwm0@0300a010 {
                        compatible = "allwinner,sunxi-pwm0";
                        pinctrl-names = "active", "sleep";
                        reg_base = <0x0300a000>;
                };

                pwm1: pwm1@0300a011 {
                        compatible = "allwinner,sunxi-pwm1";
                        pinctrl-names = "active", "sleep";
                        reg_base = <0x0300a000>;
                };

                pwm2: pwm2@0300a012 {
                        compatible = "allwinner,sunxi-pwm2";
                        pinctrl-names = "active", "sleep";
                        reg_base = <0x0300a000>;
                };

                pwm3: pwm3@0300a013 {
                        compatible = "allwinner,sunxi-pwm3";
                        pinctrl-names = "active", "sleep";
                        reg_base = <0x0300a000>;
                };

                pwm4: pwm4@0300a014 {
                        compatible = "allwinner,sunxi-pwm4";
                        pinctrl-names = "active", "sleep";
                        reg_base = <0x0300a000>;
                };

                pwm5: pwm5@0300a015 {
                        compatible = "allwinner,sunxi-pwm5";
                        pinctrl-names = "active", "sleep";
                        reg_base = <0x0300a000>;
                };


  • 在board.dts中添加如下代码
&pio {
...............//省略其他模块的引脚设置
						pwm1_pin_a: pwm1@0 {
                                pins = "PI11";#引脚名
                                function = "pwm1";
                                allwinner,muxsel = <0x05>;#复用为PWM1
                                allwinner,drive = <0xffffffff>;#驱动能力
                                allwinner,pull = <0>;
                                allwinner,data = <0xffffffff>;
                        };
                        pwm1_pin_b: pwm1@1 {
                                pins = "PI11";
                                allwinner,function = "io_disabled";
                                allwinner,muxsel = <0x07>;
                                allwinner,drive = <0xffffffff>;
                                allwinner,pull = <0>;
                                allwinner,data = <0xffffffff>;
                        };
...............//省略其他模块的引脚设置
};

                pwm1: pwm1@0300a011 {
                        pinctrl-names = "active", "sleep";
                        pinctrl-0 = <&pwm1_pin_a>;
                        pinctrl-1 = <&pwm1_pin_b>;
                        status = "okay";
                        clk_bypass_output = <0x00>;#禁用clk旁路输出
                };
  • 设置PWM工作频率和占空比
echo 1 > /sys/class/pwm/pwmchip0/export
echo 1000 > /sys/class/pwm/pwmchip0/pwm1/period
echo 500 > /sys/class/pwm/pwmchip0/pwm1/duty_cycle
echo 1 > /sys/class/pwm/pwmchip0/pwm1/enable
cat /sys/kernel/debug/pwm #查看PWM节点
cat /sys/kernel/debug/pinctrl/pinctrl-handles #查看IO节点
  • 设置GPIO属性
echo PI12 > /sys/kernel/debug/sunxi_pinctrl/sunxi_pin
echo PI12 1 > /sys/kernel/debug/sunxi_pinctrl/function
echo PI12 1 > /sys/kernel/debug/sunxi_pinctrl/data

echo PI13 > /sys/kernel/debug/sunxi_pinctrl/sunxi_pin
echo PI13 1 > /sys/kernel/debug/sunxi_pinctrl/function
echo PI13 0 > /sys/kernel/debug/sunxi_pinctrl/data

echo PI14 > /sys/kernel/debug/sunxi_pinctrl/sunxi_pin
echo PI14 1 > /sys/kernel/debug/sunxi_pinctrl/function
echo PI14 1 > /sys/kernel/debug/sunxi_pinctrl/data
  • 读取LRADC上报数据
sudo apt-get install bsdmainutils
hexdump /dev/input/event0

c语言操作读写PWM模块

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>

#define HIGH "1"
#define LOW "0"


int write_to_file(const char *filepath, const char *value) {
    int fd = open(filepath, O_WRONLY);
    if (fd < 0) {
        perror("open");
        return -1;
    }

    ssize_t len = strlen(value);
    ssize_t ret = write(fd, value, len);
    if (ret < 0) {
        perror("write");
        close(fd);
        return -1;
    } else if (ret < len) {
        fprintf(stderr, "Incomplete write\n");
        close(fd);
        return -1;
    }

    close(fd);
    return 0;
}

int setup_pwm() {

    if (write_to_file("/sys/class/pwm/pwmchip0/export", "1") < 0) {
        fprintf(stderr, "Failed to export PWM chip\n");
        return -1;
    }

    if (write_to_file("/sys/class/pwm/pwmchip0/pwm1/period", "1000") < 0) {
        fprintf(stderr, "Failed to set PWM period\n");
        return -1;
    }

    if (write_to_file("/sys/class/pwm/pwmchip0/pwm1/duty_cycle", "500") < 0) {
        fprintf(stderr, "Failed to set PWM duty cycle\n");
        return -1;
    }

    if (write_to_file("/sys/class/pwm/pwmchip0/pwm1/enable", "1") < 0) {
        fprintf(stderr, "Failed to enable PWM\n");
        return -1;
    }

    return 0;
}

int main() {
    const int delay = 1000000;

    if (setup_pwm() < 0) {
        return -1;
    }

    if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/sunxi_pin", "PI12") < 0) {
        fprintf(stderr, "Failed to set pin\n");
        return -1;
    }

    if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/function", "PI12 1") < 0) {
        fprintf(stderr, "Failed to set pin function\n");
        return -1;
    }

    if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/sunxi_pin", "PI13") < 0) {
        fprintf(stderr, "Failed to set pin\n");
        return -1;
    }

    if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/function", "PI13 1") < 0) {
        fprintf(stderr, "Failed to set pin function\n");
        return -1;
    }    
    
    if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/sunxi_pin", "PI14") < 0) {
        fprintf(stderr, "Failed to set pin\n");
        return -1;
    }

    if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/function", "PI14 1") < 0) {
        fprintf(stderr, "Failed to set pin function\n");
        return -1;
    }

    if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/data", "PI14 1") < 0) {
        fprintf(stderr, "Failed to set pin output\n");
        return -1;
    }
    
    while (1) {
        if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/data", "PI12 1") < 0) {
            fprintf(stderr, "Failed to set pin output\n");
            return -1;
        }

        if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/data", "PI13 0") < 0) {
            fprintf(stderr, "Failed to set pin output\n");
            return -1;
        }

        usleep(delay);

        if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/data", "PI12 0") < 0) {
            fprintf(stderr, "Failed to set pin output\n");
            return -1;
        }

        if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/data", "PI13 1") < 0) {
            fprintf(stderr, "Failed to set pin output\n");
            return -1;
        }
        
        usleep(delay);
    }

    return 0;
}


做开机自动登录

在root下创建.bash_profile文件,写入你要执行的程序脚本。再设置板卡root用户自启动。

vim /etc/systemd/system/getty.target.wants/serial-getty@ttyS0.service #串口登录
vim /etc/systemd/system/getty.target.wants/getty@tty1.service 

修改下面的语句

# ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear %I $TERM
ExecStart=-/sbin/agetty --autologin root --noclear %I $TERM

环境安装

#!/bin/bash

sudo apt-get update
sudo apt-get -y upgrade

sudo apt-get install -y g++
sudo apt-get install -y libncurses5-dev
sudo apt-get install -y make
sudo apt-get install -y build-essential

sudo apt-get install -y git cmake
sudo apt-get install -y gfortran
sudo apt-get install -y libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
sudo apt-get install -y --no-install-recommends libboost-all-dev
sudo apt-get install -y libgflags-dev libgoogle-glog-dev liblmdb-dev libatlas-base-dev

sudo apt-get install -y bsdmainutils

TCP以太网传输20240508

实现输入服务器IP,连接成功后,提示是否申请发送图片y/n。按提示输入y,detect_images文件夹下会保存最新识别到的舰船图片,按提示反复输入y,最新的的舰船图片会按序号依次保存。输入n,退出程序。

client.cpp

#include <iostream>
#include <fstream>
#include <cstring>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

#define SERVER_PORT           5678
#define BUFFER_SIZE           1024
#define FILE_NAME_MAX_SIZE    512

using namespace std;

const string SERVER_FILE_NAME = "/home/ds-h616/boat_detect/detect_images/result.jpg"; // 服务器上的文件路径
const string LOCAL_SAVE_PATH = "detect_images/";       // 本地保存路径

// 函数用于从完整路径中提取文件名并去掉扩展名
string extractFileNameWithoutExt(const string& fileName) {
    size_t lastDot = fileName.find_last_of('.');
    size_t lastSlash = fileName.find_last_of("/\\");
    if (lastDot != string::npos) {
        return fileName.substr(lastSlash + 1, lastDot - lastSlash - 1);
    }
    return fileName.substr(lastSlash + 1);
}

int main()
{
    static int fileCounter = 0; // 文件计数器
    string SERVER_IP_ADDRESS;   // 服务器的 IP 地址

    cout << "Enter the server IP address: ";
    cin >> SERVER_IP_ADDRESS; // 获取服务器 IP 地址

    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        cout << "WSAStartup Failed!" << endl;
        return 1;
    }

    while (true) {
        cout << "Do you want to transfer a file? (y/n): ";
        string response;
        cin >> response;
        if (response != "y") break;

        SOCKADDR_IN client_addr, server_addr;
        memset(&client_addr, 0, sizeof(client_addr));
        client_addr.sin_family = AF_INET;
        client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        client_addr.sin_port = htons(0);

        SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (client_socket == INVALID_SOCKET) {
            cout << "Create Socket Failed!" << endl;
            WSACleanup();
            return 1;
        }

        if (bind(client_socket, reinterpret_cast<SOCKADDR*>(&client_addr), sizeof(client_addr)) != 0) {
            cout << "Client Bind Port Failed!" << endl;
            closesocket(client_socket);
            WSACleanup();
            return 1;
        }

        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = inet_addr(SERVER_IP_ADDRESS.c_str());
        server_addr.sin_port = htons(SERVER_PORT);

        if (connect(client_socket, reinterpret_cast<SOCKADDR*>(&server_addr), sizeof(server_addr)) != 0) {
            cout << "Cannot Connect To " << SERVER_IP_ADDRESS << "!" << endl;
            closesocket(client_socket);
            WSACleanup();
            return 1;
        }

        cout << "Connection Established." << endl;

        char buffer[BUFFER_SIZE];
        strncpy_s(buffer, SERVER_FILE_NAME.c_str(), BUFFER_SIZE);
        send(client_socket, buffer, BUFFER_SIZE, 0);

        string baseFileName = extractFileNameWithoutExt(SERVER_FILE_NAME);
        string fullFileName = LOCAL_SAVE_PATH + baseFileName + "_" + to_string(fileCounter++) + ".jpg";
        ofstream file(fullFileName, ios::binary);

        if (!file.is_open()) {
            cout << "File: " << fullFileName << " Cannot Open To Write!" << endl;
            closesocket(client_socket);
            WSACleanup();
            return 1;
        }

        int length = 0;
        while ((length = recv(client_socket, buffer, BUFFER_SIZE, 0)) > 0) {
            file.write(buffer, length);
        }

        if (length < 0) {
            cout << "Receive Data From Server " << SERVER_IP_ADDRESS << " Failed!" << endl;
        } else {
            cout << "Receive File: " << fullFileName << " From Server [" << SERVER_IP_ADDRESS << "] Finished!" << endl;
        }

        file.close();
        closesocket(client_socket);
    }

    WSACleanup();
    return 0;
}

server.cpp

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define SERVER_PORT            5678         // 端口号
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE            1024
#define FILE_NAME_MAX_SIZE     512

using namespace std;

int main(int argc, char **argv)
{
    // 设置一个socket地址结构server_addr,代表服务器ip地址和端口
    struct sockaddr_in   server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htons(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);

    // 创建用于流协议(TCP)socket,用server_socket代表服务器向客户端提供服务的接口
    int server_socket = socket(PF_INET, SOCK_STREAM, 0);
    if (server_socket < 0)
    {
        cout << "Create Socket Failed!" << endl;
        exit(1);
    }
    else
        cout << "Create Socket Success." << endl;

    // 把socket和socket地址结构绑定
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))
    {
        cout << "Server Bind Port: " << SERVER_PORT << " Failed!" << endl;
        exit(1);
    }
    else
        cout << "Client Bind Port Success." << endl;

    // server_socket用于监听
    if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE))
    {
        cout << "Server Listen Failed!" << endl;
        exit(1);
    }
    else
        cout << "Listening...." << endl;

    // 服务器始终监听
    while(1)
    {
        // 定义客户端的socket地址结构client_addr,当收到来自客户端的请求后,调用accept
        // 接受此请求,同时将client端的地址和端口等信息写入client_addr中
        struct sockaddr_in client_addr;
        socklen_t          length = sizeof(client_addr);

        // 接受一个从client端到达server端的连接请求,将客户端的信息保存在client_addr中
        // 如果没有连接请求,则一直等待直到有连接请求为止,这是accept函数的特性
        // accpet返回一个新的socket,这个socket用来与此次连接到server的client进行通信
        // 这里的new_server_socket代表了这个通信通道
        int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);
        if (new_server_socket < 0)
        {
            cout << "Server Accept Failed!" << endl;
            break;
        }
        else
            cout << "Server Accept Success." << endl;

        char buffer[BUFFER_SIZE];
        bzero(buffer, sizeof(buffer));
        length = recv(new_server_socket, buffer, BUFFER_SIZE, 0);
        if (length < 0)
        {
            cout << "Server Recieve Data Failed!" << endl;
            break;
        }
        else
            cout << "Server Recieve Data Success." << endl;

        char file_name[FILE_NAME_MAX_SIZE + 1];
        bzero(file_name, sizeof(file_name));
        strncpy(file_name, buffer,
                strlen(buffer) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer));

        ifstream fp(file_name, ios::binary);  //获取文件操作符
        if (!fp.is_open())
        {
            cout << "File:\t" << file_name << " Not Found!" << endl;
        }
        else
        {
            bzero(buffer, BUFFER_SIZE);
            int file_block_length = 0;
            while( (file_block_length = fp.readsome(buffer, BUFFER_SIZE)) > 0)
            {
                // 发送buffer中的字符串到new_server_socket,实际上就是发送给客户端
                if (send(new_server_socket, buffer, file_block_length, 0) < 0)
                {
                    cout << "Send File:\t" << file_name << " Failed!" << endl;
                    break;
                }

                bzero(buffer, sizeof(buffer));
            }
            fp.close();
            cout << "File:\t" << file_name << " Transfer Finished!" << endl;
        }

        close(new_server_socket);
    }

    close(server_socket);

    return 0;
}

配置静态IP

nano /etc/network/interfaces

source /etc/network/interfaces.d/*
# Network is managed by Network manager
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
  address 192.168.137.36
  netmask 255.255.255.0
  gateway 192.168.137.1
#  dns-nameservers 4.4.4.4
#  dns-nameservers 8.8.8.8

nano /etc/resolv.conf 这个文件可以不用改

# Generated by NetworkManager
nameserver 192.168.137.1

# nameserver 8.8.8.8 # Replace with your nameserver ip
# nameserver 4.4.4.4 # Replace with your nameserver ip

sudo systemctl restart NetworkManager

板卡作为客户端,主机作为服务端,服务端接收多个客户端发送的图片数据20240513

首先考虑持续握手,长连接/短链接。实现客户端每100ms发送一次连接请求,并判断文件是否存在且稳定,如果满足要求则发送,完成后断开。

当我们尝试先判断文件存在且稳定,再建立连接的逻辑时,会经常出现文件传输失败的问题。

因为当你先判断文件是否存在然后再建立连接,这中间可能会有一段时间延迟。在这段时间内,文件状态可能会发生变化,导致不一致的问题。例如:

cpp复制代码if (file_exists_and_stable(FILE_PATH)) {
    // 这里可能有一段时间延迟
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    // 建立连接
}

这种情况下,文件在你判断其稳定之后到你真正建立连接并发送文件之间,可能被其他进程修改或删除,导致文件传输失败。而先建立连接再判断文件状态可以减少这种时间间隔,保证文件状态的一致性。

20240515

采用短链接方式,传一张图像就断开,再次等待连接。然后为了保证图片唯一性,识别的图片按序号排列生成,不是采用同一文件名命名,避免造成写入文件错误。客户端监听文件夹内是否有jpg文件,并排序,将最早被创建或修改的文件发送,完成后删除,关闭socket,等待下一次连接。

client.cpp

#include <iostream>
#include <fstream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <dirent.h>
#include <vector>
#include <algorithm>

#define SERVER_IP "192.168.137.1"
#define SERVER_PORT 5678
#define FILE_DIR "/home/ds-h616/boat_detect/detect_images/"

using namespace std;

bool file_exists_and_stable(string& file_path) {
    DIR *dir;
    struct dirent *ent;
    vector<string> jpg_files;

    if ((dir = opendir(FILE_DIR)) != nullptr) {
        while ((ent = readdir(dir)) != nullptr) {
            string file_name = ent->d_name;
            if (file_name.size() > 4 && file_name.substr(file_name.size() - 4) == ".jpg") {
                jpg_files.push_back(file_name);
            }
        }
        closedir(dir);
    } else {
        cerr << "Could not open directory: " << FILE_DIR << endl;
        return false;
    }

    if (jpg_files.empty()) {
        return false;
    }

    sort(jpg_files.begin(), jpg_files.end(), [](const string &a, const string &b) {
        struct stat stat_a, stat_b;
        stat((FILE_DIR + a).c_str(), &stat_a);
        stat((FILE_DIR + b).c_str(), &stat_b);
        return stat_a.st_mtime < stat_b.st_mtime;
    });

    file_path = FILE_DIR + jpg_files.front();
    struct stat file_stat;
    if (stat(file_path.c_str(), &file_stat) != 0) {
        return false;
    }

    time_t initial_time = file_stat.st_mtime;
    usleep(100000);  // Wait for a short period to ensure no more writes to the file
    stat(file_path.c_str(), &file_stat);  // Re-check the modification time
    return initial_time == file_stat.st_mtime;
}

void send_file(const string& file_path, int sock) {
    ifstream file(file_path, ios::binary);
    if (!file.is_open()) {
        cerr << "Failed to open file: " << file_path << endl;
        return;
    }

    string filename = file_path.substr(file_path.find_last_of("/") + 1);
    send(sock, filename.c_str(), filename.length(), 0);

    char buffer[1024];
    while (!file.eof()) {
        file.read(buffer, sizeof(buffer));
        int bytes_read = file.gcount();

        int total_sent = 0;
        while (total_sent < bytes_read) {
            int bytes_sent = send(sock, buffer + total_sent, bytes_read - total_sent, 0);
            if (bytes_sent < 0) {
                cerr << "Failed to send file data." << endl;
                file.close();
                return;
            }
            total_sent += bytes_sent;
        }
    }
    file.close();
    remove(file_path.c_str()); // Delete the file after sending
    cout << "File sent and deleted: " << file_path << endl;
}

int main() {
    while (true) {
        string file_path;
        if (!file_exists_and_stable(file_path)) {
            cout << "No more files to send." << endl;
            sleep(1);  // Wait for a while before checking for new files
            continue;  // No more files to send, continue to next iteration
        }

        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0) {
            cerr << "Socket creation failed." << endl;
            sleep(1);
            continue;
        }

        sockaddr_in server_addr;
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
        server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);

        if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
            cerr << "Could not connect to server." << endl;
            close(sock);
            sleep(1);
            continue;
        }
        cout << "Connected to server at " << SERVER_IP << ":" << SERVER_PORT << endl;

        send_file(file_path, sock);
        close(sock);
        usleep(100000);  // Short delay before next connection attempt
    }

    return 0;
}

server.cpp

#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <thread>
#include <mutex>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <direct.h>

#pragma comment(lib, "Ws2_32.lib")

#define SERVER_PORT            5678
#define BUFFER_SIZE            1024
#define FILE_NAME_MAX_SIZE     512

using namespace std;

mutex file_count_mutex;

void handle_client(SOCKET client_socket) {
    char buffer[BUFFER_SIZE];
    char file_name[FILE_NAME_MAX_SIZE + 1];

    sockaddr_in client_info = {};
    int addr_size = sizeof(client_info);
    getpeername(client_socket, (sockaddr*)&client_info, &addr_size);
    char* client_ip = inet_ntoa(client_info.sin_addr);

    string directory = "./" + string(client_ip);
    _mkdir(directory.c_str());

    ZeroMemory(file_name, sizeof(file_name));
    int bytes = recv(client_socket, file_name, FILE_NAME_MAX_SIZE, 0);
    if (bytes > 0) {
        file_name[bytes] = '\0';
        string final_file_name = string(file_name);
        string file_path = directory + "/" + final_file_name;

        ofstream out_file(file_path, ios::binary);
        if (!out_file) {
            cerr << "Failed to open file for writing: " << file_path << endl;
            closesocket(client_socket);
            return;
        }

        do {
            ZeroMemory(buffer, BUFFER_SIZE);
            bytes = recv(client_socket, buffer, BUFFER_SIZE, 0);
            if (bytes > 0) {
                out_file.write(buffer, bytes);
            }
        } while (bytes > 0);

        out_file.close();
        if (bytes == 0) {
            cout << "File received and saved: " << file_path << endl;
        } else {
            cerr << "Failed to receive the file data or connection lost." << endl;
        }
    } else {
        cerr << "Failed to receive the file name." << endl;
    }

    closesocket(client_socket);
}

int main() {
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    SOCKET server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket == INVALID_SOCKET) {
        cerr << "Create Socket Failed!" << endl;
        WSACleanup();
        return 1;
    }

    sockaddr_in server_addr;
    ZeroMemory(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(SERVER_PORT);

    if (bind(server_socket, (sockaddr*)&server_addr, sizeof(server_addr))) {
        cerr << "Server Bind Port: " << SERVER_PORT << " Failed!" << endl;
        WSACleanup();
        return 1;
    }

    if (listen(server_socket, 20)) {
        cerr << "Server Listen Failed!" << endl;
        WSACleanup();
        return 1;
    }

    cout << "Server is listening on port " << SERVER_PORT << endl;
    while (true) {
        sockaddr_in client_addr;
        int client_addr_len = sizeof(client_addr);
        SOCKET client_socket = accept(server_socket, (sockaddr*)&client_addr, &client_addr_len);
        if (client_socket == INVALID_SOCKET) {
            cerr << "Server Accept Failed!" << endl;
            continue;
        }
        thread(handle_client, client_socket).detach();
    }

    closesocket(server_socket);
    WSACleanup();
    return 0;
}

通过systemd 服务文件,自启动程序sudo nano /etc/systemd/system/djqd.service

[Unit]
Description=djqd Service
After=network.target

[Service]
ExecStart=/home/ds-h616/boat_detect/obj/djqd
WorkingDirectory=/home/ds-h616/boat_detect/obj/
User=root
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable djqd.service
# sudo systemctl start djqd.service
sudo systemctl status djqd.service
reboot

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ANIMZLS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值