0

0

【车道线+自动驾驶】用Paddle高层API实现车道线转角回归模型+部署

P粉084495128

P粉084495128

发布时间:2025-07-25 11:44:27

|

812人浏览过

|

来源于php中文网

原创

用Paddle2.2高层API实现车道线打角回归预测模型+Paddle inference边缘设备GPU部署


应该是全网第一个用paddle2.0+高层api实现小车车道线预测回归模型的搭建、训练和部署全流程的项目啦,希望能帮助做小车和机器人的小伙伴快速实现小车的无人驾驶功能~

车道线打角回归预测模型也是每年的全国大学生智能汽车竞赛都会包含的基础任务之一,希望能作为一个baseline让大家快速学习起来。

数据采集的整体架构如下图所示:

【车道线+自动驾驶】用Paddle高层API实现车道线转角回归模型+部署 - php中文网

智能小车上使用标准的广角摄像头作为视觉传感器,其基本参数如下表所示:

【车道线+自动驾驶】用Paddle高层API实现车道线转角回归模型+部署 - php中文网

采集的数据已上传至AI Studio:车道线检测回归数据集

1、导入必要库

In [4]
import osimport cv2import iofrom tqdm import tqdmimport numpy as npimport matplotlib.pyplot as pltfrom PIL import Image as PilImageimport paddlefrom paddle.nn import functional as F

paddle.__version__
'2.2.0'
In [5]
!unzip -oq /home/aistudio/data/data46903/0728_carline.zip

2、数据集制作+数据预处理

数据采集

通过手柄遥控小车,小车实时记录当前图片和手柄的打角数据,从而得到数据集。


数据集信息

本数据集共13448张图像和对应的打角数据。

数据具体格式

图像 >——> 打角数据

每一张图像都会对应一个打角数据。 图像和打角数据分别保存在img和data.txt下,在dataset的建立过程中,我们只需要将图像和打角数据一一对应进行封装即可。

数据对应形式如图所示:

百度智能云·曦灵
百度智能云·曦灵

百度旗下的AI数字人平台

下载

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

【车道线+自动驾驶】用Paddle高层API实现车道线转角回归模型+部署 - php中文网

2.1、定义数据集处理变量

In [2]
img_folder_path = "data/img"dataset_path = "data"txt_path = "data/data.txt"IMAGE_SIZE = (224, 224)
In [6]
# 检查数据集中图像和打角数据个数是否对应import os# 1、读取图像数据和角度数据angle_list = []
f = open(txt_path)for txt in f.readlines():
    txt.split("\n")
    angle_num = int(txt)
    angle_list.append(angle_num)
img_list = []for img_path in tqdm(os.listdir(img_folder_path)):
    img_path = os.path.join(img_folder_path, img_path)
    img_list.append(img_path)# 2、数据归一化(如果过于发散,模型不容易收敛)# 这里我的打角数据大小为:900-2100,中间值为1500for i, angle in enumerate(angle_list): 
    angle = (angle-1500)/600
    angle_list[i] = angletry:
    os.remove(os.path.join(dataset_path, "train.txt"))
    os.remove(os.path.join(dataset_path, "eval.txt"))except:    passtrain_txt = open(os.path.join(dataset_path, "train.txt"), "w")
eval_txt = open(os.path.join(dataset_path, "eval.txt"), "w")
n = 0for img_path, label in tqdm(zip(img_list, angle_list)):
    n += 1
    if n % 10 != 0:
        train_txt.write(img_path+" "+str(label))
        train_txt.write("\n")    else:
        eval_txt.write(img_path+" "+str(label))
        eval_txt.write("\n")
train_txt.close()
eval_txt.close()

2.3、定义数据集MyDataset


创建PaddlePaddle高层API—Dataloder可直接读入的数据集格式

In [7]
import randomimport iofrom paddle.io import Datasetfrom paddle.vision.transforms import transforms as Tfrom PIL import Image as PilImageimport numpy as npclass MyDataset(Dataset):
    """
    数据集定义
    """
    def __init__(self, mode, dataset_path):
        """
        构造函数
        """
        self.image_size = IMAGE_SIZE
        self.mode = mode.lower()
        self.dataset_path = dataset_path        
        assert self.mode in ['train', 'test', 'eval'], \            "mode should be 'train' or 'test' or 'eval', but got {}".format(self.mode)
        
        self.train_images = []
        self.label_list = []        with open(os.path.join(self.dataset_path, ('{}.txt'.format(self.mode))), 'r') as f:            
            for line in tqdm(f.readlines()):
                image, label = line.strip().split(' ')
                
                img = PilImage.open(image)

                self.train_images.append(image)
                self.label_list.append(label)        

    def _load_img(self, path, color_mode='rgb', transforms=[]):
        """
        统一的图像处理接口封装,用于规整图像大小和通道
        """
        with open(path, 'rb') as f:
            img = PilImage.open(io.BytesIO(f.read()))            if color_mode == 'grayscale':                # if image is not already an 8-bit, 16-bit or 32-bit grayscale image
                # convert it to an 8-bit grayscale image.
                if img.mode not in ('L', 'I;16', 'I'):
                    img = img.convert('L')            elif color_mode == 'rgba':                if img.mode != 'RGBA':
                    img = img.convert('RGBA')            elif color_mode == 'rgb':                if img.mode != 'RGB':
                    img = img.convert('RGB')            else:                raise ValueError('color_mode must be "grayscale", "rgb", or "rgba"')            
            return T.Compose([
                T.Resize(self.image_size)
            ] + transforms)(img)    def __getitem__(self, idx):
        """
        返回 image, label
        """
        train_image = self._load_img(self.train_images[idx], 
                                     transforms=[
                                         T.Transpose(), 
                                         T.Normalize(mean=[127.5], std=[127.5]) # 归一化处理,将0~255归一化到-1~1
                                     ]) # 加载原始图像
        label = self.label_list[idx] # 加载Label
    
        # 返回image, label
        train_image = np.array(train_image, dtype='float32')
        label = np.array(label, dtype='float32')        return train_image, label        
    def __len__(self):
        """
        返回数据集总数
        """
        return len(self.train_images)# 定义训练集和验证集train_dataset = MyDataset(mode='train', dataset_path=dataset_path) # 训练数据集val_dataset = MyDataset(mode='eval', dataset_path=dataset_path) # 验证数据集
100%|██████████| 13448/13448 [00:01<00:00, 12012.05it/s]
100%|██████████| 1494/1494 [00:00<00:00, 12004.41it/s]

2.4、查看数据集的具体格式,并展示一张图像和label的对应关系

In [14]
x, y = val_dataset.__getitem__(0)print(x, y)print(x.shape)print(train_dataset.__len__())with open(os.path.join(dataset_path, 'train.txt'), 'r') as f:
    i = 0

    for line in f.readlines():
        image_path, label = line.strip().split(' ')
        image = np.array(PilImage.open(image_path))    
        if i > 2:            break
        # 进行图片的展示
        plt.figure()

        plt.title(label)
        plt.imshow(image.astype('uint8'))
        plt.axis('off')

        plt.show()
        i = i + 1

3、基于高层API自定义模型结构


模型结构可自行修改以获得更好的效果。

In [86]
import paddleimport paddle.nn as nnimport paddle.nn.functional as Fclass My_Model(nn.Layer):
    def __init__(self, num_classes):
        super(My_Model, self).__init__()

        self.conv1 = paddle.nn.Conv2D(in_channels=3, out_channels=32, kernel_size=(3, 3))
        self.pool1 = paddle.nn.MaxPool2D(kernel_size=2, stride=2)

        self.conv2 = paddle.nn.Conv2D(in_channels=32, out_channels=64, kernel_size=(3,3))
        self.pool2 = paddle.nn.MaxPool2D(kernel_size=2, stride=2)

        self.conv3 = paddle.nn.Conv2D(in_channels=64, out_channels=32, kernel_size=(3,3))
        self.pool3 = paddle.nn.MaxPool2D(kernel_size=2, stride=2)
        
        self.conv4 = paddle.nn.Conv2D(in_channels=32, out_channels=16, kernel_size=(3,3))

        self.flatten = paddle.nn.Flatten()

        self.linear1 = paddle.nn.Linear(in_features=9216, out_features=16)
        self.linear2 = paddle.nn.Linear(in_features=16, out_features=1)    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x)
        x = self.pool1(x)

        x = self.conv2(x)
        x = F.relu(x)
        x = self.pool2(x)

        x = self.conv3(x)
        x = F.relu(x)
        x = self.pool3(x)

        x= self.conv4(x)
        x = F.relu(x)

        x = self.flatten(x)
        x = self.linear1(x)
        x = F.relu(x)
        x = self.linear2(x)        return x

paddle.summary(My_Model(num_classes=1), input_size=(1,3, 224,224))

4、模型训练

4.1、配置具体模型的优化器、损失函数及其他可选项

In [89]
import paddleclass SaveBestModel(paddle.callbacks.Callback):
    def __init__(self, target=0.5, path='./best_model', verbose=0):
        self.target = target
        self.epoch = None
        self.path = path    def on_epoch_end(self, epoch, logs=None):
        self.epoch = epoch    def on_eval_end(self, logs=None):
        if logs.get('loss')[0] < self.target:
            self.target = logs.get('loss')[0]
            self.model.save(self.path)            print('best model is loss {} at epoch {}'.format(self.target, self.epoch))

callback_visualdl = paddle.callbacks.VisualDL(log_dir='./')
callback_savebestmodel = SaveBestModel(target=1, path='./model')
callbacks = [callback_visualdl, callback_savebestmodel]# 模型初始化model = paddle.Model(My_Model(num_classes=1)) # 线性回归模型# 优化器optim = paddle.optimizer.Momentum(learning_rate=0.0001, 
                                 momentum=0.9, 
                                 parameters=model.parameters())# 损失函数loss = paddle.nn.MSELoss()
model.prepare(optimizer=optim, loss=loss) # 线性回归模型一定不能用交叉熵,因为这个自带softmax,需要指定输出类别# 用 DataLoader 实现数据加载train_loader = paddle.io.DataLoader(train_dataset, places=paddle.CUDAPlace(0), batch_size=64)
eval_loader = paddle.io.DataLoader(val_dataset, places=paddle.CUDAPlace(0), batch_size= 64)

4.2、模型训练

In [9]
model.fit(train_loader, 
          eval_loader, 
          epochs=5, 
          callbacks=callbacks,
          verbose=1)

4.3、保存模型


这里直接保存模型为可本地部署的形式。

In [10]
# training=True时,只会保存优化器参数和模型参数# training=False时,会保存模型结构、参数和优化器结构model.save("./model_dir/model", training=False)

5、边缘端预测


这里的预测部分和本地预测代码相同,需要保证输入图像的预处理和训练时对齐。

In [90]
import cv2import numpy as npfrom paddle.inference import Configfrom paddle.inference import create_predictor# ————————————————图像预处理函数————————————————def resize(img, target_size):
    """ resize """
    percent = float(target_size) / min(img.shape[0], img.shape[1])
    resized_width = int(round(img.shape[1] * percent))
    resized_height = int(round(img.shape[0] * percent))
    resized_short = cv2.resize(img, (resized_width, resized_height))
    resized = cv2.resize(resized_short, (target_size, target_size))    return resizeddef preprocess(img, target_size):
    mean = [127.5, 127.5, 127.5]
    std = [127.5, 127.5, 127.5]    # resize
    img = resize(img, target_size)    # bgr-> rgb && hwc->chw
    img = img[:, :, ::-1].astype('float32').transpose((2, 0, 1))
    img_mean = np.array(mean).reshape((3, 1, 1))
    img_std = np.array(std).reshape((3, 1, 1))
    img -= img_mean
    img /= img_std    return img[np.newaxis, :]#——————————————————————模型配置、预测相关函数——————————————————————————def predict_config(model_file, params_file):
    # 根据预测部署的实际情况,设置Config
    config = Config()    # 读取模型文件
    config.set_prog_file(model_file)
    config.set_params_file(params_file)    # Config默认是使用CPU预测,若要使用GPU预测,需要手动开启,设置运行的GPU卡号和分配的初始显存。
    config.enable_use_gpu(500, 0)    # 可以设置开启IR优化、开启内存优化。
    config.switch_ir_optim()
    config.enable_memory_optim()
    predictor = create_predictor(config)    return predictordef predict(image, predictor, target_size):
    img = preprocess(image, target_size)
    input_names = predictor.get_input_names()
    input_tensor = predictor.get_input_handle(input_names[0])
    input_tensor.reshape(img.shape)
    input_tensor.copy_from_cpu(img.copy())    # 执行Predictor
    predictor.run()    # 获取输出
    output_names = predictor.get_output_names()
    output_tensor = predictor.get_output_handle(output_names[0])
    output_data = output_tensor.copy_to_cpu()    print("output_names", output_names)    print("output_tensor", output_tensor)    print("output_data", output_data)    return output_dataif __name__ == '__main__':
    model_file = "model_dir/model.pdmodel"
    params_file = "model_dir/model.pdiparams"
    
    import random    # image = cv2.imread("data/img/7419.jpg")
    # image = cv2.imread("data/img/8891.jpg")
    image = cv2.imread("data/img/{}.jpg".format(random.randint(0,5000)))
    
    predictor = predict_config(model_file, params_file)
    res = predict(image, predictor, target_size=224)    # 进行图片的展示
    plt.figure()

    plt.title(res)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    plt.imshow(image.astype('uint8'))
    plt.axis('on')

    plt.show()
output_names ['relu_0.tmp_0']
output_tensor 
output_data [[0.03796072]]
--- Running analysis [ir_graph_build_pass]--- Running analysis [ir_graph_clean_pass]--- Running analysis [ir_analysis_pass]--- Running IR pass [is_test_pass]--- Running IR pass [simplify_with_basic_ops_pass]--- Running IR pass [conv_affine_channel_fuse_pass]--- Running IR pass [conv_eltwiseadd_affine_channel_fuse_pass]--- Running IR pass [conv_bn_fuse_pass]--- Running IR pass [conv_eltwiseadd_bn_fuse_pass]--- Running IR pass [embedding_eltwise_layernorm_fuse_pass]--- Running IR pass [multihead_matmul_fuse_pass_v2]--- Running IR pass [squeeze2_matmul_fuse_pass]--- Running IR pass [reshape2_matmul_fuse_pass]--- Running IR pass [flatten2_matmul_fuse_pass]--- Running IR pass [map_matmul_v2_to_mul_pass]I1117 16:08:14.154837   128 fuse_pass_base.cc:57] ---  detected 2 subgraphs--- Running IR pass [map_matmul_v2_to_matmul_pass]--- Running IR pass [map_matmul_to_mul_pass]--- Running IR pass [fc_fuse_pass]I1117 16:08:14.155347   128 fuse_pass_base.cc:57] ---  detected 2 subgraphs--- Running IR pass [fc_elementwise_layernorm_fuse_pass]--- Running IR pass [conv_elementwise_add_act_fuse_pass]--- Running IR pass [conv_elementwise_add2_act_fuse_pass]--- Running IR pass [conv_elementwise_add_fuse_pass]--- Running IR pass [transpose_flatten_concat_fuse_pass]--- Running IR pass [runtime_context_cache_pass]--- Running analysis [ir_params_sync_among_devices_pass]I1117 16:08:14.158038   128 ir_params_sync_among_devices_pass.cc:45] Sync params from CPU to GPU--- Running analysis [adjust_cudnn_workspace_size_pass]--- Running analysis [inference_op_replace_pass]--- Running analysis [memory_optimize_pass]I1117 16:08:14.159536   128 memory_optimize_pass.cc:214] Cluster name : x  size: 38535168
I1117 16:08:14.159554   128 memory_optimize_pass.cc:214] Cluster name : relu_0.tmp_0  size: 403734528
I1117 16:08:14.159556   128 memory_optimize_pass.cc:214] Cluster name : relu_3.tmp_0  size: 2359296
I1117 16:08:14.159559   128 memory_optimize_pass.cc:214] Cluster name : pool2d_0.tmp_0  size: 100933632--- Running analysis [ir_graph_to_program_pass]I1117 16:08:14.166565   128 analysis_predictor.cc:717] ======= optimize end =======
I1117 16:08:14.166777   128 naive_executor.cc:98] ---  skip [feed], feed -> x
I1117 16:08:14.167583   128 naive_executor.cc:98] ---  skip [relu_0.tmp_0], fetch -> fetch

6、车道线自动驾驶效果展示(第一人称视角)

【车道线+自动驾驶】用Paddle高层API实现车道线转角回归模型+部署 - php中文网

相关专题

更多
传感器故障解决方法
传感器故障解决方法

传感器故障排除指南:识别故障症状(如误读或错误代码)。检查电源和连接(确保连接牢固,无损坏)。校准传感器(遵循制造商说明)。诊断内部故障(目视检查、信号测试、环境影响评估)。更换传感器(选择相同规格,遵循安装说明)。验证修复(检查信号准确性,监测异常行为)。

463

2024.06.04

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

37

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

37

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

16

2026.01.13

PHP缓存策略教程大全
PHP缓存策略教程大全

本专题整合了PHP缓存相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.13

jQuery 正则表达式相关教程
jQuery 正则表达式相关教程

本专题整合了jQuery正则表达式相关教程大全,阅读专题下面的文章了解更多详细内容。

3

2026.01.13

交互式图表和动态图表教程汇总
交互式图表和动态图表教程汇总

本专题整合了交互式图表和动态图表的相关内容,阅读专题下面的文章了解更多详细内容。

45

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号