自定义Gym环境可视化指南:从render接口到训练调试的完整方案
第一次跑通强化学习训练脚本的人十个里有九个会在同一个地方多花两三天时间——不是算法调参而是被自定义gym环境里的可视化绘制卡住。CartPole、MountainCar这些内置环境拿来就能用但一旦轮到自己定义任务状态转移、奖励函数这些逻辑反而好写真正让人问出“这环境到底长什么样”的地方恰恰是那个看似可有可无的render方法。绘制窗口不出来、坐标方向反了、训练时渲染画面疯狂拖慢速度、录下来的视频颜色不对……这些问题我全踩过也帮身边同事排查过好几轮。今天就把自定义gym环境可视化的完整思路和踩坑记录整理出来从接口设计、坐标换算讲到训练闭环里的渲染节奏控制适合那些想摆脱玩具环境、自己定义一个可视化任务并且希望智能体的行为能被一眼看穿的人。1. 自定义环境时render才是真正的拦路虎先讲一个事实gym生态里的内置环境看起来轻巧内部实现却是完整的工业级组件。很多人以为画个画面是小菜一碟等自己动手才发现render这一行代码牵连着坐标系、窗口事件、帧率控制和资源释放任何一个环节出问题都会让整个训练流程卡住。我帮同事排查时听到最多的一句话是“环境reset是正常的step也能打印但render弹出一个黑色窗口什么都没有。”这种案例十有八九是绘制坐标算错了画到了可视区域外面去。因为你脑子里的世界坐标和pygame的屏幕坐标不是一个东西。为什么我会反复强调render值得花时间做不是因为它“好看”而是因为可视化本身就是最直接的调试工具。强化学习环境里最容易出错的地方是边界判定、碰撞规则、奖励惩罚位置。有一个能实时看到智能体每一步动作的渲染窗口这些错误很快就能被眼睛揪出来反过来全程靠print日志做黑盒调试一个坐标方向错误就能耗掉大半天。所以如果你问我自定义环境的第一步该做什么我的建议不是急着写reset和step而是先把render打通。环境逻辑就算全是错误只要画面能出来修复成本都很低。反过来逻辑写得再对渲染一团乱麻训练时根本没法观察效果只能闭眼盲调。2. 可视化选型pygame、OpenCV还是matplotlib在做自定义环境的渲染之前第一个要拍板的事就是选绘制后端。这个看起来很随意的决定会直接影响你后面所有的调试体验。我个人的倾向很明确以pygame为主其他方案做辅助。原因是gym生态里大量内置环境本身就是用pygame做渲染的社区习惯、资料数量、踩坑案例都集中在这套工具上。pygame的draw模块提供了圆形、矩形、多边形、线条、字体渲染等能力几乎覆盖了环境可视化所需要的全部元素而且它自带事件系统和Clock帧率控制做实时交互非常顺手。把几个方案放在同一张表里看差别很清楚方案实时窗口键盘/事件控制读取帧数组视频保存最适合的场景pygame流畅原生窗口自带event系统可用surfarray转换需另接imageio或opencv实时调试、训练演示、交互控制OpenCVimshow有卡顿窗口体验一般仅支持简单按键本身就是numpy数组VideoWriter直接写mp4rgb_array模式、录制视频matplotlib重绘慢不适合动画基本不支持需要canvas提取逐帧保存图片静态序列图、论文配图如果你只是想在训练之后保存一段mp4不考虑实时窗口OpenCV的VideoWriter确实是最省事的但如果你和我一样需要在训练间隙盯着窗口看智能体行为pygame的手感是最好的。选型时还容易忽略一个版本兼容问题。老版本的gym里render是命令式写法用render(modehuman)在调用时指定模式新版本的gymnasium推荐在构造环境中传入render_moderender()方法不带参数。两个版本混用会直接报参数错误网上很多教程还在用老写法。新项目我建议直接上gymnasium接口更清晰维护也跟得上。3. 环境骨架与渲染接口设计3.1 环境接口到底在定什么自定义环境的核心在于四个方法reset负责重置状态step负责推进一步render负责渲染画面close负责释放资源。大部分人写前两个没有问题render和close经常是被随便糊弄过去的。新版gymnasium对环境类有个推荐约定constructor里接收render_mode参数render()根据这个参数走不同分支render_fps被写进metadata里供外部读取。这个设计比老版灵活也让环境自己知道自己的输出形式是human模式弹窗给人看还是rgb_array模式把一帧图像以numpy数组形式返回给算法/录像器。在动手写绘制前一定要想清楚环境的状态空间和动作空间。拿一个二维连续导航任务举例agent在[-1,1]的方形区域里移动动作是x和y方向上的位移增量目标是到达右上角的目标点。这个任务虽然简单但可视化效果非常直观非常适合用来演示坐标系变换、运动体绘制和轨迹绘制。3.2 一个能直接跑起来的连续导航Env下面是骨架代码注意我特意没有在这个阶段添加任何绘制逻辑先把reset和step的语义钉死import gymnasium as gym import numpy as np from gymnasium import spaces class Nav2DEnv(gym.Env): metadata { render_modes: [human, rgb_array], render_fps: 30, } def __init__(self, render_modeNone): super().__init__() # 动作x和y方向的位移增量 self.action_space spaces.Box( low-0.1, high0.1, shape(2,), dtypenp.float32 ) # 观测agent当前坐标归一化到[-1, 1] self.observation_space spaces.Box( low-1.0, high1.0, shape(2,), dtypenp.float32 ) self.render_mode render_mode self.window None self.clock None def reset(self, seedNone, optionsNone): super().reset(seedseed) self.state np.array([-0.8, -0.8], dtypenp.float32) self.goal np.array([0.8, 0.8], dtypenp.float32) self.direction np.array([1.0, 0.0], dtypenp.float32) self.trajectory [self.state.copy()] self.steps 0 return self.state.copy(), {} def step(self, action): action np.clip(action, self.action_space.low, self.action_space.high) prev_state self.state.copy() self.state np.clip(self.state action, -1.0, 1.0) self.trajectory.append(self.state.copy()) self.steps 1 if np.linalg.norm(action) 1e-6: self.direction action / np.linalg.norm(action) # 势能差奖励靠近目标给正奖励 dist_before np.linalg.norm(self.goal - prev_state) dist_after np.linalg.norm(self.goal - self.state) reward float(dist_before - dist_after) * 5.0 terminated bool(np.linalg.norm(self.goal - self.state) 0.1) truncated bool(self.steps 500) if terminated: reward 10.0 return self.state.copy(), reward, terminated, truncated, {}这里有个小设计值得说。我把动作上限设为0.1意味着agent一步最多走世界坐标的0.1个单位而视觉上的边界是[-1,1]。这个比例让轨迹有足够的帧数去展示录像出来不会一闪而过。如果动作幅度太大运动体在画面上会出现“瞬移”感完全看不清路径这是很多自定义环境做出来动画效果很差的原因。3.3 render和close的标准写法骨架写完后render的入口非常简单def render(self): if self.render_mode is None: return None return self._render_frame() def close(self): if self.window is not None: pygame.display.quit() pygame.quit()真正的内容全部放在_render_frame里。想渲染human模式就返回None把画面draw到窗口并flip想渲染rgb_array模式就返回一个(H, W, 3)的uint8 numpy数组供训练脚本或录制器直接使用。4. 解码世界坐标到屏幕坐标pygame绘制的核心4.1 坐标变换为什么值得单独拎出来pygame的坐标原点在左上角x轴向右y轴向下而强化学习环境里的世界坐标通常遵循数学习惯x轴向右y轴向上。坐标系如果不做转换agent在y方向上的动作会完全镜像你发给它一个向上的动作画面上它却往下走调试时精神状态非常影响效率。坐标变换公式并不复杂但方向是踩坑大头WIDTH, HEIGHT 600, 600 MARGIN 40 WORLD_MIN, WORLD_MAX -1.0, 1.0 def world_to_screen(vec): x, y vec sx MARGIN (x - WORLD_MIN) / (WORLD_MAX - WORLD_MIN) * (WIDTH - 2 * MARGIN) sy HEIGHT - MARGIN - (y - WORLD_MIN) / (WORLD_MAX - WORLD_MIN) * (HEIGHT - 2 * MARGIN) return int(sx), int(sy)核心就两行。sx做的是线性缩放把世界坐标从[-1,1]拉伸到留白区域sy除了缩放还要用HEIGHT减去换算结果这是对y轴向下翻转的关键步骤。很多黑窗问题就是这里少了一个减法。我建议先把这条换算函数独立出来不要散落到各个绘制函数里。绘制网格、目标点、运动体、轨迹时都统一调它这样万一换分辨率只改一个地方就够了。4.2 从网格、目标点到运动体的绘制顺序绘制顺序也有讲究。先把静态背景铺掉再画动态元素最后压上文字信息这样层次感最清楚。背景网格可以用简单的三条横线、三条竖线表示世界边界和原点轴线。这里有个小技巧grid的坐标也可以用world_to_screen换算而不是手算像素位置不然换尺寸时容易漏改。def _draw_grid(self, surface): for i in range(-1, 2): # 竖线 x i x_px world_to_screen((i, 0))[0] pygame.draw.line(surface, (200, 200, 200), (x_px, MARGIN), (x_px, HEIGHT - MARGIN)) # 横线 y i y_px world_to_screen((0, i))[1] pygame.draw.line(surface, (200, 200, 200), (MARGIN, y_px), (WIDTH - MARGIN, y_px))目标点画成一个绿色圆半径大概12像素视觉上足够醒目又不会掩盖agent轨迹。agent主体则画成三角形这是比圆形更有信息量的选择三角形的尖尖永远指向运动方向你能一眼看出agent在向哪个方位移动。def _draw_agent(self, surface): d np.array(self.direction, dtypefloat) if np.linalg.norm(d) 1e-6: d np.array([1.0, 0.0]) d d / np.linalg.norm(d) # 屏幕坐标系y取反 ds np.array([d[0], -d[1]]) ns np.array([-ds[1], ds[0]]) center np.array(world_to_screen(self.state), dtypefloat) size 14.0 tip center ds * size base_left center - ds * size * 0.6 ns * size * 0.5 base_right center - ds * size * 0.6 - ns * size * 0.5 pygame.draw.polygon(surface, (0, 120, 255), [tip, base_left, base_right])这里的ds把世界方向转换到屏幕向量核心就是把y分量取反。轨迹线用pygame.draw.lines画一条折线颜色调得淡一点避免盖住当前帧的运动体。最后用pygame.font把step计数、奖励值、距离信息渲染到画布左上角。这一步看似简单实际调试时价值极高滚动打印出来的状态和画面上的位置对不上时文字信息能立刻帮你定位是哪一侧出了问题。4.3 把human和rgb_array两条路走通一个常见的“半调子做法”是只写好human模式训练时勉强能看一到录视频或并行采样就束手无策。正确做法是在_render_frame里把两种模式都支持。def _render_frame(self): if self.window is None and self.render_mode human: pygame.init() self.window pygame.display.set_mode((WIDTH, HEIGHT)) self.clock pygame.time.Clock() surface pygame.Surface((WIDTH, HEIGHT)) surface.fill((245, 245, 245)) self._draw_grid(surface) # 绘制目标点和轨迹 pygame.draw.circle(surface, (100, 200, 100), world_to_screen(self.goal), 12) if len(self.trajectory) 1: pts [world_to_screen(p) for p in self.trajectory] pygame.draw.lines(surface, (180, 180, 230), False, pts, 2) self._draw_agent(surface) if self.render_mode human: self.window.blit(surface, (0, 0)) pygame.event.pump() pygame.display.flip() self.clock.tick(self.metadata[render_fps]) return None elif self.render_mode rgb_array: # pygame.surfarray.array3d 返回 (W, H, 3)需要转成 (H, W, 3) frame pygame.surfarray.array3d(surface) return np.transpose(frame, (1, 0, 2)).astype(np.uint8)注意两个细节。第一human模式必须调用pygame.event.pump()否则窗口会一直处于“未响应”的白屏状态第二rgb_array模式千万别直接返回array3d的结果因为它的维度顺序是宽度在前、高度在后标准约定是高度在前、宽度在后不转置的话录出来的视频会被拉伸或者直接报错。5. 训练闭环与渲染节奏控制5.1 先用键盘手动控制验证环境动力学在训练任何算法之前我强烈建议先做一个用键盘手动控制agent的脚本。这是验证环境正确性成本最低的方式。import pygame import numpy as np from env import Nav2DEnv env Nav2DEnv(render_modehuman) obs, _ env.reset() running True while running: env.render() act np.zeros(2, dtypenp.float32) for event in pygame.event.get(): if event.type pygame.QUIT: running False keys pygame.key.get_pressed() if keys[pygame.K_LEFT]: act[0] - 0.05 if keys[pygame.K_RIGHT]: act[0] 0.05 if keys[pygame.K_UP]: act[1] 0.05 if keys[pygame.K_DOWN]: act[1] - 0.05 _, _, terminated, truncated, _ env.step(act) if terminated or truncated: obs, _ env.reset() env.close()手动控制至少能验证三件事方向是否和你的直觉一致、移动范围是否被边界限制住、奖励数值在屏幕上是否合理。如果在键盘控制阶段就发现按上键agent往下走那现在改坐标变换只要一分钟等训练结束再回头找问题就难了。5.2 训练时渲染频率的取舍很多人的训练脚本卡顿到怀疑人生不是因为算法复杂而是每一步都调用了render而且是在human模式里等Clock同步。human模式的tick会把帧率限制在比如30fps训练一步等一小会儿几百个episode下来就是巨大的时间浪费。我的习惯是训练过程分两段看。第一段每10个episode或每1000步开一次human渲染观察几秒钟再关掉只用于抽查行为模式第二段正式跑长训练时直接把render_mode设为None等训练结束再开一个评估脚本用rgb_array模式录视频回放。如果你用的是gymnasium录制视频可以直接用官方的RecordVideo包装器不用自己一帧一帧拼from gymnasium.wrappers import RecordVideo env Nav2DEnv(render_modergb_array) env RecordVideo( env, video_folder./videos, episode_triggerlambda episode_id: episode_id % 5 0, )这样每5个episode会自动保存一段mp4。这比手动写保存逻辑靠谱得多也省掉了自己处理目录和编码器的麻烦。5.3 录制的视频与奖励设计的互相验证打完视频后回放的价值不限于“发给别人看效果”更是奖励工程的一个探针。我见过最典型的画面agent一直在边界附近转圈画面里的轨迹绕成一团屏幕左上角奖励数值长期贴着负值。这幅画面精准告诉你你的势能奖励或者惩罚设计出了问题要么目标太远要么中间存在误导性的局部最优。如果只用episode return曲线你也能看出来训练没收敛但看不出“为什么”而可视化能直接定位到行为模式。这种调试红利在环境复杂时尤其值钱。所以不要只把render当动画看它就相当于强化学习调试时的仪表盘。6. 实战排错与性能优化6.1 常见症状对照表把最常踩的坑整理成一张表可以少走很多弯路症状根因解决办法黑屏窗口里什么都没有坐标换算把物体画到可视区域外打印几个关键点的world_to_screen结果上下颠倒按上键却往下移y轴方向未做翻转sy HEIGHT - MARGIN - (y - y_min) * scale_y窗口白屏无响应没有调用pygame.event.pump()human渲染路径里加上pump关窗口后程序依然运行未监听pygame.QUIT事件循环检查QUIT并调用close训练越跑越慢每一步都render且被Clock限速按episode抽查渲染训练时render_mode置None录出来的视频颜色偏蓝偏红RGB/BGR通道顺序或array3d转置问题imageio直接存RGB用cv2前先转BGRrgb_array维度报错array3d得到(W,H,3)未转置np.transpose(frame, (1,0,2))子进程采样崩溃pygame在多个进程重复初始化只在主进程渲染子进程只返回rgb_array中文文字变成方块系统没有可用中文字体用pygame.font.get_fonts()检查或显示英文6.2 性能优化与离屏渲染pygame.draw本身的绘制性能其实不错画几千个矩形和线段都不是瓶颈。真正的开销主要在两处一是display.flip的窗口刷新二是rgb_array模式下surfarray.array3d把surface转成numpy数组的内存拷贝。前者靠控制渲染频率解决后者靠降低分辨率解决。录视频时把画布尺寸从600x600降到256x256文件体积和生成速度都会好很多画面内容依然足够看清运动趋势。还有一个容易被忽略的点字体和Surface对象不要反复创建。如果你在_render_frame里每次调用pygame.font.Font训练一个晚上就会积攒出上千个临时对象白白增加GC压力。把字体对象做成类的成员初始化一次后续复用代码更干净性能也更好。远程服务器没有显示器时human模式会直接失败。这种情况可以用离屏模式把SDL的视频驱动设为dummy环境变量pygame仍然可以在内存里创建Surface并生成rgb_array帧录像完全不受影响。做法是在import pygame之前设置环境变量SDL_VIDEODRIVER为dummy或者用xvfb-run运行脚本。这样哪怕没有图形界面也能拿到agent行为的可视记录。最后再分享一点我自己的习惯。现在做任何自定义gym环境我都会先把可视化模块当成一等公民来写而且很早就加入键盘手动控制逻辑不等到算法阶段才补render。一个小细节是画轨迹时我会保留最近的200个点而不是全部历史点避免长时间训练后轨迹线糊成一团。画面清清爽爽调试思路才跟得上。

相关新闻

Python爬虫实战:requests+正则抓取猫眼TOP100电影榜单

Python爬虫实战:requests+正则抓取猫眼TOP100电影榜单

简介:面向Python爬虫入门者的实战资源包,以猫眼电影TOP100榜单为案例,演示如何用requests库请求网页、用正则表达式解析电影名与评分,并说明robots.txt、请求延时、反爬应对等爬虫规范与实用技巧。整包为zip格式,共6个…

2026/10/13 16:43:25 阅读更多 →
jvisualvm实战:63个Java性能诊断场景速查指南

jvisualvm实战:63个Java性能诊断场景速查指南

1. 项目概述:为什么jvisualvm不是“看看CPU就完事”的玩具jvisualvm这个名字,第一次听的人常以为是个带图形界面的top命令——点开、看个曲线、关掉。我刚接触它时也这么想,直到在某次线上服务响应延迟突增的凌晨三点,用它在3分钟…

2026/10/13 16:43:25 阅读更多 →
五连跌的幻象:统计口径与周期视角下的市场噪音过滤

五连跌的幻象:统计口径与周期视角下的市场噪音过滤

最近市场里有个说法特别流行:“五连跌的幻象”。意思是,盘面连续五天收阴,情绪已经到了崩溃边缘,但真正把数据拆开看,会发现这五天的“下跌”里掺杂了大量统计口径误差、样本窗口偏见和情绪放大效应,真实图…

2026/10/13 16:42:24 阅读更多 →

最新新闻

优卡CAD自动化原理:打版逻辑、放码引擎与COM接口实战

优卡CAD自动化原理:打版逻辑、放码引擎与COM接口实战

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

2026/10/13 17:28:39 阅读更多 →
LiteLLM:统一多模型API调用的轻量级网关实践

LiteLLM:统一多模型API调用的轻量级网关实践

1. 项目概述:LiteLLM 是什么,它解决了哪些真实痛点?LiteLLM 这个名字在最近半年的技术圈里出现频率陡增,尤其在中小型开发团队、独立开发者和AI应用快速验证场景中,几乎成了“模型网关”“统一调用层”“多模型兼容方案…

2026/10/13 17:28:39 阅读更多 →
Linux U盘策略管理:udev+udisks2+Python实现安全可控的设备管控

Linux U盘策略管理:udev+udisks2+Python实现安全可控的设备管控

简介:这是一款面向企业IT管理员与信息安全从业者的U盘安全管控工具,专为解决USB设备滥用导致的数据泄露、病毒传播等风险而设计,适用于办公终端统一安全管理、涉密环境U盘准入控制等典型场景。资源包共9个文件,含1个主程序USBMana…

2026/10/13 17:28:39 阅读更多 →
Fiji-win64实战指南:图像分析环境配置与批量处理技巧

