本帖最后由 快刀青衣 于 2023-9-16 00:29 编辑
本贴的起源是这个帖子里的讨论,然后搜索了一下发现论坛虽然有介绍贝叶斯传感器的帖子,但是示例都比较简单或者是官方的,缺少实际应用的例子。同时又搜索了一下发现很多人跟我一样有判断卧室是否处于睡眠状态的需求,因此开一帖介绍一下我的实践,抛砖引玉。
首先,要对贝叶斯传感器有一个基本的认识,这里论坛已经有两个介绍的帖子,简介和官方文档汉化,比较详细了,但是我还是强烈推荐去阅读一下官方论坛的这个帖子,对于正确理解整个传感器的工作原理非常有帮助,同时里面还给出了一个excel表格,可以通过那个表格模拟贝叶斯传感器的运行,观察概率变化,对于我们实际调试也很有用。当时就是看了这个教授的文章才真正重视起来贝叶斯传感器的使用。
在前面的基础上,介绍一下我是怎么判断卧室的睡眠情况。
首先,硬件部分。包括主卧灯、书房灯(主卧阳台改成了小书房)、台灯、主卧门窗传感器、两台电脑、主卧窗帘、主卧床的压力传感器(小米门窗改的那种)、主卧窗帘。像灯、门、电脑的开关状态和窗帘的位置都是贝叶斯的条件。其中,主卧床的压力传感器必不可少,这是避免误判的重要一环:想象一个场景,晚上八点,卧室各设备均关闭,此时除了床上没有人,整个状态已经跟晚上睡眠的时候一模一样。当然,通过人在传感器或者专设一个只检测床区域的普通人体传感器(见注1)也可以一定程度上满足要求,但压力传感器是最适合的。总而言之,一定要想办法实现床这个区域有没有人的检测,至于持续检测精确度则可以不做高的要求。
其次,生活习惯。其实大部分人关于睡眠的生活习惯是固定的,那么,具体的贝叶斯传感器设计就也要针对生活习惯来制定,这个设计包括需要哪些设备、设备具体怎么布置、各个参数怎么设置。比如,我的主卧实际上只有一个压力传感器,布置在我老婆一侧,这是因为她会坐在床上玩手机而我几乎不会,上床就一定是要睡觉了,而且我知道这个系统的运行原理,更希望HA对她是无感的。
最后,逻辑和参数。首先要明确需求的逻辑,比如对于我来说,宁愿将非睡眠状态误判为睡眠(就叫假阳性吧,下同),也不能将睡眠误判为非睡眠(就叫假阴性,下同),前者顶多是自动开灯等自动化不起作用,后者则可能造成起夜或者老婆在睡觉我推门进入的时候灯火通明,哪个更痛苦不言而喻。参数计算时,可以用过去一段时间各个设备和传感器的历史状态作为历史数据,具体的计算建议多看那个教授的帖子。
- platform: 'bayesian'
name: '主卧睡眠'
prior: 0.458
probability_threshold: 0.93
unique_id: b4755a76-31e6-11ed-a161-0242ac120002
observations:
- platform: 'state'
entity_id: 'light.bedroom_light'
prob_given_true: 0.99
prob_given_false: 0.78
to_state: 'off'
- platform: 'state'
entity_id: 'light.studyroom_light'
prob_given_true: 0.99
prob_given_false: 0.85
to_state: 'off'
- platform: 'state'
entity_id: 'light.lamp'
prob_given_true: 0.99
prob_given_false: 0.95
to_state: 'off'
- platform: 'state'
entity_id: 'binary_sensor.huawei_laptop'
prob_given_true: 0.99
prob_given_false: 0.85
to_state: 'off'
- platform: 'state'
entity_id: 'binary_sensor.surface'
prob_given_true: 0.99
prob_given_false: 0.95
to_state: 'off'
- platform: 'template'
value_template: >-
{% if states('sensor.chinese_calendar') == 'Workday' %}
{{ now().hour > 22 or now().hour < 8 or (now().hour > 13 and now().hour < 18) }}
{% else %}
{{ now().hour < 12 or (now().hour > 13 and now().hour < 18) }}
{% endif %}
prob_given_true: 0.9
prob_given_false: 0.15
- platform: 'state'
entity_id: 'binary_sensor.bedroom_door'
prob_given_true: 0.99
prob_given_false: 0.57
to_state: 'off'
- platform: 'state'
entity_id: 'binary_sensor.bedroom_bed_occupied'
prob_given_true: 0.9
prob_given_false: 0.27
to_state: 'on'
- platform: 'template'
value_template: >-
{{ states.cover.bedroom_curtain.attributes.current_position < 25 }}
prob_given_true: 0.95
prob_given_false: 0.35
- platform: 'template'
value_template: >-
{{ is_state('person.one', 'home') or is_state('person.two', 'home')}}
prob_given_true: 0.99
prob_given_false: 0.5
扯了这么多,上面就是一个卧室的贝叶斯传感器实现。介绍几点,prior很好理解,像我家主卧,每天睡眠时长约11小时,那么prior就是11/24=0.458。probability_threshold的设置就比较重要,比如像这个贝叶斯传感器,在我上面说的那个场景,即除了床上没人其他状态都跟睡觉时一样的时候,得到的概率是0.92,那么probability_threshold就设置为0.93,这样,只要床压力传感器不触发,则其他条件都满足也无法触发贝叶斯传感器。下面的就都是条件了,以binary_sensor.bedroom_bed_occupied这个主卧床有人传感器条件为例说明。首先,主卧床有人传感器是根据主卧床的压力传感器生成的,主要是进行了延时,即压力传感器保持关闭15分钟(关闭可以理解为无人),主卧床有人传感器才关闭。然后查询历史数据,发现“当主卧睡觉的11小时中主卧床有人传感器打开”的时间是10小时(因为只有一个压力传感器,所以即便进行了时延,也还是会有误判的时间出现),那么prob_given_true就是10/11=0.9;而发现“当主卧没有睡觉的13小时中主卧床有人传感器打开”的时间是3.5小时(就是我老婆坐在床上玩手机但没睡觉的时间),那么prob_given_false就是3.5/13=0.27。其他的条件以此类推计算。
然后关键来了,就是不要直接用这个贝叶斯传感器决定卧室是否睡眠。因为直接使用贝叶斯传感器判断睡眠状态有一个矛盾的点,即我们不希望假阳性就需要提高probability_threshold,但提高probability_threshold又会导致假阴性出现,反过来也是如此,二者不可兼得。举例来说,如前所述,除了床上没人其他状态都跟睡觉时一样的时候,得到的概率是0.92,由于我们希望避免假阳性从而probability_threshold设置为了0.93,那就意味着,如果卧室真的处于睡眠,但由于翻身导致主卧床有人传感器关闭了,此时概率变为0.92,会立刻小于probability_threshold从而使得假阴性出现。
- id: '1693813372395'
alias: 主卧睡眠开启
description: ''
trigger:
- platform: state
entity_id:
- binary_sensor.zhu_wo_shui_mian
to: 'on'
for:
hours: 0
minutes: 0
seconds: 0
condition: []
action:
- service: input_select.select_option
data:
option: 是
target:
entity_id: input_select.zhu_wo_shui_mian
mode: single
- id: '1693813751070'
alias: 主卧睡眠关闭
description: ''
trigger:
- platform: template
value_template: '{{ states.binary_sensor.zhu_wo_shui_mian.attributes.probability < 0.85 }}'
for:
hours: 0
minutes: 5
seconds: 0
condition: []
action:
- service: input_select.select_option
data:
option: 否
target:
entity_id: input_select.zhu_wo_shui_mian
mode: single
为了解决这个矛盾,可以设置两个input_select(也可以用binary_sensor,但是input_select保留了特殊情况下手动控制的可能,更推荐)和切换他们的自动化,关闭睡眠状态必须要probability降低到一定的水平(这意味着有多个条件都不满足了,没有睡觉的概率大大增加)。当只有一个或两个条件不满足时,probability降不下来,从而睡眠状态不会关闭,起到一定的抗干扰作用,这也是前述的“持续检测精确度则可以不做高的要求”的原因。像下图红圈部分,就是我老婆先起床导致主卧床有人传感器关闭,但由于probability一直没有降到0.85以下,所以主卧的睡眠状态一直持续到了我起床破坏了其他条件之后才关闭。
由图可知,一天的运行下来,睡眠状态的自动切换是很利落的。这个方法目前有一个bug,即人睡觉起来之后立刻出去但又不离开家,卧室保持睡觉时的状态,导致概率降不下来睡眠状态无法关闭,这个bug也可以通过增加贝叶斯传感器进行多重判断解决,但这种情况在我这里出现比较少懒得弄了,后续等众筹的压力带到了通过更新一下设置可以更容易地避免。另外还有一个缺点,就是当生活方式有较大变化时(比如白班转夜班或之类的),需要重新写一些条件,除此之外对我来说算是很不错了。
注1. 前面说可以通过专设一个只检测床区域的普通人体传感器作为条件,大概是这样:一个或者几个人体传感器检测房间其他区域,一个人体传感器专门只检测床面,然后比较他们的触发时间,当只检测床区域的人体传感器最晚动作时,则认为床上有人。重点是,当房间已经处于睡眠状态时,不进行上述动作(因为米家的人体传感器更新频率比较低,避免起夜等情况导致误判,具体的就不多扯了,各位可以自行思考)。这个本来是在还没有压力传感器出来的时候在次卧做权宜之计的,也曾经准备改造硬件通过mqtt接入缩短感应时间,但有了压力传感器之后改造就搁置了。
|