写世界模型,很多人第一反应是视频生成模型,输入一句话,输出一段画面。但新一代世界模型范式有一个更硬的思路:先用代码把世界的规则写出来,让规则自己跑起来,再把模拟出的状态渲染成画面。也就是说,画面不是“猜”出来的,而是从一个可计算的因果系统中“长”出来的。
这个范式的核心价值在于:可控、一致、可干预、可回放。你改一条物理规则,未来几秒钟的画面会跟着变;你换一个相机角度,同一个场景还能复用同一套状态;你让两个物体发生碰撞,系统会遵守你自己定义的碰撞逻辑。这种能力正是具身智能、自动驾驶、游戏AI、仿真数据生产最需要的东西。
这篇文章不介绍某个具体项目的一键部署,而是把“先写规则、再渲染画面”这条技术路线拆开讲:它和直接生成视频有什么本质区别、整体架构怎么设计、一个最小可运行的规则模拟渲染闭环怎么写、如何测试验证、如何做批量任务和接口集成,以及落地时会遇到哪些坑。
1. 核心能力速览
先把这条范式的核心能力摆出来,方便判断它适不适合你。
| 能力项 | 说明 |
|---|---|
| 范式定位 | 世界模型 / 可计算仿真,不是端到端像素生成 |
| 核心组成 | 规则模拟层 + 场景状态管理层 + 渲染层 |
| 典型能力 | 物理规则仿真、对象状态追踪、视角可控、交互响应、长时一致性、因果干预 |
| 规则模拟端 | 以 CPU / 内存为主,可用通用物理引擎或自研规则脚本 |
| 渲染端 | 取决于选型:传统光栅化、NeRF / 3DGS、扩散模型条件生成等 |
| 显存占用 | 主要发生在渲染阶段,具体以渲染后端为准,规则模拟阶段通常不依赖大显存 |
| 批量能力 | 可设计为“模拟器批量产出状态序列,渲染器批量消费” |
| 接口能力 | 可抽象为状态序列服务 / 渲染服务,支持 HTTP 或消息队列 |
| 适合人群 | 具身智能、自动驾驶仿真、游戏内容生产、AI 视频可控性研究者 |
| 使用边界 | 规则需要人工定义或从数据中学习,规则缺失时会出现状态发散 |
需要特别说明:这条路线不是一个开箱即用的单一软件,而是一套架构思想。你可以在 Unity、Unreal 这类引擎里实现,也可以在 Python 里用物理引擎加 PyTorch 渲染后端实现,甚至可以先用简单的 2D 规则脚本跑通闭环。
2. 它和传统生成范式的本质区别
要理解“先写代码模拟世界规则,再渲染画面”,最有效的方式是拿现有范式做对比。
2.1 直接生成派:从文本到像素
以视频扩散模型为代表的路线,本质上是学习“文本 -> 视频像素”的映射。给定一段提示词,模型生成一段在统计意义上像真的视频。
优点:
- 上手快,提示词即控制。
- 画面细节丰富,材质和光照往往很自然。
- 不需要显式定义物理规则。
问题也很明显:
- 因果性弱。模型并不真正理解“球碰到地面为什么弹起来”,它只是见过类似的运动模式。
- 长时一致性差。超过一定帧数后,物体位置、颜色、数量容易漂移。
- 不可精确干预。你很难让某个指定物体在指定时间出现在指定位置。
- 不可重放。同一段运动轨迹无法在不同相机角度下重新渲染。
2.2 重建派:从多视角图像到新视角
NeRF、3D Gaussian Splatting 这类方法解决的是“从已有的场景观察中重建并渲染新视角”。它能把真实场景变成可渲染的三维表达。
优势是视觉保真度高,适合静态场景编辑和视角切换。
但它并不内建“世界规则”。它不知道重力、碰撞、物体间因果关系。你想让场景中的一个杯子被碰倒然后落在地上,需要重新建模或人工编辑几何关系,复杂度很高。
2.3 规则模拟 + 渲染:可计算世界的路线
这一派的核心思想是:先构建一个世界里有什么对象、对象能做什么、对象之间遵循什么规则,然后用程序推进状态变化,最后把状态“翻译”成画面。
这套范式天然具备几个特性:
- 因果可解释。画面中的运动是由代码中的规则产生的,不是模型猜出来的。
- 状态可复用。一套状态序列可以对应多个相机轨迹、多种渲染风格。
- 可干预。改变输入状态,未来状态就按规则变化。
- 可重放。把每一帧状态序列存下来,随时可以回溯排查。
- 可扩展。新规则以代码形式加进去,不需要重新训练整个生成模型。
代价是:规则设计成本高。很多真实世界现象难以用简单规则表达,比如流体、衣物形变、复杂光照。这也是为什么这条路线通常和物理引擎、数据驱动模型结合,而不是纯手工写规则。
三者对比如下:
| 维度 | 端到端视频生成 | NeRF/3DGS 重建 | 规则模拟 + 渲染 |
|---|---|---|---|
| 因果性 | 弱 | 无 | 强,规则内建 |
| 长时一致性 | 容易漂移 | 静态场景稳定 | 取决于模拟精度 |
| 干预能力 | 提示词级 | 几何编辑级 | 状态变量级 |
| 状态复用 | 不支持 | 部分支持 | 支持 |
| 实现成本 | 训练成本高 | 采集和重建成本高 | 规则开发成本高 |
3. 技术架构拆解
一套完整的“规则模拟 + 渲染”系统,大致分成四个模块。实际落地时每个模块都可以独立替换。
3.1 场景状态层
场景状态是所有模块的公共数据格式。建议使用结构化的 JSON 或 YAML 保存,比如对象列表、属性、位置、速度、关系、规则参数。
{ "scene_id": "demo_001", "camera": { "position": [0, 2, 5], "look_at": [0, 1, 0], "fov": 60 }, "objects": [ { "id": "cube_A", "type": "rigid_body", "position": [0, 0.5, 0], "rotation": [0, 0, 0], "velocity": [0, 0, 0], "mass": 1.0 }, { "id": "ball_B", "type": "rigid_body", "position": [1, 2, 0], "velocity": [0, -1, 0], "radius": 0.2, "mass": 0.5 } ], "rules": { "gravity": [0, -9.8, 0], "restitution": 0.8, "dt": 0.01 } }关键是:状态里只保存“世界本身是什么”,不保存“画面是什么”。渲染是后续的事。
3.2 规则模拟层
规则模拟层负责从当前状态推进到下个状态。常见实现方式:
- 选用现成物理引擎,比如 MuJoCo、PhysX、Bullet、Box2D 等。
- 在游戏引擎中做规则脚本,比如 Unity 中写 C# 脚本控制 Object 行为。
- 自研轻量规则引擎,适用于业务逻辑简单但需要完全可控的场景。
模拟层需要处理的核心问题包括刚体运动、碰撞检测、触发事件、Agent 决策和约束求解。如果规则非常复杂,也可以在这一层接入强化学习策略,让策略网络输出动作,再交由物理规则求解状态转移。
3.3 渲染层
渲染层接收场景状态,输出图像或视频。根据需求有三类选择:
| 渲染方式 | 特点 | 适用场景 |
|---|---|---|
| 传统光栅化 | 速度快、可控性强、物理准确 | 游戏仿真、自动驾驶传感器仿真 |
| 神经渲染(NeRF/3DGS) | 视觉真实感强、代价高 | 高质量新视角渲染、数字孪生 |
| 扩散模型条件生成 | 能从状态生成自然细节,但稳定性需调优 | 数据增强、内容生成 |
这里有一个关键设计原则:模拟层输出状态,渲染层消费状态,两边只通过结构化数据通信。这样即使换渲染后端,前面的模拟逻辑不需要重写。
3.4 校验与反馈层
这是容易被忽略但很关键的一层。规则模拟跑久了会出现状态漂移、物体穿模、数值发散。校验层的职责包括:
- 检查对象是否越界。
- 检查速度、位置是否是合法数值。
- 检查对象关系是否符合规则。
- 对比渲染结果和状态是否一致,比如画面里球的位置和状态里球的位置是否吻合。
如果发现问题,可以选择回退状态、修正约束,或终止当前模拟并输出日志。
4. 适用场景与使用边界
这套范式适合解决“需要精确控制、需要因果一致性、需要批量产生仿真数据”的问题,不适合解决“只要好看就行”的纯创意生成。
4.1 适合谁来用
具身智能团队是一个典型场景:需要在仿真环境中生成大量带规则标签的训练数据。传统视频生成模型虽然能生成画面,但无法给出精确的物体坐标、碰撞位置、运动速度等结构化标注。而规则模拟天然带这些标注。
自动驾驶仿真也需要这种范式。你可以定义道路规则、车辆行为、传感器模型,然后让模拟器生成各种场景,再通过渲染端输出多视角画面和深度图。
游戏内容和数字孪生同样适用。规则模拟能让动画角色遵循特定行为逻辑,而不是单纯靠动作模型“补间”。
4.2 不适合什么场景
如果你只是想快速生成一段风格化视频,直接使用视频生成模型会更高效。强行先把规则写死,反而限制视觉表现力。
流体、烟雾、布料等高度复杂现象,用传统规则模拟的代价很高,通常需要专用求解器,此时数据驱动或混合方案更有优势。
开放世界级别的语义生成,比如“生成一座城市并让城市运行起来”,规则设计成本会随着对象数量和交互类型爆炸式增长。
4.3 合规与安全边界
当这套系统涉及人像、声音、真实建筑或品牌资产时,必须确认授权。仿真场景若包含人脸,需要明确是虚拟生成还是真实肖像,不能用于欺骗性或误导性场景。自动驾驶仿真生成的数据若要用于算法验证,也要和真实道路数据交叉校验,避免模型在虚拟和真实环境之间出现严重泛化差异。
5. 环境准备与落地思路
虽然没有统一的“一键包”,但一套最小可跑的环境可以按下列清单准备。
5.1 硬件环境
- 规则模拟阶段:CPU 单机即可,重点看内存大小。复杂物理场景建议 16GB 内存以上。
- 渲染阶段:如果使用 PyTorch 渲染后端,需要 NVIDIA 显卡并安装对应版本 CUDA。显存需求取决于渲染模型分辨率、batch size 和模型复杂度。
- 如果整个系统部署在容器里,注意给模拟服务和渲染服务分配合适的 CPU / GPU 配额。
5.2 软件依赖
以下是在 Python 侧搭建示例的常见选择:
# 通用示例,实际版本以项目要求为准 python -m pip install numpy matplotlib python -m pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 可选:物理引擎 python -m pip install mujoco如果选择 Unity 或 Unreal 作为模拟端,则不是 Python 环境,而是游戏引擎工程环境。此时渲染端可以直接使用引擎内置渲染管线,也可以把状态导出给外部神经渲染器。
5.3 数据与资产目录
建议按以下结构组织项目:
world_model_project/ ├── scenes/ # 场景状态配置 ├── rules/ # 规则脚本 ├── simulator/ # 模拟器核心 ├── renderers/ # 渲染后端适配 ├── outputs/ │ ├── states/ # 状态序列 │ └── frames/ # 渲染帧 └── logs/ # 运行日志状态序列和渲染帧分目录保存,排错时会非常方便。
6. 一个最小可验证的规则模拟渲染闭环
下面用一个简化 2D 小球碰撞例子,说明“先写规则、再渲染画面”的闭环逻辑。它不是完整世界模型,而是为了让流程可见、可读、可改。
6.1 定义世界状态
import copy import json world = { "objects": [ { "id": "ball", "type": "dynamic", "position": [1.0, 2.0], "velocity": [2.0, 0.0], "radius": 0.2 }, { "id": "ground", "type": "static", "shape": "segment", "position": [0.0, 0.0], "end": [10.0, 0.0] } ], "rules": { "gravity": 9.8, "restitution": 0.8, "max_position": 10.0 } }6.2 编写规则推进函数
def step_world(world, dt=1/120): rules = world["rules"] for obj in world["objects"]: if obj["type"] != "dynamic": continue # 规则:重力改变垂直速度 obj["velocity"][1] -= rules["gravity"] * dt # 规则:速度改变位置 obj["position"][0] += obj["velocity"][0] * dt obj["position"][1] += obj["velocity"][1] * dt # 规则:小球与 ground 发生碰撞 ground_y = 0.0 if obj["position"][1] - obj["radius"] <= ground_y: obj["position"][1] = obj["radius"] obj["velocity"][1] = -obj["velocity"][1] * rules["restitution"] # 增加微小能量损耗 obj["velocity"][1] *= 0.999 # 规则:边界约束 if obj["position"][0] > rules["max_position"]: obj["position"][0] = rules["max_position"] obj["velocity"][0] *= -rules["restitution"] return world这一步就是“代码模拟世界规则”。每一帧的物理状态都是规则程序算出来的,不是模型编出来的。
6.3 渲染层抽象
规则模拟只输出状态。渲染层可以按需替换。
def render_frame(state_dict): """渲染层入口:接收状态字典,返回图像或视觉表达。""" # 方案 A:直接用 matplotlib 把圆形画出来 # 方案 B:转成 3D 场景描述,交给 Unity/Unreal 渲染 # 方案 C:把状态改写成 prompt/条件,交给神经网络渲染后端 raise NotImplementedError("请接入实际的渲染后端")上面这段代码不是具体项目代码,而是一个接口约定。实战中,你可以在这个函数内部触发传统渲染,也可以调用一个神经渲染服务。
6.4 跑一段完整模拟
def simulate(world, frames=240): state_sequence = [] current_world = copy.deepcopy(world) for i in range(frames): current_world = step_world(current_world, dt=1/120) # 保存状态序列,而不是保存像素 state_sequence.append(copy.deepcopy(current_world)) # 每一帧把状态交给渲染层 # image = render_frame(current_world) return state_sequence sequence = simulate(world, frames=240) with open("outputs/states/sequence.json", "w") as f: json.dump(sequence, f, indent=2)保存状态序列的意义在于:之后只要改相机参数,或改渲染后端,不需要重新模拟,直接重放状态序列即可出图。这是“状态与渲染分离”的核心收益。
7. 功能测试与效果验证方法
落地这套范式时,不能只看“渲染出来的图好不好看”,而是要分维度验证。下面这套测试流程可以照搬。
7.1 规则正确性测试
测试目的:验证模拟结果符合预期的物理规律。
操作步骤:
- 设置一个初始速度为零的球。
- 设置重力为 9.8,无初速。
- 连续推进 1 秒,统计球的下落距离。
预期结果:下落距离接近 4.9 米(忽略碰撞)。差值过大说明时间步长或数值积分有问题。
判断标准:
- 位置变化是否符合运动学公式。
- 碰撞后速度方向和恢复系数是否匹配。
常见失败原因:
- dt 过大,导致穿透。
- 碰撞检测条件判断使用了
>而不是>=,导致穿透后甚至在下一帧才被修正。 - 恢复系数加在了错误的速度分量上。
7.2 长时一致性测试
测试目的:确认模拟长时间运行后,状态不会漂移。
操作步骤:
- 运行 1000 帧、5000 帧。
- 检查速度是否出现无穷大或 NaN。
- 检查物体是否进入边界外区域。
- 检查能量是否持续单调增加。
预期结果:位置、速度保持在合法范围内,总能量在考虑耗散的情况下不异常增长。
判断标准:状态序列中所有数值均为有限值,物体数量不变,对象 ID 不变。
常见失败原因:
- 积分步长过大导致数值发散。
- 碰撞后速度被反复应用,导致物体反复弹跳甚至越界。
- 自定义随机事件没有做边界修剪。
7.3 因果干预测试
这是“规则模拟 + 渲染”最值得测的一项。
操作步骤:
- 使用同一个初始场景,生成一段 baseline 序列。
- 修改规则中的一个变量,比如把重力从 9.8 改成 19.6。
- 重新模拟,对比后续帧的运动轨迹。
预期结果:后续状态按规则发生可控变化,且变化只出现在受力相关对象上,而不是整段画面无规律乱变。
判断标准:对比前若干帧应完全一致,从规则生效的帧开始出现差异,且差异符合物理直觉。
常见失败原因:
- 规则参数被硬编码在多个位置,修改一处但没有同步其他引用。
- 场景缓存导致干预后仍然读取旧状态。
7.4 渲染一致性测试
测试目的:确认渲染结果和状态序列一致。
操作步骤:
- 随机抽取第 N 帧状态。
- 根据状态中的物体坐标和相机参数渲染画面。
- 人工或自动检查画面中物体位置是否和状态坐标对应。
预期结果:画面中物体出现在状态坐标对应的像素位置。
常见失败原因:
- 渲染层坐标单位和模拟层坐标单位不一致。
- 相机外参没有正确应用。
- 物体 ID 在模拟和渲染之间映射错位。
8. 批量任务与接口集成
单条序列模拟只是开始。真实场景下往往需要同时生成几千个仿真片段。这时要把系统拆成模拟服务、状态存储、渲染服务三部分,让它们各自扩缩容。
8.1 批量任务配置
{ "batch_config": { "total_segments": 20, "frames_per_segment": 120, "parallel_workers": 4 }, "rule_set": "urban_driving_v1", "render_backend": "gaussian_splatting", "output_dir": "./outputs/batch_001" }批量模拟时,建议让每个任务携带独立的seed或参数扰动,避免所有片段高度相似。
8.2 模拟器批量产出状态
import concurrent.futures def run_one(world, frames): return simulate(world, frames) with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor: futures = [ executor.submit(run_one, build_world(seed=i), 120) for i in range(20) ] for future in concurrent.futures.as_completed(futures): states = future.result() # 处理并保存 states save_segment(states)这里用多进程更合适,因为模拟通常是 CPU 密集任务。真正的 GPU 渲染不应该和模拟混在同一个进程里跑,否则会互相抢占资源。
8.3 HTTP 服务抽象
如果你要把这套能力提供给其他系统,可以抽象两个接口。
模拟接口:
POST /v1/simulate 请求体: { "scene_config_path": "scenes/demo_001.json", "duration_seconds": 5.0, "fps": 24, "render_required": true }# 伪代码,实际接口路径以服务实现为准 import requests url = "http://127.0.0.1:8000/v1/simulate" resp = requests.post(url, json={ "scene_config_path": "scenes/demo_001.json", "duration_seconds": 5.0, "fps": 24, "render_required": True }, timeout=300) result = resp.json() print(result.get("video_url"))渲染接口:
POST /v1/render 请求体: { "state_sequence_id": "seq_001", "camera_trajectory_id": "cam_take_01", "resolution": [1280, 720], "backend": "neural" }实际项目中,状态序列可能很大,最好通过对象存储或文件系统引用传递,而不是塞进 HTTP 请求体。接口层面只传 ID。
8.4 批量任务失败重试
批量任务最容易出现的坑是:任务跑到一半进程崩溃,前面生成的状态全部丢失。建议每生成一段状态就立刻保存一段,用“状态文件是否存在”作为重试依据,而不是等整个片段全部完成后再写。
重试策略:
- 每段任务失败后最多重试 3 次。
- 重试时使用新的临时文件,成功后再原子替换。
- 记录失败原因到日志表。
9. 资源占用与性能观察
“规则模拟 + 渲染”的资源特征和端到端视频生成完全不同。
9.1 两阶段资源模型
模拟阶段主要消耗 CPU 和内存。复杂场景中,对象数量从百级到万级,模拟耗时可能呈非线性增长。尤其是碰撞检测,如果使用 O(n^2) 的朴素检测,物体一多性能就急剧下降。
渲染阶段主要消耗显卡。动态分辨率、采样数、3DGS 点数、扩散模型步数都会直接影响显存。如果渲染后端是扩散模型,因为要多次迭代去噪,单帧生成时间远高于传统光栅化。
9.2 观察方法
- 模拟阶段观察
top/htop中 CPU 使用率和内存占用。 - 渲染阶段观察
nvidia-smi中的显存占用和 GPU 利用率。 - 在日志中打印每一阶段耗时:模拟耗时、渲染耗时、存储耗时,方便定位瓶颈。
示例日志格式:
frame=120 sim=0.8ms render=12.4ms save=0.2ms9.3 优化方向
- 模拟层使用空间哈希或包围体层次结构加速碰撞检测。
- 渲染层开启批处理,同一材质对象合并渲染。
- 状态序列只保留变更字段,不整帧复制全量状态。
- 模拟和渲染之间使用异步队列,模拟器不需要等渲染完再算下一段。
- 如果渲染后端是扩散模型,先用小分辨率验证效果,确认后再跑高分辨率。
需要明确一点:没有万能参数。显存占用和帧率必须结合具体渲染后端、场景复杂度、分辨率自己实测。
10. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 模拟结果发散 | 积分步长过大或约束未生效 | 打印若干帧状态,检查速度是否异常 | 缩小 dt,增加位置约束修正 |
| 物体穿透地面 | 碰撞检测条件漏掉了边界情况 | 单步单帧调试碰撞逻辑 | 补足边界判断,碰撞后将位置调整到合法位置 |
| 画面闪烁或跳变 | 渲染层读到了错误状态帧 | 检查状态帧 ID 和渲染帧 ID 是否对齐 | 通过状态文件的 hash 或帧序号校验 |
| 修改规则后画面不变 | 缓存未失效或参数被硬编码 | 搜索规则参数出现的位置 | 统一从配置读取规则参数 |
| 渲染显存不足 | 分辨率、批次或后端模型过大 | 查看 nvidia-smi 确定峰值占用 | 降低分辨率、减小 batch、换轻量渲染后端 |
| 批量任务卡住 | 某个场景状态非法导致模拟死循环 | 查看日志定位卡住的任务 | 设置单任务超时,超时直接终止并标记失败 |
| API 调用超时 | 同步接口等待渲染完成 | 确认是模拟耗时还是渲染耗时 | 改用异步任务,返回任务 ID 再轮询状态 |
| 模拟和渲染坐标系不一致 | 两个模块坐标系定义不同 | 抽取单帧状态和渲染结果对比 | 统一转换函数,并在接口层做坐标校正 |
| 生成的状态序列无法重放 | 保存内容丢失对象 ID 或相机参数 | 检查序列中的首帧是否完整 | 序列元信息单独保存,和帧数据绑定 |
11. 最佳实践与部署建议
这套范式工程化落地的核心是:分层要彻底,状态要可追溯,渲染要到最终应用场景再调优。
11.1 先跑通最小闭环
第一次实现不要追求复杂的 3D 场景和高质量神经渲染。先用 2D 小球或简单地面车辆把“模拟 -> 状态保存 -> 渲染”整条链路跑通。最小闭环的价值在于锁定接口,避免后面大量返工。
11.2 状态序列作为系统核心资产
把状态序列当作正式产品数据管理。每个片段要包含场景配置、规则版本、模拟版本、时间戳、Seed 信息。这样任何一个片段出问题,你都能回放并定位。
11.3 渲染层延迟优化
能用传统光栅化的场景先用传统光栅化,不要一上来就上扩散模型。扩散渲染虽然视觉效果好,但速度慢、不稳定、成本高。比较合理的方式是:规则层不变,渲染层做成可插拔,按应用场景选择不同后端。
11.4 合规和安全使用
仿真数据可能涉及道路标识、真实建筑外观、人物形象、品牌标志。使用前要确认素材来源合法。生成的人脸或道路场景如果用于模型训练,要保留相应说明和授权材料。任何情况下,都不能用仿真画面冒充真实监控、真实新闻或真实证人事迹。
11.5 日志与监控
建议在模拟器每个对象更新时输出简短的 debug 日志,在状态保存后输出统计信息。批量系统要有统一的日志格式,方便按scene_id和segment_id检索。日志中不需要记录完整状态,只记录异常、耗时和关键指标。
12. 下一步可以怎么深入
这条范式真正值得打磨的,不是某一个渲染模型有多强,而是规则层能覆盖多少真实世界动态。你可以从几个方向继续深入:
- 接入成熟的物理引擎,把自研规则脚本替换为更真实的刚体、关节和流体模拟。
- 把状态序列作为训练数据,训练一个小型神经渲染器,让画面质量和渲染速度达到平衡。
- 尝试用 LLM 根据自然语言生成初始场景状态,再由规则模拟器推进后续帧,形成“语言定义规则 -> 代码执行规则 -> 状态驱动渲染”的完整管线。
- 结合强化学习,让策略网络输出动作序列,规则引擎负责物理可行性,渲染端负责视觉化,这是具身智能仿真数据生产很值得尝试的路线。
如果手里正好有具身智能、自动驾驶或游戏 AI 相关的项目需求,建议先把“状态与渲染分离”这条原则放进架构设计里。先写规则,再渲染画面,这套范式不一定适合所有内容生成任务,但在需要精确控制和长期一致性的场景下,它比端到端生成更接近“可用”这两个字。