Документация Engee
Notebook
diting_result_18d00bc9b74611f1baa0ce5c6ac00432_1.jpeg

Проектирование распределителей специальной и дорожно-строительной техники

В данном примере рассмотрим проектирование секционного распределителя экскаватора, а именно геометрии золотниковой пары (золотника и гильзы). Представим, что в ходе испытаний текущий распределитель не отрабатывает сигнал управления от гидравлического джойстика - имеется зона, где требуемая скорость выдвижения гидроцилиндра рабочего органа не достигается при отклонении джойстика на максимум. На регулирование и проливку при этом очень влияет геометрия канавок (Notch), которые фрезеруются на поясках золотника. Для исправления ситуации конструкторы разработали новую геометрию канавок, и применили моделирование в Engee, с целью провести виртуальный эксперимент по проливке секции распределителя. Пример золотника с канавками показан на рисунке.

Золотник с канавками_2.jpg

Откроем модель

In [ ]:
include("geometry_core.jl")
include("parameters_exporter.jl")

modelName = "DCV_HCD"
if !(modelName in [m.name for m in engee.get_all_models()]) 
    engee.load("$(@__DIR__)/$(modelName).engee"); 
end

Смоделирован золотниковый распределитель с гидравлическим управлением. Для упрощения процедуры проливки исследуется выдвижение гидроцилиндра, задействуются каналы распределителя P-A и B-T. Справа на торец золотника подается управляющий сигнал по давлению от 0 до 30 бар (избыточн.), распределитель сдвигается влево, сжимая возвратную пружину и открывая соответствующие каналы. На канал P распределителя подключен постоянный источник расхода, 1000 л/мин. При проливке проверяется расход в порту A распределителя (порт подключения к поршневой полости цилиндра) в зависимости от перемещения золотника.

Модель_3.png

Параметризация канавок

Сложная форма канавок задается при помощи нескольких областей (Regions) - пользователь параметризует геометрические параметры каждой области, и задает количество канавок на одном пояске.

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

image.png

Имеется 5 типов геометрии областей (см. документацию на Spool Edge with Notches (IL)).

image.png

Пояснения к параметризации приведены в коде параметризации золотников ниже.

Задание параметров поясков золотника

Модернизированная геометрия

Сравним 2 комплекта параметров, отличающихся друг от друга конфигурацией канавок - первый вариант - модернизированный, второй - исходный

In [ ]:
# ==============================================================================
# ПАРАМЕТРИЗАЦИЯ ПРОИЗВОЛЬНОГО КОЛИЧЕСТВА БЛОКОВ КАНАВОК ЗОЛОТНИКА В МОДЕЛИ
# ==============================================================================

# СЛОВАРЬ НАЗНАЧЕНИЯ АРГУМЕНТОВ СТРУКТУРЫ UniversalRegion ДЛЯ ВСЕХ ТИПОВ ФОРМ:
# 
# Аргументы по порядку: (shape, length, radial_depth_start, radial_depth_end, width_start, width_end, r_edge)
#
# 1. shape = "circular" (Круговой носик тупика распределительного паза)
#    - length: Физическая осевая длина скругленного носика (ΔZ), мм
#    - radial_depth_start: Постоянная радиальная глубина реза фрезы на данном участке (hRmin), мм
#    - radial_depth_end: Не используется (дублирует значение radial_depth_start для непрерывности)
#    - width_start: Начальная ширина контура (всегда равна 0.0 в крайней точке тупика), мм
#    - width_end: Конечная окружная ширина, на которую плавно выходит круговая дуга носика, мм
#    - r_edge: Не используется для данного типа формы (всегда равен 0.0)
# 
# 2. shape = "triangular" (Треугольный / V-образный регулирующий участок)
#    - length: Осевая длина участка раскрытия конуса (ΔZ), мм
#    - radial_depth_start: Постоянная радиальная глубина реза на всем участке (hRmin), мм
#    - radial_depth_end: Не используется (дублирует значение radial_depth_start)
#    - width_start: Начальная окружная ширина паза на левой границе региона, мм
#    - width_end: Конечная окружная ширина на правой границе региона (определяет угол раскрытия), мм
#    - r_edge: Не используется для данного типа формы (всегда равен 0.0)
# 
# 3. shape = "rectangular" (Прямоугольный рабочий участок с возможностью уклона дна)
#    - length: Физическая осевая длина участка (ΔZ), мм
#    - radial_depth_start: Радиальная глубина на левом краю региона (начальный уровень уклона дна), мм
#    - radial_depth_end: Радиальная глубина на правом краю региона (конечный заглубленный уровень клина), мм
#    - width_start: Постоянная окружная ширина паза (width_start должна быть строго равна width_end), мм
#    - width_end: Постоянная окружная ширина паза (width_start должна быть строго равна width_end), мм
#    - r_edge: Не используется для данного типа формы (всегда равен 0.0)
# 
# 4. shape = "trapezoidal" (Классический трапециевидный участок с острыми боковыми гранями)
#    - length: Осевая длина участка линейного расширения щек (ΔZ), мм
#    - radial_depth_start: Постоянная радиальная глубина реза на всем участке (hRmin), мм
#    - radial_depth_end: Не используется (дублирует значение radial_depth_start)
#    - width_start: Начальная окружная ширина контура на левом стыке региона, мм
#    - width_end: Конечная окружная ширина на правом выходе региона (максимальный раструб конуса), мм
#    - r_edge: Не используется для данного типа формы (всегда равен 0.0)
# 
# 5. shape = "trapezoidal_rounded" (Трапециевидный участок, образованный образующей окружностью фрезы)
#    - length: Осевая длина области по оси Z (lengthtrapr1), мм
#    - radial_depth_start: Постоянная глубина области (hRmin1), мм
#    - radial_depth_end: Дублирует значение radial_depth_start для обеспечения непрерывности
#    - width_start: Начальная окружная ширина у кромки золотника (inwidthr1), мм
#    - width_end: Конечная окружная ширина у точки сопряжения со следующей областью (endwidthr1), мм
#    - r_edge: РАДИУС ОКРУЖНОСТИ, образующей боковые стороны (dedger1 / 2.0), мм

# ==============================================================================
# ЭКСПЕРИМЕНТ №1: МОДЕРНИЗИРОВАННЫЕ ПАРАМЕТРЫ БЛОКОВ
# ==============================================================================
список_блоков_case1 = [
    
    # Блок №1: Кромка Нагнетания P-A Геометрия канавок 1
    SpoolComponent(
        "Кромка Нагнетания P-A_1",
        "DCV_HCD/Spool Edge with Notches (IL)",
        45.0,  # spool_diameter (Диаметр золотника), мм
        28.0,  # rod_diameter (Диаметр шейки/штока), мм
        2,     # nslots (Количество слотов / пазов)
        20.0,  # chamber_length_0 (Длина камеры при 0 смещении), мм
        -4.0,  # underlap (Величина перекрытия / смещения), мм
        16.0,  # underlap_max_area (Перекрытие при макс. площади), мм
        29.5,  # edge_to_edge (Расстояние между кромками), мм
        0.003, # clearance (Зазор по диаметру), мм
        0.0,   # eccentricity_ratio (Коэффициент эксцентриситета)
        true,  # enable_annular_area (Учитывать ли площадь пазов при открытии)
        true,  # enable_pressure_force (Учитывать силы давления и вариацию объема)
        true,  # couette_effect (Учитывать эффект Куэтта для трения жидкости)
        [
            UniversalRegion("circular", 4.0, 8.0, 8.0, 0.0, 4.0, 0.0),
            UniversalRegion("rectangular", 6.0, 8.0, 8.0, 4.0, 4.0, 0.0)
        ]
    ),
    
    # Блок №2: Кромка Нагнетания P-A Геометрия канавок 2
    SpoolComponent(
        "Кромка нагнетания P-A_2",
        "DCV_HCD/Spool Edge with Notches (IL)-1",
        45.0,  # spool_diameter (Диаметр золотника), мм
        28.0,  # rod_diameter (Диаметр шейки/штока), мм
        2,     # nslots (Количество слотов / пазов)
        20.0,  # chamber_length_0 (Длина камеры при 0 смещении), мм
        -5.0,  # underlap (Величина перекрытия / смещения), мм
        15.0,  # underlap_max_area (Перекрытие при макс. площади), мм
        29.5,  # edge_to_edge (Расстояние между кромками), мм
        0.003, # clearance (Зазор по диаметру), мм
        0.0,   # eccentricity_ratio (Коэффициент эксцентриситета)
        false, # enable_annular_area (Учитывать ли площадь пазов при открытии)
        false, # enable_pressure_force (Учитывать силы давления и вариацию объема)
        false, # couette_effect (Учитывать эффект Куэтта для трения жидкости)
        [
            UniversalRegion("circular", 4.0, 7.0, 7.0, 0.0, 4.0, 0.0),
            UniversalRegion("rectangular", 5.0, 7.0, 7.0, 4.0, 4.0, 0.0)
        ]
    ),

    # Блок №3: Кромка слива B-T Геометрия канавок 1
    SpoolComponent(
        "Кромка слива B-T_1",
        "DCV_HCD/Spool Edge with Notches (IL)-2", 
        45.0,  # spool_diameter (Диаметр золотника), мм
        28.0,  # rod_diameter (Диаметр шейки/штока), мм
        2,     # nslots (Количество слотов / пазов)
        20.0,  # chamber_length_0 (Длина камеры при 0 смещении), мм
        -12.0, # underlap (Величина перекрытия / смещения), мм
        8.0,   # underlap_max_area (Перекрытие при макс. площади), мм
        96.0,  # edge_to_edge (Расстояние между кромками), мм
        0.003, # clearance (Зазор по диаметру), мм
        0.0,   # eccentricity_ratio (Коэффициент эксцентриситета)
        true,  # enable_annular_area (Учитывать ли площадь пазов при открытии)
        true,  # enable_pressure_force (Учитывать силы давления и вариацию объема)
        true,  # couette_effect (Учитывать эффект Куэтта для трения жидкости)
        [
            UniversalRegion("circular", 4.0, 5.0, 5.0, 0.0, 4.0, 0.0),
            UniversalRegion("rectangular", 3.0, 5.0, 5.0, 4.0, 4.0, 0.0)
        ]
    ),

   # Блок №4: Кромка слива B-T Геометрия канавок 2
    SpoolComponent(
        "Кромка Слива B-T_2",
        "DCV_HCD/Spool Edge with Notches (IL)-3",
        45.0,  # spool_diameter (Диаметр золотника), мм
        28.5,  # rod_diameter (Диаметр шейки/штока), мм
        2,     # nslots (Количество слотов / пазов)
        20.0,  # chamber_length_0 (Длина камеры при 0 смещении), мм
        -5.0,  # underlap (Величина перекрытия / смещения), мм
        15.0,  # underlap_max_area (Перекрытие при макс. площади), мм
        95.0,  # edge_to_edge (Расстояние между кромками), мм
        0.003, # clearance (Зазор по диаметру), мм
        0.0,   # eccentricity_ratio (Коэффициент эксцентриситета)
        false, # enable_annular_area (Учитывать ли площадь пазов при открытии)
        false, # enable_pressure_force (Учитывать силы давления и вариацию объема)
        false, # couette_effect (Учитывать эффект Куэтта для трения жидкости)
        [
            UniversalRegion("circular", 4.0, 8.0, 8.0, 0.0, 4.0, 0.0),
            UniversalRegion("rectangular", 11.0, 8.0, 8.0, 4.0, 4.0, 0.0)
        ]
    )
]
Out[0]:
4-element Vector{SpoolComponent}:
 SpoolComponent("Кромка Нагнетания P-A_1", "DCV_HCD/Spool Edge with Notches (IL)", 45.0, 28.0, 2, 20.0, -4.0, 16.0, 29.5, 0.003, 0.0, true, true, true, UniversalRegion[UniversalRegion("circular", 4.0, 8.0, 8.0, 0.0, 4.0, 0.0), UniversalRegion("rectangular", 6.0, 8.0, 8.0, 4.0, 4.0, 0.0)])
 SpoolComponent("Кромка нагнетания P-A_2", "DCV_HCD/Spool Edge with Notches (IL)-1", 45.0, 28.0, 2, 20.0, -5.0, 15.0, 29.5, 0.003, 0.0, false, false, false, UniversalRegion[UniversalRegion("circular", 4.0, 7.0, 7.0, 0.0, 4.0, 0.0), UniversalRegion("rectangular", 5.0, 7.0, 7.0, 4.0, 4.0, 0.0)])
 SpoolComponent("Кромка слива B-T_1", "DCV_HCD/Spool Edge with Notches (IL)-2", 45.0, 28.0, 2, 20.0, -12.0, 8.0, 96.0, 0.003, 0.0, true, true, true, UniversalRegion[UniversalRegion("circular", 4.0, 5.0, 5.0, 0.0, 4.0, 0.0), UniversalRegion("rectangular", 3.0, 5.0, 5.0, 4.0, 4.0, 0.0)])
 SpoolComponent("Кромка Слива B-T_2", "DCV_HCD/Spool Edge with Notches (IL)-3", 45.0, 28.5, 2, 20.0, -5.0, 15.0, 95.0, 0.003, 0.0, false, false, false, UniversalRegion[UniversalRegion("circular", 4.0, 8.0, 8.0, 0.0, 4.0, 0.0), UniversalRegion("rectangular", 11.0, 8.0, 8.0, 4.0, 4.0, 0.0)])

Параметризация блоков для сравниваемого золотника (исходная геометрия)

In [ ]:
# ==============================================================================
# ЭКСПЕРИМЕНТ №2: ИСХОДНЫЕ ПАРАМЕТРЫ БЛОКОВ (КОМПЛЕКТ ПРОПИСАН ПОЛНОСТЬЮ С НУЛЯ)
# ==============================================================================
# Здесь вы можете вручную модифицировать любые параметры для проведения сравнительного анализа
список_блоков_case2 = [
    
    # Блок №1: Кромка Нагнетания P-A Геометрия канавок 1 (Модифицированный)
    SpoolComponent(
        "Кромка Нагнетания P-A_1 (Мод)",
        "DCV_HCD/Spool Edge with Notches (IL)",
        45.0,  # spool_diameter (Диаметр золотника), мм
        25.0,  # rod_diameter (Диаметр шейки/штока), мм
        2,     # nslots (Количество слотов / пазов)
        20.0,  # chamber_length_0 (Длина камеры при 0 смещении), мм
        -4.0,  # underlap (Величина перекрытия / смещения), мм  
        16.0,  # underlap_max_area (Перекрытие при макс. площади), мм
        36.0,  # edge_to_edge (Расстояние между кромками), мм
        0.003, # clearance (Зазор по диаметру), мм
        0.0,   # eccentricity_ratio (Коэффициент эксцентриситета)
        true,  # enable_annular_area (Учитывать ли площадь пазов при открытии)
        true,  # enable_pressure_force (Учитывать силы давления и вариацию объема)
        true,  # couette_effect (Учитывать эффект Куэтта для трения жидкости)
        [
            UniversalRegion("circular", 4.0, 4.0, 4.0, 0.0, 4.0, 0.0),
            UniversalRegion("rectangular", 12.5, 4.0, 4.0, 4.0, 4.0, 0.0)
        ]
    ),
    
     # Блок №2 (Модифицированный)
    SpoolComponent(
        "Кромка нагнетания P-A_2 (Мод)", "DCV_HCD/Spool Edge with Notches (IL)-1",
        45.0, 25.0, 2, 20.0, -4.0, 16.0, 36.0, 0.003, 0.0, false, false, false,
        [
            UniversalRegion("circular", 4.0, 4.0, 4.0, 0.0, 4.0, 0.0),
            UniversalRegion("rectangular", 12.5, 4.0, 4.0, 4.0, 4.0, 0.0)
        ]
    ),
    # Блок №3 (Модифицированный)
    SpoolComponent(
        "Кромка слива B-T_1 (Мод)", "DCV_HCD/Spool Edge with Notches (IL)-2", 
        45.0, 25.0, 2, 20.0, -4.0, 16.0, 96.0, 0.003, 0.0, true, true, true,
        [
            UniversalRegion("circular", 4.0, 4.0, 4.0, 0.0, 4.0, 0.0),
            UniversalRegion("rectangular", 12.5, 4.0, 4.0, 4.0, 4.0, 0.0)
        ]
    ),
    # Блок №4 (Модифицированный)
    SpoolComponent(
        "Кромка Слива B-T_2 (Мод)", "DCV_HCD/Spool Edge with Notches (IL)-3",
        45.0, 25.0, 2, 20.0, -4.0, 16.0, 96.0, 0.003, 0.0, false, false, false,
        [
            UniversalRegion("circular", 4.0, 4.0, 4.0, 0.0, 4.0, 0.0),
            UniversalRegion("rectangular", 12.5, 4.0, 4.0, 4.0, 4.0, 0.0)
        ]
    )
]
Out[0]:
4-element Vector{SpoolComponent}:
 SpoolComponent("Кромка Нагнетания P-A_1 (Мод)", "DCV_HCD/Spool Edge with Notches (IL)", 45.0, 25.0, 2, 20.0, -4.0, 16.0, 36.0, 0.003, 0.0, true, true, true, UniversalRegion[UniversalRegion("circular", 4.0, 4.0, 4.0, 0.0, 4.0, 0.0), UniversalRegion("rectangular", 12.5, 4.0, 4.0, 4.0, 4.0, 0.0)])
 SpoolComponent("Кромка нагнетания P-A_2 (Мод)", "DCV_HCD/Spool Edge with Notches (IL)-1", 45.0, 25.0, 2, 20.0, -4.0, 16.0, 36.0, 0.003, 0.0, false, false, false, UniversalRegion[UniversalRegion("circular", 4.0, 4.0, 4.0, 0.0, 4.0, 0.0), UniversalRegion("rectangular", 12.5, 4.0, 4.0, 4.0, 4.0, 0.0)])
 SpoolComponent("Кромка слива B-T_1 (Мод)", "DCV_HCD/Spool Edge with Notches (IL)-2", 45.0, 25.0, 2, 20.0, -4.0, 16.0, 96.0, 0.003, 0.0, true, true, true, UniversalRegion[UniversalRegion("circular", 4.0, 4.0, 4.0, 0.0, 4.0, 0.0), UniversalRegion("rectangular", 12.5, 4.0, 4.0, 4.0, 4.0, 0.0)])
 SpoolComponent("Кромка Слива B-T_2 (Мод)", "DCV_HCD/Spool Edge with Notches (IL)-3", 45.0, 25.0, 2, 20.0, -4.0, 16.0, 96.0, 0.003, 0.0, false, false, false, UniversalRegion[UniversalRegion("circular", 4.0, 4.0, 4.0, 0.0, 4.0, 0.0), UniversalRegion("rectangular", 12.5, 4.0, 4.0, 4.0, 4.0, 0.0)])

Отрисовка компонентов золотника

Визуально проверяем геометрию - параметры и расположение канавок, диаметр золотника, окна для каждого компонента Notch Edge

In [ ]:
# Отрисовка первого компонента из массива для контроля геометрии (например, из Кейса 1)
# Отобразим желаемый элемент в 3D и 2D-проекциях
draw_spool_component_3d(список_блоков_case1[1])
Out[0]:

Параметры запуска модели

Выбираем, какой золотник хотим проверить - первый, второй или оба сразу

In [ ]:
# ==============================================================================
# НАСТРОЙКА ЗАПУСКА: ВЫБЕРИТЕ, КАКОЙ ЭКСПЕРИМЕНТ ПРОГНАТЬ СЕЙЧАС
# ==============================================================================
run_case1 = true   # Запустить расчет для Эксперимента №1 (Модернизированный)?
run_case2 = true   # Запустить расчет для Эксперимента №2 (Исходный)?
Out[0]:
true

Запуск эксперимента и сбор данных. Отрисовка графиков

In [ ]:
# =============================================================================
# ФУНКЦИЯ ДЛЯ ВЫПОЛНЕНИЯ И СБОРА ОДНОГО ЭКСПЕРИМЕНТА (ОРИГИНАЛЬНАЯ ЛОГИКА)
# =============================================================================
function run_simulation_case(список_блоков_case)
    println("\n--> Шаг 1: Запись параметров в блоки Engee...")
    for блок in список_блоков_case
        send_params_to_engee(блок)
    end

    println("--> Шаг 2: Запуск расчета модели '$(modelName)'...")
    try
        engee.run("$(modelName)") 
        println("🚀 Расчет успешно завершен!")
    catch e
        println("⚠ Ошибка расчета: ", e)
        return nothing
    end

    println("--> Шаг 3: Экспорт результатов и прореживание точек...")
    # Выкачиваем WorkspaceArray напрямую из структуры результатов симуляции Engee
    df_Disp_DCV      = collect(WorkspaceArray{Float64}("$(modelName)/Displacement_DCV"))
    df_Disp_Cylinder = collect(WorkspaceArray{Float64}("$(modelName)/Displacement_Cylinder"))
    df_q_Piston      = collect(WorkspaceArray{Float64}("$(modelName)/Расходомер-1/Q_Piston"))
    
    step_decim = 100 
    
    # Разделяем структуры времени и значений, переводя их в чистые векторы Float64
    time_cylinder = df_Disp_Cylinder.time[1:step_decim:end]
    val_cylinder  = df_Disp_Cylinder.value[1:step_decim:end]
    val_disp_dcv  = df_Disp_DCV.value[1:step_decim:end]
    val_q_piston  = df_q_Piston.value[1:step_decim:end]
    
    return Dict(
        "time_cylinder" => time_cylinder,
        "val_cylinder"  => val_cylinder,
        "val_disp_dcv"  => val_disp_dcv,
        "val_q_piston"  => val_q_piston
    )
end

# =============================================================================
# ИСПОЛНЕНИЕ СИМУЛЯЦИЙ С СОХРАНЕНИЕМ ПРЕДЫДУЩИХ ДАННЫХ В СЕССИИ
# =============================================================================
if !@isdefined(res1) global res1 = nothing end
if !@isdefined(res2) global res2 = nothing end

if run_case1
    println("=== СИМУЛЯЦИЯ №1: ЗАПУСК ===")
    global res1 = run_simulation_case(список_блоков_case1)
else
    println("=== СИМУЛЯЦИЯ №1: ПРОПУЩЕНА (используются данные из памяти) ===")
end

if run_case2
    println("=== СИМУЛЯЦИЯ №2: ЗАПУСК ===")
    global res2 = run_simulation_case(список_блоков_case2)
else
    println("=== СИМУЛЯЦИЯ №2: ПРОПУЩЕНА (используются данные из памяти) ===")
end

# =============================================================================
# АДАПТИВНОЕ ПОСТРОЕНИЕ И НАЛОЖЕНИЕ ГРАФИКОВ (ЛОКАЛЬНЫЙ GR БЭКЕНД)
# =============================================================================
using Plots.PlotMeasures 

has_data1 = !isnothing(res1)
has_data2 = !isnothing(res2)

if !has_data1 && !has_data2
    println("⚠ Нет данных для отображения графиков. Запустите хотя бы один эксперимент (поставьте true).")
else
    # Переключаем бэкенд на GR для генерации легковесного статического PNG-изображения
    gr() 

    println("\nКомпоновка графиков (Режим: ", 
             (has_data1 && has_data2) ? "Сравнение двух комплектов" : 
             (has_data1 ? "Только Эксперимент 1 (Исходный)" : "Только Эксперимент 2 (Модифицированный)"), ")...")

    # --- ГРАФИК 1: Расход Q_Piston от перемещения золотника Displacement_DCV ---
    p1 = plot(
        title = "Статическая характеристика: Расход поршня от смещения золотника",
        xlabel = "Смещение золотника Displacement_DCV (мм)",
        ylabel = "Расход Q_Piston (л/мин)", 
        grid = true, 
        titlefont = font(10),
        bottom_margin = 15mm 
    )

    if has_data1
        p_idx1 = sortperm(res1["val_disp_dcv"])
        plot!(p1, res1["val_disp_dcv"][p_idx1], res1["val_q_piston"][p_idx1], 
              label="Эксперимент 1 (Модернизированный)", lw=2.5, color=:blue)
    end

    if has_data2
        p_idx2 = sortperm(res2["val_disp_dcv"])
        plot!(p1, res2["val_disp_dcv"][p_idx2], res2["val_q_piston"][p_idx2], 
              label="Эксперимент 2 (Исходный)", lw=2.5, color=:red, style=:dash)
    end

    # --- ГРАФИК 2: Перемещение гидроцилиндра Displacement_Cylinder от времени t ---
    p2 = plot(
        title = "Динамика выдвижения цилиндра экскаватора во времени",
        xlabel = "Время симуляции t (с)",
        ylabel = "Смещение поршня Cylinder (м)", 
        grid = true, 
        titlefont = font(10),
        top_margin = 10mm 
    )

    if has_data1
        plot!(p2, res1["time_cylinder"], res1["val_cylinder"], 
              label="Эксперимент 1 (Модернизированный)", lw=2.5, color=:darkgreen)
    end

    if has_data2
        plot!(p2, res2["time_cylinder"], res2["val_cylinder"], 
              label="Эксперимент 2 (Исходный)", lw=2.5, color=:orange, style=:dash)
    end

    # Объединяем оба холста в единый вертикальный отчет (2 строки, 1 столбец)
    combined_canvas = plot(p1, p2, layout=(2, 1), leg=true, size=(1200, 950))
    
    # Принудительно экспортируем результат в файл и рендерим его как статичную PNG
    savefig(combined_canvas, "final_simulation_report.png")
    display("image/png", read("final_simulation_report.png"))
    
    # Возвращаем интерактивный бэкенд PlotlyJS для комфортной 3D-работы в остальных ячейках
    plotlyjs() 
end
=== СИМУЛЯЦИЯ №1: ЗАПУСК ===

--> Шаг 1: Запись параметров в блоки Engee...
--> Шаг 2: Запуск расчета модели 'DCV_HCD'...
🚀 Расчет успешно завершен!
--> Шаг 3: Экспорт результатов и прореживание точек...
=== СИМУЛЯЦИЯ №2: ЗАПУСК ===

--> Шаг 1: Запись параметров в блоки Engee...
--> Шаг 2: Запуск расчета модели 'DCV_HCD'...
🚀 Расчет успешно завершен!
--> Шаг 3: Экспорт результатов и прореживание точек...

Компоновка графиков (Режим: Сравнение двух комплектов)...
Out[0]:
Plots.PlotlyJSBackend()

Заключение

Как видно из графиков, применение ассиметричных канавок дало положительный результат при проектировании золотника распределителя - при подаче сигнала регулирования от 0 до 30 бар исчезла мертвая зона, распределитель регулирует поток подачи на гидроцилиндр во всем диапазоне расходов от 0 до 1000 литров в минуту.

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

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