Fiji-win64实战指南:图像分析环境配置与批量处理技巧

简介:fiji-win64是面向科研与图像处理用户的免安装图像分析工具,基于ImageJ生态,支持Windows/Linux/macOS等多平台,尤其擅长读取Raw格式文件及处理高于8bit的图像数据,可完成直方图统计、图像拉伸、区域测量、批量分析…

2026/10/13 17:28:39 阅读更多 →
Cesium Viewer深度解析:从构造配置到性能优化与生命周期管理

Cesium Viewer深度解析:从构造配置到性能优化与生命周期管理

1. 先搞清Viewer在整个Cesium体系里的位置,别再往里面瞎塞东西了 做三维GIS和WebGL开发的人,应该都有这样一个体会:Cesium里的Viewer对象就像一栋写字楼的物业中心——你大部分时间在楼层里办事,却很少琢磨它本身是怎么运作的。这…

2026/10/13 17:28:39 阅读更多 →
空间机器人遥操作双边控制:从力反馈到主从手感的工程实践

空间机器人遥操作双边控制:从力反馈到主从手感的工程实践

简介:这是一份面向机器人控制、人工智能与机器学习方向研究者的博士学位论文,系统探讨通过智能算法提升空间机器人遥操作双边控制的精度与稳定性,重点解决时延力反馈系统中的稳定性、透明性和跟踪性问题。内容涵盖双边PD控制、从手控制力反馈…

2026/10/13 17:27:37 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 21:05:09 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →