Python零依赖手写贪吃蛇:150行代码吃透游戏循环与碰撞检测
1. 为什么我劝你用150行代码手写一遍贪吃蛇很多人学Python学到某个阶段会突然卡住语法都懂循环、函数、类也能写但就是不知道拿这些东西能做出什么。看教程的时候觉得“哦原来是这样”关掉教程自己动手大脑一片空白。这个阶段最缺的不是知识而是一个完整、可运行、代码量不大但五脏俱全的小项目。贪吃蛇就是那个最合适的靶子。它足够简单——核心逻辑用150行左右就能写完不需要任何第三方库标准库里的tkinter和random就能撑起整个游戏。它又足够完整——包含了游戏循环、事件处理、碰撞检测、状态管理、渲染刷新这些游戏开发的基础要素。你把这150行吃透再去看任何2D小游戏骨架都是通的。这篇文章我会带你从零开始把贪吃蛇的每一块拆开讲清楚。不是扔一段代码让你复制走人而是告诉你每一行为什么这么写、参数为什么这么定、哪里容易踩坑、怎么改能变成你自己的东西。代码可以直接复制运行但我更希望你看完之后能自己改出几个版本。适合谁看刚学完Python基础语法、想找个练手项目的朋友写过一些脚本但没碰过图形界面的人以及想给孩子或学生讲编程、需要一个直观案例的开发者。不需要你会tkinter我会把用到的每个方法都解释清楚。2. 整体设计思路为什么选tkinter而不是pygame2.1 零依赖是最大的优势做贪吃蛇市面上最常被推荐的是pygame。它确实专业精灵、音效、碰撞检测都有现成的。但问题在于pygame需要额外安装版本兼容偶尔出问题而且它的API对新手来说是一层新的学习成本。你本来只是想练Python逻辑结果一半时间花在查pygame文档上得不偿失。tkinter是Python标准库自带的装好Python就有import tkinter直接能用。它的Canvas组件天生适合画网格类游戏——画矩形、画圆、绑定键盘事件几行代码就能出画面。贪吃蛇这种方格游戏用Canvas画方块是最自然的选择。提示如果你用的是某些精简版Python环境tkinter可能被裁掉了。验证方法很简单命令行输入python -m tkinter能弹出一个空白窗口就说明可用。2.2 用“网格坐标”而不是“像素坐标”思考这是新手最容易绕晕的地方。屏幕是像素的但贪吃蛇的逻辑是格子的。一条蛇在20×20的格子里移动和它在400×400像素的窗口里移动是两套坐标系。我的做法是逻辑层全部用格子坐标渲染层才换算成像素。蛇头在格子(5, 3)食物在格子(12, 8)碰撞判断就是比较两个元组是否相等。只有往Canvas上画的时候才把格子坐标乘以格子边长得到像素位置。这样逻辑代码干净得像数学题不会被像素数值污染。格子边长我定为20像素窗口就是400×400。这个数值不是随便定的太小了方块看不清太大了屏幕装不下几个格子20是一个在1080P屏幕上看着舒服、在笔记本上也不挤的平衡点。你可以改成15或25只要保证窗口尺寸 格子数 × 格子边长就行。2.3 游戏循环用after而不是while True很多教程写游戏循环用while True加time.sleep这在tkinter里是灾难——它会卡死主线程窗口直接无响应。正确做法是用tkinter的after方法它相当于“过N毫秒后调用这个函数”而且不阻塞事件循环。def game_loop(self): if not self.game_over: self.move_snake() self.check_collision() self.draw() self.root.after(self.speed, self.game_loop)self.speed控制游戏速度单位是毫秒。设成150就是每150毫秒动一格节奏刚好。想加速就减小这个值想减速就增大。这个设计的好处是键盘事件、窗口关闭这些操作依然能被正常响应不会因为游戏循环而卡住。3. 核心模块拆解蛇、食物、画布各管什么3.1 蛇的数据结构用列表存坐标蛇的本质是一串有序的格子坐标。我用一个列表self.snake来存索引0是蛇头最后一个元素是蛇尾。为什么这么设计因为移动的逻辑是新蛇头加到列表最前面如果没吃到食物就删掉最后一个元素尾巴如果吃到了就保留尾巴蛇变长。self.snake [(10, 10), (9, 10), (8, 10)] # 头在右尾在左初始长度给3格方向默认向右。这里有个细节初始蛇身不能重叠也不能贴着边界否则一开局就撞墙。我选(10, 10)作为蛇头在20×20的网格里居中偏左给右边留足空间。方向用一个元组表示比如(1, 0)代表向右(-1, 0)向左(0, 1)向下(0, -1)向上。移动时新蛇头坐标 旧蛇头坐标 方向向量。这个向量加法是整个游戏最核心的一行代码。3.2 食物的生成随机但要避开蛇身食物就是一个随机格子坐标。但直接random.randint有个坑可能生成在蛇身上玩家永远吃不到。所以必须加一个循环反复随机直到找到一个不在蛇身上的位置。def generate_food(self): while True: x random.randint(0, self.grid_width - 1) y random.randint(0, self.grid_height - 1) if (x, y) not in self.snake: return (x, y)这个while True理论上可能死循环——如果蛇填满了整个网格就永远找不到空位。但实际游戏中蛇填满网格基本等于通关概率极低。严谨一点可以加个判断如果蛇的长度等于总格子数直接判定胜利。我在代码里留了这个扩展点你可以自己补上。3.3 画布渲染每次重画而不是局部更新tkinter的Canvas支持删除单个图形对象但贪吃蛇这种小游戏每次刷新直接清空重画反而更简单可靠。canvas.delete(all)一行清空然后遍历蛇身画方块、画食物、画分数。def draw(self): self.canvas.delete(all) for (x, y) in self.snake: self.canvas.create_rectangle( x * self.cell_size, y * self.cell_size, (x 1) * self.cell_size, (y 1) * self.cell_size, fill#2ecc71, outline#27ae60 ) fx, fy self.food self.canvas.create_oval( fx * self.cell_size 2, fy * self.cell_size 2, (fx 1) * self.cell_size - 2, (fy 1) * self.cell_size - 2, fill#e74c3c, outline#c0392b )蛇身用绿色矩形食物用红色圆形视觉上一眼就能区分。create_oval的坐标我往里缩了2像素让圆形和格子之间留点缝隙看起来更精致。这些颜色值你可以随便换#2ecc71是扁平化设计里常用的绿色#e74c3c是红色搭配起来不刺眼。注意delete(all)会清掉画布上所有东西包括你可能想保留的分数文字。所以分数要么在清空后重画要么用单独的Label组件放在画布外面。我选择后者分数显示在窗口顶部不跟画布混在一起。4. 完整代码逐段精讲与实操4.1 环境准备与项目骨架打开你的代码编辑器新建一个文件叫snake.py。不需要安装任何东西Python 3.6以上版本直接跑。先搭骨架import tkinter as tk import random class SnakeGame: def __init__(self, root): self.root root self.root.title(贪吃蛇) self.root.resizable(False, False) self.grid_width 20 self.grid_height 20 self.cell_size 20 self.speed 150 self.canvas tk.Canvas( root, widthself.grid_width * self.cell_size, heightself.grid_height * self.cell_size, bg#1a1a2e ) self.canvas.pack() self.score_label tk.Label(root, text分数: 0, font(Arial, 14)) self.score_label.pack() self.reset_game() self.root.bind(KeyPress, self.on_key_press) self.game_loop() def reset_game(self): self.snake [(10, 10), (9, 10), (8, 10)] self.direction (1, 0) self.next_direction (1, 0) self.food self.generate_food() self.score 0 self.game_over False self.score_label.config(text分数: 0)resizable(False, False)禁止用户拖拽改变窗口大小因为画布尺寸是固定的窗口变了画布不会跟着变会很难看。bg#1a1a2e是深蓝色背景比纯黑柔和方块在上面更醒目。next_direction这个变量是关键。为什么需要两个方向变量因为键盘事件可能在两次移动之间触发多次。比如蛇正在向右走玩家快速按了“上”然后“左”。如果直接改direction第一次移动会向上第二次移动时方向已经变成左蛇会直接撞到自己。用next_direction暂存输入每次移动前才把direction更新为next_direction并且禁止180度掉头。4.2 键盘控制与防反向逻辑def on_key_press(self, event): key event.keysym if key Up and self.direction ! (0, 1): self.next_direction (0, -1) elif key Down and self.direction ! (0, -1): self.next_direction (0, 1) elif key Left and self.direction ! (1, 0): self.next_direction (-1, 0) elif key Right and self.direction ! (-1, 0): self.next_direction (1, 0)注意tkinter的坐标系y轴向下为正。所以“上”是(0, -1)“下”是(0, 1)。这个跟数学课本相反新手经常搞混。防反向的判断逻辑是如果当前方向是向右(1, 0)就不能接受向左(-1, 0)的输入。因为蛇头向右左边是自己的身体掉头等于自杀。这个判断用self.direction而不是self.next_direction因为要基于蛇当前实际移动方向来判断。实操心得有些玩家会同时按两个方向键比如“上”和“右”几乎同时按下。由于tkinter事件是顺序处理的最终next_direction会是后按下的那个。这没问题但如果你发现手感不对可以在每次移动后重置next_direction self.direction确保没有残留输入。4.3 移动、吃食物与碰撞检测def move_snake(self): self.direction self.next_direction head_x, head_y self.snake[0] dx, dy self.direction new_head (head_x dx, head_y dy) if (new_head[0] 0 or new_head[0] self.grid_width or new_head[1] 0 or new_head[1] self.grid_height): self.game_over True return if new_head in self.snake[:-1]: self.game_over True return self.snake.insert(0, new_head) if new_head self.food: self.score 10 self.score_label.config(textf分数: {self.score}) self.food self.generate_food() else: self.snake.pop()碰撞检测分两种撞墙和撞自己。撞墙判断新蛇头的x或y是否超出0到grid_width-1的范围。撞自己判断新蛇头是否在蛇身列表里但要排除最后一个元素尾巴因为尾巴在这一帧会移走蛇头可以安全地进入尾巴原来的位置。self.snake[:-1]就是排除尾巴的蛇身。这个细节很多教程会写错导致蛇贴着尾巴走的时候误判死亡。吃到食物时insert了新蛇头但不执行pop蛇就变长了一格。没吃到时insert之后pop掉尾巴蛇的长度不变整体向前移动一格。4.4 游戏主循环与速度控制def game_loop(self): if not self.game_over: self.move_snake() self.draw() self.root.after(self.speed, self.game_loop) else: self.draw_game_over()after的递归调用形成了游戏循环。每次循环做三件事移动蛇、重绘画布、安排下一次循环。如果游戏结束就画一个结束画面不再安排下一次循环。draw_game_over我简单处理在画布中央显示“游戏结束”和最终分数。def draw_game_over(self): self.canvas.delete(all) cx self.grid_width * self.cell_size // 2 cy self.grid_height * self.cell_size // 2 self.canvas.create_text(cx, cy - 20, text游戏结束, fillwhite, font(Arial, 24, bold)) self.canvas.create_text(cx, cy 20, textf最终分数: {self.score}, fill#f1c40f, font(Arial, 16))4.5 启动入口与完整代码if __name__ __main__: root tk.Tk() game SnakeGame(root) root.mainloop()mainloop是tkinter的事件循环入口它会一直运行监听键盘、鼠标、窗口事件直到窗口关闭。把上面所有代码拼起来就是完整的150行贪吃蛇。你可以直接复制到一个.py文件里运行。5. 常见问题与排查技巧实录5.1 窗口一闪而过或直接卡死最常见的原因是用了while True做游戏循环。tkinter是单线程事件驱动的while True会占满主线程窗口无法刷新系统以为程序无响应。必须用after。另一个原因是mainloop没写或者写在了SnakeGame类实例化之前。确保顺序是创建Tk()、创建游戏对象、调用mainloop()。5.2 蛇移动速度忽快忽慢after的延迟时间不是精确的它只保证“至少等这么久”实际延迟受系统调度影响。如果你发现速度不稳定可以把speed调大一点比如200给系统留余量。另外不要在game_loop里做耗时操作比如大量计算或文件读写那会拖慢循环。5.3 食物生成在蛇身上检查generate_food里的判断条件是不是if (x, y) not in self.snake。注意self.snake是列表in操作是O(n)的蛇很长时效率会下降。但贪吃蛇最多几百格这点开销可以忽略。如果你追求极致可以把蛇身坐标存到一个set里查找变成O(1)。5.4 按方向键没反应先检查root.bind(KeyPress, self.on_key_press)有没有写。然后检查焦点问题tkinter的键盘事件绑定在root上如果焦点在别的组件上可能收不到。确保canvas或root有焦点。可以在__init__里加self.canvas.focus_set()。还有一个隐蔽的坑某些系统上方向键的keysym不是Up而是KP_Up小键盘。可以打印event.keysym看看实际值然后兼容处理。5.5 游戏结束后无法重新开始我的代码里reset_game方法已经写好了但没绑定重新开始的按键。你可以在draw_game_over之后加一行self.root.bind(Return, lambda e: self.restart())然后实现restart方法调用reset_game再调用game_loop。问题现象可能原因排查方法窗口无响应用了while True改用after递归蛇不动game_loop没调用或game_over为True检查初始化和结束条件食物在蛇身上生成时没排除蛇身加not in self.snake判断方向键无效绑定丢失或焦点问题检查bind和focus_set蛇穿墙边界判断写错检查 0和 grid_width蛇撞自己误判没排除尾巴用self.snake[:-1]判断避坑技巧调试碰撞问题时可以在move_snake里加print(new_head, self.snake)观察每一步的坐标变化。确认无误后删掉打印语句。这种“打印大法”虽然原始但对坐标类bug极其有效。6. 从能跑到好玩几个低成本高回报的改造方向代码跑起来只是起点。真正让你学到东西的是动手改。下面几个改造方向每一个都只需要改十几行代码但能让你对游戏开发的理解上一个台阶。加墙壁现在的蛇撞到边界就死。你可以改成穿墙——蛇头超出右边界时从左边出现。实现方法是在计算new_head后加取模运算new_head (new_head[0] % self.grid_width, new_head[1] % self.grid_height)。一行代码游戏体验完全不同。加难度递增每吃5个食物self.speed减少10毫秒蛇越跑越快。在吃食物的分支里加判断即可。注意speed别低于50否则人眼跟不上。加暂停功能绑定空格键按一下self.paused True再按一下恢复。在game_loop开头判断if self.paused: self.root.after(100, self.game_loop); return。这个功能看似简单但能让你理解状态机的概念。换皮肤把蛇身画成圆角矩形或者用不同颜色区分蛇头和蛇身。create_rectangle换成create_oval就是一条“毛毛虫”。颜色方案可以参考扁平化设计网站选一组对比度高的配色。加音效tkinter本身不支持音频但可以用winsoundWindows或subprocess调用系统命令播放声音。这个稍微复杂但网上有现成方案。吃到食物“嘀”一声游戏结束放个低沉音效沉浸感立刻不一样。我个人最推荐先加“穿墙”和“难度递增”这两个改动最小但让游戏从“能玩”变成“想一直玩”。改完之后你会对坐标运算、状态管理有更深的体会。7. 我踩过的几个坑和最后的小建议第一个坑是坐标系搞反。我一开始把“上”写成(0, 1)结果按上键蛇往下跑。tkinter的y轴向下这个跟直觉相反必须记住。第二个坑是**after的递归调用忘了加self.**。写成root.after也能跑但如果你有多个窗口或想控制生命周期用self.root更规范。第三个坑是蛇身列表的顺序。我一开始把蛇尾存在索引0蛇头存在最后结果移动逻辑全乱了。记住索引0永远是蛇头insert(0, new_head)和pop()都是围绕这个约定设计的。最后分享一个小技巧如果你想让蛇看起来更顺滑可以在每次移动后不立即重画而是用after延迟几毫秒再画。但这会引入输入延迟得不偿失。贪吃蛇的“格子感”本身就是它的魅力不需要追求像素级平滑。代码写完之后试着不看教程自己从零再写一遍。你会发现第二遍会卡在完全不同的地方——第一遍卡在语法第二遍卡在逻辑。把第二遍卡住的地方记下来那就是你真正需要补的知识点。

相关新闻

Elasticsearch 7.17.0 中文分词插件 IK 安装配置与实战避坑指南

Elasticsearch 7.17.0 中文分词插件 IK 安装配置与实战避坑指南

简介:elasticsearch-analysis-ik-7.17.0 是适配 Elasticsearch 7.17.0 的中文分词插件安装包,面向使用 ES 构建全文检索、日志分析与大数据搜索服务的开发者与运维人员,用于解决中文文本分词不准确、检索召回率低的问题。压缩包共 19 个文件&…

2026/10/16 0:58:31 阅读更多 →
Java高精度四则运算解析器:BigDecimal+递归下降实现

Java高精度四则运算解析器:BigDecimal+递归下降实现

1. 这不是一道“小学数学题”,而是一次对Java类型系统与表达式解析能力的实战检验很多人看到“Java实现四则运算(含小数)”这个标题,第一反应是:“不就是用 - * /写个计算器?两行代码搞定。”我第一次也这么…

2026/10/16 0:58:31 阅读更多 →
微信小程序云开发实现员工生日短信提醒:云函数+定时触发器全攻略

微信小程序云开发实现员工生日短信提醒:云函数+定时触发器全攻略

前阵子我接到一个挺有意思的需求:在微信小程序云开发上,对接阿里云短信服务,每天自动扫一遍员工信息,遇到当天生日的员工就给人家发祝福短信。接到需求的时候,团队里有人觉得这不就是一个定时任务加短信接口嘛&#xf…

2026/10/16 0:57:30 阅读更多 →

最新新闻

STEP7_HSPs硬件支持包安装与GSD协同配置指南

STEP7_HSPs硬件支持包安装与GSD协同配置指南

简介:本资源是西门子STEP7 V5.X系列编程软件的官方级热修复服务包(HSPs),专为工业自动化领域PLC工程师、系统集成商及产线维护技术人员设计,用于解决V5.1/V5.2/V5.3等各小版本中已知的功能缺陷、兼容性问题与潜在稳定性…

2026/10/16 3:33:05 阅读更多 →
Linux内存卡死优化:从swap颠簸到earlyoom的调优实战

Linux内存卡死优化:从swap颠簸到earlyoom的调优实战

如果你的Ubuntu电脑或者服务器用着用着突然变成“幻灯片”,鼠标一卡一顿,SSD指示灯狂闪,连切个窗口都要等好几秒,那大概率不是CPU不行,而是内存管理出了问题。接触过Linux一段时间的朋友都知道,Windows卡了…

2026/10/16 3:33:05 阅读更多 →
STED超分辨成像:从衍射极限原理到系统搭建与调试实战

STED超分辨成像:从衍射极限原理到系统搭建与调试实战

STED这个名字,在超分辨成像的圈子里几乎无人不晓。它全称是受激发射损耗显微镜(Stimulated Emission Depletion Microscopy),能把普通共聚焦显微镜的分辨率从大约200纳米一直压到30纳米甚至更低,直接突破光学衍射极限。…

