Имитационное моделирование на базе AnyLogic

Агентное моделирование в AnyLogic. Модель цепочки поставок

Лекция посвящена практическому построению агентной имитационной модели простой цепочки поставок в среде AnyLogic. Рассматривается полный цикл: от импорта исходных данных о расположении дистрибьюторов, ритейлеров и количестве грузовиков из Excel-файла до реализации логики заказов и движения. Изложение выстроено последовательно: создание популяций агентов на основе таблиц, их привязка к GIS карте, разработка диаграммы состояний (стейтчарта) для грузовиков, написание Java кода в событии ритейлера для генерации заказов и выбора ближайшего свободного грузовика с фильтрацией по состоянию. Демонстрируется интеграция внешних данных, агентов, ГИС и программной логики.

Основные мысли

В результате изучения лекции слушатель будет способен:
1. Создавать популяции агентов на основе таблиц, импортированных из Excel-файла.
2. Настраивать размещение агентов на GIS карте с привязкой к полю местоположения.
3. Конструировать стейтчарты с переходами по сообщению и по прибытию для управления поведением агентов.
4. Применять встроенные ГИС методы (moveTo(), getNearestAgent()) для движения агентов по карте.
5. Программировать фильтрацию агентов по состоянию с использованием Java-лямбда-выражений.
6. Создавать события с заданной интенсивностью и писать Java код для генерации новых объектов-заказов.
7. Использовать дискретное равномерное распределение для моделирования случайного целого количества товара в заказе.
8. Организовывать обмен сообщениями между агентами для запуска переходов в стейтчарте.
9. Импортировать таблицы Excel с настройкой обновления данных при запуске модели.
10. Анализировать логику работы модели, наблюдая за перемещением агентов на карте.
Показывать лекцию целиком
Краткое изложение

Для лекции используйте файл  data.xlsx.

Постановка задачи

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

Исходные данные

Характеристики — города расположения дистрибьюторов и ритейлеров, а также начальное количество грузовиков у каждого дистрибьютора — хранятся в Excel-файле. Структура файла:
• Лист Distributors: города (Марсель, Париж, Бордо, Лион, Нант) и количество грузовиков.
• Лист Retailers: список городов ритейлеров (Тулуза, Лилль, Ницца, Брест, Страсбург и др.).

Модель будет читать эти данные для создания агентов.

Создание модели и GIS-карты

Создаётся новая модель с именем Supply Chain. Единицы модельного времени — часы. На главное окно презентации добавляется GIS карта (из палитры «Пути»). Карта растягивается на всё окно, так как основное содержание — расположение объектов на карте.

Импорт данных из Excel

В палитре «Базы данных» используется элемент «База данных». Через контекстное меню выполняется импорт таблиц, указывается Excel-файл. В настройках можно установить флажок «Обновлять данные при запуске модели», чтобы при каждом запуске считывались актуальные значения. Для географических названий эта опция важна для корректного парсинга, поэтому флажок устанавливается для обеих таблиц.

После импорта в разделе «Базы данных» появляются две таблицы: Distributors и Retailers, содержимое которых соответствует листам Excel.

Создание агентов-дистрибьюторов

1. В разделе «Агент» выбирается элемент Популяция агентов и помещается на рабочую область. Задаётся имя Distributor.
2. В свойствах популяции выбирается «Импортировать таблицу базы данных», указывается таблица Distributors. Для каждой записи создаётся один агент.
3. Внешний вид агента задаётся в 2D (для снижения нагрузки) — иконка склада.
4. Созданный агент автоматически получает параметры, соответствующие столбцам таблицы: location (город) и trucks (количество грузовиков).
5. Для размещения агентов на карте используется свойство Начальное местоположение. Выбирается размещение по первому результату поиска на карте (First GIS search result), а в поле адреса указывается self.location. Это означает, что для каждого агента его позиция определяется значением параметра location.
Аналогичным образом создаётся популяция ритейлеров (Retailer) на основе таблицы Retailers. В качестве иконки выбирается магазин. Начальное местоположение также задаётся через self.location.

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

Создание грузовиков (агентов Truck)

Грузовики не создаются как простая популяция «один агент на запись», потому что их количество для каждого дистрибьютора различно и указано в столбце trucks таблицы Distributors.

1. Создаётся новый тип агента Truck. Иконка — грузовик, размер уменьшается для наглядности.
2. При создании популяции выбирается опция «Я добавлю агентов позже» (популяция остаётся пустой).
3. В свойствах популяции включается загрузка из базы данных, указывается таблица Distributors.
4. Выбирается вариант «Количество агентов задано в столбце» и указывается столбец trucks. Местоположение каждого сгенерированного грузовика определяется столбцом location.

Таким образом, для дистрибьютора в Париже с 4 грузовиками будет создано 4 агента грузовика в точке Парижа. Для проверки модель запускается — грузовики отображаются поверх иконок дистрибьюторов.

Логика движения грузовика: стейтчарт

Для описания поведения грузовика создаётся стейтчарт (диаграмма состояний) внутри агента Truck. Используются три состояния и переходы между ними.
Состояния:
• atDistributor — грузовик находится у какого либо дистрибьютора.
• movingToRetailer — движение к ритейлеру для выполнения заказа.
• movingToDistributor — движение к ближайшему дистрибьютору после доставки.
Переходы:
1. Из atDistributor в movingToRetailer. Тип перехода — по получению сообщения order. Действие: moveTo(msg.retailer). Грузовик получает заказ и направляется к ритейлеру, указанному в этом заказе.
2. Из movingToRetailer в movingToDistributor. Тип перехода — по прибытию агента. Действие: moveTo(getNearestAgent(main.distributors)). Доехав до ритейлера, грузовик сразу начинает движение к ближайшему дистрибьютору.
3. Из movingToDistributor в atDistributor. Тип перехода — по прибытию агента. Действий нет: грузовик просто меняет состояние на ожидание у дистрибьютора.

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

Агенту Truck добавляется переменная order типа Order. Она хранит текущий заказ, который выполняет грузовик.

Агент заказа (Order) и генерация заказов

Заказ также является агентом (без визуализации). Создаётся тип агента Order с параметрами:
amount — количество товара (целое число),
retailer — ритейлер, сделавший заказ (тип Retailer).

Генерация заказов реализуется внутри агента Retailer с помощью события (Event). Событие срабатывает с заданной интенсивностью — 5 заказов в день (экспоненциальное распределение интервалов). В действии события пишется Java-код:
java
Order order = new Order(uniform_discr(5, 20), this);
Truck truck = getNearestAgent(
main.trucks.stream()
.filter(t -> t.inState(atDistributor))
.collect(Collectors.toList())
);
if (truck != null) {
truck.order = order;
send(order, truck);
}

Пояснения к коду:
• Создаётся новый заказ order со случайным целым количеством от 5 до 20 (равномерное дискретное распределение) и указанием текущего ритейлера (this).
• Ищется ближайший свободный грузовик. Все грузовики модели хранятся в main.trucks. Их список фильтруется: оставляются только те, которые находятся в состоянии atDistributor. Это гарантирует, что не будет выбран грузовик, уже выполняющий другой заказ.
• Если такой грузовик найден (truck != null), его переменной order присваивается созданный заказ, и ему отправляется сообщение order. Получив сообщение, грузовик согласно стейтчарту начинает движение к ритейлеру.

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

Запуск и наблюдение

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

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

Дальнейшие темы

После построения базовой цепочки поставок будут изучены основы Java для AnyLogic, что позволит глубже понимать написанный код. Затем планируется построение модели обслуживания ветряных турбин, где будет рассмотрен эксперимент оптимизации. Далее — пешеходное моделирование на примере упрощённой модели аэропорта для анализа пассажиропотоков и оптимизации логистики.

Краткие итоги

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

Технически это реализовано через взаимодействие трёх типов агентов. Ритейлеры генерируют заказы с помощью события с заданной интенсивностью и случайным объёмом. При этом поиск исполнителя включает фильтрацию всей популяции грузовиков по состоянию atDistributor. Такой подход предотвращает назначение нового заказа грузовику, который уже находится в рейсе или на возврате. Сама логика передвижения инкапсулирована в стейтчарте, где переходы инициируются сообщениями либо фактом прибытия в точку назначения. Это позволяет чётко разделить этапы цикла обслуживания и делает поведение каждого грузовика независимым, но скоординированным через общие данные.

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

Кроме того, работа с GIS-картой и встроенными функциями moveTo() и getNearestAgent() показывает, как с минимальными усилиями получить пространственно-реалистичную картину без ручного задания маршрутов. Интеграция Java-кода в события даёт полный контроль над бизнес-логикой: здесь и создание объектов, и обращение к окружению через main, и потоковая фильтрация коллекций. Таким образом, модель не только решает конкретную учебную задачу, но и формирует набор компетенций, необходимых для самостоятельной разработки агентных имитаций с географической привязкой и динамическим распределением ресурсов.
Постановка задачи

Создаётся агентная модель цепочки поставок с дистрибьюторами, ритейлерами и грузовиками. Грузовики не привязаны к конкретному дистрибьютору: после доставки товара ритейлеру они возвращаются к ближайшему дистрибьютору. Данные о расположении и начальном количестве грузовиков берутся из Excel-файла.

Импорт данных и создание агентов

Excel-файл содержит листы Distributors (город, количество грузовиков) и Retailers (город). В модели создаётся GIS карта на всё окно презентации. Через элемент «База данных» импортируются таблицы, для обеих включается обновление данных при запуске.

Создаётся популяция дистрибьюторов (агент Distributor): импорт из таблицы Distributors, один агент на запись. Агент получает параметры location и trucks. Начальное местоположение: self.location — позиция на карте по названию города. Иконка — склад.

Аналогично создаются ритейлеры (агент Retailer) из таблицы Retailers с иконкой магазина.

Грузовики (агент Truck) создаются иначе: количество задано столбцом trucks в таблице Distributors. При создании популяции выбирается опция «Количество агентов задано в столбце», указывается столбец trucks, а для местоположения — location. Иконка — грузовик. Грузовики появляются на карте рядом с соответствующими дистрибьюторами.

Логика движения грузовика

Внутри агента Truck строится стейтчарт с состояниями:
• atDistributor (у дистрибьютора),
• movingToRetailer (движение к ритейлеру),
• movingToDistributor (движение к ближайшему дистрибьютору).

Переходы:
1. atDistributor → movingToRetailer — по получении сообщения order. Действие: moveTo(msg.retailer).
2. movingToRetailer → movingToDistributor — по прибытии агента. Действие: moveTo(getNearestAgent(main.distributors)).
3. movingToDistributor → atDistributor — по прибытии агента (без действий).

Грузовику добавляется переменная order типа Order.

Агент заказа и генерация заказов

Тип агента Order (без визуализации) содержит параметры:
amount (целое),
retailer (тип Retailer).

В агенте Retailer создаётся событие orderProduce с интенсивностью 5 заказов в день. В действии пишется код:
java
Order order = new Order(uniform_discr(5, 20), this);
Truck truck = getNearestAgent(
main.trucks.stream()
.filter(t -> t.inState(atDistributor))
.collect(Collectors.toList())
);
if (truck != null) {
truck.order = order;
send(order, truck);
}

Пояснение: равномерное дискретное распределение даёт целое количество товара от 5 до 20. Из всех грузовиков main.trucks отбираются только те, что находятся в состоянии atDistributor. Ближайший из них получает заказ, и ему отправляется сообщение, запускающее движение к ритейлеру. Если свободных грузовиков нет, заказ теряется.

Запуск и результаты

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

Дальнейшие шаги

Далее будут изучены основы Java для AnyLogic, затем построение модели ветряных турбин с оптимизацией и пешеходная модель аэропорта.

Выводы

1. Модель состоит из трёх типов агентов: дистрибьюторы, ритейлеры и грузовики, размещаемых на GIS карте.
2. Исходные данные импортируются из Excel файла; каждая запись таблицы может порождать одного или несколько агентов.
3. Популяции дистрибьюторов и ритейлеров создаются с опцией «один агент на запись», а количество грузовиков задаётся отдельным столбцом.
4. Позиции агентов на карте привязываются к значению параметра location через self.location в свойстве начального местоположения.
5. Логика движения грузовика реализована стейтчартом с состояниями: у дистрибьютора, движение к ритейлеру, движение к ближайшему дистрибьютору.
6. Переход к ритейлеру происходит при получении сообщения order, переходы обратно — автоматически по прибытии агента.
7. Метод moveTo() обеспечивает перемещение по дорогам карты, а getNearestAgent() — выбор ближайшего дистрибьютора.
8. Заказы генерируются событием внутри ритейлера с интенсивностью 5 заказов в день и случайным количеством товара от 5 до 20.
9. Для выбора исполнителя используется фильтрация всех грузовиков по состоянию atDistributor, что исключает занятых агентов.
10. Код фильтрации применяет Java Stream API и лямбда выражение, проверяющее состояние через inState().
11. При отсутствии свободных грузовиков заказ в базовой версии теряется — требуется дополнительная логика очереди.
12. Модель демонстрирует динамическое распределение транспорта без жёсткой привязки к дистрибьюторам и может масштабироваться для сложных цепей поставок.

Вопросы для самопроверки

1. Как в AnyLogic создать популяцию агентов на основе данных из Excel файла?
2. Почему для корректного отображения географических названий из Excel на карте необходимо устанавливать флажок «Обновлять данные при запуске модели»?
3. Каким образом задаётся количество создаваемых грузовиков, если оно различается для разных дистрибьюторов?
4. Для чего в свойствах начального местоположения агента используется выражение self.location?
5. Какие три состояния образуют стейтчарт грузовика и какова роль каждого из них?
6. Чем различаются переходы по получению сообщения и по прибытию агента в стейтчарте?
7. Какой метод отвечает за движение агента к заданному объекту на GIS карте?
8. Почему в коде генерации заказа применяется uniform_discr, а не uniform?
9. Зачем при поиске свободного грузовика вводится фильтрация t -> t.inState(atDistributor)?
10. Что произойдёт с заказом, если на момент генерации все грузовики заняты?
11. Каким образом грузовик узнаёт, к какому ритейлеру везти заказ?
12. Как модель обеспечивает, что после разгрузки грузовик направится именно к ближайшему, а не к исходному дистрибьютору?
Вернуться к учебному плану