别被机器人吓到!从 0 到 1 玩转启元 Q1 & T1 技能整活
谁说写机器人技能得装几百 GB 的 ROS2 仿真器?扒开神神秘秘的 .rpk 技能包马甲,它其实就是个塞了 3 个小部件的压缩胶囊!
本工作台已为你备妥 3 套改词就能跑的剧本模板、一源双包影分身构建器、全浏览器顺手牵 Token 神器 与 0.4 秒赛博替身演武场,手把手带你轻松搞定现场双轮验收与技能上架!
三分钟扒底裤:四大宝藏专区与 .rpk 技能包的真身
.rpk 技能包到底是个啥? —— 其实就是把 .zip 压缩包改了个后缀名!扒开看里面就躺着这 3 样核心宝贝:
写代码必背「五条防炸机铁律」与 Q1 / T1 安全动作表
铁律 1 · 打死别写 import robot 或 rclpy 写必暴毙
万能遥控器 robot 是管家自动塞进全局变量的!你要是手欠写一行 import robot,Python 找不到模块当场给你表演 ImportError 原地去世!且只准导 Python 3.10 标准库(time、json、random、urllib.request)。
铁律 2 · 说话必须盯够 0.7 秒“真闭嘴” 治好吞字
robot.play_tts(text) 是个不管售后的急性子,调完 0.001 秒就返回!不等说完就发下一句会直接把上一句掐脖子憋回去。更调皮的是长句换气时状态会在 1↔2 假空闲抖动,必须盯到连续 0.7 秒稳定空闲(2)才算真说完!
铁律 3 · 动作传 interrupt=False 并乖乖等扭完 不抢麦克风
黄金节奏:先等嘴巴彻底闭上 ➔ 再调 robot.play_animation(id, False) ➔ 再 time.sleep(秒数) 等它扭完。要是传了 interrupt=True,正在播的声音会被一刀切掉!
铁律 4 & 5 · 机型动作别串门 + print 必加 flush 拒绝盲调
Q1(3000系)和 T1(1~72系)动作 ID 完全不通用!而且所有日志必须写 print(..., flush=True),否则全憋在内存缓冲区里,调试时屏幕比脸还干净。
reason 24):在普通用户室内模式(motion_mode=0)下,Q1 的腿被安全结界封印了——你非让它做全身动作(whole_body),底层硬件会直接甩给你一个冷酷的 reason 24!上架技能只准用上半身动作(upper_body,3000 系列),动动手照样帅翻全场:
reason 19):T1 一会儿是两条腿站立的酷盖(biped),一会儿是四条腿趴着的修勾(quadruped)。要是它正四条腿趴着呢,你非逼它直立比心握手,它当场报 reason 19 罢工(四条腿怎么比心嘛,臣妾做不到啊!)!必须先调 robot.get_t1_robot_form() 问清形态,且四足下只认 1 / 7 / 26 / 43 四个动作!
三套现成剧本模板与在线源码精讲
1. 启元百变互动官 👑 C位全能王
- ✅ 英文动作别名(
greet / cheer / heart不用背数字) - ✅ 随机好运盲盒池(
LUCKY_POOL改词即变算命摊) - ✅ 联网金句 API + 拔网线 100% 自动切回离线保底词
- ✅ 呼吸灯带气氛组联动 + 0.7s 防抖不吞字底座
2. 社交能量补给站 🛡️ 离线保底王
- ✅ 全篇仅 78 行极简代码,30 秒从头看懂到尾
- ✅ 勇气 / 灵感 / 快乐三格充电暖心小剧场
- ✅ 零外网与外部音频依赖,现场首轮保底锁分首选
"""启元百变互动官(Q1 / T1 通用全能技能模板,纯 Python 3.10 标准库)。
设计目标:
1. 开箱即用:可直接打包部署到 Q1 或 T1 真机运行,完成「迎宾 → 每日金句(联网+断网兜底) → 随机抽签互动 → 谢幕」完整闭环。
2. 极易改编:只需修改顶部的配置区(台词、动作别名、抽签池、是否开启联网/灯光),即可快速改成迎宾、算命、播报、小剧场等任意技能。
3. 严守契约:不导入 robot / ROS2,内蔵 TTS 0.7 秒防抖轮询与超时保护,自动适配 Q1 upper_body 与 T1 双足/四足动作白名单。
"""
import json
import random
import time
import urllib.request
# ==============================================================================
# 【第一部分:积木配置区 —— 改这里就能变成你的新技能!】
# ==============================================================================
# 构建脚本 build.py 会在打包时自动将此值替换为 "q1" 或 "t1"
ROBOT_MODEL = "q1"
# 功能开关
ENABLE_NET_QUOTE = False # True=尝试联网拉取每日一言(失败自动用离线兜底);False=纯离线最稳模式
ENABLE_RANDOM_DRAW = True # True=从 LUCKY_POOL 随机抽签;False=固定使用第一支签
ENABLE_MOOD_EFFECTS = True # True=尝试联动呼吸灯带与面部表情(自带安全容错)
# 联网 API 配置与断网兜底金句
QUOTE_API_URL = "https://v1.hitokoto.cn/?c=i&encode=json"
FALLBACK_QUOTE = "代码写得稳,真机跑得准,每一次动手都是超酷的创造!"
# 随机互动签文池:(签文台词, 动作语义名)
LUCKY_POOL = (
("叮!为你抽到「真机丝滑上上签」!今天你的所有动作指令全部一次通过!", "cheer"),
("叮!为你抽到「脑洞大开幸运签」!你的创意全场最亮眼,评委看了都点赞!", "heart"),
("叮!为你抽到「零Bug欧皇签」!编译不报错,网络不超时,好运直接拉满!", "greet"),
)
# 主流程固定剧本:(动作语义名, 台词文本, 备注说明)
# 支持占位符:{quote} 会自动填入联网/兜底金句,{lucky_text} 会填入抽签结果
OPENING_LINE = "哈喽大家好!我是启元百变互动官,很高兴在活动现场见到你!"
CLOSING_LINE = "今日互动圆满完成!祝你玩得开心,作品大放异彩,我们下次见!"
# 语义化动作映射表:把好记的名字映射到 Q1 / T1 的安全动作 ID 和保持秒数 (动作ID, 等待秒数)
# - Q1 严格只用室内模式允许的 upper_body 动作(3000 系)
# - T1 四足/双足均默认使用四足白名单兼容动作(26 喜悦 / 43 律动),也可在 biped 下扩展
ACTION_TABLE = {
"q1": {
"greet": (3001, 6.5), # 招手
"cheer": (3008, 6.5), # 加油
"heart": (3007, 6.5), # 双手比心
},
"t1_quadruped": {
"greet": (26, 16.0), # 喜悦(四足安全白名单)
"cheer": (43, 16.0), # 律动(四足安全白名单)
"heart": (26, 16.0), # 喜悦(四足安全白名单)
},
"t1_biped": {
"greet": (26, 16.0), # 喜悦(双足同样安全可用)
"cheer": (43, 16.0), # 律动
"heart": (26, 16.0), # 喜悦
},
}
POLL_SECONDS = 0.1
IDLE_SECONDS = 0.7
# ==============================================================================
# 【第二部分:安全硬件底座 —— 防吞字轮询、形态校验、网络兜底(一般无需修改)】
# ==============================================================================
def audio_state() -> int:
"""读取并校验音频状态:1=播放中,2=空闲。"""
snapshot = robot.get_audio_play_state()
if isinstance(snapshot, dict):
state = snapshot.get("state")
if isinstance(state, dict) and state.get("value") in (1, 2):
return state["value"]
raise RuntimeError("AUDIO_STATE_INVALID")
def speak(text: str) -> None:
"""说话并等待真实播完(含 4 秒起声监控 + 0.7 秒防抖滤波 + 超时保护)。"""
print("SPEAK " + text, flush=True)
robot.play_tts(text)
start_deadline = time.monotonic() + 4.0
while audio_state() != 1:
if time.monotonic() >= start_deadline:
raise RuntimeError("TTS_NOT_STARTED")
time.sleep(POLL_SECONDS)
finish_deadline = time.monotonic() + max(len(text) * 0.45, 4.0) + 8.0
idle_since = None
while time.monotonic() < finish_deadline:
if audio_state() == 1:
idle_since = None
elif idle_since is None:
idle_since = time.monotonic()
elif time.monotonic() - idle_since >= IDLE_SECONDS:
return
time.sleep(POLL_SECONDS)
raise RuntimeError("TTS_NOT_FINISHED")
def trigger_mood(color: str = "#FF6B2B", mode: int = 3) -> None:
"""可选的灯带氛围触发(带安全容错,不影响主流程与离线替身测试)。"""
if not ENABLE_MOOD_EFFECTS:
return
play_light = getattr(robot, "play_light", None)
if callable(play_light):
try:
play_light(color, mode)
except Exception as exc:
print("LIGHT_SKIP " + str(exc), flush=True)
def fetch_quote() -> str:
"""获取今日金句:若开启联网则请求 API,超时或断网时 100% 自动回退到离线金句。"""
if not ENABLE_NET_QUOTE:
return FALLBACK_QUOTE
try:
req = urllib.request.Request(QUOTE_API_URL, headers={"User-Agent": "PrimeBot-Skill/1.0"})
with urllib.request.urlopen(req, timeout=2.5) as resp:
data = json.loads(resp.read().decode("utf-8"))
hitokoto = data.get("hitokoto")
if isinstance(hitokoto, str) and hitokoto.strip():
return hitokoto.strip()
except Exception as exc:
print("NET_FALLBACK " + str(exc), flush=True)
return FALLBACK_QUOTE
def resolve_action_profile() -> dict:
"""根据机型(Q1 / T1)与实时形态(biped / quadruped)选择安全动作表。"""
if ROBOT_MODEL == "q1":
return ACTION_TABLE["q1"]
if ROBOT_MODEL == "t1":
form = robot.get_t1_robot_form()
if form not in ("biped", "quadruped"):
raise RuntimeError("T1_FORM_UNKNOWN")
print("T1_FORM " + form, flush=True)
return ACTION_TABLE["t1_" + form]
raise RuntimeError("ROBOT_MODEL_UNKNOWN")
# ==============================================================================
# 【第三部分:主演出流程】
# ==============================================================================
def main() -> None:
profile = resolve_action_profile()
print("UNIVERSAL_SKILL_START model=" + ROBOT_MODEL, flush=True)
trigger_mood("#FF6B2B", 3)
# 1. 开场问候
speak(OPENING_LINE)
# 2. 组装三幕互动内容(迎宾问候 → 今日灵感金句 → 幸运抽签)
quote_text = fetch_quote()
lucky_line, lucky_action = (
random.choice(LUCKY_POOL) if ENABLE_RANDOM_DRAW else LUCKY_POOL[0]
)
steps = (
("greet", "第一幕,元气问候。先挥个手,给你打满今日份的开发者Buff!"),
("cheer", "第二幕,灵感广播。送你一句话:" + quote_text),
(lucky_action, "第三幕,好运盲盒。" + lucky_line),
)
# 3. 逐幕执行:先说话播完 → 再触发安全动作 → 等待动作完成
for index, (action_key, line) in enumerate(steps, 1):
speak(line)
animation_id, hold = profile[action_key]
accepted = robot.play_animation(animation_id, interrupt=False)
print("ANIMATION step=%d key=%s id=%d accepted=%s" % (index, action_key, animation_id, accepted), flush=True)
if not accepted:
raise RuntimeError("ANIMATION_REJECTED id=%d" % animation_id)
time.sleep(hold)
# 4. 收场谢幕与完成标记
speak(CLOSING_LINE)
print("UNIVERSAL_SKILL_DONE steps=%d" % len(steps), flush=True)
# 机器人运行时在注入全局 robot 对象后直接执行
main()
"""社交能量补给站:基于官方示例的 TTS 轮询与动作编排,纯标准库。"""
import time
ROBOT_MODEL = "q1"
POLL_SECONDS = 0.1
IDLE_SECONDS = 0.7
STEPS = (
"第一格,勇气充电。今天你已经迈出了第一步,给自己加个油!",
"第二格,灵感充电。先让一个小点子跑起来,再慢慢把它变精彩!",
"第三格,快乐充电。请收下这份机械式认真,和百分之百的祝福!",
)
# 等待时长为保守初值,现场按动作权威表、任务日志与实际观感调整。
MOTIONS = {
"q1": ((3001, 7.0), (3008, 7.0), (3007, 7.0)),
"t1": ((26, 18.0), (43, 18.0), (26, 18.0)),
}
def audio_state() -> int:
snapshot = robot.get_audio_play_state()
if isinstance(snapshot, dict):
state = snapshot.get("state")
if isinstance(state, dict) and state.get("value") in (1, 2):
return state["value"]
raise RuntimeError("AUDIO_STATE_INVALID")
def speak(text: str) -> None:
"""必须观测到起声及稳定空闲;超时终止,避免下一句截断当前音频。"""
print("SPEAK " + text, flush=True)
robot.play_tts(text)
start_deadline = time.monotonic() + 4.0
while audio_state() != 1:
if time.monotonic() >= start_deadline:
raise RuntimeError("TTS_NOT_STARTED")
time.sleep(POLL_SECONDS)
finish_deadline = time.monotonic() + max(len(text) * 0.45, 4.0) + 8.0
idle_since = None
while time.monotonic() < finish_deadline:
if audio_state() == 1:
idle_since = None
elif idle_since is None:
idle_since = time.monotonic()
elif time.monotonic() - idle_since >= IDLE_SECONDS:
return
time.sleep(POLL_SECONDS)
raise RuntimeError("TTS_NOT_FINISHED")
def choreography() -> tuple:
if ROBOT_MODEL == "t1":
form = robot.get_t1_robot_form()
if form not in ("biped", "quadruped"):
raise RuntimeError("T1_FORM_UNKNOWN")
# 两种形态均使用 26/43,属于手册所列的四足允许集合。
print("T1_FORM " + form, flush=True)
return MOTIONS[ROBOT_MODEL]
def main() -> None:
motions = choreography()
print("ENERGY_STATION_START model=" + ROBOT_MODEL, flush=True)
speak("欢迎来到社交能量补给站!不用扫码,送你三格好心情。")
for index, (text, (animation_id, hold)) in enumerate(zip(STEPS, motions), 1):
speak(text)
accepted = robot.play_animation(animation_id, interrupt=False)
print("ANIMATION step=%d id=%d accepted=%s" % (index, animation_id, accepted), flush=True)
if not accepted:
raise RuntimeError("ANIMATION_REJECTED id=%d" % animation_id)
time.sleep(hold)
speak("充电完成!愿你带着自己的点子出发。掌声送给每一个敢动手的人!")
print("ENERGY_STATION_DONE steps=%d" % len(STEPS), flush=True)
# 与官方示例一样,在运行时 exec 注入 robot 后直接执行。
main()
"""从 Example/ 下的技能源码构建 Q1/T1 双机型 .rpk 包及可审阅的发布元数据。"""
import importlib.util
import json
import shutil
import tempfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
WORKSPACE = ROOT.parent
DEV_KIT = ROOT / "primehub_dev_kit"
EXAMPLE_ROOT = WORKSPACE / "Example"
OUTPUT = ROOT / "dist"
SKILL_SPECS = (
{
"folder": "energy_station",
"pkg_prefix": "cans518_energy_station",
"publish_prefix": "publish",
"title": "社交能量补给站",
"short_desc": "三格好心情,给每个敢动手的人充个电",
"long_desc_tpl": (
"离线小剧场:开场、勇气/灵感/快乐三格充电、谢幕。"
"每格短台词配动作,无需联网或语音输入。"
"仅适配%s,使用预设动作,运行时请留出周围1米空间。"
"基于官方示例改编;台词和编排可在源码中修改。"
),
"icon": "skill-game.svg",
},
{
"folder": "universal_skill",
"pkg_prefix": "cans518_universal_skill",
"publish_prefix": "publish_universal",
"title": "启元百变互动官",
"short_desc": "迎宾问候+灵感广播+好运盲盒抽签的通用互动剧场",
"long_desc_tpl": (
"Q1/T1通用互动技能模板:包含元气迎宾、每日灵感金句(支持联网API与断网自动兜底)、"
"随机好运抽签及谢幕。仅适配%s,内置形态检测与TTS防抖轮询,运行时请留出周围1米空间。"
),
"icon": "skill-verify.svg",
},
)
def build_skill(pack_module, spec_cfg: dict) -> None:
source_dir = EXAMPLE_ROOT / spec_cfg["folder"]
for model, model_id, model_code in (("q1", 100, "robot_q"), ("t1", 101, "robot_t")):
with tempfile.TemporaryDirectory(prefix="qiyuan_build_") as staging:
skill_dir = Path(staging) / (spec_cfg["folder"] + "_" + model)
shutil.copytree(source_dir, skill_dir, ignore=shutil.ignore_patterns("__pycache__", "*.pyc"))
script = skill_dir / "src/main.py"
source = script.read_text(encoding="utf-8")
assignment = 'ROBOT_MODEL = "q1"'
if source.count(assignment) != 1:
raise ValueError("期望恰好一个 ROBOT_MODEL 声明: " + str(script))
script.write_text(source.replace(assignment, 'ROBOT_MODEL = "%s"' % model), encoding="utf-8")
config = skill_dir / "cfg/pkg.yaml"
package_name = "%s_%s_20261024" % (spec_cfg["pkg_prefix"], model)
lines = []
for line in config.read_text(encoding="utf-8").splitlines():
if line.startswith("package_name:"):
line = "package_name: " + package_name
elif line.startswith("robot_model:"):
line = "robot_model: " + model
lines.append(line)
config.write_text("\n".join(lines) + "\n", encoding="utf-8")
pack_module.pack(skill_dir, OUTPUT)
metadata = {
"skillName": spec_cfg["title"] + " " + model.upper(),
"version": "0.1.0",
"skillDesc": spec_cfg["short_desc"],
"long_description": spec_cfg["long_desc_tpl"] % model.upper(),
"categoryIds": [104],
"categoryCodeList": ["action"],
"tagsIds": [127],
"tagsCodeList": ["companion"],
"robotModelIds": [model_id],
"robotModelCodeList": [model_code],
"iconUrl": spec_cfg["icon"],
}
publish_file = "%s_%s.json" % (spec_cfg["publish_prefix"], model)
(OUTPUT / publish_file).write_text(
json.dumps(metadata, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
)
def build() -> None:
spec = importlib.util.spec_from_file_location("primehub_pack", DEV_KIT / "pack.py")
pack_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(pack_module)
OUTPUT.mkdir(exist_ok=True)
for spec_cfg in SKILL_SPECS:
build_skill(pack_module, spec_cfg)
if __name__ == "__main__":
build()
从 0 到 1 八步傻瓜式闯关(纯 Python 命令行)
派体检官检查你的电脑上 Python 温室环境、国密加密库 gmssl、ssh/scp/curl 三件套以及 PrimeHub 市场匿名搜索通不通。
PREFLIGHT_OK 并搜出官方「51 点歌台」python Tools/scripts/preflight.py
再也不用专门下火狐了!随便用 Chrome / Edge / Brave / Firefox 登录 primehub.primebot.com 后,连浏览器都不用关,一键把最新登录凭证顺进保险箱。
my 子命令不报错,说明发版通行证 100% 绿灯# 1. 自动扫描 Chrome / Edge / Brave / Firefox 顺走最新 Token
python Tools/primehub_dev_kit/extract_token.py
# 2. 验明正身(查查当前账号下的技能列表)
python Tools/primehub_dev_kit/primehub_publish.py my
打开《启元百变互动官》源码最顶部的【积木配置区】,改改开场白、往 LUCKY_POOL 塞几条搞笑运势签,或者拨动联网金句开关(拔网线也能自动切回离线保底词!)。
robot 铁律ENABLE_NET_QUOTE = False # True=联网拉每日金句(没网自动用 FALLBACK_QUOTE 兜底!)
ENABLE_RANDOM_DRAW = True # True=从 LUCKY_POOL 随机抽签
LUCKY_POOL = (
("叮!为你抽到「真机丝滑上上签」!动作一次通过!", "cheer"),
("叮!为你抽到「脑洞大开幸运签」!全场为你点赞!", "heart"),
)
不用苦哈哈维护两份代码!构建器会自动把源码复印两份并分别注入 q1 / t1 参数,在 成品弹药库 里吐出 4 个 .rpk 安装包和 4 份上架小抄。
OK .../*.rpkpython Tools/scripts/build.py
还没连真机心里没底?用“虚拟快进时钟 + 假机器人替身”在电脑上 0.4 秒把 Q1、T1 双足、T1 四足、0.3 秒大喘气抖动、拔网线降级与花式报错熔断全测一遍!
OK(⚠️ 敲黑板:电脑替身彩排通过 ≠ 真机跑通!)python -m unittest discover -s Tools/tests -v
🚨 上机前保命三结界:① 周围 1 米内清走人类与水杯;② 先低位姿态试跑再站立测试;③ 运行期间谁也别拿手机 App 抢方向盘遥控!
SSH 提示密码时敲 1。第一遍先不加 --run 把机上真实 YAML 动作表抓回 验证与探测报告厅,核对无误后再加 --run 起跳!
final: success + 硬件日志印着 创建Animation task成功# 1. 第一步:先侦察真机环境、抓回机上 YAML 动作表并上传 .rpk 包(先不跑!)
python Tools/scripts/robot_run.py --model q1 --ip 192.168.112.222 --skill universal_skill
# 2. 第二步:确认周围 1 米安全净空后,追加 --run 正式点火起跳!
python Tools/scripts/robot_run.py --model q1 --ip 192.168.112.222 --skill universal_skill --run
Step 7(双响炮验收):原样再跑一遍 --run,确保连续两轮独立稳过且亲耳听完 5 段台词不吞字。
Step 8(上架封神):敲右侧命令上架 PrimeHub(注意同名重复发布会生出双胞胎条目,换版先 delete),最后掏出手机官方 App 搜到下载并当众跑一遍!
{'code': 1000, 'msg': '成功'} + 手机 App 下载演出成功!# 1. 一键国密加密上传并发布对应机型到 PrimeHub 技能市场
python Tools/primehub_dev_kit/primehub_publish.py publish Tools/dist/universal_skill_q1.rpk --metadata-file Tools/dist/publish_universal_q1.json
# 2. 查查自家在架列表与全网公开搜索验货
python Tools/primehub_dev_kit/primehub_publish.py my
python Tools/primehub_dev_kit/primehub_publish.py search "启元百变互动官 Q1"
活动现场 2.5 小时安全结界与满贯打勾表
1. ⭕ 半径 1 米净空结界:机器人挥起手来六亲不认,周围 1 米内清走所有人类、水杯与笔记本电脑。
2. 🐢 先蹲着再站着(先低位后站立):初次上机调试先在安全低位姿态下试跑,确认温顺不抽风后再站立演示。
3. 📵 别抢方向盘(严禁同时用 App 遥控):技能运行期间严禁任何人同时用手机 App 遥控机器人(会触发底层运控精神分裂保护停机),一切听现场导师指挥。
机器人闹脾气怎么办?拒绝码(reason)10 秒哄好字典
| 日志报错 / 翻车现场 | 机器人在抱怨啥?(大白话翻译) | 10 秒钟怎么哄好它? |
|---|---|---|
| reason 24 | Q1 在普通用户室内模式下,下盘被安全结界封印了,拒做全身动作(whole_body) |
乖乖换成 Q1 upper_body 上半身动作 ID(如 3001 招手、3007 比心、3008 加油) |
| reason 19 | T1 正四条腿趴着当修勾呢,你非逼它做双足站立专属动作(四条腿怎么比心嘛!) | 换成四足通吃王炸动作(26 喜悦、43 律动、1、7)或用 get_t1_robot_form() 分支 |
| reason 42 | 上一个动作还在扭呢,动作队列堵车了,新动作挤不进去 | 把上一个动作后面的 time.sleep(hold) 等待秒数调大一点 |
| reason 28 | 上一个动作自带“霸体”不可打断(比如 T1 动作 1 打招呼要慢悠悠扭 15s) |
老老实实等够它的完整时长(如 16~18 秒),或者换用短平快的动作 26 / 43 |
| reason 17 | 机器人翻遍了固件字典,也没找到你瞎编的这个动作 ID | 打开 验证与探测报告厅 里抓回来的机上真实 YAML 动作表核对 |
TTS_NOT_FINISHED |
单句台词写成老太太的裹脚布太长了,超出了语音轮询等待上限 | 把单句台词精简到 30 字以内,或者调大 speak() 里的超时余量 |
final: timeout |
整场演出磨磨唧唧超过了 deploy_test.py 默认 120 秒监工窗口 |
适当缩减动作等待秒数,把整场演出控制在黄金 60~90 秒内 |
启元官网配色 · 6 张高清全彩作战图库
全站专题导航地图(攻略 · 剧本 · 工具 · 报告一网打尽)
🔥 核心必读阁 通关攻略
🎭 技能剧本库 源码精讲
🛠️ 军火工具箱 工程解析
📘 规则与报告室 铁证归档
- 🧭 官方规则与报告档案室总览
- 📜 官方活动说明与实操任务
- 📘 PrimeHub 技能开发技术指南
- 🤝 项目交接与现场执行指南
- 📋 赛前准备状态与命令总账单
- 🏛️ 验证、鉴权与探测报告总厅
- ✅ 赛前准备验收实测记录
- 📍 上海站活动时间与场地核验
- 🔒 工作区独立隔离验证说明