Перевод статьи Mania Beetz про фреймворк для обучения роботов в виртуальных средах

This commit is contained in:
Igor Brylyov 2021-06-12 15:58:12 +03:00 committed by openfablab
parent 5c732dd19c
commit a3c4250675
14 changed files with 958 additions and 8 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View file

@ -0,0 +1,263 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib tk\n",
"import time, math\n",
"from random import random\n",
"import numpy as np\n",
"from IPython import display\n",
"from PIL import Image\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.animation as animation\n",
"writer = animation.writers['ffmpeg'](fps=30, metadata=dict(artist='Me'), bitrate=1800)\n",
"\n",
"# Коды элементов\n",
"\n",
"BAD = 0\n",
"GOOD = 1\n",
"BASIC_FLOOR = 2\n",
"FENCE_PORT = 3\n",
"CORNER_PORT = 4\n",
"STORAGE_PORT = 5\n",
"GATE_LEAF_PORT = 6\n",
"GATE_PORT = 7\n",
"TRACK = 8\n",
"MACHINE_PORT = 9\n",
"WORK_PORT = 10\n",
"MM_PORT = 11\n",
"POWER_PORT = 12\n",
"UTIL_PORT = 13\n",
"PLANNER_PORT = 14\n",
"MINE_PORT = 15\n",
"\n",
"#Словарь, где хранятся ссылки на клетки по координатам\n",
"\n",
"cells={}\n",
"\n",
"class FloorElement():\n",
" def __init__(self,parent,x,y,element_type):\n",
" self.x=x\n",
" self.y=y\n",
" self.element_type=element_type\n",
" self.parent=parent\n",
" self.parent.floor[(self.x,self.y)]=self\n",
"\n",
"class Cell():\n",
" def __init__(self,x,y):\n",
" self.x=x\n",
" self.y=y\n",
" cells[(self.x,self.y)] = self\n",
" self.floor={}\n",
" self.content={}\n",
" self.generate()\n",
" def destroy(self):\n",
" del cells[(self.x,self.y)] \n",
" def generate(self):\n",
"\n",
" # Настройка параметров клетки\n",
" self.max_machine_size=20 # Максимальное число блоков в машине \n",
" self.work_machine_number_max=30 # Максимальное число рабочих машин\n",
" self.planners_max=3 # Максимальное число планировщиков\n",
" self.utils_max=3 # Максимальное число утилизаторов\n",
" self.powers_max=4 # Максимальное число электростанций\n",
" self.mines_max=4 # Максимальное число шахт\n",
" self.storage_number_max_without_mm=500 # Максимальная емкость склада\n",
" self.mm_max=50 # Максимальное число мобильных манипуляторов (ММ)\n",
" \n",
" self.work_machine_number_rep=12 # Число рабочих машин, достаточное для репликации\n",
" self.storage_number_rep_without_mm=200 # Емкость склада, достаточная для репликации\n",
" self.work_machine_number_grow=6 # Число рабочих машин, достаточное для автономного роста\n",
" self.storage_number_grow_without_mm=100 # Емкость склада, достаточная для автономного роста\n",
" self.storage_free=15 # Число складских ячеек в резерве\n",
" self.machine_free=2 # Число машиномест в резерве \n",
"\n",
" # Найти общее количество машин (включая служебные) и общую емкость склада (включая места для ММ)\n",
" \n",
" self.machine_number_max=self.work_machine_number_max+self.planners_max+self.utils_max+self.powers_max+self.mines_max\n",
" self.storage_number_max=self.mm_max+self.storage_number_max_without_mm\n",
"\n",
" # Подобрать прямоугольную форму машины, по возможности близкую к квадратной, содержащую количество\n",
" # блоков не меньше заданного и хорошо стыкуемую с соседями, дорожками и складской зоной\n",
" # Определить ширину и высоту машины, фактическое количество в ней блоков и занимаемую ей площадь\n",
" \n",
" self.machine_height=round(math.sqrt(self.max_machine_size),0)//3*3+1\n",
" self.machine_width=self.max_machine_size//self.machine_height\n",
" if self.machine_width*self.machine_height<self.max_machine_size:\n",
" self.machine_width=self.machine_width+1\n",
" self.fact_machine_size=self.machine_height*self.machine_width\n",
" self.fact_machine_area=(self.machine_height+5)*(self.machine_width+5)\n",
"\n",
" # Подобрать прямоугольную форму рабочей зоны, по возможности близкую к квадратной, содержащую \n",
" # количество машин не меньше заданного и хорошо стыкуемую с дорожками и складскими зонами\n",
" # Определить ширину и высоту рабочей зоны, фактическое количество машин и занимаемую ей площадь\n",
" \n",
" self.machine_coloumns=round(math.sqrt(self.machine_number_max),0)\n",
" self.machine_rows=self.machine_coloumns\n",
" if self.machine_coloumns*self.machine_rows<self.machine_number_max:\n",
" self.machine_rows+=1\n",
" if self.machine_width%2==0:\n",
" if self.machine_coloumns%2==1:\n",
" if self.machine_coloumns%2==1:\n",
" self.machine_coloumns+=1\n",
" else:\n",
" self.machine_coloumns,self.machine_rows=self.machine_rows,self.machine_coloumns\n",
" self.siderows=self.machine_rows*(1+(self.machine_height-1)/6)\n",
" if self.siderows!=int(self.siderows):\n",
" self.machine_rows+=1\n",
" self.siderows=self.machine_rows*(1+(self.machine_height-1)/6) \n",
" self.fact_machine_number=self.machine_coloumns*self.machine_rows\n",
"\n",
" # Рассчитать размер территории, занимаемой клеткой\n",
" \n",
" self.fact_machine_block_area=self.fact_machine_number*self.fact_machine_area\n",
" self.storage_desired_area=self.storage_number_max*5.5\n",
" self.total_desired_area=self.storage_desired_area+self.fact_machine_block_area\n",
" self.presize=(int(math.sqrt(self.total_desired_area)))\n",
" self.presize=(1+self.presize//6)*6\n",
" if (self.presize-(self.siderows*6))%12!=0:\n",
" self.presize+=6\n",
" self.realsize=self.presize+5\n",
"\n",
" # Создать территорию\n",
" \n",
" self.Terrain=np.ones((self.realsize,self.realsize))\n",
" #Terrain=np.sign(np.array(Image.open(\"intest.png\"))[:,:,0])\n",
"\n",
" # Создать карту, заполненную элементами пола\n",
" \n",
" self.Map=self.Terrain*BASIC_FLOOR\n",
"\n",
" # Создать забор\n",
" \n",
" self.Map[0]=self.Map[-1]=self.Map[:,0]=self.Map[:,-1]=FENCE_PORT\n",
"\n",
" # Создать углы\n",
" \n",
" self.Map[0,0]=self.Map[0,-1]=self.Map[-1,0]=self.Map[-1,-1]=CORNER_PORT\n",
"\n",
" # Создать трассу по периметру клетки\n",
" \n",
" self.Map[2:-2,2:-2]=TRACK\n",
" self.Map[3:-3,3:-3]=BASIC_FLOOR\n",
"\n",
" # Создать ворота, порты ворот, складские места\n",
" \n",
" for y in range(4,self.presize+2,6):\n",
" self.Map[y-2,2:-3]=TRACK\n",
" self.Map[y,0]=self.Map[y+2,0]=self.Map[y,-1]=self.Map[y+2,-1]=GATE_LEAF_PORT\n",
" self.Map[0,y]=self.Map[0,y+2]=self.Map[-1,y]=self.Map[-1,y+2]=GATE_LEAF_PORT\n",
" self.Map[y+1,0]=self.Map[y+1,-1]=self.Map[0,y+1]=self.Map[-1,y+1]=GATE_PORT\n",
" self.Map[y+1,1]=self.Map[y+1,-2]=self.Map[1,y+1]=self.Map[-2,y+1]=TRACK\n",
" for x in range(4,self.presize+2,2):\n",
" self.Map[y,x]=self.Map[y+2,x]=STORAGE_PORT\n",
" \n",
" # Создать машинную зону \n",
" \n",
" self.center=int(self.realsize/2)\n",
" self.mxstart=int(self.center-(self.machine_coloumns*(self.machine_width+5))/2)\n",
" self.mystart=int(self.center-(self.siderows*3))\n",
" self.mxstop=int(self.center+(self.machine_coloumns*(self.machine_width+5))/2)\n",
" if self.mxstart%2!=0:\n",
" self.mxstart-=1\n",
" self.mxstop-=1\n",
" self.mystop=int(self.center+(self.siderows*3))\n",
" \n",
" self.Map[self.mystart+1:self.mystop-1,self.mxstart:self.mxstop]=BASIC_FLOOR\n",
" self.Map[2:-2,self.mxstart]=self.Map[2:-2,self.mxstop]=TRACK\n",
"\n",
" special_machines_list=([PLANNER_PORT]*self.planners_max+[POWER_PORT]*self.powers_max+\n",
" [UTIL_PORT]*self.utils_max+[MINE_PORT]*self.mines_max)\n",
" \n",
" for y in range(self.mystart,self.mystop,int(self.machine_height)+5):\n",
" self.Map[y,self.mxstart:self.mxstop]=TRACK\n",
" for x in range(self.mxstart,self.mxstop,int(self.machine_width)+5):\n",
" kind=MACHINE_PORT\n",
" if len(special_machines_list)>0:\n",
" kind = special_machines_list.pop(0)\n",
" self.Map[3+y:3+y+int(self.machine_height),3+x:3+x+int(self.machine_width)]=kind\n",
" for sy in range(3+y,3+y+int(self.machine_height),2):\n",
" self.Map[sy,2+x]=WORK_PORT\n",
" for sx in range(4+x,3+x+int(self.machine_width),2):\n",
" self.Map[2+y,sx]=WORK_PORT\n",
" zystart=3+y\n",
" if self.Map[2+y,2+x+int(self.machine_width)]==WORK_PORT:\n",
" zystart+=1\n",
" for zy in range(zystart,3+y+int(self.machine_height),2):\n",
" self.Map[zy,3+x+int(self.machine_width)]=WORK_PORT\n",
" zxstart=3+x\n",
" if self.Map[2+y+int(self.machine_height),2+x]==WORK_PORT:\n",
" zxstart+=1\n",
" for zx in range(zxstart,3+x+int(self.machine_width),2):\n",
" self.Map[3+y+int(self.machine_height),zx]=WORK_PORT\n",
"\n",
" # Создать дополнительные вертикальные трассы возле машинной зоны \n",
" \n",
" for x in range(self.mxstart,self.mxstop,int(self.machine_width)+5):\n",
" if self.Map[4,x] == STORAGE_PORT:\n",
" self.Map[2:-3,x]=TRACK\n",
" else:\n",
" self.Map[self.mystart:self.mystop,x]=TRACK\n",
"\n",
" # Создать порты мобильных манипуляторов\n",
" \n",
" mm_counter=self.mm_max\n",
" for y in range(self.realsize):\n",
" for x in range(self.realsize):\n",
" if mm_counter>0:\n",
" if self.Map[y,x]==STORAGE_PORT:\n",
" self.Map[y,x]=MM_PORT\n",
" mm_counter-=1\n",
" if self.Map[y-2,x]==TRACK:\n",
" self.Map[y-1,x]=TRACK\n",
" elif self.Map[y+2,x]==TRACK:\n",
" self.Map[y+1,x]=TRACK\n",
"\n",
" # Зарегистрировать элементы карты\n",
" \n",
" for y in range(self.realsize):\n",
" for x in range(self.realsize):\n",
" if self.Map[y,x]>1: \n",
" FloorElement(self,x,y,self.Map[y,x])\n",
" \n",
"# Создать клетку \n",
" \n",
"c=Cell(20,20)\n",
"\n",
"# Показать клетку\n",
"\n",
"fig=plt.figure(figsize=(7, 5))\n",
"ax=fig.add_subplot(1, 1, 1)\n",
"im1=plt.imshow(c.Map, cmap=plt.get_cmap('bone'))\n",
"ax.set_title('Map MS:'+str(c.max_machine_size)+' MN:'+str(c.work_machine_number_max)+\n",
" ' SN:'+str(c.storage_number_max_without_mm)+' MM:'+str(c.mm_max)) \n",
"plt.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.5"
}
},
"nbformat": 4,
"nbformat_minor": 1
}

