darknet源码解读-load_data

    这里的数据加载部分的代码由detector.c文件中train_detector函数中load_data处开始解读。

void train_detector(char *datacfg, char *cfgfile, char *weightfile, int *gpus, int ngpus, int clear)
{
    list *options = read_data_cfg(datacfg);
    char *train_images = option_find_str(options, "train", "data/train.list");
	//store weights?
    char *backup_directory = option_find_str(options, "backup", "/backup/");

    srand(time(0));

	//from /a/b/yolov2.cfg extract yolov2
    char *base = basecfg(cfgfile); //network config
    printf("%s\n", base);
	
    float avg_loss = -1;
    network **nets = calloc(ngpus, sizeof(network));

    srand(time(0));
    int seed = rand();
    int i;
    for(i = 0; i < ngpus; ++i){
        srand(seed);
#ifdef GPU
        cuda_set_device(gpus[i]);
#endif
		//create network for every GPU
        nets[i] = load_network(cfgfile, weightfile, clear);
        nets[i]->learning_rate *= ngpus;
    }
    srand(time(0));
    network *net = nets[0];

	//subdivisions,why not divide?
    int imgs = net->batch * net->subdivisions * ngpus;
    printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay);

	data train, buffer;

    //the last layer e.g. [region] for yolov2
    layer l = net->layers[net->n - 1];

    int classes = l.classes; 
    float jitter = l.jitter;

    list *plist = get_paths(train_images);
    //int N = plist->size;
    char **paths = (char **)list_to_array(plist);

    load_args args = get_base_args(net);
    args.coords = l.coords;
    args.paths = paths;
    args.n = imgs;        //一次加载的数量       
    args.m = plist->size; //总的图片数量
    args.classes = classes;
    args.jitter = jitter;
    args.num_boxes = l.max_boxes;
    args.d = &buffer;
    args.type = DETECTION_DATA;
    //args.type = INSTANCE_DATA;
    args.threads = 64;

	/*n张图片以及图片上的truth box会被加载到buffer.X,buffer.y里面去*/
    pthread_t load_thread = load_data(args); 
    ....
}

在输入到load_data函数的args结构中有几个参数需要关注,args.n它是表示这一次加载的图像的数量,args.m是表示训练集中图像的总量,args.num_boxes表示一张图像中允许的最大的检测框的数量。

pthread_t load_data(load_args args)
{
    pthread_t thread;
    struct load_args *ptr = calloc(1, sizeof(struct load_args));
    *ptr = args; //e.g. ptr->path from args.path
    if(pthread_create(&thread, 0, load_threads, ptr)) error("Thread creation failed");
    return thread;
}

load_data函数里面会创建一个load_threads的线程,从名字上来理解它是一个用来加载线程的线程,数据的加载并不是由这个线程直接来负责的,它更像是一个数据加载线程管理者的角色。

void *load_threads(void *ptr)
{
    int i;
    load_args args = *(load_args *)ptr;
    if (args.threads == 0) args.threads = 1;
	
    data *out = args.d; 
    int total = args.n;
    free(ptr);

	data *buffers = calloc(args.threads, sizeof(data));
	
    pthread_t *threads = calloc(args.threads, sizeof(pthread_t));
    for(i = 0; i < args.threads; ++i){
        args.d = buffers + i;
		//why not total/args.threads?
        args.n = (i+1) * total/args.threads - i * total/args.threads;
        threads[i] = load_data_in_thread(args);
    }

	//waiting for thread to load data
    for(i = 0; i < args.threads; ++i){
        pthread_join(threads[i], 0);
    }
	
    *out = concat_datas(buffers, args.threads);
    out->shallow = 0;
    for(i = 0; i < args.threads; ++i){
        buffers[i].shallow = 1;
        free_data(buffers[i]);
    }
    free(buffers);
    free(threads);
    return 0;
}

为什么上面说load_threads是一个数据加载线程管理者的角色就是因为在load_data_in_thread中会创建真正负责加载数据的线程,load_threads函数内部保存这些数据加载子线程的线程id,通过pthread_join函数等待这些子线程完成数据加载。创建多少个子线程由传入的args.threads成员决定,因为一次加载的图像的数量是args.n,现在有args.threads个线程去完成这项工作,所以分配到单个线程的话只需要去加载args.n/args.threads张图像。

pthread_t load_data_in_thread(load_args args)
{
    pthread_t thread;
    struct load_args *ptr = calloc(1, sizeof(struct load_args));
    *ptr = args;
    if(pthread_create(&thread, 0, load_thread, ptr)) error("Thread creation failed");
    return thread;
}

