Типы данных с фиксированной точкой¶
В данном примере рассмотрим возможности взаимодействия с числами с фиксированной точкой как в скриптах, так и в моделях.
Числа с фиксированной точкой – это числа, бинарное представление которых ограничено размерами их целой и дробной части. Данные числа не имеют возможности изменения положения точки в битовом представлении числа. Для взаимодействия с такими числами в Engee используется библиотека FixedPointNumbers.
В данной библиотеке для задания чисел с фиксированной точкой имеются две основные команды Nifj и Qifj, где:
i и j – это количество бит, выделяемое на целую и дробную часть;
N – указывает, на то, что число беззнаковое;
Q – указывает, на то, что один бит числа выделяется на знак. Нет целой части положительное.
Примечание: Суммарное количество всех бит, выделяемых на число, должно быть равно: 8, 16, 32 или 64 битам
using FixedPointNumbers # Подключение библиотеки
Далее рассмотрим несколько примеров задания и применения таких чисел. Первый вариант – это определение беззнакового дробного числа, на целую часть которого выделяется 0 бит.
x = N0f8(0.8)
Далее прибавим к этому числу целое число с выделенными 2 битами на целую часть. Как мы видим, результирующий тип данных имеет 8 бит на целую и на дробную части. Тем самым мы можем наблюдать автоматическое увелечения разрядности числа, которое реализовано для предотвращения переполнений.
x + N2f6(2.8)
Далее рассмотрим число со знаком, на целую часть которого выделенно 2 бита, 5 битами на дробную часть и один бит соответственно на знак.
x = Q2f5(-1.8)
Увеличив данное число в три раза, мы получим переполнения по целой части числа, тем самым потеряем знак числа.
3x
Дабы избежать переполнения, мы можем принудительно назначить тип. В данном варианте мы выполним сложения с числом с другим расположением точки и другой размерностью числа. При сложении двух чисел результирующий тип данных будет назначен, опираясь на число с большей разрядностью.
Q4f11(2.8)-x
Если мы хотим выполнить действия над числом без знака и числом со знаком, нам необходимо беззнаковое число привести к виду числа с выделенным битом на знак. Для этого используется команда float.
x=Q2f5(float(N2f6(2.8)))
Q4f11(2.8)+x
Теперь перейдём к применению чисел с фиксированной точкой в моделях. Для этого в 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]);
Просмотрим структуру записанных данных. Как мы видим, ниже записанные данные представлены в формате String15.
dump(command_fixed[101])
Выделим из этих данных часть, описывающую числа, отделив её от типа данных. С помощью команды, описанной ниже, мы можем просмотреть сам исходный тип данных.
t = string(command_fixed)[findfirst("Q",string(command_fixed))[1]:end];
t[1:5]
Далее, опираясь на принципы выделения из строки типа, реализуем функцию, которая выделяет из строки числа в формате Float32.
function str_fi2num(a)
str = string(a)[1:findfirst("Q",string(a))[1]-1];
num = parse.(Float32,str);
return num
end
Применим эту функцию и построим график записанных данных, а также проведём анализ на основе записанных в процессе моделирования данных с плавающей точкой и сравним их по точности.
command_num = str_fi2num.(command_fixed);
command = Matrix(CSV.read("$(@__DIR__)/command.csv", DataFrame)); #загрузка данных
# Построение графиков
plot(command[:,2])
plot!(command_num)
Как мы видим, графики приблизительно совпадают. Для того, чтобы точно определить погрешность, найдём их разницу.
plot(command[:,2]-command_num)
Разница появляется в пятом знаке после запятой. Также видно, что в результате применяемая нами логика чисел с фиксированной точкой не повлияла на принцип работы ПИД-регулятора. Несмотря на потерю по точности, система стремится к равновесию.
Вывод¶
В данном примере мы разобрали возможности применения логики с фиксированной точкой как в скриптах, так и в моделях. Также выяснили, как их можно сохранить из модели и провести анализ или продолжить дальнейшую обработку в скриптах.