Приложения алгебры кортежей. Часть 1. Гибкая система счисления с простыми основаниями

В настоящее время известно большое число систем счисления. Подробный перечень (не знаю, насколько полный) приведен в англоязычной Википедии. В этом списке я не нашел ту систему, которая будет изложена здесь. Она относится к классу систем с переменным основанием (mixed radix). Предлагаю ее назвать Flexible number system with a Prime Radixes, сокращенно FPR-системой счисления.

Но для того, чтобы ее понять, необходимы знания некоторых понятий алгебры кортежей (АК) и частично упорядоченных множеств хотя бы в том объеме, который имеется в соответствующей статье в Википедии. Об АК кратко было рассказано в статье «Как совместить логику и семантику в одной алгебраической системе». Там же есть ссылки на публикации с более подробным описанием АК.

В данной статье будут обоснованы следующие преимущества предложенной системы счисления:

  • она универсальна — позволяет ТОЧНО выразить все (за исключением нуля) конечные целые и рациональные (с любым ненулевым целым числом в знаменателе) числа, а также некоторые классы иррациональных чисел;

  • ее использование позволяет сократить вычислительную сложность алгоритма умножения чисел;

  • в ней существенно уменьшается объем памяти для записи и хранения многих больших чисел.

Свойство гибкости

Для понимания данного текста важную роль играют такие понятия АК как схема отношения, фиктивная компонента и обобщенные операции. Напомню, что атрибуты – это имена переменных, домены – множества, задающие области значений атрибутов, схема отношения — заключенная в прямые скобки последовательность атрибутов, фиктивные компоненты в АК – это предельные значения доменов атрибутов: \ast — множество всех значений соответствующего атрибута (полная компонента) и \varnothing (пустая компонента).

Обобщенные операции – это операции с АК-объектами, имеющими разные схемы отношения. Для выполнения этих операций АК-объекты предварительно приводятся к одной схеме отношения с помощью простой операции добавление фиктивного атрибута. В частности, в ПРИМЕРе статьи Подсказки 1 и 2 («Во второй комнате нет тигра, а третья комната не пуста», «Первая комната не пуста, а во второй нет тигра») можно было выразить в сокращенных схемах отношения
\quad \quad M_1[R_2R_3] = [\{L,E\} \quad \{L,T\}] и
\quad \quad M_2[R_1R_2] = [\{L,T\} \quad \{L,E\}].

Затем вычислить дополнение Подсказки 2 в сокращенной схеме отношения
\overline{M_2}[R_1R_2] = \begin{bmatrix} \{E\} & * \\ * & \{T\} \end{bmatrix}
и вычислить обобщенное пересечение, предварительно добавив на место отсутствующих фиктивные атрибуты,
M_1[R_2R_3] \cap_G \overline{M_2}[ R_1R_2] = [\ast \quad \{L,E\} \quad \{L,T\}] \cap \begin {bmatrix} \{E\} & * & *\\ * & \{T\} & * \end{bmatrix} = [\{E\}\quad \{L,E\} \quad \{L,T\}].

Результат получился тот же самый.

Аналогично определяются и обобщенные отношения: объекты с разными схемами отношения перед проверкой равенства или включения приводятся к одинаковым схемам отношения с помощью добавления фиктивных атрибутов.

Обобщенные операции и отношения как раз и обеспечивают в математической системе свойство гибкости.

Стоит отметить, что схема отношения, с помощью которой можно придать системе свойство гибкости, полезна и в других разделах математики помимо реляционной алгебры, например, в теории функций многих переменных. Иногда это помогает упростить изложение, а в некоторых случаях избежать серьезных ошибок. Например, в логическом выводе на основе исчисления предикатов схема отношения не используется, в результате чего разрешается замена переменных в алгоритме унификации. Это приводит к тому, что в некоторых случаях искажаются заданные условия исходной задачи. Об этом можно прочитать в статье «Как вычислять интересные следствия» (с. 165).

Решетка числовых кортежей

Для представления чисел в FPR-системе счисления будут использоваться числовые n-кортежи (т.е. кортежи чисел длины n).

Решетка – это частично упорядоченное множество (сокращенно у-множество (poset)), для которого определены две операции: точная нижняя грань (обозначается inf или \wedge) и точная верхняя грань (обозначается sup или \vee). В общем случае эти операции выполняются для всех пар элементов решетки, но бывают другие варианты (нам они не понадобятся). В отличие от просто верхней (или нижней) грани точная верхняя (нижняя) грань дает в результате единственный элемент решетки.

Рассмотрим у-множество, элементами которого являются числовые n-кортежи. Введем для них отношение порядка \sqsubseteq (обычно отношение порядка в общем случае для у-множеств обозначается \leq, но мы этот знак будем здесь использовать для отношения «меньше или равно» для обычных чисел). Пусть даны числовые n-кортежи A=(a_1, a_2, \cdots, a_n) и B=(b_1, b_2, \cdots, b_n). Определим для них отношение порядка и операции.

  • A \sqsubseteq B подтверждается, если и только если a_i \leq b_i для всех i = 1, 2,\ldots, n. В противном случае n-кортежи не сравнимы.

  • A \wedge B = (min(a_1, b_1), min(a_2, b_2), \cdots, min(a_n, b_n)).

  • A \vee B = (max(a_1, b_1), max(a_2, b_2), \cdots, max(a_n, b_n)).

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

Помимо «решеточных» операций \wedge и \vee для числовых кортежей можно ввести «экзотические» операции, например, сложение и вычитание:

  • A + B = ((a_1 + b_1), (a_2 + b_2), \cdots, (a_n + b_n)).

  • A - B = ((a_1 - b_1), (a_2 - b_2), \cdots, (a_n - b_n)).

Пока неясно, для чего они нужны, но дальше все станет понятным.

FPR-система счисления

Для числовых n-кортежей введем следующие обозначения и определения.
Рассмотрим возрастающий ряд простых чисел без пропусков и введем для них соответствующие обозначения P_i:

\begin{matrix}  P_1 & P_2 & P_3 & P_4 & P_5 &  \cdots & P_{15} & \cdots & P_{84} & \cdots & P_{169} & \cdots \\ 2 & 3 & 5 & 7 & 11 & \cdots & 47 & \cdots & 433 & \cdots & 1009 & \cdots \end{matrix}

Переменные P_i будем использовать в качестве атрибутов числовых кортежей, строго соблюдая порядок в схемах отношений: атрибут P_m расположен после атрибута P_k лишь при условии k < m.

Сначала рассмотрим вариант, в котором компонентами наших числовых кортежей будут неотрицательные целые числа. Они будут обозначать степени соответствующих простых чисел. Как известно, любое целое число можно разложить на простые множители. Например,
484 = 2 \cdot 2 \cdot 11 \cdot 11 = 2^2 \cdot 11^2. Тогда, если использовать схему отношения, получим следующую запись этого числа с помощью числовых кортежей:
484 \Rightarrow N_i[P_1P_5](2, 2) (после схемы отношения числа записывается числовой кортеж, с помощью которого это число можно выразить в позиционной системе счисления).

В качестве компонент в числовых кортежах допускаются отрицательные числа. Это позволяет отобразить в FPR-системе счисления любое положительное рациональное число. Например,
\frac {33} {40}\Rightarrow N_k[P_1P_2P_3P_5](-3, 1, -1, 1).

Известно, что любое число в степени 0 равно 1. Тогда можно считать, что компонента 0 в числовом кортеже играет роль фиктивной компоненты.

Например, число 484 можно выразить не только в схеме отношения [P_1P_5], но и, допустим, в схеме отношения [P_1P_2P_4P_5]:

484 = 2^2 \cdot 3^0 \cdot 7^0 \cdot 11^2 \Rightarrow N_m[P_1P_2P_4P_5](2, 0, 0, 2).

Отсюда ясно, что с помощью фиктивных компонент и соответственно фиктивных атрибутов можно выполнять операции с числами, у которых числовые кортежи имеют разные схемы отношения. В качестве примера вычислим произведение чисел 1176 и 495, используя числовые кортежи. Запишем эти числа в FPR-системе счисления:

1176 \Rightarrow N_1[P_1P_2P_4](3, 1, 2).

495 \Rightarrow N_2[P_2P_3P_5](2, 1, 1).

Ясно, что схемы отношения у этих чисел разные, но мы можем сделать их одинаковыми, добавив недостающие фиктивные атрибуты. Тогда получим

1176 \Rightarrow N_1[P_1P_2P_3P_4P_5](3, 1, 0, 2, 0).

495 \Rightarrow N_2[P_1P_2P_3P_4P_5](0. 2, 1, 0, 1).

Теперь, чтобы вычислить произведение этих чисел, нам достаточно вычислить «сумму» этих числовых кортежей, т.е. выполнить покомпонентное сложение. Получим
N_3[P_1P_2P_3P_4P_5](3. 3, 1, 2, 1) \Rightarrow 2^3 \cdot 3^3 \cdot 5 \cdot 7^2 \cdot 11 = 582120.

Операции в FPR-системе счисления

  • Обобщенное сложение (+_G): после добавления недостающих фиктивных атрибутов в числовых кортежах вычисляются суммы соответствующих компонент. Эта операция соответствует произведению исходных чисел.

  • Обобщенное вычитание (-_G): после добавления недостающих фиктивных атрибутов в числовых кортежах вычисляются разности соответствующих компонент. Эта операция соответствует делению исходных чисел.

Например, вычислим результат деления 495 на число 1176. В результате получим
(0. 2, 1, 0, 1) - (3, 1, 0, 2, 0) = (-3, 1, 1, -2, 1)

По полученному числовому кортежу восстановим число
N_4[P_1P_2P_3P_4P_5](-3, 1, 1, -2, 1) \Rightarrow \frac {3 \cdot 5 \cdot 11} {2^3 \cdot 7^2}= \frac {165} {392}.

  • Обобщенная нижняя грань (\wedge_G): если числовые кортежи содержат только неотрицательные целые числа, то тут все понятно – мы вычисляем наибольший общий делитель (НОД) двух исходных чисел. А если в числовых кортежах содержатся отрицательные целые числа, тогда что? Нетрудно доказать, что это наибольшее рациональное число, результатом деления на которое исходных рациональных чисел, получатся целые числа.

  • Обобщенная верхняя грань (\vee_G): для числовых кортежей с неотрицательными целыми числами вычисляется наименьшее общее кратное (НОК) исходных чисел.

  • Умножение на константу (имеется в виду умножение всех элементов числового кортежа на одну и ту же константу). Если константа – целое положительное k, то тут все понятно: исходное число возводится в степень k. А если константа дробь? Тоже ничего сверхъестественного: исходное число возводится в дробную степень. Тем самым появляется возможность выразить в FPR-системе счисления иррациональные числа.

  • Отрицательные исходные числа. Приведенные операции с числовыми кортежами показывают, что их элементами могут быть отрицательные и рациональные числа. С помощью таких числовых кортежей можно выразить все положительные целые, рациональные и некоторые классы иррациональных чисел (те, у которых сомножители являются результатом вычисления дробных степеней целых чисел). Невыразимо в этой системе точное значение нуля и отрицательные числа. Но с отрицательными числами положение можно легко исправить. Введем еще один атрибут P_0 = -1. Тогда четное целое значение соответствующего элемента в числовом кортеже означает, что мы имеем дело с положительными исходными числами, если же соответствующее число в кортеже целое нечетное, то исходное число отрицательное.

А возможны ли для атрибута P_0 другие значения в числовых кортежах? На ум приходит число \frac 1 2. В этом случае нашему взору откроется мнимое число.

Отношения в FPR-системе счисления

Обозначим исходные числа в позиционной системе счисления, которым соответствуют числовые кортежи A и B, соответственно N(A) и N(B).

  • Обобщенное отношение частичного порядка (\sqsubseteq_G). Если в числовых кортежах содержатся только неотрицательные целые числа, то это отношение соответствует известному отношению делимости (\mid) для множества положительных целых чисел. Если числовые кортежи содержат отрицательные целые числа, то исходные числа могут быть и дробями, но при этом если A \sqsubseteq_G B, то можно легко доказать, что результатом деления \frac {N(B)} {N(A)} будет целое число. Это обстоятельство позволяет ввести новое отношение порядка по делимости не только для целых, но и для рациональных чисел (\mid_R). А вот как интерпретируются случаи, когда в числовых кортежах содержатся несократимые дроби, пока непонятно.

  • Отношение порядка по величине. Ясно, что если A \sqsubseteq_G B, то всегда верно N(A) \le N(B). Это можно легко доказать, используя свойство неубывания показательной функции a^x, где

    x — неотрицательное действительное число.

Но если A \sqsubseteq_G B не соблюдается, то сравнение N(A) и N(B) по величине с помощью числовых кортежей становится проблемой.

Нерешенные проблемы

Хотя теории FPR-систем счисления пока что нет, но проблемы уже возникли. Но, может быть, это обстоятельство как раз и увеличивает шансы перехода данных заметок в теорию?

  1. Проблема сравнения чисел по величине. Из предыдущего ясно, что из A \sqsubseteq_G B следует N(A) \le N(B). Но если отношение порядка для числовых кортежей не соблюдается, то как проверить отношение порядка по величине для исходных чисел, не переводя их в позиционную систему счисления с постоянным основанием? Ответа на это вопрос нет, как нет и доказательства того, что такой алгоритм проверки для общего случая невозможен.

  2. Проблема суммирования. Используя FPR-систему счисления, можно легко вычислить произведения и рациональные степени соответствующих чисел. Но можно ли построить алгоритмы для вычисления суммы чисел, не переводя их в традиционную систему счисления?

В процессе развития новой теории, несомненно, появятся и другие проблемы.

Позитивные моменты

  1. Предложенная система позволяет точно выразить все конечные рациональные (за исключением 0) числа и некоторые классы иррациональных чисел.

  2. Нетрудно проверить, что вычислительная сложность алгоритма вычисления произведения чисел в FPR-системе счисления линейно зависит от числа атрибутов (n) в обобщенной схеме отношения этих чисел, что соответствует оценке O(n) вычислительной сложности. Эта оценка существенно меньше оценок вычислительной сложности известных алгоритмов для традиционных систем счисления. Здесь возможно следующее возражение: но ведь перевод числа из FPR-системы счисления в традиционную требует немалых вычислительных ресурсов, которые не учитываются при оценке вычислительной сложности этого алгоритма для FPR-системы.

Отвечаю: все правильно, но всегда ли нам нужен такой перевод? Например, в промежуточных вычислениях можно обойтись и без перевода.

  1. Для многих больших чисел FPR-система счисления позволяет значительно сэкономить место в памяти. Для тех, кто сомневается в этом, предлагаю сравнить объемы памяти для хранения записи N[R_4R_{181}](3, 100) и соответствующего ей числа N, выраженного в традиционной системе счисления.

Заключение

Не исключено, что у этих заметок есть шанс стать основой для нового раздела в теории чисел. Впрочем, дойдет ли дело до этого, неизвестно. Сам я этим вряд ли займусь. Во-первых, потому что непрофессионал в теории чисел, а, во-вторых, мне более близки проблемы, связанные другими приложениями алгебры кортежей.

Но если кто-то всерьез займется этой проблемой, то на соавторстве не буду настаивать. И не буду возражать, если найдется более подходящее название для этой системы счисления. Например, можно назвать просто и без затей: К-система (не подумайте плохого: под буквой «К» подразумевается не моя фамилия, а слово «Кортеж»).

Но от ссылки на эту статью не откажусь.

На форуме dxdy (https://dxdy.ru/topic155252.html ) предложили программу на языке PARI/GP, с помощью которой заданное целое число в десятичной системе счисления можно преобразовать в запись этого числа в FPR-системе, причем в формате TeX. Пример работы программы (перевод числа 1234567890 в FPR-запись):

? fpr(1234567890)

1234567890_{10}=[P_{1}P_{2}P_{3}P_{504}P_{529}](1,2,1,1,1)

А вот код программы ( с разрешения автора):

[code]fpr(x)=my(f=factor(x),n=#f[,1]);print1(«$»,x,»_{10}=«);for(i=1,n,print1(«P_{«,primepi(f[i,1]),»}»));print(«$»)[\code]

Продолжение следует.

Дизайн баннера выполнен Анной Горской


ссылка на оригинал статьи https://habr.com/ru/articles/752836/

JsonProperty.EFCore: Упрощаем работу с JSON-полями в Entity Framework Core

Иногда перед разработчиками встает задача использования JSON-полей в Entity Framework Core. Традиционный подход с использованием Fluent API требует написания дополнительного кода, что может усложнить проект. Пакет JsonProperty.EFCore решает эту проблему. Эта статья расскажет о пользе JsonProperty.EFCore и о том, как он упрощает работу с JSON-полями, что делает его удобным инструментом для разработчиков.

Проблема: Сложное управление JSON-полями

Entity Framework Core отлично поддерживает работу с реляционными базами данных, но управление JSON-полями может стать непростой задачей. Настройка Fluent API для отображения JSON-полей на свойства сущностей требует написания дополнительного кода, что может увеличить сложность проекта. С учетом возникающей иногда необходимости хранить данные в формате JSON разработчикам необходимо эффективное решение для интеграции JSON-полей в модели EF Core.

JsonProperty.EFCore: Упрощенный подход

JsonProperty.EFCore предлагает новое решение для управления JSON-полями. Он позволяет использовать JSON-поля в EF Core без необходимости настройки сложного Fluent API. Благодаря этому открытому проекту NuGet, разработчики могут упростить свой рабочий процесс и сосредоточиться на создании логики приложения, минуя сложные настройки EF Core.

Особенности и преимущества

  1. Простая интеграция: JsonProperty.EFCore предлагает простой процесс интеграции. Разработчики просто добавляют пакет, указывают директиву using и создают модель сущности, как обычно — не требуется дополнительная настройка для JSON-полей.

  2. Поддержка обобщенных типов: Пакет поддерживает обобщенные типы, такие как JsonEnumerable<T> и JsonDictionary<TKey, TValue>, что позволяет разработчикам работать с пользовательскими типами элементов в коллекциях JSON без усилий.

  3. Безупречное управление JSON: С JsonProperty.EFCore управление JSON-полями становится намного проще. Разработчики могут непосредственно добавлять свойства типов JsonEnumerable, JsonList, JsonDictionary или JsonItem в свои модели сущностей и легко управлять JSON-полями.

  4. Строгая сериализация типов: Пакет позволяет разработчикам включить строгую сериализацию типов. При включении типы данных включаются в JSON, обеспечивая повышенную целостность и последовательность преобразования данных.

  5. Полиморфизм: строгая типизация позволяет сохранить полиморфные типы при сериализации и десериализации в JSON.

Примеры использования

Давайте рассмотрим несколько примеров использования JsonProperty.EFCore, чтобы продемонстрировать его полезность и удобство:

  1. Хранение параметров продукта:

    Допустим, у нас есть сущность «Product» с различными параметрами, сохраняемыми в JSON-полях. С JsonProperty.EFCore добавление и управление этими параметрами становится невероятно простым:

    public class Product {     public int Id { get; set; }     public string Name { get; set; }     public JsonDictionary Parameters { get; set; } = new(); }

    Запись JsonDictionary аналогична JsonDictionary<string, object>. При этом полиморфизм позволяет хранить значения любых типов в таком словаре.

    А вот пример управления коллекцией JsonDictionary:

    Product product = new() {Name="Phone",Price=500.95m,Amount=21,Parameters={     VirtualDictionary = new Dictionary<string,object>() {         {"Camera",13.5 },{"OS","Android" },{"Screen","1080x900"},{"Storage",32}     } }}; db.Goods.Add(product); db.SaveChanges();

    Это сгенерирует следующие данные для поля в формате JSON, если настройка JsonSettings.StrictTypeSerialization имеет значение true (по умолчанию):

    {   "Camera": [13.5, "System.Double"],   "OS": ["Android", "System.String"],   "Screen": ["1080x900", "System.String"],   "Storage": [32, "System.Int32"] }

    Также можно добавлять и редактировать элементы поля JsonDictionary:

    Product product = db.Goods.FirstOrDefault(); product.Parameters.Add("Battery capacity", 3000); product.Parameters.Edit(dict => {   dict["Battery capacity"] = 4000;   dict["Storage"] = 64;   dict.Add("RAM", 4);   return dict; });

    После этого JSON-поле примет следующий вид:

    {   "Camera": [13.5, "System.Double"],   "OS": ["Android", "System.String"],   "Screen": ["1080x900", "System.String"],   "Storage": [64, "System.Int32"],   "Battery capacity": [4000, "System.Int32"],   "RAM": [4, "System.Int32"] }
  2. Управление элементами списка дел:

    Предположим, у нас есть сущность «Note» с коллекцией элементов «TodoItem», сохраняемых в JSON. JsonProperty.EFCore облегчает работу с коллекциями JSON:

    public class Note {     public int Id { get; set; }     public string Header { get; set; }     public JsonList<TodoItem> Todos { get; set; } = new(); }

    При этом в списке JsonList<TodoItem> можно также хранить элементы с типом, наследуемым от TodoItem.

  3. Простое добавление полиморфного поля:

    Допустим, нам надо добавить в нашу сущность поле с абстрактным или просто полиморфным типом, без организации сложной системы сущностей и таблиц для наследуемых типов. Здесь отлично поможет JsonProperty.EFCore:

    using JsonProperty.EFCore;  class MyEntity {     public int Id { get; set; }     public int Title { get; set; }     public JsonItem<Base> Content { get; set; } = new(); }

    Теперь можно использовать полиморфное поле:

    MyEntity myEntity = new(); myEntity.Content.Serialize(new DerivedType1()); Base val = myEntity.Content.Deserialize(); Console.WriteLine(val is DerivedType1); //true

Заключение

JsonProperty.EFCore — это ценный открытый проект, который упрощает управление JSON-полями в Entity Framework Core. Избавляя от необходимости настройки Fluent API, он дает возможность разработчикам проще работать с JSON-полями, обеспечивая простоту и эффективность разработки. Простая интеграция, поддержка обобщенных типов и полиморфизм делают JsonProperty.EFCore полезным инструментом для разработчиков приложений.

Если вы хотите упростить управление JSON-полями в EF Core, попробуйте JsonProperty.EFCore. Посетите репозиторий на GitHub, чтобы узнать больше и оценить проект. Удачной разработки!


ссылка на оригинал статьи https://habr.com/ru/articles/752838/

Подводные камни децентрализованной торговли

Всем привет! Меня зовут Павел Найданов. Начинал фронтенд-разработчиком, но вот уже несколько лет, как сменил специализацию: теперь работаю в отделе Solidity-разработки компании MetaLamp. На протяжении нескольких лет мы вместе с коллегами изучаем различные аспекты разработки DeFi приложений и собираем наши наработки и инсайты в статьи, которые помогут новичкам быстрее понять, как устроен web3.

Перед вами третий материал, который мы подготовили в рамках цикла статей о децентрализованных финансах. Поскольку DeFi занимают одно из ключевых мест в мире web3, рекомендую начать погружение в предметную область именно с изучения подобных материалов. А если вам нужен путеводитель по миру web3, можно совершенно бесплатно воспользоваться нашей картой развития, выложенной на GitHub: так изучение новой предметной области будет более системным и продуктивным. 

В этой статье я расскажу про несколько важных аспектов в работе DeFi приложений.  Рассмотрим на примерах:

  • проблему ценообразования при выполнении транзакций;

  • опасность потери прибыли для поставщиков ликвидности;

  • виды атак при выполнении транзакций

Знание этих вопросов поможет начинающим web3 разработчикам понять, с какими вызовами придётся столкнуться при работе с реальными проектами. Да и просто пользователям пригодится знание нюансов работы DeFi приложений.

Price slippage

Во время обмена токенов обычно происходит следующая история. Между ожидаемой ценой перед совершением транзакции и фактической ценой после ее выполнения  есть разница. Эту разницу мы называем проскальзыванием цены (slippage).

Неопытного пользователя может обескуражить это несовпадение. Но факт остаётся фактом: такая ситуация почти всегда сопутствует любому обмену токенов в DEX на базе AMM.

При совершении сделки в децентрализованном обменнике ожидаемая цена может заметно отличаться от реальной. Так происходит потому, что ожидаемая цена зависит от прошлого состояния блокчейна. А оно может успеть измениться между созданием транзакций и их выполнением.

Пример проскальзывания цены

Предположим, у нас есть децентрализованный обменник, основанный на уже знакомом принципе AMM. В этом обменнике поставщики ликвидности создали ликвидную пару.

Представим, что Алиса и Боб хотят купить 1 ETH за USDT в нашем импровизированном обменнике. Стартовая цена 1 ETH на момент обмена — 1 400 USDT. Соответственно, и Алиса, и Боб отправляют в обменник 1 400 USDT.

Поскольку обменник децентрализованный и работает на основе блокчейн-технологии, то операции проводятся атомарно. Из-за пошагового  выполнения операций, транзакция Боба совершается раньше транзакции Алисы. Но транзакция не просто выполнилась раньше, она ещё и повлияла на цену ETH – он подорожал. После покупки Боба цена ETH подросла относительно его цены до момента покупки. Все по правилам AMM.

Как и почему это произошло? Цена актива зависит от соотношения количества токенов в пуле ликвидности. Если одного токена становится меньше, то его цена незначительно возрастает относительно другого токена.

Получается, что на момент исполнения транзакции Алисы, цена ETH уже изменилась и стала равна 1540 USDT. Но так как Алиса отправила USDT меньше, то эфира она получит тоже меньше. Другими словами, эфир стал для неё дороже. Это и есть то самое проскальзывание фактической цены относительно ожидаемой.

Важно! Проскальзывание часто выражается в процентах, которые указывают, насколько может изменится цена токена.

Фактическая цена обмена (то количество токена, которое ты получишь в результате) зависит от двух факторов: 

1) объёма токенов в ликвидной паре, 

 2) порядка транзакций на обмен в этой паре.

Итак, на стоимость обмена будет влиять место твоей транзакции в очереди обменов среди других пользователей и количество ликвидности в пуле

Почему возникает slippage?

Пример Алисы и Боба позволяет нам выделить две основные причины проскальзывания: ликвидность и волатильность.

  1. Волатильность – это изменчивость стоимости актива за определенный промежуток времени. Любой токен подвержен изменчивости.

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

    • Каждая покупка токена увеличивает его стоимость, поскольку его остаётся меньше в пуле. Каждая продажа токена уменьшает его стоимость, так как его в пуле станет больше.

    • Обычно волатильность считают в процентах за определённый промежуток времени: год, месяц или день. Волатильность токена измеряют отклонением oт среднего значения стоимости. Чeм выше отклонение, тeм выше уровень волатильности.  Получается, чем больше меняется цена токена за промежуток времени, тем больше токен волатилен.

На изображении ниже представлены три вида волатильности цены токена на временном таймлайне.

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

Давай предположим, что мы хотим приобрести 5 ETH.

Левый график. Ликвидность низкая. В пуле осталось 10 ETH. График показывает значительное смещение по кривой соотношения количества токенов. Покупка всего 5 ETHсильно изменит стоимость оставшихся 5 ETH в пуле. Согласно математике AMM, опустошить пул невозможно. Просто оставшиеся эфиры будут стоить очень и очень дорого.

Правый график. Ликвидность высокая. В пуле — 1000 токенов. Согласно графику видно, что покупка 5 ETH окажет гораздо меньшее влияние на количество эфира в пуле. Соответственно, стоимость ETH поменяется незначительно. То есть цена изменится меньше, если кто-то нас опередит и тоже купит 5 ETH.

Обратите внимание: чем больше ликвидности в пуле, тем меньше риска проскальзывания цены. Ожидаемая стоимость токена будет практически совпадать с фактической.

Как Uniswap демонстрирует slippage?

С проскальзыванием работают следующие закономерности:

  1. Чем больше токенов участвует в сделке, тем больше она влияет на цену.

  2. Чем меньше токенов в пуле ликвидности, тем больше проскальзывание.

Увидеть эти зависимости на практике можно, если поиграть с параметрами сделок в приложении Uniswap. Можно выбрать в приложении токены для обмена и попробовать изменять их количество, которое готов отдать в пул ликвидности.

В дополнительных опциях свопа есть параметр «Price impact». Он наглядно показывает влияние цены сделки на стоимость покупаемого токена.

Посмотрим, что будет, если взять  какой-нибудь токен с очень ограниченным пулом ликвидности. Попробуем обменять токен USDT на ASM – токен, который поддерживает платформу Assemble.

В модальном окне свопа Uniswap заблаговременно предупредит о том, что сделка окажет критическое влияние на цену токена. Изменение цены превысит 91%, что нарушит баланс токенов в пуле ликвидности. В данном случае это сигнал того, что мы выкупим большую часть ликвидности.

О плюсах проскальзывания

Проскальзывание — это изменение цены токена в момент сделки. И интересно то, что оно может быть не только отрицательным.

Ожидаемая цена может измениться в обе стороны: быть ниже или выше ожидаемой. Соответственно и проскальзывания бывают двух видов: положительные и отрицательные.

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

Если фактическая цена исполнения выше, чем ожидаемая цена сделки на покупку, это считается отрицательным проскальзыванием, поскольку дает нам менее выгодную цену, чем мы изначально ожидали.

Это работает как для покупки, так и для продажи токена.

Как Uniswap работает со slippage?

На некоторых обменниках можно в ручном режиме установить величину проскальзывания (0.5%, 1%, 5%). Так, децентрализованный обменник Uniswap в настройках свопа дает возможность указать slippage.

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

Для пользователя это является потенциальной проблемой или даже угрозой.

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

На следующем скриншоте я выставил высокое проскальзывание и Uniswap предупредил меня о потенциальной угрозе frontrun.

Боты могут опередить транзакции пользователя и тем самым повлиять на фактическую цену сделки. Для пользователя это означает дополнительные издержки. Именно так работает frontrun — одна из самых распространённых атак ботов, о которой мы поговорим позже.

Impermanent loss

Любой пул ликвидности полагается на поставщиков ликвидности, которые уменьшают волатильность токенов. Интерес поставщика заключается в получении вознаграждения за предоставление своих токенов в пул. Гарантированная награда, что может быть лучше? Но не всё так просто.

Когда мы предоставляем ликвидность в пул, цена внесённого актива может измениться. Это временное несоответствие между текущей ценой токенов в пуле и рыночной и называется непостоянными потерями. Эти потери возможные и незафиксированные. Постоянными и зафиксированными они станут только в момент вывода средств из пула.

Проще говоря, чем сильнее изменяется цена актива, тем большим потерям может подвергнуться пользователь, если решит забрать средства. Непостоянные потери – это та разница в прибыли, которую он недополучает из-за того, что предоставил токены в пул, а не хранит на кошельке.

Пример возникновения непостоянных потерь

Условие: Предположим, что я хочу стать поставщиком ликвидности для пула токенов USDT/ETH. Договоримся на берегу:

  • добавить токены нам будет необходимо в соотношении 1:1

  • цена 1 ETH равна 100 USDT

  • договоримся, что 1 ETH равен 100$

  • всего в пуле ликвидности на данный момент 10 ETH и 1000 USDT

Процесс:

  1. Я вношу в пул ликвидности 1 ETH и 100 USDT.

    Согласно договорённостям, стоимость 1 ETH и 100 USDT, внесённых в пул ликвидности, будет равна 200$.

    Высчитаем нашу долю внесённых токенов относительно всех токенов пула и получим 10%. Это значит, что мы будем получать 10% комиссий за обмены из нашего пула.

    Вспомним про механизм AMM с постоянным уравнением: X * Y = K, где X — количество ETH, Y — количество USDT, K — постоянное значение.

    В данном случае 10 ETH * 1000 USDT = 10 000. Это значение должно оставаться постоянным до и после любых обменов в пуле.

  1. Стоимость 1 ETH поднимается до 400$.

    Да, это произошло, но соотношение токенов в пуле также говорит, что цена эфира — 100 USDT. Арбитражные боты воспользуются этой возможностью. Они выявляют расхождения в ценах на разных обменниках и организуют процесс «купить в одном месте дешевле, продать в другом месте дороже». Прибыль кладут себе в карман. Подробнее об арбитражных ботах мы поговорим чуть позже.

  2. После работы ботов в пуле останется 5 ETH и 2000 USDT.

    Таким образом, сохранится наша постоянная K, которая равна 10 000. Также нивелируется разница в стоимости 1 ETH в пуле и за пределами обменника.

  3. А теперь пришло время забрать 10% вложенной нами ликвидности.

    Это 0,5 ETH и 200 USDT. В долларовом эквиваленте это — 400$. В два раза больше первоначально вложенной суммы! Также не стоит забывать про комиссионные сборы с пользователей за использование нашего пула ликвидности для обмена токенами. Предположим, что наше вознаграждение составило 20$.

Мы заработали 220$ в чистом виде! Приятно? Безусловно!

Однако, если бы мы просто держали свои ETH и USDT на кошельке, у нас был бы 1 ETH стоимостью 400$ и 100 USDT стоимостью 100$. Вместе это — 500$.

Разница между возможной стоимостью в 500$ и полученной стоимостью в 420$ и есть те самые непостоянные потери.

Заметим, что потери непостоянны и не зафиксированы, пока мы не выведем токены из пула. К тому же, комиссионные выплаты должны частично или полностью свести на нет потери, а в идеале даже вывести нас в плюс.

А есть ли способ избежать непостоянных потерь?

К сожалению, поставщик ликвидности не может полностью избежать непостоянных потерь. Но можно снизить риск, если использовать пулы со стейблкоинами и менее волатильными токенами.

Для оценки непостоянных потерь при изменении цены токенов можно использовать графическое представление.

Согласно графику мы можем сделать вывод, что:

  • 1,25-кратное изменение цены — убыток 0,6%

  • 1,50-кратное изменение цены — убыток 2,0%

  • 1,75-кратное изменение цены — убыток 3,8%

  • 2-кратное изменение цены — убыток 5,7%

  • 3-кратное изменение цены — убыток 13,4%

  • 4-кратное изменение цены — убыток 20,0%

  • 5-кратное изменение цены — убыток 25,5%

Чтобы быстро и удобно считать непостоянные потери ты можешь воспользоваться специальным сервисом. Попробуй этот калькулятор непостоянных потерь. Любой из подобных сервисов предложит ввести стоимость токенов на момент добавления ликвидности в пул и на момент вывода.

Arbitrage. Mev. Gas auction

Давайте разберёмся с арбитражем и распространенными атаками ботов.

Цены на один и тот же актив могут различаться на разных обменниках. Когда мы покупаем токены на одном обменнике и продаём их на другом с целью получения выгоды — это и есть арбитраж.

На самом деле, это не просто концепция «купи дешевле, продай дороже», а образ мышления, направленный на оценку ценности токена и спроса на него. Чтобы получить прибыль, очень часто нужно выстроить сложную цепочку сделок.

Не стоит забывать, что пользователь всегда может понести убытки, если цена токена упала в момент сделки. Заработать на арбитраже в ручном режиме сложно, поэтому процесс автоматизируется программно.

Арбитражные боты влияют на рыночные цены и на других участников рынка. Они уравновешивают курсы на обмен токенов по всему рынку, повышая его общую эффективность. Это благоприятно отражается на пользователях. В этом плане, арбитраж полезен, так как увеличивает объём торговли и доход от комиссии.

Но не все боты полезны или хотя бы безвредны. Существует целый ряд ботов, которые в стремлении получить прибыль могут оказывать отрицательный эффект на работу DEX.

Чем опасны боты?

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

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

Майнеры для составления блока выбирают транзакции из mempool на основе наибольшей выгоды, которую они могут получить. Для этого они могут включать, исключать и менять порядок транзакций на своё усмотрение в создаваемом блоке. Процесс извлечения такой прибыли называется Miner Extractable Value (MEV).

Вместе с майнерами за mempool следят и боты, так как содержимое mempool видно всем (специфика блокчейна). Боты могут успевать совершать собственные транзакции на опережение или после исполнения целевой транзакции.

Например, видя целевую транзакцию о пополнение пула ликвидности значительной суммой токенов, которая повлияет на увеличение цены, бот-злоумышленник может успеть:

  1. Провести покупку токенов по маленькой цене.

  2. Выждать транзакцию на ожидание.

  3. Продать токены по новой выросшей цене.

Провести свою транзакцию бот может путём искусственного повышения цены за газ. Из-за этого майнер будет брать из mempool эту транзакцию в первую очередь – из-за желания извлечь максимальный MEV.

Самое интересное здесь то, что ботов-злоумышленников может быть несколько. И тогда между ними начинается конкуренция по предложению наибольшей цены за газ. Такое явление называется войной за газ. Минус явления: такая война увеличивает комиссию для обычных пользователей.

Стоимость обмена токенов в DEX на AMM зависит от порядка транзакций. А этим порядком можно манипулировать, если предлагать большую плату за газ. Боты стараются направить свою транзакцию в точно определённое место среди других транзакций в ожидании. Таким образом создаются атаки, которые эксплуатируют майнеров.

Рассмотрим самые распространённые атаки ботов:

  1. Front-running

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

    Например, Алиса хочет приобрести много токенов. Это приведёт к повышению цены, так как токена останется в обороте меньше, а спрос на него останется прежним. Боту выгодно купить токен раньше Алисы. Бот это понимает и проводит атаку типа front-running.

    Согласно схеме срабатывает следующий сценарий:

    1. Алиса инициирует покупку токена и её транзакция попадает в mempool.

    2. Бот видит транзакцию Алисы и применяет на ней свою стратегию заработка. Стратегия заключается в поиске транзакций, которые могут существенно повлиять на стоимость токена. После нахождения такой транзакции, бот должен первым купить токен по ещё не изменившейся цене, чтобы удержать её или продать дороже в будущем.

    3. Транзакция Алисы полностью подходит для бота. Он может на ней заработать, если купит токены раньше. Тогда бот начинает действовать. Он инициирует свою транзакцию на покупку токена, которая также попадает в mempool. Но бот предлагает майнеру цену за газ больше, чем предложила Алиса. Однако, он не предлагает слишком большую цену. Чтобы транзакция выполнилась очень быстро, ему нужно учитывать накладные расходы за свои действия и соблюдать баланс, иначе можно уйти в минус.

    4. Всё это время майнер занимается своими прямыми обязанностями и составляет блок с транзакциями. Действует он, как мы уже говорили, согласно принципу MEV, пытаясь извлечь максимальную прибыль. В mempool огромное количество транзакций. Поэтому всегда есть большая вероятность, что транзакция жертвы ещё не выполнилась и все описанные выше манипуляции бота с транзакциями были успешно проведены.

    5. Так как транзакция бота предлагает для майнера больше газа, логично, что он вставляет её в блок раньше транзакции Алисы. Это значит, что транзакция бота будет выполнена раньше транзакции Алисы.

      Чем опасна эта атака? Алиса ведь всё равно купит свой токен рано или поздно? Дело в том, что транзакция бота спровоцирует проскальзывание цены. Алиса будет неприятно удивлена, когда увидит разницу между фактической и реальной ценой покупки.

      Транзакция бота, также как и любая другая покупка токена, приведёт к повышению его стоимости. А это означает, что Алиса заплатит больше ожидаемого за покупку и получит меньше взамен, так как бот купит токен раньше неё.

  2. Back-running

    Атака похожа на front-running с той лишь разницей, что бот выполняет собственную транзакцию сразу после целевой транзакции.

    Например, бот отслеживает mempool на появление новых пулов ликвидности. Если он находит новый пул, то скупает как можно больше токенов. Однако скупает он не все из них, поскольку у пользователей должна оставаться возможность купить токены.

    Затем бот ждёт, пока цена вырастет и другие пользователи начнут покупать токены. В этот момент бот продаёт токены по более высокой цене. Смысл стратегии в том, чтобы как можно раньше купить токен с целью продать его по более высокой цене.

    Согласно этой схеме происходит следующий сценарий:

    1. Бот ищет новый, недавно появившийся токен и покупает его. Очень часто цена таких токенов низкая.

    2. Алиса тоже узнаёт про этот токен и желает его купить. Отметим, что между ботом и Алисой есть и другие пользователи, которые купили этот токен. В данном случае Алиса просто является сигналом для бота.

    3. Алиса инициирует покупку токена. Её транзакция попадает в mempool.

    4. Бот видит транзакцию Алисы и применяет на ней свою стратегию заработка. Стратегия заключается в ожидании, когда цена токена достаточно вырастет. Напомню, когда Алиса купит токен, его стоимость вырастет. Это будет существенный рост, так как Алиса состоятельный покупатель и всегда покупает в больших объёмах.

    5. Транзакция Алисы полностью подходит для бота, потому что она поднимет стоимость и бот сможет на ней заработать. Тогда бот инициирует свою транзакцию на продажу токена. Транзакция также попадает в mempool. Но в этот раз бот предлагает майнеру цену за газ меньше, чем предложила Алиса. Слишком маленькую цену бот не выставит, так как ему важно, чтобы транзакция исполнилась максимально близко к транзакции Алисы.

    6. Все это время майнер занимается своими прямыми обязанностями и составляет блок с транзакциями. Поэтому сначала он добавляет в блок транзакцию Алисы на покупку токена, а потом транзакцию бота на продажу токена.

    Ты можешь спросить, в чём здесь опасность для Алисы? Ведь она купит раньше бота. Да, раньше, поэтому это не front-running, а back-running. После того, как Алиса купит свои токены, бот продаст свои. Помнишь, что для большей выгоды он скупил большую часть токенов?

    Продажа огромного количества токенов может сильно обвалить стоимость нового, не окрепшего токена. А в результате получится, что Алиса будет держать на руках токены, которые сильно упали в цене и превратились в фантики.

  3. Sandwich

Эта атака комбинирует в себе front-running и back-running атаки.

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

Бот, который видит транзакцию Алисы, создает две собственные транзакции, которые он вставит до и после транзакции Алисы. Первая транзакция бота покупает токен, что увеличивает цену транзакции Алисы, а вторая транзакция — это продаже токена по более высокой цене с прибылью.

Согласно этой схеме происходит следующий сценарий:

  1. Стратегия бота заключается в поиске транзакции, которая повысит стоимость токена достаточно для заработка.

  2. Алиса инициирует покупку токена. Транзакция попадает в mempool.

  3. Бот, который постоянно мониторит mempool, видит транзакцию Алисы и применяет свою стратегию заработка.

  4. Транзакция Алисы полностью подходит для бота: она поднимет стоимость и бот сможет на ней заработать. Тогда бот инициирует две транзакции. Первая на покупку токена — бот выставляет цену газа больше Алисы. Вторая на продажу токена — бот выставляет цену газа меньше Алисы.

  5. Обе транзакции также попадают в mempool.

  6. Все это время майнер занимается своими прямыми обязанностями и составляет блок с транзакциями. Из-за правильно предложенных цен за газ первая транзакция бота будет выполнена майнером раньше транзакции Алисы, а вторая после.

    В данном случае неприятность для Алисы — это проскальзывание цены. То есть фактическая цена покупки для Алисы будет больше, а токена в таком случае она купит меньше, чем ожидала.

Бот смог заработать на разнице покупки и продажи. Он купил первой транзакцией токен дешевле. Второй транзакцией продал дороже, чем купил. Профит положил себе в карман.

Вывод

На этом все! Мы изучили много особенностей DEX. Почему цена за токены может проскальзывать и как можно на это повлиять, опасности добавления ликвидности в пул, связанные с непостоянной потерей. Отдельно поговорили о том, как работают арбитражеры и какие основные типы атак бывают.

Ты, конечно же, хочешь спросить, для чего мне может понадобится эта теория?

Если ты продолжишь погружение в мир децентрализованных финансов, то тебе придётся решать проблемы, о которых мы говорили выше в своих собственных проектах. А тут, как говорится, предупрежден, значит вооружен! И, кто знает, может быть именно тебе удастся создать децентрализованный обменник без проскальзывания цены, непостоянных потерь и с самой современной защитой от атак ботов.


ссылка на оригинал статьи https://habr.com/ru/articles/752846/

Разработка сервопривода с BLDC мотором

Открытый проект серво‑контроллера MC50 продолжает развиваться. На этот раз поговорим о создании сервопривода.

Сервоприводы в умном доме нужны повсеместно: в запорных кранах, в электрических замках, в моторизированных кронштейнах, столах, жалюзи, в автоматических дверях, калитках, окнах, маркизах, воротах, поворотных видеокамерах, регулируемых креслах, электро‑пандусах и проч. Поэтому технология сервоприводов весьма востребована и тут есть где развернуться творчеству.

Предыдущие статьи по теме проекта MC50

В прошлой статье было показано как заставить BLDC мотор крутиться с помощью периферии микроконтроллера S5D9 семейства Renesas Synergy™

Теперь это вращение надо заставить делать полезную работу. Сторонние наблюдатели могут сказать, что работа уже сделана если мотор вращается, цель достигнута. Ставь редуктор и в прод. Но на самом деле проблемы только начинаются.

Конструкция

Конструкция состоит из мотор‑редуктора, дополнительного редуктора 29/40, простого узла с серво‑потенциометром для контроля за скоростью выходного вала и термистором для контроля за температурой мотора. Как видно, внешний редуктор даёт удобно разместить серво‑потенциометр. Дополнительно он увеличивает момент силы 1.3 раза Такая конструкция в разных масштабах встречается во множестве сервоприводов.

Конструкция сервопривода

Конструкция сервопривода

Схема подключения

Плата серво‑контроллера рассчитана на питание от внешнего источника напряжением от 20 до 30 В. Но силовой драйвер продолжает функционировать при понижении напряжения до 7 В. При отсутствии внешнего источника напряжения плата может питаться от USB, но силовой драйвер при этом функционировать не будет.

Схема подключения серво-контроллера MC50

Схема подключения серво-контроллера MC50

О частоте вращения BLDC мотора

Частота вращения BLDC двигателя определяется приложенным к нему напряжением при условии постоянной нагрузки на валу (считаем что 6-шаговая коммутация в это время работает безукоризненно, напряжение меняем ШИМ‑ом). Эта частота записывается в даташите на мотор. Если момент силы нагрузки будет больше или меньше, то частота будет разная при неизменном напряжении. Но мы не знаем точно момент силы нагрузки и он скорее всего будет переменным.

Для идеального двигателя без нагрузки, без трения и без сопротивления обмоток частоту вращения при определенном напряжении можно узнать вращая мотор внешним приводом до достижения заданного напряжения. Частота вращения в этот момент и будет искомой частотой. Но тут важна форма напряжения. Применив 6-шаговую коммутацию к мотору с синусоидальной обратной ЭДС, не получится получить ту же частоту.

Идеальный BLDC двигатель будет крутиться с бесконечной частотой при подаче на него бесконечного напряжения. У реального же BLDC двигателя в какой‑то момент сначала наступит насыщение магнитопровода, резко повысится ток через обмотки, обмотки раскалятся и спустя некоторое время мотор загорится. Поэтому частота вращения и допустимый момент силы ещё ограничиваются температурой мотора.

Разработка архитектуры управления.

Если для для управления голым двигателем архитектура достаточно ясна, то для управления сервоприводом с неизвестной динамикой нагрузки все несколько сложнее.

Для начала отметим, что частота вращения ротора не равна частоте вращения сервопривода, а в динамике с реальной механикой с люфтами и упругими реакциями даже не пропорциональна.

Для управления частотой вращения самое лёгкое решение — это просто менять скважность широтно‑импульсной модуляции (ШИМ).

Простейшая модель сервопривода в Simulink. Здесь реализована 6-шаговая коммутация и вращение только в одном направлении

Простейшая модель сервопривода в Simulink. Здесь реализована 6-шаговая коммутация и вращение только в одном направлении

Файл модели здесь

При постоянном моменте силы нагрузки на мотор или ручном управлении все довольно хорошо получается. Мы линейно меняем скважность и добиваемся нужной частоты вращения. Коммутация автоматическая с помощью датчиков Холла находящихся в моторе. Метод отлично работает в самокатах.

Осциллограмма сигналов  при вращении груза в вертикальной плоскости

Осциллограмма сигналов при вращении груза в вертикальной плоскости

Но как только момент силы начнёт меняться частота вращения начнёт пульсировать, как показано на видео ниже.

Непостоянство частоты вращения при отсутствии автоматического регулирования

Непостоянство частоты вращения при отсутствии автоматического регулирования

Самое очевидное решение — это применить PID управление с обратной связью по частоте вращения. К сожалению все не так просто. Серво‑приводы могут предназначаться для управления угловой скоростью или для управления положением, в обоих случаях нужны разные алгоритмы. И в обоих случаях простой одноконтурный PID очень плохо работает. В случае со стабилизацией положения нужен регулятор с переходной характеристикой не менее третьего порядка. А PID имеет второй порядок. т. е. оставаясь в рамках PID нужно две петли управления, вложенные одна в другую. Нам нужна хорошая, быстрая, оптимизируемая, масштабируемая реализация PID алгоритма.

Такая реализация есть в среде MATLAB Simulink в виде модели конвертируемой в исходники на C. Ниже представлена модель в виде открытой расширяемой подсистемы.

Файлы модели здесь.

Модель универсального PID регулятора

Модель универсального PID регулятора

Модель повторяет функциональность встроенного PID компонента Simulink, но при этом выводит все коэффициенты PID как входные аргументы в одной шине. Аргументы можно менять и в стандартном компоненте используя символьные обозначения переменных из рабочего пространства, но это влечёт за собой неявные зависимости приводящие к ошибкам. А шина обеспечивает генерацию аргумента как передаваемой структуры. Это уменьшает объем кода при интеграции модели в embedded проект и упрощает рефакторинг моделей.

Диалог настройки PID регулятора в Simulink

Диалог настройки PID регулятора в Simulink

Для предотвращения бесконечного нарастания интегральной составляющей (anti‑windup) применён метод clamping.

Кроме того, в нашей модели реализован ограничитель скорости нарастания выходного сигнала. Это вносит дополнительную защиту в модель от предельных осцилляций в случае использования некорректных начальных коэффициентов во время тюнинга.

Сгенерированные из модели исходники находятся в директории MATLAB_PID.

Выбор одноконтурного или двухконтурного управления скоростью вращения серво-привода.

Чтобы сделать выбор архитектуры петли управления проведём эксперимент. Сделаем в среде FreeMaster две записи сигналов в разных режимах управления мотором. Записывать будем одновременно четыре сигнала:

  • сигнала включения,

  • тока протекающего через мотор,

  • состояния датчиков Холла, отражающих скорость вращения ротора,

  • скорость выходного вала привода.

Первая запись ниже сделана во время скачкообразного включения вращения мотора в режиме 6-step с максимальным напряжением.

Вторая запись ниже сделана во время скачкообразного включения вращения мотора в режиме 6-step с удержанием постоянного тока через мотор с помощью петли управления током.

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

Очевидно в первом случае практически трудно будет проводить регулировку скорости регулируя алгоритмом PID скважность ШИМ. Во всяком случае с интервалом регулирования меньшим 0.1 сек. Поскольку скорость откликается совершенно не похожим на линейную систему образом. А классический PID хорош только для линейных систем.

Во втором случае отклик гораздо более линейный и PID здесь подойдёт. Вернее тут подойдёт простой PI контур, поскольку отклик демонстрирует инерциальную нагрузку без упругости (осцилляций). Но надо помнить — тут уже применён высокоскоростной контур поддержки заданного тока. т. е. в данном случае у нас получится 2-контурная система: быстрый PI контур управления током через ШИМ и медленный PI для управления скоростью через задание тока.

В итоге пришли к следующей архитектуре в составе модели Simulink :

Архитектура с двумя петлями управления скоростью вращения сервопривода в среде Simulink

Архитектура с двумя петлями управления скоростью вращения сервопривода в среде Simulink

Файл модели здесь

Модель включает мотор-редуктор с планетарной передачей и датчиками Холла, внешний редуктор, серво сенсор с моделью измерения скорости включая шумы измерения, 3-фазный инвертер с CMOS транзисторами, ШИМ модулятор и два контура управления: по скорости и по току. Цветами на модели выделяются участки работающие с разной частотой тактирования. Контур тока работает на частоте 16 Кгц, контур скорости на частоте 100 Гц.

Измерения частоты вращения ротора датчиками Холла

В модели показанной выше частота вращения ротора измеряется по датчикам Холла. Но в реальности оказалось, что измерение длительности интервалов между фронтами импульсов с датчиков плохо подходит для определения частоты вращения ротора. Из графика ниже следует, что при постоянной частоте разница в длительностях интервалов может составлять до 30%. Такая погрешность потом выразится в недостаточной точности или быстроте стабилизации частоты вращения.

В нашей программе мы применяем иной способ измерения частоты вращения. Таймера захватывают время каждого фронта сигнала, но разницу считают не от предыдущего фронта, а от последнего фронта того же полюса.   

Используется логика захвата таймеров GPT3211 и GPT3212 (согласно хидеру из SSP они имеет имена R_GPTB3 и R_GPTB4). Таймера имеют по два модуля захвата, поэтому для сигналов с трех датчиков нужно два таймера. Таймера тактируются абсолютно синхронно благодаря команде одновременного запуска (все таймера в Synergy можно запустить синхронно). 

Здесь фрагмент того как производится измерение в обработчике прерываний от АЦП:

  st = R_GPTB3->GTST;  // Читаем флаги состояния capture таймера  GPT3211    if (st & BIT(0))     // Проверяем флаг TCFA. Input Capture/Compare Match Flag A   {     // Здесь если событие захвата произошло      R_GPTB3->GTST = 0;      // Очищаем флаг захвата и остальные флаги.                             // За другие флаги захвата не беспокоимся, поскольку они при штатной работе не могут быть взведены в этот момент.      val = R_GPTB3->GTCCRA;  // Читаем регистр с захваченным состоянием счетчика      // Здесь рассчитываем разницу по отношению к предыдущему захваченному значению на том же угле поворота ротора, т.е. для полюса с номером в переменной hall_u_cnt     if (hall_u_prev_arr[hall_u_cnt] > 0)     {       if (val > hall_u_prev_arr[hall_u_cnt])       {         hall_u_capt_arr[hall_u_cnt] = val - hall_u_prev_arr[hall_u_cnt];       }       else       {         // Корректируем если было произошло переполнение счетчика между захватами         hall_u_capt_arr[hall_u_cnt] =  0x7FFFFFFF -(hall_u_prev_arr[hall_u_cnt] - val -1);       }        hall_u_capt = hall_u_capt_arr[hall_u_cnt]; // Сохраняем разницу в промежуточную переменную. Эта переменная используется при отладке через FreeMaster       // .......................................................       one_turn_period = hall_u_capt;             // Записываем длительность полного оборота ротора в глобальную переменную для дальнейшего использования остальными задачами       // .......................................................     }     hall_u_prev_arr[hall_u_cnt] = val;         // Сохраняем текущее захваченное значение в переменную предыдущего значения     hall_u_cnt++;                              // Ведем счет полюсов. Для каждого полюса сохраняется своя измеренная величина     if (hall_u_cnt >= 8) hall_u_cnt = 0;      // Определяем направление вращения     h = R_IOPORT5->PCNTR2 & 0x7;  // Читаем сигналы датчиков Холла здесь снова, несмотря на то что они уже были прочитаны в обработчике прерывания                                   // Это нужно поскольку capture логика может сработать уже после того как в ISR были прочитаны состояния датчиков     if ((h == 0b100) || (h == 0b011))      rotating_direction = 1; // Направление вращения определяем по паттернам сигнало с датчиков сразу после текущего фронта     else if ((h == 0b010) || (h == 0b101)) rotating_direction = -1;      R_GPTB5->GTCLR_b.CCLR13 = 1;  // Сброс счетчика отслеживающего остановку вращения     R_GPTB5->GTST           = 0;  // Сброс флагов счетчика отслеживающего остановку вращения   }    if (st & BIT(1))   {     // Input capture/compare match of GTCCRB occurred     R_GPTB3->GTST = 0;     val = R_GPTB3->GTCCRB;     if (hall_v_prev_arr[hall_v_cnt]>0)     {       if (val > hall_v_prev_arr[hall_v_cnt])       {         hall_v_capt_arr[hall_v_cnt] = val - hall_v_prev_arr[hall_v_cnt];       }       else       {         hall_v_capt_arr[hall_v_cnt] =  0x7FFFFFFF -(hall_v_prev_arr[hall_v_cnt] - val -1);       }       hall_v_capt = hall_v_capt_arr[hall_v_cnt];       one_turn_period = hall_v_capt;     }     hall_v_prev_arr[hall_v_cnt] = val;     hall_v_cnt++;     if (hall_v_cnt >= 8) hall_v_cnt = 0;      h = R_IOPORT5->PCNTR2 & 0x7;  // Читаем сигналы датчиков Холла здесь чтобы они были прочитаны не раньше чем стработает capture логика     if ((h == 0b110) || (h == 0b001))      rotating_direction = 1;     else if ((h == 0b011) || (h == 0b100)) rotating_direction = -1;      R_GPTB5->GTCLR_b.CCLR13 = 1;  // Сброс счетчика отслеживающего остановку вращения     R_GPTB5->GTST           = 0;   }     st = R_GPTB4->GTST;    if (st & BIT(0))   {     // Input capture/compare match of GTCCRA occurred     R_GPTB4->GTST = 0;     val = R_GPTB4->GTCCRA;     if (hall_w_prev_arr[hall_w_cnt]>0)     {       if (val > hall_w_prev_arr[hall_w_cnt])       {         hall_w_capt_arr[hall_w_cnt] = val - hall_w_prev_arr[hall_w_cnt];       }       else       {         hall_w_capt_arr[hall_w_cnt] =  0x7FFFFFFF -(hall_w_prev_arr[hall_w_cnt] - val -1);       }       hall_w_capt = hall_w_capt_arr[hall_w_cnt];       one_turn_period = hall_w_capt;     }     hall_w_prev_arr[hall_w_cnt] = val;     hall_w_cnt++;     if (hall_w_cnt >= 8) hall_w_cnt = 0;      h = R_IOPORT5->PCNTR2 & 0x7;  // Читаем сигналы датчиков Холла здесь чтобы они были прочитаны не раньше чем стработает capture логика     if ((h == 0b101) || (h == 0b010))      rotating_direction = 1;     else if ((h == 0b110) || (h == 0b001)) rotating_direction = -1;      R_GPTB5->GTCLR_b.CCLR13 = 1;  // Сброс счетчика отслеживающего остановку вращения     R_GPTB5->GTST           = 0;   } 

Здесь мы получаем значение частоты вращения c девиацией меньшей 0.3%, но с гораздо большей задержкой чем могли бы получить измеряя длительности между соседними фронтами. Но в последнем случае нам пришлось бы применить фильтр, который внёс бы ещё большую задержку.

Экстраполяция частоты вращения при внезапной остановке

Вращение ротора может внезапно прекратится при встрече сервопривода с препятствием. Но тогда блок расчёта частоты вращения по датчикам Холла просто не будет выполняться. В результате показания частоты застынут, а алгоритм 6-шаговой коммутации застынет на постоянном включении одной ветви обмотки. Поэтому нужно реализовать блок экстраполяции частоты вращения при отсутствии сигналов с датчиков Холла. Пример реализации показан ниже.

  // Блок линейно понижающий скорость в случает отсутствия сигналов с датчиков Холла   if (one_turn_period != 0)   {     if (R_GPTB5->GTST & BIT(6))  // Проверяем флаг TCFPO. Overflow Flag     {       // В случае переполения тамера сразу отмечаем скорость как нулевую.       // Поскольку переполение таймера происходит с периодом в две секунды, то такую низкую скрость принимаем как нулевую       one_turn_period = 0;     }     else     {       // За один оброт мотора мы имеем 8 полюсов * 3 датчика = 24 события сброса таймера R_GPTB5       // И если таймер R_GPTB5 смог набрать 2/3 (16/24) длительности одного оборота и не был сброшен значит скорость упала       // и можно начинать снижать оценку текущей скорости       uint32_t no_edge_period = R_GPTB5->GTCNT*16;       if (no_edge_period > one_turn_period)       {         one_turn_period = no_edge_period;       }     }   } 

Через некоторое время показатель частоты вращения упадёт до нуля и алгоритм управления скоростью примет решение об отключении тока.

Влияние способа коммутации силовых транзисторов на качество управления и энергопотребление

В прошлой статье о 6-шаговом управлении коммутацией были показаны несколько способов переключения транзисторов при ШИМ модуляции. В нашей программе оставлено два способа. Между ними можно выбирать с помощью настройки параметра Enable soft commutation.

На нашем мотор‑редукторе эти два способа показали только незначительные различия. При одинаковой частоте вращения ток потребления был идентичным. Но при мягкой коммутации вращение мотора начиналось при меньшем значении коэффициенте заполнения. Дело в том, что мотор из‑за трения не начинает крутиться до того момента как коэффициент заполнения ШИМ не превысит некий порог. Этот порог в случае жёсткой коммутации в нашем случае был в районе 55%, а в случае мягкой коммутации в районе 30%. т. е. мягкая коммутация даёт больший диапазон регулирования ШИМ — 70%, в противовес жёсткой — 45%. Это отражается потом и на качестве управления частотой вращения. При мягкой коммутации качество управления несколько лучше.

Способ коммутации выбирается параметром en_soft_commutation.

Регулировка тока затворов силовых транзисторов

На плате серво‑контроллера используется микросхема драйвера TMC6200. Микросхема получает сигналы от микроконтроллера и управляет затворами силовых транзисторов. Микроконтроллер не может напрямую управлять затворами поскольку там нужны высокие напряжения и довольно большие импульсные токи.

Типичная схема драйвера на TMC6200

Типичная схема драйвера на TMC6200

У этой микросхемы есть возможность через интерфейс SPI задавать уровень выходных токов на затворы. Есть 4 уровня: от самого слабого 400…600 мА до самого сильного 1200…1800 мА.

Как показали испытания всех возможных уровней, на ток потребления они практически не влияют. Измерения мощности при работе двигателя весьма зашумлены, и с учётом этого шума возможно допустить наличие некоторой разницы в пределах 100 мВт.

Силовые транзисторы на схеме подобраны оптимально по отношению к драйверу, поэтому регулировка его силы не оказывает заметного влияния. Стоит ожидать в общем случае, что чем больше ток драйвера тем больше в нем будет потерь. В настройках программы можно свободно менять уровни токов, а также уровни детекции и защиты от коротких замыканий.

Управлять настройками драйвера TMC6200 можно с помощью ряда параметров. Настройки загружаются при старте основной задачи управления мотором.

Калибровка смещения токовых усилителей драйвера TMC6200

Микросхема TMC6200 имеет встроенные усилители напряжения с токовых шунтов расположенных в фазах серво драйвера. Наличие встроенных усилителей упрощает схему. Есть возможность менять коэффициент усиления через интерфейс SPI.

Однако недостатком этих усилителей является довольно большое напряжение смещения. Ещё хуже, то что оно сильно зависит от температуры микросхемы. Но мало того, оно разное для каждого из 3 усилителей и может меняться во времени даже при постоянной внешней температуре. В нашей программе используется самый чувствительный режим с усилением в 20 раз. При таком усилении смещение на выходе усилителей достигает 240 мВ и более.

Решение проблемы найдено в том что перед каждым включением мотора производится перекалибровка смещения нуля для каждого из 3 усилителей. Перекалибровка длится около 15 мсек и существенного влияния на быстродействие не оказывает.

Что делать с обратной ЭДС? 

Как только мотор начинают крутить внешние силы вместо нашего драйвера он превращается в генератор. В генератор мотор также превращается на короткие промежутки времени когда происходит торможение. Если частота вращения вызванная внешними силами превышает частоту вращения на номинальном напряжении при холостом ходе, то повышается напряжение на шине питания. Ниже на осциллограмме показаны скачки напряжения на шине питания когда сервопривод вращает груз на рычаге с постоянным значением ШИМ. В моменты опускания груза напряжение на шине питания повышается на 5.5 В. Это происходит потому что частота вращения ротора в эти моменты превышает максимальную частоту вращения на номинальном напряжении по даташиту.

Осциллограмма выброса обратной ЭДС на шине питания

Осциллограмма выброса обратной ЭДС на шине питания

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

Проблема определения скорости вращения серво-потенциометра

Если внимательно посмотреть на сигнал приходящий с серво‑потенциометра установленного на выходном валу, то можно заметить, что он содержит довольно существенный шум.
Чтобы найти скорость надо получить функцию дифференциала угла поворота от времени. При цифровом управлении это будет просто разница между текущим и предыдущим положением. Как видно из скриншота, после такой операции получается абсолютно зашумлённый сигнал. В этом случае применяем глубокую двухступенчатую фильтрацию. Фильтрацию выполняем с частотой ШИМ т.е. 16 КГц.

Вид сигнала с серво-потенциометра

Вид сигнала с серво-потенциометра
Модель Simulink для тестирования вариантов фильтрации сигнала скорости вращения выходного вала сервопривода.

Модель Simulink для тестирования вариантов фильтрации сигнала скорости вращения выходного вала сервопривода.

Поскольку фильтр вносит фазовую задержку мы не можем на основе такого отфильтрованного сигнала управлять как угодно быстро и точно.

Для того чтобы узнать задержку в результате фильтрации измерим её на модели

Посмотрим разницу во времени между сигналами Serv.speed и Meas.serv.speed. Это два разных значения скорости вращения выходного вала в градусах в секунду. Первая величина реальная, снятая непосредственно с вала, а вторая оценочная, после фильтрации дифференциала сигнала позиции вала.

Вид окна Data Inspector в MATLAB сигналов скорости вращения выходного вала в градусах в сек

Вид окна Data Inspector в MATLAB сигналов скорости вращения выходного вала в градусах в сек

При коэффициенте экспоненциального фильтра (на модели обозначен как Speed exp.filter1) равного 1-1/2048 (0.99951) получаем задержку 83 мс.

Вид окна Data Inspector в MATLAB сигналов скорости вращения выходного вала в градусах в сек

Вид окна Data Inspector в MATLAB сигналов скорости вращения выходного вала в градусах в сек

При коэффициенте экспоненциального фильтра равного 1-1/256 (0.9961) получаем задержку 16 мс.

Задержки существенные. Они ограничивают такт регулятора скорости величиной не более 10…50 Гц.

Как видно, простая комбинация FIR и IIR фильтров не слишком хороший способ получить чистое значение скорости. Но пока останавливаемся на этом.

К вопросу о применении магнитных сенсоров вместо серво-потенциометра

Можно слышать мнение о лучших свойствах магнитных сенсоров по сравнению с серво-потенциометром. Был проведён эксперимент с GMR сенсорами угла поворота TLI5012B.

Была изготовлена отладочная плата с этим сенсором, написан софт для обмена с FreeMaster. Непосредственно над сенсором закрепили диаметрально поляризованный магнит. Магнит повернут так чтобы сенсор показывал угол близкий к нулю. И были проведены замеры показаний при смене ориентации платы относительно магнитного поля земли

Отладочная плата для проверки магнитных сенсоров угла

Отладочная плата для проверки магнитных сенсоров угла

Вот результат изменения показаний сенсора при повороте платы в поле земли:

Как видно присутствие рядом мощного магнита не спасло сенсор от чувствительности к полю земли. Изменение показаний достигают 10%.

Шум серво-потенциометра достигает 5 сэмплов на любом угле поворота при 12-битном АЦП. На середине диапазона это будет равно 5/2048 = 0.24%. У GMR сенсора TLI5012 шум составляет 0.025 %

Однако деформации платы с сенсором на доли миллиметра приводили к искажениям показаний на 0.06 %. Сенсор очень чувствителен к изменению расстояния до магнита и очень чувствителен к вибрациям.

Изменение дистанции между осями сенсора и магнита на 1 мм приводит к изменению значения угла на 1 градус.

Появление на расстоянии 50 см магнита аналогичного приложенного к сенсору также вызывает флуктуации приблизительно в 1 градус.

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

Разработка приложений для MC50

Программный фреймворк серво контроллера MC50 выполнен на базе Azure RTOS. На данном этапе он содержит в себе практически весь состав промежуточного программного обеспечения Azure RTOS с драйверами периферии. Поверх слоя промежуточного ПО находится несколько прикладных задач: управления мотором, управления микросхемой 3-фазного драйвера мотора, измерения, GUI, управление выходами и другие.

Грубая структура программного обеспечения

Грубая структура программного обеспечения

В текущей версии софта реализовано управление рычагом на заданные углы с заданной скоростью. С помощью ручного энкодера производится калибровка углов, настройка скорости поворота и номер узла на шине CAN. Все остальные параметры можно настроить через терминал VT100 через USB VCOM или через интернет, как это было показано в предыдущих статьях. Команды сервоприводу могут подаваться с ручного энкодера, по шине CAN и через протокол FreeMaster через USB.

Разработка продолжается…


ссылка на оригинал статьи https://habr.com/ru/articles/752842/

React Custom Hook: useMediaQuery

In this article series, we embark on a journey through the realm of custom React hooks, discovering their immense potential for elevating your development projects. Our focus today is on the «useMediaQuery» hook, one of the many carefully crafted hooks available in the collection of React custom hooks.

Github: https://github.com/sergeyleschev/react-custom-hooks

import { useState, useEffect } from "react" import useEventListener from "../useEventListener/useEventListener"  export default function useMediaQuery(mediaQuery) {     const [isMatch, setIsMatch] = useState(false)     const [mediaQueryList, setMediaQueryList] = useState(null)     useEffect(() => {         const list = window.matchMedia(mediaQuery)         setMediaQueryList(list)         setIsMatch(list.matches)     }, [mediaQuery])     useEventListener("change", e => setIsMatch(e.matches), mediaQueryList)     return isMatch }

The useMediaQuery hook allows you to dynamically update your UI based on a given media query. Simply pass in the desired media query as a parameter, and the hook will return a boolean value indicating whether the media query matches the current viewport size.

One of the key advantages of this custom hook is its simplicity and reusability. With just a few lines of code, you can effortlessly implement responsive behavior throughout your application. Whether you need to conditionally render components, apply specific styles, or trigger different functionality based on screen size, useMediaQuery has got you covered.

import useMediaQuery from "./useMediaQuery"  export default function MediaQueryComponent() {     const isLarge = useMediaQuery("(min-width: 200px)")     return <div>Large: {isLarge.toString()}</div> }

This hook is not limited to specific use cases; it can be utilized in a variety of scenarios. For instance, you can use it to dynamically adjust the layout of a navigation menu, hide or show certain elements based on screen size, or even optimize the loading of data based on the available space. The possibilities are endless, and the useMediaQuery hook empowers you to deliver a seamless user experience across different devices and screen sizes.

Full Version | React Custom Hooks: https://habr.com/en/articles/746760/


ссылка на оригинал статьи https://habr.com/ru/articles/752808/