void *load_thread(void *ptr)
{
    //printf("Loading data: %d\n", rand());
    load_args a = *(struct load_args*)ptr;
    if(a.exposure == 0) a.exposure = 1;
    if(a.saturation == 0) a.saturation = 1;
    if(a.aspect == 0) a.aspect = 1;

    if (a.type == OLD_CLASSIFICATION_DATA){
        *a.d = load_data_old(a.paths, a.n, a.m, a.labels, a.classes, a.w, a.h);
    } 
    //...省略...
    } else if (a.type == DETECTION_DATA){
    	//detection data
        *a.d = load_data_detection(a.n, a.paths, a.m, a.w, a.h, a.num_boxes, a.classes, a.jitter, a.hue, a.saturation, a.exposure);
    } 
    //...省略...
    }
    free(ptr);
    return 0;
}

load_data_in_thread函数中会创建真正的负责加载数据的子线程load_thread并返回线程描述符,load_thread会根据要加载的数据类型调用相应的函数,我这里只考虑DETECTION_DATA也就是检测数据的情形,因此会进一步调用load_data_detection函数。

/*
m,total of images
n,part of images for this thread
boxes,max number of boxes per picture
*/
data load_data_detection(int n, char **paths, int m, int w, int h, int boxes, int classes, float jitter, float hue, float saturation, float exposure)
{
    char **random_paths = get_random_paths(paths, n, m);
    int i;
    data d = {0};
    d.shallow = 0;

    d.X.rows = n;
    d.X.vals = calloc(d.X.rows, sizeof(float*));
    d.X.cols = h*w*3;

	//n * boxes * 5(x,y,h,w,score)
    d.y = make_matrix(n, 5*boxes); 
    
    for(i = 0; i < n; ++i){
        image orig = load_image_color(random_paths[i], 0, 0); //load an origin image
        image sized = make_image(w, h, orig.c); //make empty image,size is (w,h,c)
        fill_image(sized, .5);

        float dw = jitter * orig.w;
        float dh = jitter * orig.h;

		//width to height ratio after jitter
        float new_ar = (orig.w + rand_uniform(-dw, dw)) / (orig.h + rand_uniform(-dh, dh));
        float scale = rand_uniform(.25, 2);

        float nw, nh;

		//change w,h but keep the ratio,why?
        if(new_ar < 1){
            nh = scale * h;
            nw = nh * new_ar;
        } else {
            nw = scale * w;
            nh = nw / new_ar;
        }

        float dx = rand_uniform(0, w - nw);
        float dy = rand_uniform(0, h - nh);

        place_image(orig, nw, nh, dx, dy, sized);

        random_distort_image(sized, hue, saturation, exposure);

		//rand flip
        int flip = rand()%2;
        if(flip) flip_image(sized);

		//X is ready
        d.X.vals[i] = sized.data;

		//y is ready
        fill_truth_detection(random_paths[i], boxes, d.y.vals[i], classes, flip, -dx/w, -dy/h, nw/w, nh/h);

        free_image(orig);
    }
    free(random_paths);
    return d;
}

这里的核心是这个n次的for循环,每次循环都加载一张图像。由load_image_color将图像文件路径中的图像加载到image结构中,因为我们要求的尺寸是(w,h)的,所以紧接着通过make_image生成一张(w,h,orig.c)的空白图像。将原始图像进行一定的变换后填充到生成的空白图像中,这里面对原始图像一系列变换的意义,我至今不甚了解。整个函数中还有一个比较需要注意的点就是fill_truth_detection这个函数。

void fill_truth_detection(char *path, int num_boxes, float *truth, int classes, int flip, float dx, float dy, float sx, float sy)
{
    char labelpath[4096];
    find_replace(path, "images", "labels", labelpath);
    find_replace(labelpath, "JPEGImages", "labels", labelpath);

    find_replace(labelpath, "raw", "labels", labelpath);
    find_replace(labelpath, ".jpg", ".txt", labelpath);
    find_replace(labelpath, ".png", ".txt", labelpath);
    find_replace(labelpath, ".JPG", ".txt", labelpath);
    find_replace(labelpath, ".JPEG", ".txt", labelpath);
	//上面一大堆就是根据数据集目录结构将图片路径变换成labels文档的路径
	
    int count = 0;
    box_label *boxes = read_boxes(labelpath, &count);

	//disrupt the box order
	randomize_boxes(boxes, count);

	//(x,y,w,h)有所调整,所以truth box也要有所纠正
    correct_boxes(boxes, count, dx, dy, sx, sy, flip);
    if(count > num_boxes) count = num_boxes;

	float x,y,w,h;
    int id;
    int i;
    int sub = 0;

	//what?
    for (i = 0; i < count; ++i) {
        x =  boxes[i].x;
        y =  boxes[i].y;
        w =  boxes[i].w;
        h =  boxes[i].h;
        id = boxes[i].id;

		//什么意思?太小忽略?看样子是的!
        if ((w < .001 || h < .001)) {
            ++sub; 
            continue;
        }

        truth[(i-sub)*5+0] = x;
        truth[(i-sub)*5+1] = y;
        truth[(i-sub)*5+2] = w;
        truth[(i-sub)*5+3] = h;
        truth[(i-sub)*5+4] = id;
    }
    free(boxes);
}

 

