『瀚思彼岸』» 智能家居技术论坛

 找回密码
 立即注册
查看: 5571|回复: 16

我想修改一个组件,请教python大神一个关于编程的问题

[复制链接]

16

主题

135

帖子

1085

积分

金牌会员

Rank: 6Rank: 6

积分
1085
金钱
950
HASS币
0
发表于 2018-3-28 00:23:06 | 显示全部楼层 |阅读模式
本帖最后由 roc634 于 2018-3-28 15:10 编辑

先说明一下我现在的情况,我买了一个串口的声音传感器模块,通过修改内置的二氧化碳插件co2sensor.py实现了读取数据,但是由于这个声音传感器模块是每秒发送两次数据,加上声音的特性,使得表现出来的曲线初看起来就像一把锯子,基本不能直接看出声音变化的趋势,见下图,我希望取一定时间或者若干个数据的平均值(10个,20个或者10秒,20秒等,这个需要实验后才能确定),这样表现出来的声音曲线能够像下面的气压曲线那么一目也然,我估计出来的效果应该差不多是我在噪声曲线上手工画的那个蓝色的线。

噪声曲线.jpg






我修改后能够正常读取数据的文件是这样的:


def read_mh_z19_with_temperature(serial_device):
    """ Read the CO2 PPM concenration and temperature from a MH-Z19 sensor"""

    logger = logging.getLogger(__name__)

    ser = serial.Serial(port=serial_device,
                        baudrate=115200,
                        parity=serial.PARITY_NONE,
                        stopbits=serial.STOPBITS_ONE,
                        bytesize=serial.EIGHTBITS)

    sbuf = bytearray()
    starttime = time.time()
    finished = False
    timeout = 2
    res = None
    while not finished:
        mytime = time.time()
        if mytime - starttime > timeout:
            logger.error("read timeout after %s seconds, read %s bytes",
                         timeout, len(sbuf))
            return None

        if ser.inWaiting() > 0:
            sbuf += ser.read(1)
            if len(sbuf) == MHZ19_SIZE and sbuf[:3] == MZH19_READ:
                # TODO: check checksum

                res = round((sbuf[4]*256 + sbuf[3])*0.1, 1)
                logger.debug("Finished reading data %s", sbuf)
                finished = True

        else:
            time.sleep(.1)
            logger.debug("Serial waiting for data, buffer length=%s",
                         len(sbuf))

    return (res,0)


我试过了将读取的数据先存到一个数组中,然后再计算平均值。也试过了将数据直接相加,然后取平均值的方法,但是都没有成功,后果就是噪声的数值不再显示,见下图。

是否显示.jpg

后来我发现,只要在while的循环外定义一个空列表,或者为了在外面加一个嵌套的循环而定义一个变量的初始值为0,其他一切都不变,程序就出错。见下图:

定义空列表1.jpg

而在函数之外定义的全局变量,则不会出错,噪声数据依然可以显示:

定义空列表2.jpg


由于本人也是python的初学者,自己折腾了一个多星期也没搞明白,不得已麻烦大神们,希望能得到你们的指导。

由于只是添加一个列表或者一个参数就出错,实在不能继续下去,因此下面的代码也不知道对不对,请大神们帮看一下,不胜感激!!

我希望用列表来保存10个数据,然后取平均值的代码:
""""
Read data from CO2 sensor
"""

import time
import logging

import serial

MHZ19_SIZE = 6
MZH19_READ = bytes([0xBB, 0xAA, 0x01])
#sound = []

def read_mh_z19(serial_device):
    """ Read the CO2 PPM concenration from a MH-Z19 sensor"""

    result = read_mh_z19_with_temperature(serial_device)
    if result is None:
        return None
    ppm = result
    return ppm


def read_mh_z19_with_temperature(serial_device):
    """ Read the CO2 PPM concenration and temperature from a MH-Z19 sensor"""

    logger = logging.getLogger(__name__)

    ser = serial.Serial(port=serial_device,
                        baudrate=115200,
                        parity=serial.PARITY_NONE,
                        stopbits=serial.STOPBITS_ONE,
                        bytesize=serial.EIGHTBITS)

    sbuf = bytearray()
    starttime = time.time()
    finished = False
    timeout = 2
    res = None
        sound = []
        while len(sound) < 10:
        while not finished:
            mytime = time.time()
            if mytime - starttime > timeout:
                logger.error("read timeout after %s seconds, read %s bytes",
                         timeout, len(sbuf))
                return None

            if ser.inWaiting() > 0:
                sbuf += ser.read(1)
                if len(sbuf) == MHZ19_SIZE and sbuf[:3] == MZH19_READ:
                    # TODO: check checksum

                    res = round((sbuf[4]*256 + sbuf[3])*0.1, 1)
                    logger.debug("Finished reading data %s", sbuf)
                    finished = True

            else:
                time.sleep(.1)
                logger.debug("Serial waiting for data, buffer length=%s",
                         len(sbuf))
            sound.append(res)
                        
                ave = round(sum(sound)/len(sound), 1)
    return (ave, 0)

需要说明的是,这个插件返回的数据格式是元组,即(50,30)这样的格式,第一个数据是原插件的co2浓度,第二个数据是温度。所以我最后也加0将其变为元组,以符合原插件的要求。我知道直接改原来系统插件是不好的,但是在我有能力自己写之前,能通过借用来实现就已经很有成就感了。希望以后可以自己写吧。
回复

使用道具 举报

27

主题

387

帖子

3105

积分

元老级技术达人

积分
3105
金钱
2698
HASS币
40

教程狂人

发表于 2018-3-28 09:43:33 | 显示全部楼层
不好意思 我沒有完全看完你的程式
單純就變數的方面來討論
你在函數裡面設定變數 這樣在函數執行完畢後 變數就會被消滅
下一次再執行時就會數值會再初始化
如果變數放在全域
在函數執行完畢後 變數會繼續保持
所以就看你這一個變數是否要消滅或保持來決定放哪裡

小小建議 你可以參考看看

评分

参与人数 1金钱 +20 收起 理由
+ 20 赠人玫瑰,手留余香!

查看全部评分

回复

使用道具 举报

15

主题

193

帖子

2044

积分

论坛技术达人

积分
2044
金钱
1831
HASS币
0

卓越贡献

发表于 2018-3-28 09:53:51 | 显示全部楼层
我猜测你使用的是MHZ-19 CO2组件,那么此组件中有这么一部分代码
class MHZClient(object):
    """Get the latest data from the MH-Z sensor."""

    def __init__(self, co2sensor, serial):
        """Initialize the sensor."""
        self.co2sensor = co2sensor
        self._serial = serial
        self.data = dict()

    @Throttle(MIN_TIME_BETWEEN_UPDATES)
    def update(self):
        """Get the latest data the MH-Z19 sensor."""
        self.data = {}
        try:
            result = self.co2sensor.read_mh_z19_with_temperature(self._serial)
            if result is None:
                return
            co2, temperature = result

        except OSError as err:
            _LOGGER.error("Could not open serial connection to %s (%s)",
                          self._serial, err)
            return

        if temperature is not None:
            self.data[SENSOR_TEMPERATURE] = temperature
        if co2 is not None and 0 < co2 <= 5000:
            self.data[SENSOR_CO2] = co2

该组件是通过定时去运行MHZClient(object)的类获取设备数据,然后存入一个命名为 data的字典中
所以,我的做法是修改def update(self):这个方法,让他保存一个或多个你需要的数据,当下次更新的时候,用本次更新获取的数据和之前保存的做计算,然后根据你的需要存入data
不建议是直接修改co2sensor.py,我有一个计算网速的组件,必须记录前一次采集到的数据包大小和采集时间,然后本次的数据包大小和时间,做相减相除得出结果。方法是类似的

评分

参与人数 1金钱 +20 收起 理由
+ 20 赠人玫瑰,手留余香!

查看全部评分

回复

使用道具 举报

26

主题

1190

帖子

5382

积分

元老级技术达人

积分
5382
金钱
4167
HASS币
100
发表于 2018-3-28 10:51:10 | 显示全部楼层
我理解下你的意思
全局定义一个  sound
然后每次获取传感器数据的时候网sound[]里写,再算平均值
如果你是这个意思,那我从python语法上纠正一下全局变量和局部变量。

sound = []

def xxxx()
-----
        sound = []
        while len(sound) < 10:
-----
应该是全局变量的使用方式不对,你这样在函数里的sound永远都是局部变量,值都为空[];

在函数内声明时添加global后即可在函数内调用修改全局变量
       global sound
       while len(sound) < 10:

评分

参与人数 1金钱 +20 收起 理由
+ 20 赠人玫瑰,手留余香!

查看全部评分

回复

使用道具 举报

3

主题

222

帖子

3027

积分

元老级技术达人

积分
3027
金钱
2800
HASS币
10
发表于 2018-3-28 11:28:59 | 显示全部楼层
建议不在本组件内进行数值计算, 毕竟原始数据还是非常有价值的。
个人推荐用appdaemon 创建一个sensor 调用ha的history数据库,然后进行曲线拟合
这样好处是,原始曲线和拟合曲线有对照,可以观测到不同指定函数的拟合效果是否符合要求
最最重要的好处是,用appdaemon不用每次改py就要重启ha,保存即可见效,方便调试

评分

参与人数 1金钱 +20 收起 理由
+ 20 赠人玫瑰,手留余香!

查看全部评分

回复

使用道具 举报

123

主题

4626

帖子

1万

积分

管理员

囧死

Rank: 9Rank: 9Rank: 9

积分
16015
金钱
11304
HASS币
45
发表于 2018-3-28 11:46:04 | 显示全部楼层
楼主,顶级大神们都来了,还不出来好好膜拜下
回复

使用道具 举报

16

主题

135

帖子

1085

积分

金牌会员

Rank: 6Rank: 6

积分
1085
金钱
950
HASS币
0
 楼主| 发表于 2018-3-28 16:16:45 | 显示全部楼层
windgo 发表于 2018-3-28 09:43
不好意思 我沒有完全看完你的程式
單純就變數的方面來討論
你在函數裡面設定變數 這樣在函數執行完畢後 變 ...

谢谢您的建议。
我就是想添加局部变量,这样计算完后,下次再计算时列表就会被清空,正好符合我的需要。
但是我在函数内声明一个空列表后,组件程序就出错了,噪声数据不能在HA里显示了。而在函数外声明全局变量就没有出错,这正是我不明白是什么原因的所在。
回复

使用道具 举报

16

主题

135

帖子

1085

积分

金牌会员

Rank: 6Rank: 6

积分
1085
金钱
950
HASS币
0
 楼主| 发表于 2018-3-28 16:20:18 | 显示全部楼层
syjjx 发表于 2018-3-28 09:53
我猜测你使用的是MHZ-19 CO2组件,那么此组件中有这么一部分代码
[code]class MHZClien ...

谢谢大神的回复。没错,我改的就是这个组件。
由于对python也是刚刚入门的水平,我之前能想到的就是直接在co2sensor.py里面计算。
现在我对类以及您提到的修改def update(self):这个方法还不是很理解,请问您可以提供你计算网速的组件给我参考一下么?在目前众大神给我提供的帮助里,我觉得您这个是我目前能跳起来够得到的那种。谢谢了!
回复

使用道具 举报

16

主题

135

帖子

1085

积分

金牌会员

Rank: 6Rank: 6

积分
1085
金钱
950
HASS币
0
 楼主| 发表于 2018-3-28 16:29:52 | 显示全部楼层
25989406 发表于 2018-3-28 10:51
我理解下你的意思
全局定义一个  sound
然后每次获取传感器数据的时候网sound[]里写,再算平均值

谢谢大神的回复。
正如您指出的,我在帖子里的代码里出现了两次声明sound的情况,是因为我粘贴上来时忘了注释掉其中一个了。
其实我已经试过了在函数外声明一个全局变量,就像这样:
sound = []

def xxxx()
-----
        while len(sound) < 10:
-----

还有在函数内声明一个局部变量,就像这样:
def xxxx()
-----
        sound = []
        while len(sound) < 10:
-----

以及在函数内声明一个全局变量,就像您建议的那样:
def xxxx()
-----
        global sound = []
        while len(sound) < 10:
-----
其中后两种情况都出错,就是HA里面不显示噪声数值了。我实在是不知道为什么不对,希望能继续得到您的指导,谢谢!
回复

使用道具 举报

16

主题

135

帖子

1085

积分

金牌会员

Rank: 6Rank: 6

积分
1085
金钱
950
HASS币
0
 楼主| 发表于 2018-3-28 16:39:49 | 显示全部楼层
blindlight 发表于 2018-3-28 11:28
建议不在本组件内进行数值计算, 毕竟原始数据还是非常有价值的。
个人推荐用appdaemon 创建一个sensor 调 ...

谢谢大神的回复。
目前我还在努力学习python的知识,对您的建议我暂时只能理解大致的意思,具体的步骤还没有头绪。我也了解HA官方也建议不修改内置的组件,而是创建自己的自定义组件。我现在计划先通过修改co2sensor.py实现目标,然后自己通过在修改内置组件学到的知识,创建自定义组件,实现数据的读取和展示。您提到的用appdaemon 创建一个sensor,并且调用history数据库的建议,包括对history数据库的分析和展示,我已经列入计划了,等我的能力具备会尝试的。到时有问题再向您请教。非常感谢!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Hassbian

GMT+8, 2024-4-27 02:20 , Processed in 0.063681 second(s), 36 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表