@page phase_comparison_balsam Сравнение фазовой части Balsam 165 и Balsam 167 # Сравнение фазовой части Balsam 165 и Balsam 167 ## Область сравнения Сравнивались: - захват фазных сигналов АЦП; - удаление нулевого смещения; - масштабирование в физические единицы; - расчёт общего действующего значения; - расчёт уровней отдельных фаз A, B и C; - диагностика перекоса и обрыва фаз; - диагностические биты и регистры Modbus; - пороги напряжения и выдержки времени. Исходные проекты: - `K:/git_project/Balsam_165_periph`; - `K:/git_project/Balsam_167_periph`. Основные файлы: | Проект | Файл | |---|---| | Balsam 165 | `K:/git_project/Balsam_165_periph/Source/Internal/measure.c` | | Balsam 165 | `K:/git_project/Balsam_165_periph/Source/Internal/ADC.c` | | Balsam 165 | `K:/git_project/Balsam_165_periph/Source/Internal/Include/measure.h` | | Balsam 165 | `K:/git_project/Balsam_165_periph/Doc/Balsam 165 data stru.xls` | | Balsam 167 | `Source/Internal/measure.c` | | Balsam 167 | `Source/Internal/ADC.c` | | Balsam 167 | `Source/Internal/Include/measure.h` | | Balsam 167 | `Doc/Balsam 167 data stru.xls` | ## Вывод Balsam 167 использует более правильный способ расчёта действующих значений отдельных фаз: усреднение квадрата с последующим извлечением корня. Balsam 165 сглаживает модуль сигнала, что не является настоящим RMS. При этом в Balsam 167 появились две ошибки индексации: 1. Парный канал записывается по абсолютному индексу, но читается по относительному. 2. Бит `Ignor` устанавливается у абсолютного номера датчика, но проверяется у относительного `chan`. Для УМП, где `pow_addr == 0`, первая ошибка не проявляется. Для силового блока она может приводить к дублированию фазных уровней и ложному `Wry + Stop`. Рекомендуемый вариант: сохранить RMS-алгоритм Balsam 167, исправить индексацию и выполнять диагностику трёх фаз один раз после обработки всей пары каналов. ## Сводная таблица | Параметр | Balsam 165 | Balsam 167 | Оценка | |---|---|---|---| | Частота АЦП | 5000 Гц | 3750 Гц | Изменена | | Выбор фазных входов АЦП | `CONV02…CONV05` | `CONV02…CONV05` | Одинаковый | | Формула `im_calc()` | Одинаковая | Одинаковая | Без изменений | | Фаза B | `-A-C`, для `dsk_COMM` эквивалентная разность | `-A-C` после смены знака A | По модулю эквивалентно | | Уровень отдельной фазы | EMA от `abs(x)` | `sqrt(EMA(x²))` | 167 корректнее | | Частота фильтра результата | 5 Гц | 3 Гц | 167 медленнее | | Тип `lev_count[]` | `float` | `int` | В 167 раньше теряется дробная часть | | Порог перекоса | 20% | 30% | 167 менее чувствителен | | Выдержка измеряемой фазы | около 1,5 с | около 3 с | 167 медленнее | | Выдержка восстановленной фазы | около 0,75 с | около 1,5 с | В обеих версиях вдвое короче | | `Curr_Edge`, УМП | 100 | 300 | Порог существенно повышен | | `Curr_Edge`, силовой блок | 40 | 200 | Порог существенно повышен | | Низкое `Utr` | без выдержки | счётчик около 3 с | Улучшено, но блокируется `Ignor` | | Верхнее `Utr` | без выдержки | без выдержки | Без улучшения | | Калибровка входов | жёсткий `curK[]` | Modbus/EEPROM `Caliber[]` | 167 гибче | | Пары каналов силового блока | корректные относительные индексы | несогласованные индексы | Регрессия 167 | | Обработка `Ignor` | правильный абсолютный датчик | проверяется `chan` | Регрессия 167 | | Отдельные биты фаз A/B/C | нет | нет | Общий недостаток | | Контроль пределов АЦП U/I | нет | нет | Общий недостаток | ## 1. Захват сигналов АЦП Для УМП обе версии используют одинаковые входы: ```c CONV02 = 0x5; // напряжение 1 CONV03 = 0x4; // напряжение 2 CONV04 = 0x7; // ток 1 CONV05 = 0x2; // ток 2 ``` Данные читаются из результатов `ADCRESULT2…ADCRESULT5`, после чего вызывается `Current_count(i)`. Различается частота обработки: ```text Balsam 165: ADC_FREQ = 5000 Гц Balsam 167: ADC_FREQ = 3750 Гц ``` Назначение входов УМП: | `chan` | Сигнал | |---:|---| | 0 | `Utr A` | | 1 | `Utr C` | | 2 | `Itr A` | | 3 | `Itr C` | Фаза B физически не измеряется. ## 2. Нулевое смещение В обеих версиях используется медленное усреднение: ```text zero[n] = zero[n-1] + (ADC-zero[n-1]) / (5 * ADC_FREQ) ``` ### Balsam 165 Ноль записывается постоянно: ```c Zero_lev[chan] = filterbat(&out_filter[chan], zer_count[chan]); ``` Для подстройки используются те же `out_filter[]`, которые участвуют в другой обработке сигналов. ### Balsam 167 Добавлены отдельные фильтры `zer_filter[]`: ```c Zer = filterbat(&zer_filter[chan], zer_count[chan]); if (WAKE < (WAKE_TIME / 2)) Zero_lev[chan] = Zer; ``` Разделение фильтров является улучшением. Однако после того как `WAKE` достиг нуля, условие остаётся истинным. Подстройка нуля фактически продолжается во время всей работы. Если подстройка нужна только при запуске, должно проверяться также `WAKE > 0`. ## 3. Масштабирование ### Balsam 165 Используются жёстко заданные коэффициенты `curK[]`: | Устройство | Напряжение | Ток | |---|---:|---:| | УМП 1/2 | `0.386` | `4.82` | | Силовой блок | `0.402` | — | Коэффициенты одинаковы для однотипных каналов и не берутся из EEPROM. ### Balsam 167 Коэффициенты вычисляются из `Caliber[0…3]`: ```text напряжение: powK = Caliber / 10000 ток: powK = Caliber / 1000 ``` Преимущества: - индивидуальная калибровка каждого входа; - отдельные значения для экземпляров устройств; - сохранение и загрузка через EEPROM. Значения тока заметно отличаются от Balsam 165. Например, для УМП 1 в Balsam 167 используются `3.505` и `3.380` вместо `4.82`. Это может быть связано с изменением аппаратной части или новой калибровкой и требует стендовой сверки. ## 4. Общий уровень трёхфазного сигнала Функция `im_calc()` в проектах совпадает: ```text isA = -1.5 * (a + b) isB = cos(π/6) * (a - b) Amplitude = 2/3 * sqrt(isA² + isB²) Level = Amplitude / sqrt(2) ``` Эквивалентная формула амплитуды: ```text Amplitude = 2/sqrt(3) * sqrt(a² + a*b + b²) ``` ### Фильтрация общего уровня Balsam 165 сначала делит амплитуду на `sqrt(2)`, затем фильтрует `Level` фильтром 5 Гц. Для тока и всех каналов `dsk_COMM` используется дополнительная статическая переменная `ffLev`. `ffLev` одна на все вызовы функции. На силовом блоке результаты разных пар могут влиять друг на друга. Balsam 167 фильтрует амплитуду отдельным фильтром текущего датчика: ```c Amplitude = filterbat(&out_filter[sens], im_calc(Current, aCurrent)); Level = Amplitude / RADIX2; ``` Это устраняет общий `ffLev` и разделяет состояния фильтров разных пар. ### Зона нечувствительности Balsam 165: ```text Level < Curr_Edge -> Level = 0 ``` Для УМП порог равен 100 единицам RMS. Balsam 167: ```text напряжение: Amplitude < 20 -> 0 ток: Amplitude < 75 -> 0 ``` В пересчёте на RMS это примерно 14,1 для напряжения и 53,0 для тока. Поэтому Balsam 167 публикует слабые сигналы, которые Balsam 165 обнулял. ## 5. Уровни отдельных фаз ### Balsam 165: среднее модуля ```c lev_count[chan] += (fabs(Current) - lev_count[chan]) / (ADC_FREQ / 5); ``` Для синусоиды установившееся значение соответствует среднему модулю: ```text MeanAbs = 2/π * Amplitude ≈ 0.6366 * Amplitude ``` ### Balsam 167: настоящий RMS ```c lev_quadr[chan] += (Current * Current - lev_quadr[chan]) / ADC_FREQ; lev_count[chan] = sqrt(lev_quadr[chan]); ``` Для синусоиды: ```text RMS = 1/sqrt(2) * Amplitude ≈ 0.7071 * Amplitude ``` При одинаковом входном масштабе Balsam 167 показывает примерно в 1,1107 раза, или на 11,1%, больше Balsam 165. Это изменение влияет на абсолютные фазные уровни, но почти не влияет на отношение минимальной фазы к максимальной, используемое для `Wry`. ### Скорость сглаживания Balsam 165 использует коэффициент `5/ADC_FREQ`, что соответствует постоянной времени около 0,2 с. Balsam 167 использует коэффициент `1/ADC_FREQ`, что соответствует постоянной времени около 1 с. Фазные уровни Balsam 167 изменяются медленнее и стабильнее. ## 6. Восстановление фазы B Для УМП в обеих версиях: ```text B = -A - C ``` Для силового блока в Balsam 165 используется: ```text third = current_channel - first_channel ``` В Balsam 167 сначала меняется знак сохранённого первого канала, затем применяется общая формула `-Current-aCurrent`. Результат отличается знаком, но для RMS знак не имеет значения. Основной недостаток общий: фаза B не измеряется. Неисправность B определяется только через математическую связь с A и C. ## 7. Карта фазных регистров В Balsam 165 код записывает: ```text 0x68 + n*3 + 0 = текущий нечётный канал, для УМП фаза C 0x68 + n*3 + 1 = парный канал, для УМП фаза A 0x68 + n*3 + 2 = восстановленная фаза B ``` То есть фактический порядок для УМП: ```text C, A, B ``` В Balsam 167 первые две записи переставлены: ```text 0x68 + n*3 + 0 = парный канал A 0x68 + n*3 + 1 = текущий канал C 0x68 + n*3 + 2 = восстановленная фаза B ``` Порядок соответствует карте Balsam 167: | Регистр | УМП | |---:|---| | `0x68` | `Utr A` | | `0x69` | `Utr C` | | `0x6A` | `Utr B` | | `0x6B` | `Itr A` | | `0x6C` | `Itr C` | | `0x6D` | `Itr B` | В карте Balsam 165 эти регистры не описаны как фазные. Адрес `0x68` обозначен как яркость ламп, хотя исходный код записывает туда фазный уровень. Это риск несовместимости прошивки с внешним ПО и документацией. ## 8. Диагностика `Wry` В обеих версиях используется отношение: ```text Max = max(phase_A, phase_C, phase_B) deviation = (Max - phase) / Max ``` ### Balsam 165 ```text deviation > 20% Max > Curr_Edge выдержка измеряемой фазы ≈ 1,5 с ``` `Curr_Edge`: | Режим | Значение | |---|---:| | УМП | 100 | | Силовой блок | 40 | ### Balsam 167 ```text deviation > 30% Max > Curr_Edge выдержка измеряемой фазы ≈ 3 с ``` `Curr_Edge`: | Режим | Значение | |---|---:| | УМП | 300 | | Силовой блок | 200 | Balsam 167 значительно менее чувствителен к перекосу и реагирует медленнее. ### Ошибка выдержки восстановленной фазы Один счётчик `err_count[thrd]` вызывается при обработке обоих каналов пары. Поэтому он увеличивается дважды за цикл АЦП. Фактическая выдержка постоянного дефекта фазы B: | Проект | Заданная | Фактическая для B | |---|---:|---:| | Balsam 165 | 1,5 с | около 0,75 с | | Balsam 167 | 3 с | около 1,5 с | Проблема присутствует в обеих версиях. ### Деление на ноль Обе версии сначала вычисляют отношение с делением на `Max`, а затем проверяют нижний порог: ```c ((Max - phase) / Max > limit) && (Max > Curr_Edge) ``` При `Max == 0` выполняется `0/0`. Безопасный порядок: ```c (Max > Curr_Edge) && (((Max - phase) / Max) > limit) ``` ## 9. Различия обработки `Ignor` ### Balsam 165 Используется правильный абсолютный номер датчика: ```c sens = tpl_cans + chan; ignor = sens_error[sens].bit.Ignor; ``` Ошибки `Wry`, `Out` и `Hyper` продолжают вычисляться и передаваться, но `Stop` не устанавливается для игнорируемого канала. ### Balsam 167 Проверяется относительный индекс: ```c sens = pow_addr + chan; if (WAKE || sens_error[chan].bit.Ignor) goto fin; ``` Последствия различаются по типу устройства. #### УМП Для УМП `pow_addr == 0`, поэтому индекс формально совпадает. Каналам напряжения `0x00` и `0x01` по умолчанию устанавливается `Ignor`. В результате Balsam 167 полностью пропускает для напряжения: - `Wry`; - `Out` при `Utr < 300 В`; - `Hyper` при `Utr > 420 В`; - `Stop`. В Balsam 165 эти информационные биты вычислялись, но `Stop` блокировался. #### Силовой блок На силовом блоке `pow_addr != 0`. `Ignor` устанавливается в `sens_error[pow_addr+chan]`, но проверяется `sens_error[chan]`. Из-за этого игнорирование может не сработать, а `Wry + Stop` сформироваться для канала, который должен быть исключён из диагностики. ## 10. Регрессия `sens_pair` в Balsam 167 В Balsam 165 пары хранятся относительно `chan`: ```c sens_pair[0] = 1; sens_pair[1] = 0; sens_pair[2] = 3; sens_pair[3] = 2; pair = sens_pair[chan]; ``` В Balsam 167 пары записываются по абсолютному индексу: ```c sens_pair[pow_addr+i] = pow_addr+(i^1); ``` Но чтение осталось старым: ```c pair = sens_pair[chan]; ``` Для силового блока элементы `sens_pair[0…3]` после общей инициализации равны `0, 1, 2, 3`. Следовательно, `pair` указывает на текущий канал, а не на соседний. Последствия: - один фазный уровень может записываться дважды; - максимальная фаза выбирается из неполного набора; - `Wry` может быть пропущен или сформирован ошибочно; - карта `0x68…0x6D` для силового блока не соответствует ожидаемой. Вероятное исправление: ```c pair = sens_pair[sens] - pow_addr; ``` Окончательный вариант следует подтвердить стендовым тестом и ожидаемым порядком фаз в регистрах. ## 11. Пороговые уровни УМП Заводские уставки в проектах совпадают: | Сигнал | Нижний порог | Верхний порог | |---|---:|---:| | `Utr` | 300 В | 420 В | | `Itr` | 800 А, предупреждение | 900 А, авария | Различается обработка нижнего напряжения: | Проект | Реакция `Utr < 300 В` | |---|---| | Balsam 165 | `Out` сразу; `Stop` блокируется при штатном `Ignor` | | Balsam 167 | счётчик около 3 с, но при штатном `Ignor` проверка не выполняется | Верхний порог `Utr > 420 В` в обеих версиях не имеет отдельной выдержки и гистерезиса. ## 12. Диагностические биты Расположение основных битов не изменилось: | Бит | Маска | Имя | Назначение | |---:|---:|---|---| | 0 | `0x0001` | `Tear` | обрыв датчика/предельное значение АЦП | | 2 | `0x0004` | `Wry` | перекос фаз | | 3 | `0x0008` | `Out` | ниже нижнего порога | | 4 | `0x0010` | `Over` | выше предупредительного порога | | 5 | `0x0020` | `Hyper` | выше аварийного порога | | 8 | `0x0100` | `Stop` | авария | | 9 | `0x0200` | `Ready` | готовность | | 14 | `0x4000` | `Ignor` | игнорировать аварию | | 15 | `0x8000` | `Bypas` | отключить канал | В обеих версиях `Current_count()` не формирует `Tear` для фазных входов U/I. Поэтому неисправность измерительного тракта нельзя надёжно отличить от пропадания напряжения или тока. ## 13. Что следует перенести из Balsam 165 В Balsam 167 полезно вернуть следующие свойства реализации 165: 1. Согласованную систему индексов `sens_pair`. 2. Проверку `Ignor` через абсолютный индекс `sens_error[sens]`. 3. Использование `float` для внутренних фазных уровней до момента публикации. Не следует возвращать: - усреднение `fabs()` вместо RMS; - общий статический фильтр `ffLev`; - немедленное срабатывание нижнего напряжения; - жёсткие коэффициенты `curK[]`. ## 14. Рекомендуемая итоговая реализация 1. Считать все абсолютные номера датчиков один раз: ```c sens = pow_addr + chan; ``` 2. Хранить `sens_pair[]` либо полностью в относительных, либо полностью в абсолютных индексах. Не смешивать системы адресации. 3. Сохранить RMS Balsam 167, но хранить `lev_count[]` как `float`. 4. После поступления обоих отсчётов пары вычислять A, C и B в одном месте. 5. Один раз за цикл обновлять три RMS и три диагностических счётчика. 6. Проверять `Max > Curr_Edge` до деления. 7. Проверять `Ignor` через `sens_error[sens]`. 8. Разделить понятия: - информационная ошибка присутствует; - ошибка участвует в формировании `Stop`. 9. Добавить отдельную маску фаз A/B/C. 10. Добавить `Tear` по сырому значению АЦП. 11. Добавить выдержку и гистерезис для 300/420 В. 12. Явно определить, должна ли подстройка нуля работать после запуска. ## 15. Минимальные сравнительные тесты | Тест | Что проверить | |---|---| | Одинаковая синусоида на обеих версиях | Разницу MeanAbs и RMS около 11,1% | | Номинальные A и C | Правильный порядок A/C/B в `0x68…0x6D` | | Обрыв A | RMS A, `Wry`, выдержку | | Обрыв C | RMS C, `Wry`, выдержку | | Нарушение вычисленной B | Отсутствие двойной скорости счётчика | | Перекос 20%, 25%, 30%, 35% | Различия порогов 165 и 167 | | `Max == 0` | Отсутствие деления на ноль | | `Utr = 299 В` | `Out` и выдержку | | `Utr = 421 В` | `Hyper` и отсутствие дребезга | | `Ignor = 1` | Ошибка видна, но не создаёт `Stop`, если это требуемая семантика | | Силовой блок, две пары | Отсутствие дублирования фазных RMS | | АЦП около 0 и 4095 | Формирование `Tear` | ## Заключение Фазовый расчёт Balsam 167 является хорошей основой благодаря настоящему RMS, раздельным фильтрам и калибровке из регистров. Главный риск версии 167 находится не в математике RMS, а в адресации состояния каналов. До исправления `sens_pair` и `Ignor` фазные регистры и `Wry` силового блока нельзя считать надёжными. Для УМП необходимо отдельно решить, должны ли напряжения с установленным `Ignor` продолжать формировать информационные `Wry`, `Out` и `Hyper`, как это происходило в Balsam 165.