2026/10/16 3:33:05 阅读更多 →
Flutter鸿蒙开发实战:植物识别应用跨平台适配全攻略

Flutter鸿蒙开发实战:植物识别应用跨平台适配全攻略

最近我把一款植物识别类小工具从最初的单端Demo扩展成了支持 iOS、Android 和鸿蒙三个平台的 Flutter 应用,整个过程比预想的有意思,也踩了不少和平台适配有关的坑。这个项目表面上是“拍照识植物”,实际上把 Flutter 跨平台能力、图像预处理…

2026/10/16 3:33:05 阅读更多 →
Flutter鸿蒙端侧植物识别实战:从拍照到模型推理全链路

Flutter鸿蒙端侧植物识别实战:从拍照到模型推理全链路

跨端开发圈子里这两年最让人纠结的一件事,就是鸿蒙设备的适配怎么做。Flutter 很早就跑通了移动端、桌面和 Web,但鸿蒙这边受 SDK 演进影响,资料一直比较碎。前阵子我把一个植物识别助手从 Demo 做到了能装进鸿蒙手机跑通识别,整个…

2026/10/16 3:33:05 阅读更多 →
TPShop部署中的数据库验证:环境配置与排错完整指南

TPShop部署中的数据库验证:环境配置与排错完整指南

我敢说,十个声称"TPShop部署很简单"的人里,至少有七个会栽在数据库验证这一步。TPShop这套开源商城系统确实成熟,环境要求不高,安装文档也写得清楚,但正因为"看起来简单",大家往往在文…

2026/10/16 3:32:05 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →