Типы данных с фиксированной точкой
В данном примере рассмотрим возможности взаимодействия с числами с фиксированной точкой как в скриптах, так и в моделях.
Числа с фиксированной точкой – это числа, бинарное представление которых ограничено размерами их целой и дробной части. Данные числа не имеют возможности изменения положения точки в битовом представлении числа.
Рассмотрим реализации, представленные в Engee. Для определения числа с фиксированной точкой используется функция fi(Value, Sign, Total_bits, Fractional_bits)
. Она позволяет задавать любые числа внутри одной команды, а также в ней отсутствует привязка к фиксированному количеству бит на слово, что, соответственно, позволяет получить алгоритм с оптимальными затратами по памяти.
Теперь рассмотрим, что значат параметры этой функции.
- Value – значения исходного числа.
- Sign – знак (1-знаковый, 0-беззнаковый типы).
- Total_bits – полная разрядность слова.
- Fractional_bits – размер дробной части.
Value = 128.9
Sign = 1;
Total_bits = 16;
Fractional_bits = 7;
println("fi: $(fi(Value, Sign, Total_bits, Fractional_bits))")
Проведём некоторые тесты для fi()
.
Зададим беззнаковое 7 битное число с нулевой целой частью.
x = fi(0.8,0,7,7)
Теперь прибавим к нему число с целой частью. Как мы видим, тип числа был автоматически переназначен.
x + fi(2.8,0,8,6)
Теперь объявим отрицательное 8-битное число с 5 битами на дробную часть.
x = fi(-1.8,1,8,5)
Умножим его на 3. Как мы можем заметить, итоговое число имеет на 64 бита больше выделяемой памяти на целую часть. Это связано с тем, что тройка имеет тип данных Int64.
3x
Соответственно, если мы выполним операцию вычитания, тип данных также будет переопределён.
fi(2.8,1,16,11)-x
Помимо этого, мы сами можем явно переназначить тип числа.
x = fi(x,1,7,5)
Как мы видим, представление, реализованное в Engee, – действительно универсальный, а также устойчивый к переполнениям инструмент, так как он автоматически добавляет биты к слову в зависимости от типов данных, применяемых для того или иного числа.
Применение вычислений с фиксированной точкой в моделях
Теперь перейдём к применению чисел с фиксированной точкой в моделях. Для этого в Engee в настройках блоков предусмотрена возможность выбора выходного типа данных. На рисунке ниже показан интерфейс одного из таких блоков.
В данном примере реализована модель ПИД-регулятора с применением логики фиксированной точки.
Пропорционально-интегрально-дифференцирующий регулятор — устройство в управляющем контуре с обратной связью. ПИД-регулятор в системе автоматического управления дает управляющий сигнал хорошей точности и высокого качества.
На рисунке ниже показана реализованная нами модель.
Перейдём к запуску этой модели.
function run_model( name_model, path_to_folder )
Path = path_to_folder * "/" * name_model * ".engee"
if name_model in [m.name for m in engee.get_all_models()] # Проверка условия загрузки модели в ядро
model = engee.open( name_model ) # Открыть модель
model_output = engee.run( model, verbose=true ); # Запустить модель
else
model = engee.load( Path, force=true ) # Загрузить модель
model_output = engee.run( model, verbose=true ); # Запустить модель
engee.close( name_model, force=true ); # Закрыть модель
end
return model_output
end
# Запуск модели
run_model( "simple_model_pid_fixed", @__DIR__ )
Далее для обработки данных моделирования, записанных в CSV, нам понадобится подключить две библиотеки – DataFrames и CSV. Для отображения этих данных задействуем библиотеку Plots.
# Подключение библиотек
using CSV
using DataFrames
using Plots
Теперь выполним чтение данных из CSV, построим результирующий график и проведём анализ этих данных.
command_fixed = Matrix(CSV.read("$(@__DIR__)/command_fixed.csv", DataFrame)); #загрузка данных
command_fixed = (command_fixed[:,2]);
Просмотрим структуру записанных данных. Как мы видим ниже, записанные данные представлены в стандартном формате и не требуют дополнительной обработки.
dump(command_fixed[101])
Построим график записанных данных, а также проведём анализ на основе записанных в процессе моделирования данных с плавающей точкой и сравним их по точности.
command = Matrix(CSV.read("$(@__DIR__)/command.csv", DataFrame)); #загрузка данных
# Построение графиков
plot(command[:,2])
plot!(command_fixed)
Как мы видим, графики приблизительно совпадают. Для того, чтобы точно определить погрешность, найдём их разницу.
sum(command[:,2]-command_fixed)
Оказалось, что разница нулевая. Также видно, что в результате применяемая нами логика чисел с фиксированной точкой не повлияла на принцип работы ПИД-регулятора. Несмотря на потерю точности, система стремится к равновесию.
Вывод
В данном примере мы разобрали возможности применения логики с фиксированной точкой в скриптах и в моделях. Также выяснили, как их можно сохранить из модели и провести анализ или продолжить дальнейшую обработку в скриптах.