资讯动态

给家用机器人写一个自主发现任务的 Skill:从被动响应到主动规划

发布时间:2026/10/9 13:09:02 来源:尧图企业网站定制
家用机器人和工业机器人最大的区别不是硬件精度而是任务从哪来。工业场景里任务由 MES 或工单系统下发机器人只管执行。家里没有工单系统任务来源是人。所以现在绝大多数家用机器人的工作模式就是人喊一句它动一下人不喊它就待机。我一直在想一个问题如果机器人能自己观察环境自己判断现在是不是该做点什么那它的价值会不会完全不一样比如地上有东西该收、猫碗空了该添粮、人出门了该关灯——这些事都不需要人专门下指令但确实需要有人做。这篇不是概念文章。我拿八界机器人当实验对象实际写了一个 Skill让它周期性检查环境状态自己判断有没有该触发的事件。下面把能跑通的部分、跑不通的部分、以及为什么跑不通都写清楚。先搞清楚八界 Skill 到底能拿到什么写任何自主决策逻辑之前必须先确认一件事这个平台给 Skill 暴露了哪些能力。如果连环境状态都读不到自主发现就是空谈。八界的 Skill 机制我理解是这样的Skill 是一个带元信息的 Python 模块声明自己需要订阅哪些事件、能调用哪些设备能力运行时由框架把上下文注入进来。核心是几个部分——一个描述文件声明 skill 名称、触发方式、权限一个执行入口以及框架提供的上下文对象。【注意】八界 Skill 的具体字段名和上下文对象的方法不同固件/框架版本会有差异。我手上用的是当时能拿到的版本下面代码里的接口名以你实际文档为准我这里只保证结构逻辑是对的不保证字段名逐字一致。这一点我没有在多个版本上验证过。一个 Skill 大致长这样# skill.pySKILL_META{name:proactive_home_watch,version:0.1.0,description:周期性感知环境判断是否有需要主动处理的事件,triggers:[timer],# 定时触发而不是等语音指令interval_sec:30,# 每 30 秒跑一次permissions:[sensor.read,device.control,tts.speak],}defrun(ctx):...关键点在于triggers里用的是timer而不是语音。这是整个实验的前提——如果 Skill 只能被语音唤醒那自主发现根本无从谈起因为唤醒源还是人。定时触发意味着机器人有了一个自己的心跳它可以在没人说话的时候主动跑逻辑。permissions决定它能读什么、控制什么。读传感器和控制设备是两类完全不同的权限我建议一开始只申请sensor.read先把能判断跑通再考虑能动手。原因后面会讲。感知层把环境变成结构化状态自主决策的第一步不是决策是感知。人做判断靠的是眼睛扫一圈机器人得先把这一圈变成代码能处理的数据。我设计的感知函数只做一件事把当前环境读成一个字典。不判断、不决策纯采集。defperceive(ctx):采集当前环境状态返回结构化字典。只读不做任何控制。state{}# 人体存在家里有没有人state[someone_home]ctx.sensor.get(presence_home)# bool# 各房间是否有人state[rooms_occupied]ctx.sensor.get(rooms_with_presence,default[])# 光照state[lux]ctx.sensor.get(ambient_lux,defaultNone)# 几个可控设备的状态state[lights_on]ctx.sensor.get(lights_on,default[])state[cat_bowl_g]ctx.sensor.get(cat_bowl_weight_g,defaultNone)# 时间上下文nowctx.now()state[hour]now.hour state[is_night]now.hour23ornow.hour6returnstate这里有几个设计上的取舍值得说。第一所有ctx.sensor.get我都给了default。原因是家里的传感器不一定齐全有的家庭装了人体传感器有的没装。如果传感器缺失直接抛异常整个 Skill 就崩了。给默认值意味着读不到和读到 False在逻辑上可以区分处理但至少不会中断。真正的判断逻辑里要显式处理None。第二我没有在这里做任何阈值判断。原因很简单感知和决策分开后面调试的时候才能定位问题——是没读到数据还是读到了但判断错了。这两类 bug 的排查方式完全不同。第三rooms_occupied这类列表型状态比单个布尔值信息量大得多。知道家里有人和知道人在书房是两回事后者才能支撑客厅没人就关灯这种判断。决策层什么时候该多管闲事这是整个实验最有意思的部分。机器人感知到了状态但状态本身不构成任务。客厅灯开着不是任务客厅没人但灯开着才是。我一开始的想法是写一堆 if-else 规则。写了几条之后发现两个问题一是规则会越写越多二是规则之间会打架。比如晚上该开灯和没人该关灯如果两个条件同时满足怎么办。后来我把决策拆成了两层规则生成候选动作仲裁器选一个执行。defgenerate_candidates(state):根据当前状态生成候选动作不执行只生成。candidates[]# 规则 1没人但灯亮着ifnotstate[someone_home]andstate[lights_on]:candidates.append({action:turn_off_lights,targets:state[lights_on],reason:家中无人但灯未关,priority:60,})# 规则 2猫碗快空了ifstate[cat_bowl_g]isnotNoneandstate[cat_bowl_g]50:candidates.append({action:notify_cat_food_low,reason:f猫碗余量{state[cat_bowl_g]}g,priority:40,})# 规则 3深夜有人活动且灯全灭ifstate[is_night]andstate[someone_home]andnotstate[lights_on]:candidates.append({action:suggest_night_light,reason:夜间活动但无照明,priority:30,})returncandidates注意priority这个字段。它不是重要性而是如果我只能做一件事先做哪件。关灯是确定性收益省电提醒添粮是信息推送建议夜灯只是建议。给它们排优先级是为了应对同一时刻多个规则命中的情况。仲裁逻辑很简单defarbitrate(candidates,state):从候选动作里选一个。同一时刻最多执行一个动作。ifnotcandidates:returnNone# 优先级高的先选同优先级按 reason 稳定排序保证可复现candidates.sort(keylambdac:(-c[priority],c[reason]))returncandidates[0]【关键结论】同一时刻只执行一个动作这是我做的一个明确取舍。理由有两个一是家用场景下同时触发多个动作行为会显得很吵、很打扰二是多个动作同时执行出问题时很难定位是哪个动作引起的。宁可分几次心跳慢慢做。防抖别让机器人变成话痨第一版跑起来之后我立刻遇到了一个非常现实的问题它太吵了。猫碗余量低于阈值是个持续状态不是瞬时事件。如果每 30 秒检查一次那它会每 30 秒提醒一次猫粮快没了。关灯也一样如果关灯动作失败了比如设备没响应下一个心跳它还会再试无限循环。所以决策层之上必须再加一层状态记忆。我给 Skill 加了一个持久化的最近动作记录importtime COOLDOWN_SEC600# 同一动作 10 分钟内不重复STATE_KEYproactive_last_actionsdefshould_suppress(ctx,action):判断某动作是否处于冷却期是则跳过。lastctx.store.get(STATE_KEY,default{})last_tslast.get(action,0)return(time.time()-last_ts)COOLDOWN_SECdefmark_done(ctx,action):lastctx.store.get(STATE_KEY,default{})last[action]time.time()ctx.store.set(STATE_KEY,last)ctx.store是框架提供的持久化存储跨心跳保留。如果平台没有这个能力退而求其次可以用一个模块级变量但重启就丢了效果会差一些。【踩坑提醒】冷却时间不能对所有动作一刀切。关灯这种状态型动作冷却可以短一点比如 2 分钟因为关完灯状态就变了正常不会重复触发。而提醒添粮这种通知型动作冷却要长我用了 10 分钟否则就是骚扰。我一开始用同一个值结果要么关灯反应太慢要么提醒太频繁。把整条链路串起来主入口就是把上面几层按顺序接起来defrun(ctx):# 1. 感知stateperceive(ctx)# 2. 生成候选candidatesgenerate_candidates(state)# 3. 过滤掉冷却期内的fresh[cforcincandidatesifnotshould_suppress(ctx,c[action])]# 4. 仲裁chosenarbitrate(fresh,state)ifchosenisNone:return{status:idle}# 5. 执行try:execute(ctx,chosen)mark_done(ctx,chosen[action])return{status:acted,action:chosen[action],reason:chosen[reason]}exceptExceptionase:# 执行失败不标记完成下个心跳会重试return{status:error,action:chosen[action],error:str(e)}这里有个细节值得强调执行失败时不调用mark_done。这样下一个心跳它会自然重试。但这也带来一个风险——如果这个动作永远失败比如设备离线它会每个心跳都重试。我实际跑的时候加了一个失败计数器连续失败 3 次就临时禁用该动作一段时间。这段代码没放进来但思路是必要的。execute函数按动作类型分发这里就不展开了本质是调ctx.device.control之类的方法。实际跑下来哪些成立哪些不成立这套东西跑起来之后我对家用机器人自主发现任务这件事的判断变得具体了很多。成立的部分基于确定性状态规则的任务比如没人灯亮→关灯、“猫碗低于阈值→提醒”判断准确率很高因为输入是明确的传感器读数。这类任务不需要理解语义只需要读数和阈值。不成立的部分任何需要理解场景的任务。比如地上有东西该收——这需要视觉识别 判断这个物体是不是该收的一个玩具和一根充电线处理方式完全不同。这类任务在我这个 Skill 框架里根本没法表达因为它依赖视觉模型和常识推理而我的 Skill 只拿到了简单的传感器读数。半成立的部分“人出门了该做什么这类。如果presence_home传感器可靠判断没问题。但实际跑的时候我发现人在沙发上不动人体传感器也可能判定为无人”。所以基于存在传感器的离家判断必须加时间窗比如持续无人 5 分钟才算离家否则会误触发。这个时间窗我是在实际观察中加的不是一开始就想到的。我对自主发现这件事的看法写完这个 Skill我的结论比开始时更保守也更具体。机器人能不能自己发现该干什么取决于任务能不能被表达成传感器状态 规则。能就能自主不能就只能等人下指令。而家里的大部分任务恰恰落在不能这一侧——它们需要理解场景、理解物品、理解人的意图。所以我现在的判断是短期内家用机器人的自主会集中在低风险、确定性强的状态型任务上关灯、提醒、环境调节而整理、收纳、照顾这类真正有价值的任务仍然需要更强的感知和推理能力短期内靠规则引擎是够不到的。这个 Skill 的价值不在于它做了多少事而在于它把机器人自己决定做什么这条链路跑通了一遍让我能具体地看到瓶颈在哪一层——不在决策逻辑而在感知。规则再好读不到、读不准决策就是空中楼阁。如果后面要往下做我会优先补感知层接入视觉、接入更细粒度的设备状态而不是继续堆决策规则。决策规则堆得再多也解决不了看不见的问题。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