View file

@ -0,0 +1,107 @@
---
id: generation
title: Генеративный дизайн машинной клетки
---
Клетка - стандартный фрагмент автоматической производственной системы (АПС), способный к автономной работе, развитию и репликации. Она состоит из оболочки, рабочей, складской и транспортной зон.
Идеальная карта клетки Map[] в нашей модели представляет собой квадратный массив чисел, указывающих, что именно и где должно в ней находиться. Таким образом, карта позволяет определить маршруты перемещения мобильных манипуляторов (ММ), места для складирования объектов, места для монтажа машин, места для обмена объектами с окружающей средой, порядок постройки и развития клетки. Сопоставление фактического состояния элемента территории с элементом карты позволяет выявлять ошибки и аварии.
Алгоритм генеративного дизайна позволяет автоматически спроектировать карту для любого набора заданных в начале программы параметров клетки, учитывая размер и количество машин, площади складов, порты для заряда мобильных манипуляторов и т.п.
(см. рабочий python-код в файле generation.ipynb - лучше открывать с помощью Jupyter Notebook)
## Описание элементов карты и их числовые коды:
* 0. **территория, непригодная к использованию** (пока не реализовано, предполагается что на сложной местности, содержащей непроходимые участки тоже можно построить клетку с ограниченным функционалом, огибающую эти участки)
* 1. **территория, пригодная к использованию** (на ней можно строить клетку путем установки готовых блоков с помощью ММ)
* 2. **элемент пола базовый** имеет ножки адаптивной высоты и зацепы для сборки в площадки произвольного размера и формы. ММ укладывают элементы пола прямо на территорию, пригодную к использованию, регулируют ножки по высоте и соединяют с соседними. Между элементами пола могут передаваться электричество и сигналы управления, а при необходимости - и другие коммуникации, не загромождая поверхность над ними и обеспечивая избыточную связность клетки (при повреждении или отсутствии значительного числа элементов пола все равно обеспечивается надежная передача энергии и сигналов управления). При застройке территории ММ может ездить по элементам пола и при этом устанавливать новый элемент пола рядом с собой. Затем продвигатсья дальше. Это позволяет при желании обходиться вообще без аккумуляторов в ММ, питая их все вреям через элементы пола, либо обходиться вообще без ММ, придав элементам пола возможность перемещения объектов.
* 3. **порт для монтажа прямого элемента забора** Забор отделяет клетку от внешней среды, ничего материального не впуская и не выпуская. Граничит с заборами других клеток и потому может участвовать в передаче данных между клетками. Устанавливается с помощью ММ на элемент пола, имеет два разъема на противоположных сторонах для соединения с себе подобными.
* 4. **порт для монтажа углового элемента забора**. Угловой элемент забора граничит сразу с несколькими углами заборов соседних клеток и может играть особую роль в коммуникации. Также может содержать маяки для навигации ММ. Устанавливается на элемент пола, имеет два разъема на смежных сторонах для соединения с элементами забора. Может быть при желании унифицирован с прямым элементом забора.
* 5. **складское место** - элемент пола, предназначенный для установки на него коробок с объектами. Может отличаться от базовых элементов пола, а может и не отличаться. Расстановка складских мест на карте производится так, чтобы коробки на них не мешали друг другу, и к каждому месту был обеспечен доступ ММ хотя бы с одной стороны.
* 6. **порт створки ворот** - элемент забора, способный частично открываться для передвижения ММ между клетками. При этом ММ (возможно, с грузом) проходит через специальный элемент - порт ворот, а сами ворота, находящиеся по бокам от него, приоткрываются. Ничего и никого другого ворота не пропускают. Таким образом, чтобы перейти из одной клетки в другую (например, вновь строящуюся) ММ сначала проходит "свои", потом "чужие" ворота.
* 7. **порт ворот** - элемент забора, спообный пропускать через себя коробки и ММ. Мы предполагаем что ММ может быть шире чем порт ворот (который сам должен быть перевозимым при помощи ММ), поэтому окружаем его с двух сторон воротами. Коробка же несколько уже чем порт ворот, поэтому может проходить через него в обоих направлениях, если он будет открыт. По умолчанию же порт ворот закрыт и открывается лишь когда ММ передает через него груз в соседнюю клетку. Вариант использования 1: ММ с грузом подъезжает вплотную к порту ворот своей клетки и сквозь него кладет коробку в порт ворот чужой клетки, откуда его забирает ММ той клетки. Вариант использования 2: ММ с грузом кладет коробку в порт ворот своей клетки, а ММ другой клетки забирает его оттуда.
* 8. **трасса** - базовый элемент пола, учитываемый при построении маршрутов и используемый для перемещения ММ во время работы клетки. Расстановка трасс на карте производится так чтобы обеспечить доступ ММ во все необходимые места и избегать заторов при работе многих ММ.
* 9. **машинный порт** - элемент пола на которых устанавливается машинный блок (автоматизированное рабочее место (АРМ), станок или любой другой компонент такого рода). Машинные порты собраны в группы, образующие места для монтажа машины.
* 10. **рабочий порт** - элемент пола, на который устанавливается коробка с объектом для его использования машиной, находящейся рядом. Машины изначально проектируются так чтобы брать и помещать объекты в коробки находящиеся здесь. Вокруг каждой машины создается как можно больше рабочих портов так чтобы они не мешали друг другу и могли независимо обслуживаться ММ.
* 11. **порт мобильного манипулятора** - элемент пола, предназначенный для заряда и парковки мобильного манипулятора
* 12. **порт электростанции** - машинный порт, зарезервированный для монтажа блоков специфических машин - электростанций
* 13. **порт утилизатора** - машинный порт, зарезервированный для монтажа блоков специфических машин - утилизаторов отходов и брака
* 14. **порт планировщика** - машинный порт, зарезервированный для монтажа блоков специфических машин - планировщиков
* 15. **порт шахты** - машинный порт, зарезервированный для монтажа блоков специфических машин - шахт, автоматически добывающих полезные ископаемые из недр земли. Может отличаться возможностью доступа через него вниз. При этом должна сохраняться возможность проезда по нему ММ.
Мобильный манипулятор может брать, перемещать и устанавливать любые из этих блоков, а также другие мобильные маниепуляторы и коробки. Обрабатываемые АПС бъекты могут находиться в боксах и путешествовать с ними, а в рабочих портах машин удаляться из коробок и помещаться в них. Также в рабочих портах могнут появляться (как результат работы машин) объекты, не помещающиеся в коробки: сами коробки, мобильные манипуляторы, все вышеперечисленные пронумерованные блоки.
## Алгоритм генеративного дизайна
В качестве исходных данных используются:
* максимальное число блоков в машине
* число рабочих машин в клетке (для роста, для репликации, максимальное)
* максимальное число планировщиков, утилизаторов, электростанций, шахт
* емкость склада (для роста, для репликации, максимальная)
* максимальное число мобильных манипуляторов
* число машиномест и складских мест оставляемое пустым для резерва
Далее выполняются следующие действия:
* Найти общее количество машин (включая служебные) и общую емкость склада (включая места для ММ)
* Подобрать прямоугольную форму машины, по возможности близкую к квадратной, содержащую количество блоков не меньше заданного и хорошо стыкуемую с соседями, дорожками и складской зоной. Определить ширину и высоту машины, фактическое количество в ней блоков и занимаемую ей площадь.
* Подобрать прямоугольную форму рабочей зоны, по возможности близкую к квадратной, содержащую количество машин не меньше заданного и хорошо стыкуемую с дорожками и складскими зонами. Определить ширину и высоту рабочей зоны, фактическое количество машин и занимаемую ей площадь.
* Рассчитать размер территории, занимаемой клеткой
* Создать карту как числовой массив (или импортировать из файла для учета непроходимых территорий)
* Заполнить ее базовыми элементами пола
* Указать порты для прямых и угловых элементов забора
* Задать трассу по периметру клетки
* Указать порты для монтажа ворот и створок ворот
* Раставить складские места
* Создать посередине клетки рабочую зону - машины, их рабочие порты, трассы между ними
* Создать дополнительные вертикальные трассы возле машинной зоны
* Создать порты мобильных манипуляторов
* Зарегистрировать элементы карты как экземпляры классов
Примеры работы алгоритма. Над картинками указаны задаваемые число блоков в машине, число машин, число складских мест и ММ.
![Artificial cells examples](Figure1.png)
Можно генерировать и очень большие клетки с сотнями тысяч и даже миллионами элементов. Инструменты видуализации matplotlib позволяют величивать фрагменты и "бродить" по клетке.
![Artificial cells examples](Figure2.png)
## Порядок фактической постройки клетки
Когда у нас есть общая карта клетки, можно, сопоставляя реальность с этой картой осмысленно диагностировать, ремонтировать и воспроизводить клетки.
Начинаем с анализа территории. Если есть непроходимые, непригодные участки, смотрим, достаточный ли запас места чтобы их обойти (вычеркиваем машины и складские позиции на этих территориях и смотрим, достаточно ли места для работ I и II очереди) или бракуем весь квадрат и ничего там не строим.
Задача I очереди строительства - установить все необходимое для самостоятельного развития новой клетки.
* Все элементы пола. Строительство начинается с одной или нескольких из сторон. Первыми укладываются элементы пола, граничащие с воротами материнской клетки. После этого ММ огут заехать на них и установить смежные с ними элементы пола и так далее.
* Забор с угловыми элементами и воротами
* По одному экземпляру служебных машин: электростанция, планировщик, утилизатор, шахта
* Рабочие машины I очереди
* Коробки с объектами I очереди
После этого клетка может развиваться сама. Начинаются работы II очереди (самостоятельно или при помощи соседей). Их цель - обеспечить клетку всем необходимым для самостоятельной репликации.
* Рабочие машины II очереди
* Коробки с объектами II очереди
После этого клетка способна самостоятельно производить себе подобные. Дальнейшее ее поведение может определяться, например, следующим алгоритмом:
* Если рядом есть строящиеся клетки, помогаем им в работах I и II очереди (получаем доступ к плану производства и подписываемся на выполнение его части, производим и отправляем)
* Иначе, если рядом есть пустое место, создаем и строим там новую клетку (выпускаем стандартный план ее производства и выполняем его, сами или при помощи соседей).
* Иначе, если со стороны одного из соседей приходит высокий (выше заданного предела) уровень сигнала об активном строительстве новых клеток ("гормона роста"), производим и передаем все производимое туда. Так как строящиеся клетки могут быть не близко, в этих условиях производим и отправляем только наиболее стандартные и массовые компоненты вроде элементов пола и ММ.
* Иначе ведем работы III очереди - нарабатываем элементы пола, машины и коробки с объектами, оставляя свободным лишь установленное резервное количество мест складских и машинных.
## Задачи на будущее
* умная регистрация машинных блоков, машиномест со связанными портами и объектов
* перемещение ММ, перевозка коробок
* правила дорожного движения для ММ и тесты на случайные перемещения коробок толпой мм
* репликация как монтаж готовых элементов пола и блоков при помощи ММ
* демонтаж и замена случайно поврежденных блоков
* межклеточное взаимодействие
* соответсвие объектов коробкам
* подробное описание работы машин - что из чего они делают
* генерация технологических графов и их исполнение клеткой
* перейти от абстрактных к конкретным машинам и объектам

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View file

@ -0,0 +1,368 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2 2\n",
"2 2\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Traceback (most recent call last):\n",
" File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\cbook\\__init__.py\", line 388, in process\n",
" proxy(*args, **kwargs)\n",
" File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\cbook\\__init__.py\", line 228, in __call__\n",
" return mtd(*args, **kwargs)\n",
" File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\animation.py\", line 1308, in _handle_resize\n",
" self._init_draw()\n",
" File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\animation.py\", line 1750, in _init_draw\n",
" self._draw_frame(next(self.new_frame_seq()))\n",
" File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\animation.py\", line 1772, in _draw_frame\n",
" self._drawn_artists = self._func(framedata, *self._args)\n",
" File \"<ipython-input-3-63ec1324f14b>\", line 268, in updatefig\n",
" im1=plt.imshow(Zm, cmap=plt.get_cmap('magma').with_extremes(bad='#101020'))\n",
"AttributeError: 'ListedColormap' object has no attribute 'with_extremes'\n",
"Traceback (most recent call last):\n",
" File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\cbook\\__init__.py\", line 388, in process\n",
" proxy(*args, **kwargs)\n",
" File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\cbook\\__init__.py\", line 228, in __call__\n",
" return mtd(*args, **kwargs)\n",
" File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\animation.py\", line 1308, in _handle_resize\n",
" self._init_draw()\n",
" File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\animation.py\", line 1750, in _init_draw\n",
" self._draw_frame(next(self.new_frame_seq()))\n",
" File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\animation.py\", line 1772, in _draw_frame\n",
" self._drawn_artists = self._func(framedata, *self._args)\n",
" File \"<ipython-input-3-63ec1324f14b>\", line 268, in updatefig\n",
" im1=plt.imshow(Zm, cmap=plt.get_cmap('magma').with_extremes(bad='#101020'))\n",
"AttributeError: 'ListedColormap' object has no attribute 'with_extremes'\n"
]
}
],
"source": [
"%matplotlib tk\n",
"import time, math\n",
"from random import random\n",
"import numpy as np\n",
"from IPython import display\n",
"from IPython.display import clear_output\n",
"from PIL import Image\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.animation as animation\n",
"\n",
"# Генерация карт по картинке\n",
"\n",
"Terrain=np.sign(np.array(Image.open(\"test.png\"))[:,:,0])\n",
"ShowTerrain=Terrain*1.0\n",
"Flow=Store=TransportActivity=Conc=Terrain*0.0\n",
"Dir=Terrain*0\n",
"\n",
"# Генерация случайных препятствий\n",
"\n",
"#for i in range(100):\n",
"# Terrain[int(random()*Live.shape[0]-2),int(random()*Live.shape[1]-2)]=0\n",
"\n",
"# Настройка параметров клетки\n",
" \n",
"START_COMPLETENESS = 1\n",
"#GROW_COMPLETENESS = 7\n",
"REP_COMPLETENESS = 20\n",
"#MAX_COMPLETENESS = 100\n",
"\n",
"# Настройка параметров распространеняи сигнала об активном строительстве \n",
"\n",
"INCOMPLETENESS_SIGNAL_EMISSION_RATIO = 0.1\n",
"INCOMPLETENESS_SIGNAL_DIFFUSION_RATIO = 0.45\n",
"INCOMPLETENESS_SIGNAL_DECAY_RATIO = 0.9\n",
"INCOMPLETENESS_SIGNAL_THRESHOLD = 1\n",
"\n",
"# Настройка бобмбардировки \n",
" \n",
"BOMB_MIN=0\n",
"BOMB_START=60000\n",
"BOMB_MAX=1\n",
"BOMB_SIZE=2\n",
"b=BOMB_MIN\n",
"\n",
"# Выбор способа отображения цифровыми клавишами 0-5\n",
"\n",
"def on_press(event):\n",
" global DISPLAY\n",
" global ax\n",
" global plt\n",
" DISPLAY=int(event.key)\n",
" if DISPLAY==0:\n",
" ax.set_title('Terrain') \n",
" elif DISPLAY==1:\n",
" ax.set_title('Incompleteness Signal Concentration')\n",
" elif DISPLAY==2:\n",
" ax.set_title('Incompleteness Signal Direction')\n",
" elif DISPLAY==3:\n",
" ax.set_title('Transport Direction')\n",
" elif DISPLAY==4:\n",
" ax.set_title('Storage')\n",
" elif DISPLAY==5:\n",
" ax.set_title('Transport Activity')\n",
" plt.draw()\n",
"\n",
"# Настройка графического отображения\n",
"\n",
"fig=plt.figure()#(figsize=(7, 5))\n",
"ax=fig.add_subplot(1, 1, 1)\n",
"im1=plt.imshow(ShowTerrain, cmap=plt.get_cmap('bone'), animated=True)\n",
"fig.canvas.mpl_connect('key_press_event', on_press)\n",
"DISPLAY=0\n",
"ax.set_title('Terrain') \n",
"writer = animation.writers['ffmpeg'](fps=15, metadata=dict(artist='Me'), bitrate=1800)\n",
"framecounter=0\n",
"\n",
"# Словарь для хранения клеток по координатам\n",
"\n",
"cells={}\n",
" \n",
"class Cell():\n",
" def __init__(self,x,y):\n",
" self.x=x\n",
" self.y=y\n",
" self.productivity=1\n",
" self.transportivity=10\n",
" self.completeness=START_COMPLETENESS\n",
" self.conc=0\n",
" self.flow=0\n",
" self.storage=0\n",
" cells[(self.x,self.y)] = self\n",
" self.refresh()\n",
" def refresh(self):\n",
" if self.completeness<1:\n",
" self.destroy()\n",
" addconc=(REP_COMPLETENESS-self.completeness)*INCOMPLETENESS_SIGNAL_EMISSION_RATIO\n",
" if self.completeness>=REP_COMPLETENESS:\n",
" addconc=0\n",
" self.conc+=addconc\n",
" self.conc*=INCOMPLETENESS_SIGNAL_DECAY_RATIO\n",
" def destroy(self):\n",
" del cells[(self.x,self.y)]\n",
"\n",
"# Создаем первоначальную клетку\n",
" \n",
"a=Cell(20,20)\n",
"a.completeness=REP_COMPLETENESS\n",
"a.refresh()\n",
"\n",
"# Расчеты в каждом кадре\n",
"\n",
"def updatefig(*args):\n",
" global b,TransportActivity,ShowTerrain,Store,framecounter\n",
" \n",
" # Очистка карт для визуализации\n",
" \n",
" TransportActivity=Terrain*0.0\n",
" ShowTerrain=Terrain*1\n",
" Store=Terrain*0.0\n",
" Conc=Terrain*0.0\n",
" \n",
" clist=list(cells.values())\n",
" for c in clist: # Расчеты для каждой клетки\n",
" c.refresh()\n",
" \n",
" # Отображение различных параметров клетки \n",
" Store[c.x,c.y]=c.storage\n",
" ShowTerrain[c.x,c.y]=c.completeness\n",
" Conc[c.x,c.y]=c.conc\n",
" \n",
" if c.completeness>=REP_COMPLETENESS: # Если клетка зрелая, то:\n",
" c.storage+=c.productivity # Производство объектов на склад\n",
" \n",
" # Составляем список соседних участков с учетом границ карты\n",
" \n",
" neighbors=[]\n",
" if c.x>0:\n",
" neighbors.append((c.x-1,c.y))\n",
" if c.x<Terrain.shape[0]-1:\n",
" neighbors.append((c.x+1,c.y))\n",
" if c.y>0:\n",
" neighbors.append((c.x,c.y-1))\n",
" if c.y<Terrain.shape[1]-1:\n",
" neighbors.append((c.x,c.y+1)) \n",
"\n",
" candidatemax=0\n",
" concmax=0\n",
" fx=0\n",
" fy=0\n",
" cneed=None\n",
" \n",
" for (nx,ny) in neighbors: # Для каждого участка из списка соседей\n",
" if (nx,ny) in cells.keys(): # Если на нем есть клетка \n",
" \n",
" # Расчет градиента сигнала роста\n",
" \n",
" f=cells[(nx,ny)]\n",
" vconc=f.conc-c.conc\n",
" fx+=vconc*(nx-c.x)\n",
" fy+=vconc*(ny-c.y)\n",
" \n",
" # Выявление соседа с наибольшей концентрацией сигнала роста\n",
" \n",
" if f.conc>concmax:\n",
" cneed=f\n",
" needer=int((nx-c.x)+2*(ny-c.y)+3)\n",
" if needer==5:\n",
" needer=3\n",
" concmax=f.conc\n",
" \n",
" # Диффузия сигнала роста\n",
" \n",
" aconc=(c.conc+f.conc)/2\n",
" dconc=(aconc-c.conc)*INCOMPLETENESS_SIGNAL_DIFFUSION_RATIO \n",
" c.conc+=dconc\n",
" f.conc-=dconc\n",
"\n",
" # Выявление наиболее готовой недостроенной соседней клетки\n",
" \n",
" if f.completeness<REP_COMPLETENESS: \n",
" if candidatemax<f.completeness:\n",
" candidatemax=f.completeness\n",
" candidate=(nx,ny)\n",
" \n",
" # Если на участке нет клетки, проверяем можно ли там построить новую\n",
" # Постройка новых клеток имеет меньший приоритет, чем достройка начатых\n",
" \n",
" else: \n",
" if Terrain[nx,ny]>0:\n",
" if candidatemax<1:\n",
" candidate=(nx,ny)\n",
" candidatemax=1\n",
" \n",
" # Отображение на картах направления транспорта и градиента сигнала роста\n",
" \n",
" if concmax>0:\n",
" Dir[c.x,c.y]=needer\n",
" Flow[c.x,c.y]=math.atan2(fx,fy)\n",
" \n",
" \n",
" if candidatemax>0: # Если есть недостроенные или пустые участки\n",
" if candidatemax==1: # Если есть только пустые\n",
" cneed=Cell(candidate[0],candidate[1]) # Создать клетку\n",
" else: # Иначе\n",
" cneed=cells[(candidate[0],candidate[1])] # Помогаем строящейся клетке\n",
" \n",
" # Транспорт объектов в соседние клетки\n",
" \n",
" if cneed:\n",
" \n",
" # Установим объем грузоперевозок в зависимости от концентрации сигнала роста\n",
" # и ограничим его минимум и максимум\n",
" \n",
" kgr=(cneed.conc)/INCOMPLETENESS_SIGNAL_THRESHOLD\n",
" if kgr<0:\n",
" kgr=0\n",
" if kgr>1:\n",
" kgr=1\n",
" \n",
" # Если запасы не меньше, чем планируемый экспорт,\n",
" # то отправим сколько планировали\n",
" \n",
" if c.storage>=c.transportivity*kgr:\n",
" cneed.completeness+=c.transportivity*kgr # Передача\n",
" TransportActivity[c.x,c.y]+=c.transportivity*kgr # Отображение на карте перевозок\n",
" c.storage-=c.transportivity*kgr # Удаление со склада\n",
"\n",
" # Иначе отправим сколько можем\n",
" \n",
" else:\n",
" cneed.completeness+=c.storage # Передача \n",
" TransportActivity[c.x,c.y]+=c.storage # Отображение на карте перевозок \n",
" c.storage=0 # Удаление со склада\n",
" \n",
" # Если получатель принял больше чем нужно для строительства, \n",
" # излишки у него складируются\n",
" # (на следующем ходу он может передать их дальше)\n",
" \n",
" if cneed.completeness>REP_COMPLETENESS:\n",
" cneed.storage+=cneed.completeness-REP_COMPLETENESS\n",
" cneed.completeness=REP_COMPLETENESS\n",
" cneed.refresh()\n",
" \n",
" # Бомбардировка\n",
" \n",
" if len(cells)>BOMB_START:\n",
" b=BOMB_MAX\n",
" for i in range(b): \n",
" sx=int(random()*Terrain.shape[0])\n",
" sy=int(random()*Terrain.shape[1])\n",
" for xi in range(sx,sx+BOMB_SIZE):\n",
" for yi in range(sy,sy+BOMB_SIZE):\n",
" if (xi,yi) in cells.keys():\n",
" cells[(xi,yi)].destroy()\n",
"\n",
" # Вывод текстом номера кадра и числа клеток (чтобы следить за процессом при записи анимации в файл) \n",
" \n",
" framecounter+=1\n",
" clear_output(wait=True)\n",
" print(framecounter,len(cells)) \n",
" \n",
" # Отображение выбранной карты\n",
" \n",
" if DISPLAY==0:\n",
" im1=plt.imshow(ShowTerrain, cmap=plt.get_cmap('bone'))\n",
" elif DISPLAY==1:\n",
" Zm = np.ma.masked_where(Terrain == 0, Conc)\n",
" im1=plt.imshow(Zm, cmap=plt.get_cmap('magma').with_extremes(bad='#101020'))\n",
" elif DISPLAY==2:\n",
" Zm = np.ma.masked_where(TransportActivity<0.1, Flow)\n",
" im1=plt.imshow(Zm, cmap=plt.get_cmap('hsv').with_extremes(bad='k'))\n",
" elif DISPLAY==3:\n",
" Zm = np.ma.masked_where(TransportActivity<0.1, Dir)\n",
" im1=plt.imshow(Zm, cmap=plt.get_cmap('brg').with_extremes(bad='k'))\n",
" elif DISPLAY==4:\n",
" im1=plt.imshow(Store, cmap=plt.get_cmap('gist_stern'))\n",
" elif DISPLAY==5:\n",
" Zm = np.ma.masked_where(Terrain == 0, TransportActivity)\n",
" im1=plt.imshow(Zm, cmap=plt.get_cmap('hot').with_extremes(bad='#101020'))\n",
" return im1,\n",
"\n",
"# Настройка анимации\n",
"ani = animation.FuncAnimation(fig, updatefig, frames=600, interval=3, blit=True)\n",
"plt.show()\n",
"#ani.save('im0.mp4', writer=writer) # Сохранить анимацию в файле"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.5"
}
},
"nbformat": 4,
"nbformat_minor": 1
}