<think>我们正在处理一个关于darknet_ros启动卡死的问题。根据引用内容,我们得知用户可能是在ROS环境中使用racecar和darknet_ros功能包时遇到了问题。由于问题描述为启动时卡死,我们需要分析可能的原因并提供解决方案。 可能的原因和解决方案: 1. 硬件资源不足:darknet_ros运行深度学习模型,需要较多计算资源。如果硬件(特别是GPU)不足,可能导致卡死。 解决方案:确保计算机满足最低硬件要求,尤其是GPU显存。可以尝试运行较小的模型(如tiny版本)或使用CPU模式(但性能会下降)。 2. 模型文件缺失或路径错误:darknet_ros需要加载预训练的权重文件和配置文件。如果这些文件缺失或路径配置错误,可能导致程序卡死(实际上可能在等待下载或无法找到文件而阻塞)。 解决方案:检查darknet_ros的配置文件(通常在`darknet_ros/`目录下的yaml文件中)中指定的权重文件和配置文件路径是否正确。确保这些文件存在。如果缺失,需要下载并放置到正确位置。 3. ROS参数配置错误:darknet_ros的启动文件(launch文件)中可能配置了错误的参数,例如摄像头话题名称、模型参数等,导致程序无法正常初始化。 解决方案:检查启动文件(如`darknet_ros.launch`)和配置文件(如`yolo.yaml`),确保话题名称与图像发布的话题一致,模型配置正确。 4. 图像话题未发布:如果darknet_ros订阅的图像话题没有数据,它可能会在等待图像时卡住(这取决于代码实现,但通常不会卡死,而是等待)。但也有可能因为其他同步问题导致卡死。 解决方案:确保有节点在发布图像话题(如摄像头驱动节点)。可以使用`rostopic list`和`rostopic hz /camera/image`(话题名称根据实际情况)来检查。 5. 软件版本不兼容:darknet_ros可能依赖于特定版本的ROS、OpenCV、CUDA等,版本不兼容可能导致崩溃或卡死。 解决方案:检查darknet_ros的README,确认所依赖的软件版本。确保环境一致。 6. 程序bug:darknet_ros本身可能存在bug,导致在某些情况下卡死。 解决方案:查看darknet_ros的issue列表,看是否有类似问题。如果有,按照issue中的解决方案处理。也可以尝试更新到最新版本。 7. 日志输出:运行darknet_ros时,查看终端输出的日志,可能包含错误信息。 解决方案:在启动时,使用`roslaunch darknet_ros darknet_ros.launch`命令,观察终端输出,定位错误。 具体步骤建议: - 首先,检查硬件资源使用情况(如使用`nvidia-smi`查看GPU显存),确认是否资源不足。 - 其次,检查模型文件。darknet_ros默认会尝试下载模型,但有时下载失败。可以手动下载并放置到`darknet_ros`的相应目录(如`darknet_ros/yolo_network_config/weights`和`darknet_ros/yolo_network_config/cfg`)。模型文件可以从官方YOLO网站或darknet_ros提供的链接下载。 - 然后,检查配置文件。打开`darknet_ros/config/ros.yaml`(或类似名称的yaml文件),确认`image_topic`是否正确。同样,检查`yolo.yaml`中的模型设置。 - 运行`roscore`,然后单独运行图像发布节点(例如使用usb_cam发布图像),再启动darknet_ros,观察是否仍然卡死。 - 如果上述步骤无效,尝试在darknet_ros的launch文件中添加`output="screen"`以获取更多输出信息,例如: ```xml <node pkg="darknet_ros" type="darknet_ros" name="darknet_ros" output="screen" /> ``` 然后查看启动时的详细输出。 如果问题仍然存在,建议在darknet_ros的GitHub仓库中提交issue,并提供详细的环境信息和错误日志。 注意:引用内容中提到了下载racecar和darknet_ros功能包,但用户的问题集中在darknet_ros上,所以我们聚焦于此。 根据以上分析,我们给出一个具体的检查步骤示例: 步骤1:检查模型文件 - 进入darknet_ros目录:`cd ~/catkin_ws/src/darknet_ros/darknet_ros/yolo_network_config/weights` - 检查是否有权重文件(如yolov3.weights)。如果没有,需要下载。YOLOv3权重可以从以下链接下载:https://pjreddie.com/media/files/yolov3.weights - 同样,检查cfg目录下是否有对应的配置文件(如yolov3.cfg),如果没有,从darknet的官方仓库获取:https://github.com/pjreddie/darknet/tree/master/cfg 步骤2:修改配置文件 - 打开`darknet_ros/config/ros.yaml`,确认`camera_topic`是否正确(例如,如果是racecar,可能是`/camera/image_raw`,请根据实际调整)。 - 打开`darknet_ros/config/yolov3.yaml`(或类似),检查权重文件和配置文件的路径,例如: ``` yolo_model: config_file: name: yolov3.cfg weight_file: name: yolov3.weights ``` 步骤3:运行测试 - 启动roscore:`roscore` - 在另一个终端,启动一个图像源,例如使用usb_cam:`roslaunch usb_cam usb_cam-test.launch`(确保已安装usb_cam包) - 然后启动darknet_ros:`roslaunch darknet_ros darknet_ros.launch` 观察终端输出,看是否有错误信息。 如果用户按照以上步骤仍然无法解决,可能需要进一步提供日志信息。 相关问题: 1. darknet_ros启动时如何查看详细的日志输出? 2. 如何为darknet_ros配置使用自己的YOLO模型? 3. 在资源受限的设备上如何优化darknet_ros的运行?</think>### darknet_ros 启动卡死问题解决方案 遇到 darknet_ros 启动卡死的问题,通常与硬件资源、配置错误或依赖冲突有关。以下是系统性的排查步骤和解决方案: --- ### 一、核心排查步骤 1. **检查硬件资源** - 运行 `nvidia-smi` 查看 GPU 显存占用(需在启动 darknet_ros 前执行) - 若显存不足,模型加载会卡死,建议: - 改用轻量模型(如 `yolov3-tiny.weights`) - 在 `darknet_ros/config/yolov3.yaml` 中降低分辨率: ```yaml yolo_model: width: 416 # 改为 320 或 224 height: 416 ``` 2. **验证模型文件完整性** - 检查权重文件(.weights)和配置文件(.cfg)是否完整: ```bash cd ~/catkin_ws/src/darknet_ros/darknet_ros/yolo_network_config/weights ls -lh # 确认文件大小正常(e.g. yolov3.weights ≈ 248MB) ``` - 文件缺失时重新下载: ```bash wget https://pjreddie.com/media/files/yolov3.weights ``` 3. **检查话题订阅** - 在 `darknet_ros/config/ros.yaml` 确认图像话题匹配摄像头驱动: ```yaml camera_reading: topic: /camera/image_raw # 需与实际话题一致 ``` - 使用 `rostopic hz /camera/image_raw` 验证图像流是否正常。 --- ### 二、高级解决方案 4. **启用调试输出** 修改启动文件添加输出重定向: ```xml <!-- darknet_ros/launch/darknet_ros.launch --> <node name="darknet_ros" pkg="darknet_ros" type="darknet_ros" output="screen" /> ``` 观察终端输出的错误日志(如 CUDA 错误或模型加载失败)。 5. **CUDA/cuDNN 兼容性** - 运行官方 Darknet 测试: ```bash git clone https://github.com/pjreddie/darknet cd darknet && make ./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg ``` - 若官方测试失败,需重装匹配的 CUDA/cuDNN 版本。 6. **内存泄漏修复(常见于旧版)** 更新 darknet_ros 源码: ```bash cd ~/catkin_ws/src/darknet_ros git pull origin master # 确保版本 ≥ 1.1.4 catkin_make ``` --- ### 三、替代方案 7. **CPU 模式启动** 在 `darknet_ros.launch` 中添加参数: ```xml <param name="darknet_ros/config_path" value="$(find darknet_ros)/config/yolov3.yaml" /> <param name="darknet_ros/gpu" type="bool" value="false" /> ``` 注意:CPU 模式速度显著下降,仅用于验证是否 GPU 相关。 --- ### 典型错误日志分析 | 日志信息 | 解决方案 | |---------|---------| | `CUDA Error: out of memory` | 减少模型尺寸或降低分辨率 | | `Cannot load image from topic` | 检查摄像头驱动和 ros.yaml 配置 | | `Failed loading weights` | 重新下载权重文件 | | `Waiting for image...` | 确认图像话题有数据发布 | > 提示:若仍无法解决,在 GitHub Issue 中搜索类似问题(参考 [darknet_ros Issues](https://github.com/leggedrobotics/darknet_ros/issues))[^1]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值