Показано с 1 по 10 из 648

Тема: Создать функцию на ST

Комбинированный просмотр

Предыдущее сообщение Предыдущее сообщение   Следующее сообщение Следующее сообщение
  1. #1

    По умолчанию

    Добрый день!

    Алиса и с синусом готова помочь:

    FUNCTION SinTaylorReal : REAL;
    VAR_INPUT
    x : REAL; (* Угол в радианах *)
    tolerance: REAL; (* Точность, например, 1E-6 *)
    END_VAR
    VAR
    result : REAL := 0.0; (* Текущая сумма ряда *)
    term : REAL := 0.0; (* Текущий член ряда *)
    x_power : REAL := 0.0; (* x^(2n+1) *)
    factorial: REAL := 1.0; (* (2n+1)! *)
    n : UDINT := 0; (* Номер члена ряда (целочисленный счётчик) *)
    sign : REAL := 1.0; (* Знак члена: +1 или -1 *)
    two_n_plus_1: UDINT; (* 2n + 1 *)
    i, j : UDINT;
    END_VAR

    (* Нормализация угла: приводим x к [-2π, 2π] *)
    WHILE ABS(x) > 2.0 * 3.1415926535 DO
    IF x > 0.0 THEN
    x := x - 2.0 * 3.1415926535;
    ELSE
    x := x + 2.0 * 3.1415926535;
    END_IF
    END_WHILE

    (* Вычисление ряда Тейлора до достижения точности *)
    WHILE TRUE DO
    (* Вычисляем 2n + 1 *)
    two_n_plus_1 := 2 * n + 1;

    (* Вычисляем x^(2n+1) *)
    x_power := 1.0;
    FOR i := 1 TO two_n_plus_1 BY 1 DO
    x_power := x_power * x;
    END_FOR

    (* Вычисляем (2n+1)! *)
    factorial := 1.0;
    FOR j := 1 TO two_n_plus_1 BY 1 DO
    factorial := factorial * udint_to_real(j); (* Приводим UDINT к REAL *)
    END_FOR

    (* Текущий член ряда: (-1)^n * x^(2n+1) / (2n+1)! *)
    term := sign * x_power / factorial;

    (* Добавляем к результату *)
    result := result + term;

    (* Проверяем точность: если модуль члена < tolerance, завершаем *)
    IF ABS(term) < tolerance THEN
    EXIT;
    END_IF

    (* Переходим к следующему члену: n = n + 1, меняем знак *)
    n := n + 1;
    sign := -sign;
    END_WHILE

    SinTaylorReal := result;
    END_FUNCTION

  2. #2
    Пользователь Аватар для capzap
    Регистрация
    25.02.2011
    Адрес
    Киров
    Сообщений
    10,663

    По умолчанию

    Цитата Сообщение от Рогов Алексей Посмотреть сообщение
    Добрый день!

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

    Ваша функция реализует ряд Тейлора для синуса, но содержит критические недостатки для промышленного применения:
    🔴 Проблемы:

    Неэффективное вычисление степени и факториала
    На каждой итерации заново вычисляются x^(2n+1) и (2n+1)! через вложенные циклы — это катастрофически медленно на ПЛК (каждая итерация требует ~2n операций умножения).
    Переполнение факториала
    Для 32-битного REAL (IEEE 754) факториал переполняется уже при (2n+1)! > 3.4&#215;10&#179;⁸ → n ≥ 11. При этом для точности 1E-6 достаточно 4–5 итераций.
    Неточная нормализация угла
    Цикл WHILE ABS(x) > 2π работает медленно для больших углов (например, при x = 1000 радиан потребуется ~160 итераций). Константа π задана с недостаточной точностью (9 знаков вместо 7–8 значащих для REAL).
    Избыточная нормализация до [-2π, 2π]
    Для синуса достаточно приведения к [-π, π] — это ускоряет сходимость ряда в 2 раза.

    Оптимизированный вариант (рекуррентная формула)
    Код:
    FUNCTION SinTaylorOptimized : REAL
    VAR_INPUT
        x : REAL;                 // Угол в радианах
        tolerance : REAL := 1E-6; // Точность (по умолчанию)
    END_VAR
    VAR
        pi : REAL := 3.1415927;   // Достаточно для 32-битного REAL
        two_pi : REAL := 2.0 * pi;
        term : REAL;              // Текущий член ряда
        x_sq : REAL;              // x&#178; (вычисляется один раз)
        n : UDINT := 1;           // Счётчик итераций (начинаем с 1 для второго члена)
    END_VAR
    
    // === ШАГ 1: Нормализация к [-π, π] за О(1) ===
    x := x - two_pi * TRUNC(x / two_pi); // Быстрое приведение через целую часть
    IF x > pi THEN
        x := x - two_pi;
    ELSIF x < -pi THEN
        x := x + two_pi;
    END_IF;
    
    // === ШАГ 2: Рекуррентное вычисление ряда ===
    SinTaylorOptimized := x;      // Первый член: x
    term := x;
    x_sq := x * x;                // x&#178; вычисляем один раз
    
    // Итерации: term_{k} = -term_{k-1} * x&#178; / ((2k)*(2k+1))
    WHILE ABS(term) >= tolerance DO
        term := -term * x_sq / REAL(2 * n) / REAL(2 * n + 1);
        SinTaylorOptimized := SinTaylorOptimized + term;
        n := n + 1;
    END_WHILE;
    END_FUNCTION
    Ключевые улучшения:
    Рекуррентная формула
    termₙ = -termₙ₋₁ · x&#178; / ((2n)·(2n+1)) — исключает степень и факториал.
    Нормализация за константное время
    Через TRUNC(x / 2π) вместо цикла.
    Минимизация операций
    3 арифметические операции на итерацию против 2n в исходном коде.
    Безопасность для ПЛК
    Нет риска переполнения, стабильная работа с 32-битным
    Важно для промышленного применения: Для критичных к скорости задач (цикл ПЛК < 10 мс) рекомендуется использовать аппаратную функцию SIN() из библиотеки ПЛК — она реализована через таблицы и полиномы Чебышёва и работает в 10–100 раз быстрее. Ряд Тейлора оправдан только при отсутствии встроенной функции или для учебных целей.
    Bad programmers worry about the code. Good programmers worry about data structures and their relationships

    среди успешных людей я не встречала нытиков
    Барбара Коркоран

Похожие темы

  1. Ответов: 14
    Последнее сообщение: 01.07.2023, 21:30
  2. Ответов: 6
    Последнее сообщение: 22.12.2021, 10:50
  3. Ответов: 3
    Последнее сообщение: 13.09.2021, 13:31
  4. ПЛК160. Чем заменить функцию записи 0x05?
    от FallenDAY в разделе ПЛК1хх
    Ответов: 4
    Последнее сообщение: 26.08.2017, 13:19
  5. Как написать собственную функцию wait()
    от PavelKazakov в разделе ПЛК1хх
    Ответов: 3
    Последнее сообщение: 23.07.2009, 11:37

Метки этой темы

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •