【OpenAI】Python:基于 Gym-CarRacing 的自动驾驶项目(1) | 前置知识介绍 | 项目环境准备 | 手把手带你一步步实现

简介: 【OpenAI】Python:基于 Gym-CarRacing 的自动驾驶项目(1) | 前置知识介绍 | 项目环境准备 | 手把手带你一步步实现

猛戳!跟哥们一起玩蛇啊 👉 《一起玩蛇》🐍

💭 写在前面: 本篇是关于多伦多大学自动驾驶专业项目 Gym-CarRacing 的博客。GYM-Box2D CarRacing 是一种在 OpenAI Gym 平台上开发和比较强化学习算法的模拟环境。它是流行的 Box2D 物理引擎的一个版本,经过修改以支持模拟汽车在赛道上行驶的物理过程。由于内容比较多所以分多次更新,本篇是关于前置知识介绍,以及项目环境准备的。具体如下:

  • 自动驾驶的背景知识介绍。
  • 然后会讲解本项目可能所需的知识点,需要用到图像处理算法和基础车道线检测算法,这里的讲解并不会太细,读者如果对不熟悉可以在单独搜索,C站上也有不少介绍这些算法的博客。
  • 项目所需的环境安装教程(这个部分是我在博客审阅阶段临时增添的),因为该项目需要 Conda 和 gym 环境和一堆必不可少的软件包(少一个都跑不了)。我看了网上有不少朋友在做该项目时,在环境安装上踩了不少坑。所以为了方便需要做该项目的同学、对该项目感兴趣的朋友,我这里准备了环境安装的教程。

🔗 多伦多大学自动驾驶专项课程:Motion Planning for Self-Driving Cars | Coursera

🔗 Gym Car Racing 文档:Car Racing - Gym Documentation

  本篇博客全站热榜排名:4


Ⅰ. 背景知识介绍

0x00 引入: 什么是自动驾驶?

" Autonomous(自动)+  driving(驾驶) "

 自动驾驶,即汽车自主认知周边环境并安全行驶的技术。

Self-driving car, autonomous vehicle, driver-less car, or robotic car……

0x01 自动驾驶的基本组件

  • 汽车(Car):实际移动的车辆,代理人应该控制它
  • 传感器(Sensors):探测周围环境的设备
  • 代理人(Agent):一个在给定的周围环境中安全地驱动 var 的物体

传感器(Sensors on self-driving cars):

  • 460 个激光雷达摄像机、RGB 摄像机

自动驾驶的目标(Goal of Autonomous Driving):

  • 在给定的情况下的安全驾驶
  • 根据给定的情况 (state) 安全驾驶汽车

映射函数:Sensor Input → Action

  • 模块化管线(modular Pipelines)
  • 端到端训练(End-to-End Learning)
  • 直接感知(Direct Perception)

0x02 模块化组件(Modular Pipeline)

每个模块连接下一个模块的输入:低级感知、场景解析、路径训练、车辆控制。

❓ 需要思考的问题:为遵循选定的路径,我们应将手柄转多少度?我们应该以什么速度前进?

Ⅱ. 前置知识

0x00 车道标记与车道检测(Lane marking & Lane detection)

  • 使用梯度图或边缘过滤的图像,我们可以通过阈值处理来检测车道标记。
  • 考虑在附近存在相反梯度的点。

0x01 边缘检测(Edge Detection)

  • 通过用边缘过滤器对图像进行卷积,得到两个方向的梯度图。
  • 这里也可以使用其他的边缘核进行边缘检测。  

边缘检测是图像处理的一种常见技术,用于检测图像中的边缘和边界。这对于自动驾驶系统来说是非常重要的,因为边缘检测可以帮助系统识别道路、车辆、行人等重要物体。

通常边缘检测是通过使用边缘过滤器对图像进行卷积来实现的,边缘过滤器是一种特殊的卷积核,其中包含了两个方向的梯度图,可以检测出图像中的垂直和水平边缘。比如图中显示的 Sobel 过滤器,就可以得到一张图像的 方向和 方向的梯度图,其中 方向的梯度图可以检测出图像中的垂直边缘, 方向的梯度图可以检测出图像中的水平边缘。

除了 Sobel 过滤器之外,还有许多其他的边缘核可以用于边缘检测。比如 Canny 边缘检测算法,这是一种非常流行的边缘检测算法,它可以有效地消除噪声并提供清晰的边缘检测结果。

Canny 边缘检测是由 John F. Canny 在 1986 年提出的一种边缘检测算法。

它是一种多步骤的边缘检测算法,包括以下几个步骤:

  • 图像高斯滤波:使用高斯滤波器对图像进行模糊处理,以减少噪声并使边缘更加明显。
  • 计算图像梯度:使用 Sobel 过滤器或其他方法计算图像的梯度,并使用梯度的方向和大小来表示图像中的边缘。
  • 非极大值抑制:使用非极大值抑制算法来去除图像中的假边缘。
  • 双阈值检测:使用两个阈值来区分真正的边缘和假边缘。
  • 边缘连接:将检测到的边缘连接起来,以形成完整的边缘。

Canny 边缘检测算法的优点是它能够有效地消除噪声,并提供清晰的边缘检测结果。但是,由于它是一种多步骤的算法,所以它的计算复杂度较高,不太适用于实时边缘检测场景。

0x02 IPM 逆透视变换(Inverse Perspective Mapping)

IPM(Inverse Perspective Mapping)是一种图像处理技术,它可以将一张透视变换后的图像进行逆变换,使其看起来像是从俯视角度拍摄的。这对于自动驾驶系统来说非常重要,因为它可以帮助系统更准确地识别道路、车辆、行人等物体。

  • 在通常情况下,道路是在平面上的。
  • 如果三维变换是已知的,我们就可以将道路图像投射到地面平面上。

0x03 车道线检测:参数化车道标线估算(Parametric Lane Marking Estimation)

为了给汽车导航,我们需要将检测到的标记像素与一个更有语义的曲线模型与之相匹。

0x04 贝塞尔曲线(Bezier Curve)

贝塞尔曲线(Bézier curve)是一种数学曲线,贝塞尔曲线常用于计算机图形学中,因为它们可以用于创建平滑的曲线和图形。贝塞尔曲线是通过控制点来描述曲线形状的,其中一个或多个控制点用于指定曲线的形状。

贝塞尔曲线可以通过控制点的位置来控制曲线的形状,并可以通过改变控制点的位置来改变曲线的形状。这使得贝塞尔曲线非常适用于创建复杂的曲线和图形。

  • 一个由控制点定义的多项式曲线

0x05 线性贝塞尔曲线(Linear Bezier Curve)

线性贝塞尔曲线是一种特殊的贝塞尔曲线,它由两个控制点和一个起始点和一个终止点组成。线性贝塞尔曲线是最简单的贝塞尔曲线之一,它可以用来描述直线。线性贝塞尔曲线的方程:

其中,  是贝塞尔曲线上的点, 是参数, 是控制点。

  • 类似线性插值法(linear interpolation)

0x06 二次贝塞尔曲线(Quadratic Bezier Curve)

由一个起始点、一个终止点和两个控制点组成。二次贝塞尔曲线是一种二次方程,可用来描述曲线和复杂的形状。二次贝塞尔曲线的方程:

其中, 是贝塞尔曲线上的点, 是参数, 是控制点。

  • 两个线性内插点的内插

0x07 三次贝塞尔曲线(Cubic Bezier Curve)

由一个起始点、一个终止点和三个控制点组成。三次贝塞尔曲线是一种三次方程,它可以用来描述曲线和复杂的形状。三次贝塞尔曲线的方程:

其中, 是贝塞尔曲线上的点, 是参数, 是控制点。

  • 二次点的插值

0x08 B样条曲线(B-Spline Curve)

B样条曲线是通过一系列的控制点来描述曲线形状的,这些控制点可以用来指定曲线的形状。B样条曲线通常使用 B样条曲线方程来描述,这是一个多项式方程。B样条曲线有许多不同的类型,包括二次B样条曲线、三次B样条曲线和四次B样条曲线。

已知 个控制点 ,可定义 次 B 样条曲线的表达式为:

由控制点列表和程度定义的曲线,一条单片多项式曲线(它不同于与贝塞尔曲线)。

Ⅲ. 项目所需环境准备

0x00 前言

我默认读者已经安装了 Python,这里简单讲一下如何安装 Conda 和 gym。网上的资料很多,与之相比,我写的安装教程可能远没有那些专门安装环境的文章细致。我这里只是做简单介绍,旨在方便大家能快速准备好环境,能让程序正常跑起来的。因为我看了网上有不少人在做该项目时,在环境安装上踩了不少坑,很是让人头疼。所以这里我准备了 Conda 和 gym 环境安装的教程。

0x01 Step1:Python 开发环境设置 —— 安装 Conda

操作系统以 Window 为准进行说明(Linux 同理)

安装 Anaconda 或 Miniconda:

Step1:打开安装包后会进入欢迎界面,点击 Next>

Step2:许可协议界面,选择同意:

Step3:看情况选择,我们选择 Just Me:

Step4:选择安装路径,默认是在 C 盘下的,点击 Browse 按钮可呼出窗口更换路径:

Step5:这里全部勾选

Step6:等待即可,可能有点慢,但是绝对没有 Vivado 慢!安装完毕后点击 Next >

Final Step:点击 Finish

0x02 Step2:Conda 安装完毕后的环境设置

通过命令行操作,设置环境并激活即可:

🔍 官方说明:Managing environments — conda

详细查阅 Creating and activating an environment 部分。

安装命令示例:

> conda env create –n autodriving
> conda activate autodirving
> conda install python
> pip install pytorch

Step1:打开命令提示符,快捷键 Win+R 输入 cmd 后回车:

Step2:检查是否安装正常,在 cmd 中输入 conda

conda

没问题:

Step3:打开命令行下输入:

conda create -n python=2.7

Step4:进入环境内部

conda activate [文件名]

0x03 关于 OpenAI GYM 的介绍

强化学习框架(https://github.com/openai/gym)

0x04 安装练习所需的软件包

  • 下载解压文件后自行解压。
  • 显示命令窗口后设置 Conda 环境。
  • 找到到解压缩文件夹后执行以下命令:
  • cd 到 /envs/box2d,python car_racing 执行 py 命令,确保 Acttion 没问题(方向键可以移动汽车就行)。

CarRacing 环境基本代码示例:

gym.make()         环境配置。
env.reset()        设置初始变量。
env.step(action)   执行动作并返回以下观察值、补偿、是否结束。

0x05 设置 GYM 环境

Step1:打开命令窗口设置准备 Conda

创建项目文件夹: conda create -n gym python=3.8

这里我去名为 3.8

稍等片刻,会问你 yes 还是 no,我们输入 y 即可:

之后会开始提取安装包,耐心等待……

之后输入 conda activate gym 进行激活:

Step2:cd 至解压的文件夹位置

cd {{installed gym path}}

我们是把 gym 解压到桌面的,我们 cd 过去即可。

Step3:输入下列指令

pip install -e .[box2d]
pip install matplotlib
pip install scipy
pip install pyglet
pip install pygame

挨个安装即可:

如果显示安装失败,可能是因为 python无法识别安装的版本,导致 pip install Box2D 显示无法安装,可以尝试输入以下指令安装:

python -m pip install Box2D

全部安装完毕后,跳转到 gym/envs/box2d,运行 python car_racing.py 命令测试环境:

cd gym/envs/box2d

(注意,gym 文件夹还有一个 gym 文件夹)

然后输入 python car_racing.py 运行,如果正常运行,就说明环境装好了。

🚩 运行效果如下:

至此,环境已全部准备完毕!

有些人开了就不会关了,很尴尬……  建议直接强制关机 23333(滑稽)

拉闸!简单粗暴,一步到位!!!优雅,永不过时。

Ⅳ. 项目准备

0x00 实验说明:Box2D CarRacing 的 lane_dection

🚩 实践目标:实现一个模块化组件框架,落实简化版的模块化流水线。了解基本概念,并积累开发一个简单的自驱应用程序的经验。

🔨 环境选用:OpenAI GYM

  • Action:转向、加速、刹车
  • Sensor input:  屏幕(显示汽车的状态和路径信息)

📜 尝试:

  • 为汽车上方的部分找到一个好的裁剪,一个好的方法来分配车道边界的边缘,一个好的梯度阈值和样条平滑度的参数选择。
  • 尝试找到失败的案例。

* 提供基础框架,只需要在 TODO 位置填写代码即可!

我就不提供资源下载链接了,直接手动吧:首先在桌面上创建一个文件夹,我们取名  skeleton ,然后创建出如下名称的 py 文件,将代码 CV 进去。

  • lane detection.py    
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import find_peaks
from scipy.interpolate import splprep, splev
from scipy.optimize import minimize
import time
class LaneDetection:
    '''
    Lane detection module using edge detection and b-spline fitting
    args: 
        cut_size (cut_size=65) cut the image at the front of the car
        spline_smoothness (default=10)
        gradient_threshold (default=14)
        distance_maxima_gradient (default=3)
    '''
    def __init__(self, cut_size=65, spline_smoothness=10, gradient_threshold=14, distance_maxima_gradient=3):
        self.car_position = np.array([48,0])
        self.spline_smoothness = spline_smoothness
        self.cut_size = cut_size
        self.gradient_threshold = gradient_threshold
        self.distance_maxima_gradient = distance_maxima_gradient
        self.lane_boundary1_old = 0
        self.lane_boundary2_old = 0
    def cut_gray(self, state_image_full):
        '''
        ##### TODO #####
        This function should cut the image at the front end of the car (e.g. pixel row 65) 
        and translate to gray scale
        input:
            state_image_full 96x96x3
        output:
            gray_state_image 65x96x1
        '''
        return gray_state_image[::-1] 
    def edge_detection(self, gray_image):
        '''
        ##### TODO #####
        In order to find edges in the gray state image, 
        this function should derive the absolute gradients of the gray state image.
        Derive the absolute gradients using numpy for each pixel. 
        To ignore small gradients, set all gradients below a threshold (self.gradient_threshold) to zero. 
        input:
            gray_state_image 65x96x1
        output:
            gradient_sum 65x96x1
        '''
        return gradient_sum
    def find_maxima_gradient_rowwise(self, gradient_sum):
        '''
        ##### TODO #####
        This function should output arguments of local maxima for each row of the gradient image.
        You can use scipy.signal.find_peaks to detect maxima. 
        Hint: Use distance argument for a better robustness.
        input:
            gradient_sum 65x96x1
        output:
            maxima (np.array) shape : (Number_maxima, 2)
        '''
        return argmaxima
    def find_first_lane_point(self, gradient_sum):
        '''
        Find the first lane_boundaries points above the car.
        Special cases like just detecting one lane_boundary or more than two are considered. 
        Even though there is space for improvement ;) 
        input:
            gradient_sum 65x96x1
        output: 
            lane_boundary1_startpoint
            lane_boundary2_startpoint
            lanes_found  true if lane_boundaries were found
        '''
        # Variable if lanes were found or not
        lanes_found = False
        row = 0
        # loop through the rows
        while not lanes_found:
            # Find peaks with min distance of at least 3 pixel 
            argmaxima = find_peaks(gradient_sum[row],distance=3)[0]
            # if one lane_boundary is found
            if argmaxima.shape[0] == 1:
                lane_boundary1_startpoint = np.array([[argmaxima[0],  row]])
                if argmaxima[0] < 48:
                    lane_boundary2_startpoint = np.array([[0,  row]])
                else: 
                    lane_boundary2_startpoint = np.array([[96,  row]])
                lanes_found = True
            # if 2 lane_boundaries are found
            elif argmaxima.shape[0] == 2:
                lane_boundary1_startpoint = np.array([[argmaxima[0],  row]])
                lane_boundary2_startpoint = np.array([[argmaxima[1],  row]])
                lanes_found = True
            # if more than 2 lane_boundaries are found
            elif argmaxima.shape[0] > 2:
                # if more than two maxima then take the two lanes next to the car, regarding least square
                A = np.argsort((argmaxima - self.car_position[0])**2)
                lane_boundary1_startpoint = np.array([[argmaxima[A[0]],  0]])
                lane_boundary2_startpoint = np.array([[argmaxima[A[1]],  0]])
                lanes_found = True
            row += 1
            # if no lane_boundaries are found
            if row == self.cut_size:
                lane_boundary1_startpoint = np.array([[0,  0]])
                lane_boundary2_startpoint = np.array([[0,  0]])
                break
        return lane_boundary1_startpoint, lane_boundary2_startpoint, lanes_found
    def lane_detection(self, state_image_full):
        '''
        ##### TODO #####
        This function should perform the road detection 
        args:
            state_image_full [96, 96, 3]
        out:
            lane_boundary1 spline
            lane_boundary2 spline
        '''
        # to gray
        gray_state = self.cut_gray(state_image_full)
        # edge detection via gradient sum and thresholding
        gradient_sum = self.edge_detection(gray_state)
        maxima = self.find_maxima_gradient_rowwise(gradient_sum)
        # first lane_boundary points
        lane_boundary1_points, lane_boundary2_points, lane_found = self.find_first_lane_point(gradient_sum)
        # if no lane was found,use lane_boundaries of the preceding step
        if lane_found:
            ##### TODO #####
            #  in every iteration: 
            # 1- find maximum/edge with the lowest distance to the last lane boundary point 
            # 2- append maximum to lane_boundary1_points or lane_boundary2_points
            # 3- delete maximum from maxima
            # 4- stop loop if there is no maximum left 
            #    or if the distance to the next one is too big (>=100)
            # lane_boundary 1
            # lane_boundary 2
            ################
            ##### TODO #####
            # spline fitting using scipy.interpolate.splprep 
            # and the arguments self.spline_smoothness
            # 
            # if there are more lane_boundary points points than spline parameters 
            # else use perceding spline
            if lane_boundary1_points.shape[0] > 4 and lane_boundary2_points.shape[0] > 4:
                # Pay attention: the first lane_boundary point might occur twice
                # lane_boundary 1
                # lane_boundary 2
            else:
                lane_boundary1 = self.lane_boundary1_old
                lane_boundary2 = self.lane_boundary2_old
            ################
        else:
            lane_boundary1 = self.lane_boundary1_old
            lane_boundary2 = self.lane_boundary2_old
        self.lane_boundary1_old = lane_boundary1
        self.lane_boundary2_old = lane_boundary2
        # output the spline
        return lane_boundary1, lane_boundary2
    def plot_state_lane(self, state_image_full, steps, fig, waypoints=[]):
        '''
        Plot lanes and way points
        '''
        # evaluate spline for 6 different spline parameters.
        t = np.linspace(0, 1, 6)
        lane_boundary1_points_points = np.array(splev(t, self.lane_boundary1_old))
        lane_boundary2_points_points = np.array(splev(t, self.lane_boundary2_old))
        plt.gcf().clear()
        plt.imshow(state_image_full[::-1])
        plt.plot(lane_boundary1_points_points[0], lane_boundary1_points_points[1]+96-self.cut_size, linewidth=5, color='orange')
        plt.plot(lane_boundary2_points_points[0], lane_boundary2_points_points[1]+96-self.cut_size, linewidth=5, color='orange')
        if len(waypoints):
            plt.scatter(waypoints[0], waypoints[1]+96-self.cut_size, color='white')
        plt.axis('off')
        plt.xlim((-0.5,95.5))
        plt.ylim((-0.5,95.5))
        plt.gca().axes.get_xaxis().set_visible(False)
        plt.gca().axes.get_yaxis().set_visible(False)
        fig.canvas.flush_events()
  • detection.py (用于测试,无需修改)
import gym
from gym.envs.box2d.car_racing import CarRacing
import pygame
from lane_detection import LaneDetection
import matplotlib.pyplot as plt
import numpy as np
import pyglet
from pyglet import gl
from pyglet.window import key
# action variables
action = np.array([0.0, 0.0, 0.0])
def register_input():
    for event in pygame.event.get():
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                action[0] = -1.0
            if event.key == pygame.K_RIGHT:
                action[0] = +1.0
            if event.key == pygame.K_UP:
                action[1] = +0.5
            if event.key == pygame.K_DOWN:
                action[2] = +0.8  # set 1.0 for wheels to block to zero rotation
            if event.key == pygame.K_r:
                global retry
                retry = True
            if event.key == pygame.K_s:
                global record
                record = True
            if event.key == pygame.K_q:
                global quit
                quit = True
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT and action[0] < 0.0:
                action[0] = 0
            if event.key == pygame.K_RIGHT and action[0] > 0.0:
                action[0] = 0
            if event.key == pygame.K_UP:
                action[1] = 0
            if event.key == pygame.K_DOWN:
                action[2] = 0
# init environement
env = CarRacing()
env.render()
env.reset()
# define variables
total_reward = 0.0
steps = 0
retry = False
quit = False
# init modules of the pipeline
LD_module = LaneDetection()
# init extra plot
fig = plt.figure()
plt.ion()
plt.show()
while not quit:
    env.reset()
    retry = False
    while True:
        # perform step
        register_input()
        s, r, done, speed= env.step(action)
        # lane detection
        splines = LD_module.lane_detection(s)
        # reward
        total_reward += r
        # outputs during training
        if steps % 2 == 0 or done:
            print("\naction " + str(["{:+0.2f}".format(x) for x in action]))
            print("step {} total_reward {:+0.2f}".format(steps, total_reward))
            LD_module.plot_state_lane(s, steps, fig)
        steps += 1
        env.render()
        if done or retry or quit: break
env.close()

我们会在下一章节讲解原理和有关代码的实现。

📌 [ 笔者 ]   王亦优
📃 [ 更新 ]   2022.12.29
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,
              本人也很想知道这些错误,恳望读者批评指正!

📜 参考资料 

[6] Montemerlo M, Becker J, Bhat S, etal. Junior:TheStanfordentryintheUrbanChallenge

Slide Credit: Steven Waslander

LaValle: Rapidly-exploring random trees: A new tool for path planning. Techical Report, 1998

Dolgov et al.: Practical Search Techniques in Path Planning for Autonomous Driving. STAIR, 2008.

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. https://baike.baidu.com/.

. [EB/OL]. []. https://blog.waymo.com/2021/10/the-waymo-driver-handbook-perception.html.

相关文章
|
1月前
|
Python
Python项目配置Dockerfile
该Dockerfile基于阿里云Alinux3的Python 3.11.1镜像构建,使用阿里云PyPI镜像加速依赖安装,部署一个运行于5000端口、时区为上海的Python应用。
117 1
|
3月前
|
监控 大数据 API
Python 技术员实践指南:从项目落地到技术优化
本内容涵盖Python开发的实战项目、技术攻关与工程化实践,包括自动化脚本(日志分析系统)和Web后端(轻量化API服务)两大项目类型。通过使用正则表达式、Flask框架等技术,解决日志分析效率低与API服务性能优化等问题。同时深入探讨内存泄漏排查、CPU瓶颈优化,并提供团队协作规范与代码审查流程。延伸至AI、大数据及DevOps领域,如商品推荐系统、PySpark数据处理和Airflow任务编排,助力开发者全面提升从编码到架构的能力,积累高并发与大数据场景下的实战经验。
Python 技术员实践指南:从项目落地到技术优化
|
4月前
|
人工智能 安全 Shell
Jupyter MCP服务器部署实战:AI模型与Python环境无缝集成教程
Jupyter MCP服务器基于模型上下文协议(MCP),实现大型语言模型与Jupyter环境的无缝集成。它通过标准化接口,让AI模型安全访问和操作Jupyter核心组件,如内核、文件系统和终端。本文深入解析其技术架构、功能特性及部署方法。MCP服务器解决了传统AI模型缺乏实时上下文感知的问题,支持代码执行、变量状态获取、文件管理等功能,提升编程效率。同时,严格的权限控制确保了安全性。作为智能化交互工具,Jupyter MCP为动态计算环境与AI模型之间搭建了高效桥梁。
303 2
Jupyter MCP服务器部署实战:AI模型与Python环境无缝集成教程
|
4月前
|
Python
在VScode环境下配置Python环境的方法
经过上述步骤,你的VSCode环境就已经配置好了。请尽情享受这扇你为自己开启的知识之窗。如同你在冒险世界中前行,你的探索之路只有越走越广,你获得的知识只会越来越丰富,你的能力只会越来越强。
393 37
|
5月前
|
程序员 测试技术 开发工具
怎么开发Python第三方库?手把手教你参与开源项目!
大家好,我是程序员晚枫。本文将分享如何开发Python第三方库,并以我维护的开源项目 **popdf** 为例,指导参与开源贡献。Popdf是一个PDF操作库,支持PDF转Word、转图片、合并与加密等功能。文章涵盖从fork项目、本地开发、单元测试到提交PR的全流程,适合想了解开源贡献的开发者。欢迎访问[popdf](https://gitcode.com/python4office/popdf),一起交流学习!
194 21
怎么开发Python第三方库?手把手教你参与开源项目!
|
8月前
|
测试技术 Python
【03】做一个精美的打飞机小游戏,规划游戏项目目录-分门别类所有的资源-库-类-逻辑-打包为可玩的exe-练习python打包为可执行exe-优雅草卓伊凡-持续更新-分享源代码和游戏包供游玩-1.0.2版本
【03】做一个精美的打飞机小游戏,规划游戏项目目录-分门别类所有的资源-库-类-逻辑-打包为可玩的exe-练习python打包为可执行exe-优雅草卓伊凡-持续更新-分享源代码和游戏包供游玩-1.0.2版本
320 31
【03】做一个精美的打飞机小游戏,规划游戏项目目录-分门别类所有的资源-库-类-逻辑-打包为可玩的exe-练习python打包为可执行exe-优雅草卓伊凡-持续更新-分享源代码和游戏包供游玩-1.0.2版本
|
6月前
|
Docker Python 容器
Docker——阿里云服务器使用Docker部署python项目全程小记
本文记录了我在阿里云服务器上使用Docker部署python项目(flask为例)的全过程,在这里记录和分享一下,希望可以给大家提供一些参考。
641 1
|
6月前
|
Java API Docker
在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境
以上内容是一个简单的实现在Java后端中通过DockerClient操作Docker生成python环境并执行代码,最后销毁的案例全过程,也是实现一个简单的在线编程后端API的完整流程,你可以在此基础上添加额外的辅助功能,比如上传文件、编辑文件、查阅文件、自定义安装等功能。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境
|
8月前
|
Shell Linux Ruby
Python3虚拟环境venv
`venv` 是 Python 的虚拟环境工具,用于为不同项目创建独立的运行环境,避免依赖冲突。通过 `python3 -m venv` 命令创建虚拟环境,并使用 `source bin/activate` 激活。激活后,所有 Python 包将安装在该环境中,不影响系统全局环境。退出环境使用 `deactivate` 命令。每个虚拟环境拥有独立的包集合,确保项目间的隔离性。删除虚拟环境只需删除其目录即可。
466 34
|
7月前
|
传感器 人工智能 机器人
D1net阅闻|OpenAI机器人项目招新 或自研传感器
D1net阅闻|OpenAI机器人项目招新 或自研传感器

推荐镜像

更多