Контроль влажности и температуры в погребе, подвале.

Тема в разделе "Глядите, что я сделал", создана пользователем ИгорьК, 28 сен 2016.

Метки:
  1. alp69

    alp69 Форумчанин

    Родственники построили погреб. Соответственно, возникла тема по поддержанию микроклимата. Решил изготовить устройство по мотивам данной темы. Но хочу использовать не только абсолютные значения влажности, но и задействовать анализ такого параметра, как температура точки росы.
    Вычисляется она по формуле
    Код (Lua):
    (237.7*((17.27*temp)/(237.7+temp))+ math.log(humi/100))/ (17.27-(((17.27*temp)/(237.7+temp))+ math.log(humi/100)))
    Как мы видим, в формуле требуется вычислить натуральный логарифм math.log(humi/100). Однако в прошивке такой функции не оказалось.
    Первой мыслью было загнать в таблицу значения логарифма от 0 до 100 (значения относительной влажности более 100% не рассматриваем) и делать выборку по ключевому полю (ключ=влажность в %):
    Код (Lua):
    t = {}
    t[1] = 0
    t[2] = 0.6931 -- ln(2)
    t[3] = 1.0986 -- ln(3)
    ... и так далее до 100
    Возможно так оно будет работать даже быстрее. Но возрастной перфекционизм и желание оперировать не только целыми значениями, направили меня другим путем.
    Написал функцию (код не стыренный):
    Код (Lua):
    -- ##################################################################
    -- Функция вычисления натурального логарифма путем преобразования
    -- из ряда Тейлора для любого числа > 1
    -- ##################################################################
    -- URL формулы https://wikimedia.org/api/rest_v1/media/math/render/svg/ffded915cdc900c0944340217624650781bc6fbd
    -- URL источника https://ru.wikipedia.org/wiki/%D0%9D%D0%B0%D1%82%D1%83%D1%80%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BB%D0%BE%D0%B3%D0%B0%D1%80%D0%B8%D1%84%D0%BC#math_Ряд_1
    -- раздел "Методы вычисления логарифма" https://ru.wikipedia.org/wiki/%D0%9D%D0%B0%D1%82%D1%83%D1%80%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BB%D0%BE%D0%B3%D0%B0%D1%80%D0%B8%D1%84%D0%BC#Методы_вычисления_логарифма
    -- ##################################################################
    -- Формула ln(xx) = 2 * (x + (x^3)/3 + (x^5)/5 + (x^7)/7 + ...)
    -- ##################################################################
    -- Аргумент функции (xx) - число более (!!!!!) единицы,
    -- натуральный логарифм которого необходимо вычислить.
    -- ##################################################################
    function my_ln(xx)
    local t1 = tmr.now()-- взводим секундомер (можно удалить)
    local x= (xx-1)/(xx+1)
    local n=0 --счетчик (можно удалить)
    local i=1 -- показатель степени и значение знаменателя
    local rez=0 -- инициализация переменной результата вычмслений
    local rez_ch=2 -- частное от текущего и предыдущего результатов
                   -- (необходимо для установки предела точности вычислений)
    while rez_ch > 1.00000001 do
        tek = (x^i)/i
        rez_old = rez
        rez = rez + tek*2
        i=i+2
        rez_ch=rez/rez_old
        n=n+1 --счетчик (можно удалить)
    end
    ln_my=tonumber(string.format("%.5f",rez))
    local t2 = (tmr.now()-t1)/1000 -- останавливаем секундомер (можно удалить)
    print("Выполнено "..n.." итераций за "..t2.." миллисекунд; ln("..xx..") = "..ln_my)
    return ln_my
    end

    -- ##################################################################
    --print(my_ln(41.736)) -- проверка. Можно удалить
    При проверке скорости работы при вычислении ln(9000) выдало такой результат:
    Код (Lua):
    Выполнено 14863 итераций за 2247.584 миллисекунд; ln(9000) = 9.1048
    Вместе с тем, при вычислении ln(100):
    Код (Lua):

    Выполнено 282 итераций за 45.472 миллисекунд; ln(100) = 4.60517
     
    И это следует учесть при задании интервалов между измерениями, чтобы дать время контроллеру на расчеты.
    За сим пока все.
    Будут конструктивные предложения по допиливанию функции - пишите.
     
    Последнее редактирование: 27 окт 2020
    ИгорьК нравится это.
  2. ИгорьК

    ИгорьК Гуру

    Круто. Я не помощник, ибо до сих пор не уверен что 2 + 3 будет 5.

    Практический вопрос - что с точкой росы делать?

    Ну и из опыта. Не смотря ни на какие расчёты, стены могут быть холоднее чем показания датчиков воздуха, поэтому тривиально: чем суше, тем лучше. Опираться на точку росы - иметь мокрые стены.
     
  3. alp69

    alp69 Форумчанин

    Анализировать выпадет ли вода из"всосанного" извне воздуха конденсатом на стенах.
     
  4. ИгорьК

    ИгорьК Гуру

    Нет, не думаю что в этом есть смысл.

    Смотри, у тебя есть некоторая влажность в погребе. Есть некоторая влажность на улице.

    Если на улице воздух в абсолютном исчислении суше, то качать его надо без размышлений. Это сушит подвал независимо ни от чего. Если там влажность выше - добавлять её в подвал смысла нет не при каких обстоятельствах.

    Не вижу здесь точку росы.
     
  5. ИгорьК

    ИгорьК Гуру

    ПыСы. У меня, кстати, подвал сейчас работает на Lua.
     
  6. alp69

    alp69 Форумчанин

    Даже если он суше, это еще не значит, что в подвале он не выпадет росой. Значит надо поднимать точку росы нагревом. Точка росы нужна для того, чтобы принимать решение о нагреве. Точнее о его отключении.
     
    ИгорьК нравится это.
  7. alp69

    alp69 Форумчанин

    Кстати, дошел до внедрения функции в код и обратил внимание, что она должна по-другому вычисляться. В формуле точки росы идет humi/100. А в этом случае нужна другая формула. Это логарифм числа от 0 до 1. А в текущей версии >1. Завтра поправлю.
     
    ИгорьК нравится это.
  8. ИгорьК

    ИгорьК Гуру

    И да и нет.
    Его в таком случае лучше не греть, а сушить осушителем. Это и эффективнее и менее энергозатратно.
     
  9. alp69

    alp69 Форумчанин

    Не буду полемизировать по поводу того, какими параметрами пользоваться для обеспечения микроклимата и как его обеспечивать - греть или сушить. А то это напоминает спор "остроконечников" с "тупоконечниками" ;)
    Ну а насчет
    как обещал - доработал код, который вычисляет натуральный логарифм любого положительного числа. Ну разве что производительность ESP ограничит хотелки. В частности, вычисление натурального логарифма чисел более 5000 может привести к перезагрузке модуля. Этому способствует медленная сходимость ряда Тейлора. Хотя на свежеперезагруженном модуле иногда получалось посчитать и 10000, и 15000. Но это скорее исключение.
    Несмотря на недостатки, функция выполняет свою задачу в рамках граничных условий в соответствии с замыслом.
    Ниже код с "плюшками" в виде комментариев, замера скорости вычисления, вывода на экран результата через print(), а также код без "плюшек".
    Код (Lua):
    -- ##################################################################
    -- Функция вычисления натурального логарифма с использованием разложения
    -- в ряд Тейлора для любого положительного числа
    -- ##################################################################
    -- URL формул [URL]https://wikimedia.org/api/rest_v1/media/math/render/svg/ffded915cdc900c0944340217624650781bc6fbd[/URL]
    -- URL источника [URL='https://ru.wikipedia.org/wiki/%D0%9D%D0%B0%D1%82%D1%83%D1%80%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BB%D0%BE%D0%B3%D0%B0%D1%80%D0%B8%D1%84%D0%BC#math_Ряд_1']https://ru.wikipedia.org/wiki/Натуральный_логарифм#math_Ряд_1[/URL]
    -- раздел "Методы вычисления логарифма" [URL='https://ru.wikipedia.org/wiki/%D0%9D%D0%B0%D1%82%D1%83%D1%80%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BB%D0%BE%D0%B3%D0%B0%D1%80%D0%B8%D1%84%D0%BC#Методы_вычисления_логарифма']https://ru.wikipedia.org/wiki/Натуральный_логарифм#Методы_вычисления_логарифма[/URL]
    -- ##################################################################
    -- Формула для x<=1 => ln(1+x) = x - (x^2)/2 + (x^3)/3 - (x^4)/4 + ...)
    -- ##################################################################
    -- Формула для x>1 => ln(1+x)/(1-x) = 2 * (x + (x^3)/3 + (x^5)/5 + (x^7)/7 + ...)
    -- ##################################################################,

    function my_ln(xx)
    local x, t1, t2, n, i, rez, rez_old, rez_ch, ln_my
    t1 = tmr.now()-- взводим секундомер (можно удалить)
    n=0 --счетчик (можно удалить)
    i=1 -- показатель степени и значение знаменателя
    rez=0 -- инициализация переменной результата вычмслений

    if xx >1 then
        x= (xx-1)/(xx+1)
        rez_ch=2 -- частное от текущего и предыдущего результатов
                   -- (необходимо для установки предела точности вычислений)
    while rez_ch > 1.000000001 do
        tek = (x^i)/i
        rez_old = rez
        rez = rez + tek*2
        i=i+2
        rez_ch=rez/rez_old
        n=n+1 --счетчик (можно удалить)
    end
        ln_my=tonumber(string.format("%.5f",rez))
    elseif xx <= 1 then
        x= (1-xx)*-1
        rez_ch=0 -- частное от текущего и предыдущего результатов
                      -- (необходимо для установки предела точности вычислений)
        rez_old = xx
        znak = 1
        while rez_ch <= 1 or rez_ch >= 1.00001 do
            tek = znak * (x^i)/i
            rez = rez + tek
            rez_ch=rez/rez_old
            rez_old = rez
            i=i+1
            n=n+1 --счетчик (можно удалить)
            znak = znak * -1
        end
        ln_my=tonumber(string.format("%.5f",rez))
    elseif xx <= 0 then
        ln_my = "Расчет невозможен!\n!!! Введите положительное число !!!"
    end
        local t2 = (tmr.now()-t1)/1000 -- останавливаем секундомер (можно удалить)
    print("Выполнено "..n.." итераций за "..t2.." миллисекунд; ln("..xx..") = "..ln_my)
    x, t1, t2, n, i, rez, rez_old, rez_ch = nil
    return ln_my
    end

    -- ##################################################################
    --print(my_ln(88.13)) -- проверка. Можно удалить
     
    Код (Lua):

    function my_ln(xx)
    local x, i, rez, rez_old, rez_ch, ln_my
    i=1
    rez=0
    if xx >1 then
        x= (xx-1)/(xx+1)
        rez_ch=2
    while rez_ch > 1.000000001 do
        tek = (x^i)/i
        rez_old = rez
        rez = rez + tek*2
        i=i+2
        rez_ch=rez/rez_old
    end
        ln_my=tonumber(string.format("%.5f",rez))
    elseif xx <= 1 then
        x= (1-xx)*-1
        rez_ch=0
        rez_old = xx
        znak = 1
        while rez_ch <= 1 or rez_ch >= 1.00001 do
            tek = znak * (x^i)/i
            rez = rez + tek
            rez_ch=rez/rez_old
            rez_old = rez
            i=i+1
            znak = znak * -1
        end
        ln_my=tonumber(string.format("%.5f",rez))
    elseif xx <= 0 then
        ln_my = "Расчет невозможен!\n!!! Введите положительное число !!!"
    end
    x, i, rez, rez_old, rez_ch = nil
    return ln_my
    end
     

    P.S. Прошу модераторов внести изменения в начало поста #141 и после внесения изменений удалить это P.S.:
    "UPD 28.10.2020:
    Опубликованный в данном посте код функции my_ln() обеспечивает вычисление натурального логарифма чисел более 1.
    Код, обеспечивающий вычисления натурального логарифма любого положительного числа (в т.ч. от 0 до 1), опубликован в посте #149"
    И заменить первую формулу на
    Код (Lua):
    T=(327.7*f)/(17.27-f)
    --где
    f=((17.27*t)/(237.7+t))+ln(h/100)
    --где t - температура воздуха, гр.;
    --       h - относительная влажность, %
     
     
    Последнее редактирование: 28 окт 2020
    ИгорьК нравится это.
  10. ИгорьК

    ИгорьК Гуру

    Сколько итераций приблизительно делает while?

    Попробую завтра понять как это работает и переписать без while. Ну и внесу изменения в посты.

    Если время будет.
     
  11. alp69

    alp69 Форумчанин

    Это зависит от аргумента и точности. Для точности 5 знаков после запятой (стотысячная):
    Код (Lua):

    ln(0.1) = -2.30242
    Выполнено 63 итераций за 12.883 миллисекунд;

    ln(0.5) = -0.69314
    Выполнено 14 итераций за 4.873 миллисекунд;

    ln(0.9) = -0.10536
    Выполнено 6 итераций за 3.61 миллисекунд;

    ln(1) = 0
    Выполнено 2 итераций за 2.235 миллисекунд;

    ln(10) = 2.30259
    Выполнено 41 итераций за 8.6 миллисекунд;

    ln(100) = 4.60517
    Выполнено 336 итераций за 52.029 миллисекунд;

    ln(1000) = 6.90775
    Выполнено 2722 итераций за 403.128 миллисекунд;

    ln(5000) = 8.51718
    Выполнено 11536 итераций за 1699.025 миллисекунд;
     
     
    Последнее редактирование: 29 окт 2020
  12. alp69

    alp69 Форумчанин

    По поводу "понять как это работает" - ссылка на расчетные формулы в коде. Там же, в коде, в условиях while присутствуют числа 1.000000001 и 1.00001. Это константы, с которыми сравнивается отношение (rez_ch) очередного результата вычислений (rez) к предыдущему вычисленному результату (rez_old) при прокрутке цикла. Попробуйте вручную прогнать цикл с выводом результата (rez) и частного (rez_ch) на каждом обороте цикла. Увидите как результат приходит к требуемой точности с каждым шагом цикла (т.н. "схождение ряда"), а частное (rez_ch) приближается к условию цикла.
    Приведенные выше константы получены эмпирически и разнятся ввиду того, что вычисления в диапазонах 0<x<=1 и x>1 ведутся по разным формулам. По этой же причине цикл вычислений в диапазоне 0<x<=1 имеет двойное условие. Количеством нулей в константах можно установить требуемую точность вычислений. Но лучше этим сильно не увлекаться - увеличится число итераций и модуль может начать сваливаться в перезагрузку.
    В коде точность чуть больше 5 знаков, но при выводе результат обрезается (не округляется) до 5 знаков. Это сделано для получения гарантированно точного результата.
     
    Последнее редактирование: 29 окт 2020
  13. parovoZZ

    parovoZZ Гуру

    А для чего вообще вычислять точку росы? Вот допустим, сейчас относительная влажность 90%. Через 30 минут упало атмосферное давление. Относительная влажность под 100%. Выпала роса и появился туман. Вот что с этим делать?
     
    Airbus нравится это.
  14. ИгорьК

    ИгорьК Гуру

    Попробуй так:
    Код (Lua):
    var = 0.2
    callexit = function(dt)
        local count = 0
        for _ in pairs(debug.getregistry()) do  count = count + 1 end
        print('Rez:', dt, 'ip:', (wifi.sta.getip()), 'Reg:', count, 'Heap:', node.heap() )
    end

    function my_ln(xx, call)
        local i, rez, rez_old, rez_ch, x, aboveOne, beforeAndOne, exit  = 1, 0

        exit = function (rezn)
            x, i,rez, rez_old, rez_ch, aboveOne, beforeAndOne, exit = nil
            if call then call(tonumber(string.format("%.5f",rezn))) end
        end
        if xx <= 0 then
            print("\n!!! Введите положительное число !!!\n")
            call = nil
            return exit()
        end

        aboveOne = function(rez, x, rez_ch)
            if rez_ch < 1.000000001 then return exit(rez) end
            tek = (x^i)/i
            rez_old = rez
            rez = rez + tek*2
            i=i+2
            rez_ch = rez/rez_old
            return aboveOne(rez, x, rez_ch)
        end

        beforeAndOne = function(rez, rez_ch, rez_old, znak)
            if rez_ch >1 and rez_ch < 1.00001 then return exit(rez) end
            tek = znak * (x^i)/i
            rez = rez + tek
            rez_ch = rez/rez_old
            rez_old = rez
            i=i+1
            znak = znak * -1
            return beforeAndOne(rez,rez_ch,rez_old, znak)
        end

        if xx >1 then
            x = (xx-1)/(xx+1)
            rez_ch = 2
            aboveOne(rez, x, rez_ch)
        elseif xx <= 1 then
            x = (1-xx)*-1
            rez_ch = 0
            rez_old = xx
            znak = 1
            beforeAndOne(rez,rez_ch,rez_old, znak)
        end
    end

    my_ln(var, callexit)
    Объяснять нужно, почему все переписал?
    И там у тебя была ошибочка при var = 0

    ПыСы. Я, правда, так и не вникал почему оно так, а не эдак :)
     
    Последнее редактирование: 29 окт 2020
  15. ИгорьК

    ИгорьК Гуру

    upload_2020-10-29_12-24-58.png
    Какие аргументы ожидаются?

    Поиграл сейчас со своим кодом - для 8000 уже неустройчиво.
    Проблема то вот в чем. Замкнутые циклы более 15 мс гарантировыанно рушат WiFi .
    Здесь сделал через рекурсию с хвостовым вызовом, но все равно 8000 - предел. Если будут такие нагрузки, придется переменные складывать в таблицу и дергать функции таймером.

    upload_2020-10-29_12-39-53.png

    ESP32:

    upload_2020-10-29_12-53-43.png
     
    Последнее редактирование: 29 окт 2020
  16. ИгорьК

    ИгорьК Гуру

    ESP8266:

    upload_2020-10-30_10-18-28.png

    upload_2020-10-30_10-24-49.png

    Так и еще во время вычисления логарифма сетка подключается:
    upload_2020-10-30_10-35-9.png

    upload_2020-10-30_10-55-8.png
     
    Последнее редактирование: 30 окт 2020
  17. Airbus

    Airbus Радиохулиган Модератор

    А логарифмы зачем?
     
  18. ИгорьК

    ИгорьК Гуру

    Я развлекаюсь, вызов.
    @alp69 будет считать точку росы и греть погреб. С этим я не согласен. Но задача посчитать логарифм 1 000 000 и не убить процессы показалась интересной.

    upload_2020-10-30_12-18-41.png
     
    Последнее редактирование: 30 окт 2020
  19. ИгорьК

    ИгорьК Гуру

    Итог по логарифмам.
    Разбил формулу @alp69 на два файла, исходя из сути вычислений. "Распределить" файлы по аргументам несложно при их вызове.

    Вызывать:
    Код (Lua):
    rtctime.set(0)
    var = 2
    callexit = function(dt)
        local count = 0
        for _ in pairs(debug.getregistry()) do  count = count + 1 end
        print('Rez:', dt, 'ip:', (wifi.sta.getip()), 'Reg:', count, 'Heap:', node.heap() )
        --print('Rez:', dt, 'Reg:', count, 'Heap:', node.heap() ) -- ESP32
    end

    dofile('logarifmAfterOne.lua')(var, callexit)
    -- или от аргумента
    --dofile('logBerofreAndOne.lua')(var, callexit)
     

    Вложения:

    • logarifm.zip
      Размер файла:
      1,6 КБ
      Просмотров:
      71
    Последнее редактирование: 30 окт 2020
    alp69 нравится это.
  20. alp69

    alp69 Форумчанин

    Не совсем так. Пока не определился греть или сушить.;)
    Хочу сначала смоделировать процессы, затем провести натурный эксперимент. А уж потом определяться с конструкцией и алгоритмами.