PrimeBot 启元 Q1 & T1 技能整活总门户
😜 Developer Playground · 零门槛大白话 · 极速通关

别被机器人吓到!从 0 到 1 玩转启元 Q1 & T1 技能整活

谁说写机器人技能得装几百 GB 的 ROS2 仿真器?扒开神神秘秘的 .rpk 技能包马甲,它其实就是个塞了 3 个小部件的压缩胶囊!
本工作台已为你备妥 3 套改词就能跑的剧本模板、一源双包影分身构建器、全浏览器顺手牵 Token 神器 与 0.4 秒赛博替身演武场,手把手带你轻松搞定现场双轮验收与技能上架!

启元机器人 Developer Playground
⚡ 现场偷懒专用 · 命令参数实时联动台 选好导师分给你的机型(Q1人形 / T1修勾)、挑个剧本、填上局域网 IP,底下 Step 6~8 的命令自动帮你改好,复制就能直接冲!
01 · ANATOMY

三分钟扒底裤:四大宝藏专区与 .rpk 技能包的真身

点开任意专题卡片,即可直达对应的图文详解与源码精讲

1. 核心必读阁 保命必读

大白话闯关攻略、防炸机铁律与现场抢分清单

2. 技能剧本库 3套现成剧本

抄作业不丢人!改几句台词就能变身你的原创技能

3. 军火工具箱 自动化神器

影分身打包、0.4秒替身试毒与全浏览器顺 Token

4. 规则与报告室 铁证档案

飞书官方评分圣旨、交接指南与实测证据陈列馆

🤫 课代表悄悄话:神神秘秘的 .rpk 技能包到底是个啥? —— 其实就是把 .zip 压缩包改了个后缀名!扒开看里面就躺着这 3 样核心宝贝:

① 技能包户口本 身份登记

一共就 6 行字!写明唯一英文小名 package_name、版本号,以及这包是喂给谁吃的(robot_model: q1 或 t1,狗粮猫粮严禁混吃!)。

② 演出台词剧本 核心代码

机器人肚子里的老管家 lumina_server 会在拉起代码前,偷偷往全局变量里塞一个万能遥控器 robot,直接按遥控器就能说话跳舞!

③ 相亲简历说明 玩法攻略

夸夸你的技能有多好玩,顺便提醒围观群众退后 1 米(如果要放内置音效,可额外配备媒体库存放 .wav 音频)。

02 · CONTRACT

写代码必背「五条防炸机铁律」与 Q1 / T1 安全动作表

更详细的 API 签名与喂 AI 咒语见 机器人运行时契约与避坑必读 →

铁律 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),否则全憋在内存缓冲区里,调试时屏幕比脸还干净。

🤖 Q1 保命口诀(专治傲娇拒绝码 reason 24):在普通用户室内模式(motion_mode=0)下,Q1 的腿被安全结界封印了——你非让它做全身动作(whole_body),底层硬件会直接甩给你一个冷酷的 reason 24!上架技能只准用上半身动作(upper_body,3000 系列),动动手照样帅翻全场:
3001 👋 招手(迎宾首选) 3002 🤝 握手 3003 🤜🤛 碰拳 3004 🙋 挥手 3005 😄 开心 3006 😅 擦汗 3007 🫶 双手比心(求赞必杀) 3008 💪 加油(打气首选) 3011 🙏 抱拳 3021 🫡 敬礼 3046 💃 舞蹈 A 3047 🕺 舞蹈 B
🐾 T1 保命口诀(专治四条腿没法比心 reason 19):T1 一会儿是两条腿站立的酷盖(biped),一会儿是四条腿趴着的修勾(quadruped)。要是它正四条腿趴着呢,你非逼它直立比心握手,它当场报 reason 19 罢工(四条腿怎么比心嘛,臣妾做不到啊!)!必须先调 robot.get_t1_robot_form() 问清形态,且四足下只认 1 / 7 / 26 / 43 四个动作!
✅ 双足站着 + 四足趴着通吃王炸动作(闭眼用,永不报 reason 19):
26 🎉 喜悦 (~5s,本项目主力) 43 🎵 律动 (~9s,摇头晃脑极有节奏) 1 👋 打招呼 (~15s,霸体慢动作不可打断) 7 💃 跳舞 (~33s,超长才艺秀)
🔹 仅限双足站立(biped)才准调用的动作:
2 🤝 握手 6 🫶 比心 10 / 11 🕺 舞蹈 20 / 21 / 22 📸 摆拍姿势 31 🙌 欢呼 52 🌙 太空步
03 · EXAMPLES

三套现成剧本模板与在线源码精讲

点击卡片链接即可查看带课代表逐段批注的源码精讲 · 查看剧本库总览 →

1. 启元百变互动官 👑 C位全能王

导览:魔改攻略 | 核心源码精讲 →

  • ✅ 英文动作别名(greet / cheer / heart 不用背数字)
  • ✅ 随机好运盲盒池(LUCKY_POOL 改词即变算命摊)
  • ✅ 联网金句 API + 拔网线 100% 自动切回离线保底词
  • ✅ 呼吸灯带气氛组联动 + 0.7s 防抖不吞字底座

2. 社交能量补给站 🛡️ 离线保底王

导览:项目说明 | 核心源码精讲 →

  • ✅ 全篇仅 78 行极简代码,30 秒从头看懂到尾
  • ✅ 勇气 / 灵感 / 快乐三格充电暖心小剧场
  • ✅ 零外网与外部音频依赖,现场首轮保底锁分首选

3. 官方点歌台 🎵 高阶炫技王

导览:项目说明 | 核心源码精讲 →

  • 🎵 联网搜歌断点续传下载 + ffmpeg 转 .wav
  • 🎵 纯标准库算音频包络与自相关 BPM 节拍
  • 🎵 适合想研究内置播音与卡点蹦迪的高手取经
《启元百变互动官》核心源码 · 👉 打开带课代表逐段拆解与行号的源码精讲页
"""启元百变互动官(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()
04 · WORKFLOW

从 0 到 1 八步傻瓜式闯关(纯 Python 命令行)

图文完整版见 从零开发 Q1/T1 技能 8 步 SOP →
STEP 0

30 秒环境大体检

查看体检仪原理 →

派体检官检查你的电脑上 Python 温室环境、国密加密库 gmssl、ssh/scp/curl 三件套以及 PrimeHub 市场匿名搜索通不通。

✓ 过关暗号:终端欢快吐出 PREFLIGHT_OK 并搜出官方「51 点歌台」
终端命令
python Tools/scripts/preflight.py
STEP 1

全浏览器顺手牵羊提 Token

查看提Token原理 →

再也不用专门下火狐了!随便用 Chrome / Edge / Brave / Firefox 登录 primehub.primebot.com 后,连浏览器都不用关,一键把最新登录凭证顺进保险箱。

✓ 过关暗号:跑 my 子命令不报错,说明发版通行证 100% 绿灯
自动扫描全浏览器 / 也可传 --browser edge 或 --token
# 1. 自动扫描 Chrome / Edge / Brave / Firefox 顺走最新 Token
python Tools/primehub_dev_kit/extract_token.py

# 2. 验明正身(查查当前账号下的技能列表)
python Tools/primehub_dev_kit/primehub_publish.py my
STEP 2 - 3

花 3 分钟改几句台词与抽签词

查看源码精讲 →

打开《启元百变互动官》源码最顶部的【积木配置区】,改改开场白、往 LUCKY_POOL 塞几条搞笑运势签,或者拨动联网金句开关(拔网线也能自动切回离线保底词!)。

✓ 过关暗号:只动顶部配置字典,严守纯标准库与全局 robot 铁律
《启元百变互动官》顶部积木配置区
ENABLE_NET_QUOTE = False     # True=联网拉每日金句(没网自动用 FALLBACK_QUOTE 兜底!)
ENABLE_RANDOM_DRAW = True    # True=从 LUCKY_POOL 随机抽签
LUCKY_POOL = (
    ("叮!为你抽到「真机丝滑上上签」!动作一次通过!", "cheer"),
    ("叮!为你抽到「脑洞大开幸运签」!全场为你点赞!", "heart"),
)
STEP 4

启动“一源双包”影分身构建器

查看构建器精讲 →

不用苦哈哈维护两份代码!构建器会自动把源码复印两份并分别注入 q1 / t1 参数,在 成品弹药库 里吐出 4 个 .rpk 安装包和 4 份上架小抄。

✓ 过关暗号:终端连打 4 行 OK .../*.rpk
一键双机型打包
python Tools/scripts/build.py
STEP 5

0.4 秒“赛博替身”试毒

查看替身演武场 →

还没连真机心里没底?用“虚拟快进时钟 + 假机器人替身”在电脑上 0.4 秒把 Q1、T1 双足、T1 四足、0.3 秒大喘气抖动、拔网线降级与花式报错熔断全测一遍!

✓ 过关暗号:全部测试 OK(⚠️ 敲黑板:电脑替身彩排通过 ≠ 真机跑通!)
运行全套替身仿真单元测试
python -m unittest discover -s Tools/tests -v
STEP 6

现场连真机:先派侦察兵抓动作表,再加 --run 点火!

查看真机点火器 →

🚨 上机前保命三结界:① 周围 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 - 8

真机“双响炮”连续跑两轮 + 发射火箭上架 PrimeHub

查看发版火箭筒 →

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"
05 · CHECKLIST

活动现场 2.5 小时安全结界与满贯打勾表

点击即可打勾(自动保存在浏览器) · 完整时间轴见 现场 2.5 小时行动与安全清单 →
🚨 现场真机保命三结界(全程焊死在脑子里,一票否决!):
1. ⭕ 半径 1 米净空结界:机器人挥起手来六亲不认,周围 1 米内清走所有人类、水杯与笔记本电脑。
2. 🐢 先蹲着再站着(先低位后站立):初次上机调试先在安全低位姿态下试跑,确认温顺不抽风后再站立演示。
3. 📵 别抢方向盘(严禁同时用 App 遥控):技能运行期间严禁任何人同时用手机 App 遥控机器人(会触发底层运控精神分裂保护停机),一切听现场导师指挥。
06 · TROUBLESHOOT

机器人闹脾气怎么办?拒绝码(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 秒内
07 · VISUAL ATLAS

启元官网配色 · 6 张高清全彩作战图库

点击标签切换图解,点击图片可全屏放大看清每一个细节
工作区全景架构图
图 1:启元 Q1 & T1 技能整活工作台四大专区与 .rpk 内部三件套扒皮图(点击图片全屏放大)
八步通关流程图
图 2:从 Step 0 开工体检到 Step 8 市场封神的完整命令与过关暗号(点击图片全屏放大)
运行时与动作红线
图 3:lumina_server 老管家塞遥控器机制、0.7 秒闭嘴防抖与 Q1/T1 安全动作白名单(点击图片全屏放大)
一源双包与替身仿真
图 4:一源双包影分身流水线与 0.4 秒赛博替身演武场架构(点击图片全屏放大)
全浏览器提Token与发布
图 5:Chrome / Edge / Brave / Firefox 全浏览器顺走 Token 与国密 SM4 上架闭环(点击图片全屏放大)
现场时间轴与排障速查
图 6:官方活动视觉、2.5 小时现场抢分时间表、安全三结界与拒绝码急救表(点击图片全屏放大)
08 · SITEMAP

全站专题导航地图(攻略 · 剧本 · 工具 · 报告一网打尽)

点击任一标题即可直达对应的专题指南、源码精讲或核验报告