View file

@ -0,0 +1,42 @@
---
id: growth
title: Модель распространения клеток робофабрики
---
## Режимы отображения (выбираются цифровыми клавишами 0-5)
* 0. Terrain - местность, черным показаны непригодные для строительства участки, светлым - клетки, яркость пропорциональна степени зрелости. (см. файл terrain.mp4)
* 1. Incompleteness Signal Concentration - концентрация сигнала роста. Он вырабатывается в недостроенных клетках, диффундирует в соседние и распадается согласно настройкам. В режиме бомбардировки видно как вокруг исчезнувших клеток вспыхивают облачка сигнала роста и тают по мере зарастания раны.
* 2. Incompleteness Signal Direction - направление наибольшего градиента сигнала роста. Каждый цвет соответствует определенному направлению на плоскости. При бомбардировке зияющие раны искажают вокруг себя карту градиента, призывая строительные блоки. Если это происходит возле участка активного роста, то как только рана залечивается, фронт роста перетягивает градиент на себя.
* 3. Transport Direction - направление фактического транспорта блоков из данной клетки, каждый цвет соответствует одному из четырех направлений (вправо,влево,вверх,вниз). Черный соответствует пренебрежимо малому уровню транспорта.
* 4. Storage - объем запасов на складе клетки. Близкие к фронт роста клетки не накапливают запасов а все их передают в зону стройки. Поэтому за фронтом роста тянется фронт минимума запасов. Когда фронт роста ушел далеко, уровень запасов в клетках растет. При уничтожении группы клеток, соседи расходуют запасы на их регенерацию. (см. файл storage.mp4)
* 5. Transport Activity - объем транспорта запасов. Тоже следует за фронтом роста.
![Growth graphs](graph.png)
## Работа алгоритма
(см. рабочий python-код в файле growth.ipynb - лучше открывать с помощью Jupyter Notebook)
Территория с препятствиями задается файлом test.png. Белые - доступные для занятие области территории, черные - недоступные.
![Test terrain](test.png)
* По картинке из файла генерируется карта местности, на нее можно добавить случайные препятствия.
* Задаются параметры клеток и параметры распространеняи сигнала об активном строительстве
* Настраивается момент начала и интенсивность бомбардировки
* Создается первоначальная клетка
Затем в каждом кадре для каждой клетки выполняется следующий цикл:
* Если клетка зрелая, то производит некоторое количество объектов на свой склад
* Составляем список соседних участков с учетом границ карты
* Для каждого участка из списка соседей если на нем есть клетка, расчитываем градиент сигнала роста
* Выявляем соседа с наибольшей концентрацией сигнала роста
* Моделируем диффузию сигнала роста между клетками
* Определяем наиболее готовую недостроенную соседнюю клетку
* Если на соседнем участке нет клетки, проверяем можно ли там построить новую.
* Если есть недостроенные участки - помогаем им, если есть только пустые - строим новую (постройка новых клеток имеет меньший приоритет, чем достройка начатых).
* Устанавливаем объем отдаваемого груза в зависимости от концентрации сигнала роста и ограничиваем его минимум и максимум
* Если запасы не меньше, чем планируемый экспорт, то отправим сколько планировали, иначе отправим сколько можем
* Если получатель принял больше чем нужно для строительства, излишки у него складируются (на следующем ходу он может передать их дальше)
* Если задано, проводится бомбарировка - случайные группы клеток выходят из строя с заданной интенсивностью

Binary file not shown.

Binary file not shown.

BIN
docs/models/growth/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB