В этой статье я расскажу про свой эксперимент: я создал в Unity симуляцию радиоуправляемых машинок, которые эволюционируют. «Мозгом» каждой машинки является электронная схема. Я заставил эти схемы мутировать(случайно меняться) и скрещиваться(обмениваться частями), чтобы создавать новые модели машин и улучшать их. Их «интеллект» и поведение меняются в зависимости от того, сколько блоков они успешно поднимают.
Старт симуляции: Мутация: У машинки меняется "ДНК" (её электронная схема) Скрещивание: Эта машинка объединяет свои "гены" с другой, и рождается новая. Принцип такой: «мозг» машинки (её радиосхема) учится методом проб и ошибок. Каждый раз, когда она успешно поднимает блок, она получает положительный сигнал (подкрепление), который заставляет её повторять удачные действия в будущем. Основная задача заключалась в организации непрерывного обмена данными между Unity и NGspice.
Я сделал так: программа в Юнити создавала описание схемы, отправляла его в NGspice (симулятор), тот рассчитывал все токи и напряжения и возвращал готовый ответ обратно в Юнити.
Понимаете, NGspice только кажется простым. На самом деле это мощный симулятор, созданный для профессионалов, и его исходный код — на языке C. Поэтому, хотя перенос его функций на C# для полной интеграции с Unity — дело важное, я считаю, что сначала нужно сосредоточиться на вызове NGspice из Unity.
Вот так развивалось поведение машинок:
Сперва они могли только ехать вперёд. Потом понемногу начали управлять своей рукой-манипулятором. В какой-то момент они сообразили, что можно оттолкнуться этой рукой от пола и подпрыгнуть. И уже на основе этого прыжка у них рождалось новое, куда более сложное поведение. Стоит одной машинке схватить блок, её успешная радиосхема («ДНК») передаётся по наследству, и среди всех машинок начинается настоящая гонка за блоками. И тут я увидел кое-что удивительное: они начали отбирать и воровать блоки друг у друга. Это поведение возникло само по себе, я его в них не закладывал. Меня это потрясло и невероятно обрадовало. Я никогда не думал, что они смогут додуматься до такого сами. Моя гипотеза: настоящий интеллект нельзя просто написать кодом или натренировать на данных. Его нужно «выращивать», как растение, с помощью эволюции.
Как это работает в симуляции:
Система поощрений: Машинка получает «награду», если она наедет на синий кубик или дотронется до него своим усиком-датчиком,либо схватить своей роборукой красный кубик.
Цикл эволюции:
Начало (Мутация): У каждой машинки в «мозгу» (радиосхеме) появляется случайное изменение.
Финал (Скрещивание): Самые успешные машинки (с наибольшей наградой) объединяют свои схемы, чтобы создать следующее, более умное поколение. К чему приводит такая эволюция? Схемы начинают жить в своем «обществе». Между ними возникает взаимодействие: они борются за блоки, толкают друг друга, а их странные «прыжки» — возможно, это даже примитивный «язык», на котором они «общаются». В процессе эволюции (мутации и скрещивания) постоянно меняются «мозги» для машинок. Они собраны из разных радиодеталей так сложно и хаотично, что уже невозможно понять, как именно они работают.
Мой рецепт первичного ИИ: начать с простого поощрения за блоки, постепенно наращивать сложность задач и позволить эволюции сделать всю остальную работу. Нейросети тоже можно «выращивать» эволюцией, мутируя их параметры. Однако аналоговые схемы, как в моём эксперименте, эффективнее — они потребляют меньше энергии и не требуют мощного охлаждения. Также в отличие от цифровых систем, мои аналоговые схемы работают в реальном времени. Эта временная зависимость дает им «чувство времени», которое помогает ориентироваться в пространстве. Я усложнил задачу: добавил в мир карандаши и холст для рисования. Настроив систему поощрений так, чтобы машинки получали "баллы" за рисование, я заставил их брать карандаши и оставлять на холсте свои каракули.
Входные данные: 16x16 пиксельная камера, реагирующая на свет, преобразуется в набор постоянных напряжений.
Выходные данные: Сигналы снимаются с резисторов, которые имитируют моторы. Эти сигналы (рассчитанные напряжения) передаются в Unity для управления колёсами и манипулятором.
Формирование схемы: Между входами и выходами эволюционным путём создаётся случайная схема. Процесс мутации может:
Добавить или удалить любой элемент.
Изменить его расположение в схеме.
Изменить его номинал (например, сопротивление резистора).
Цикл эволюции выглядит так:
Находим лидера — машинку с наибольшим количеством очков.
Находим претендента — машинку с немного меньшим счётом.
Скрещиваем их — создаём новое поколение на основе "мозгов" лучших из текущего.
Рассмотрим минимум из того, что нужно знать о ngspice для данного проекта.
.title – это просто метка
.subckt – это подсхема. У меня используется для безопасного подключения транзистора или диода через резисторы.
.ends – конец подсхемы.
Vk a 0 DC Value Источник постоянного тока, где k,a – постоянные значения.Value – значение напряжения на источнике питания. Пример V1 1 0 DC 1.193 V3 3 0 DC 1.250
Rk a b Value Резистор.k a b – целые значения.a,b – места куда подключается резистор.Value – номинал резистора Пример R1 257 0 1000 R10 266 257 100
*start scheme Комментарий, после которого начинается генерироваться цепь.
Ck a b Value. Конденсатор. K,a,b – целые числа. Числа a,b – место, куда подключается конденсатор.Value – номинал конденсатора. Пример С1 254 340 100m
Lk a b Value Индуктивность.k,a,b – целые значения,a,b – место,кода подключается индуктивность,Value – номинал индуктивности. Пример L1 331 340 223m
Xk a b c TRANS_SAFE Транзистор. Осуществляется вызов .subckt TRANS_SAFE.k a b c- целые числа.a b c – места куда подключается транзистор. TRANS_SAFE – имя подсхемы транзистора. Пример X2 180 877 593 TRANS_SAFE
Xk a b DIODE_SAFE Диод, вызов .subckt DIODE_SAFE k,a,b – целые числа.a b – место куда подключается диод DIODE_SAFE – имя подсхемы Пример X4 632 561 DIODE_SAFE
.tran – анализ радиосхем
.control – начало анализа схемы
.endc – конец анализа схемы
run – запуск анализа
print v(257) v(258) > output.txt –это вывод потенциалов в точках 257 и 258 в текстовый файл output.txt
Рассмотрим типичный код нетлиста, сгенерированный программой c# unity:
Начало нетлиста
Этот код отвечает за безопасное подключение транзистора и диода.
безопасное подключение транзистора и диода.
.title Simple Circuit
.model m1 NPN
.model m2 D
.subckt TRANS_SAFE base_int collector_int emitter_int
Q1 collector base emitter m1
rbase base base_int 10k
rcollector collector collector_int 1k
remitter emitter emitter_int 100
.ends
.subckt DIODE_SAFE in_int out_int
D1 in out m2
rin in in_int 1k
rout out out_int 1k
.ends
Это напряжения на входе фронтальной камеры(текстуры 16x16)
напряжения на входе фронтальной камеры(текстуры 16x16)
V1 1 0 DC 1.193
V2 2 0 DC 1.227
V3 3 0 DC 1.250
V4 4 0 DC 1.232
V5 5 0 DC 1.227
V6 6 0 DC 1.211
V7 7 0 DC 1.193
V8 8 0 DC 1.355
V9 9 0 DC 1.374
V10 10 0 DC 1.232
V11 11 0 DC 1.252
V12 12 0 DC 1.270
V13 13 0 DC 1.290
V14 14 0 DC 1.309
V15 15 0 DC 1.270
V16 16 0 DC 1.227
V17 17 0 DC 1.445
V18 18 0 DC 1.270
V19 19 0 DC 1.309
V20 20 0 DC 1.335
V21 21 0 DC 1.329
V22 22 0 DC 1.315
V23 23 0 DC 1.309
V24 24 0 DC 1.309
V25 25 0 DC 1.353
V26 26 0 DC 1.367
V27 27 0 DC 1.372
V28 28 0 DC 1.372
V29 29 0 DC 1.392
V30 30 0 DC 1.368
V31 31 0 DC 1.309
V32 32 0 DC 1.484
V33 33 0 DC 2.281
V34 34 0 DC 1.574
V35 35 0 DC 1.555
V36 36 0 DC 1.550
V37 37 0 DC 1.536
V38 38 0 DC 1.452
V39 39 0 DC 1.432
V40 40 0 DC 2.140
V41 41 0 DC 1.456
V42 42 0 DC 1.456
V43 43 0 DC 1.472
V44 44 0 DC 1.509
V45 45 0 DC 1.536
V46 46 0 DC 1.536
V47 47 0 DC 1.536
V48 48 0 DC 1.550
V49 49 0 DC 1.637
V50 50 0 DC 1.632
V51 51 0 DC 1.617
V52 52 0 DC 1.613
V53 53 0 DC 1.599
V54 54 0 DC 1.593
V55 55 0 DC 1.593
V56 56 0 DC 1.574
V57 57 0 DC 2.301
V58 58 0 DC 1.555
V59 59 0 DC 1.554
V60 60 0 DC 1.536
V61 61 0 DC 1.656
V62 62 0 DC 1.656
V63 63 0 DC 1.670
V64 64 0 DC 1.656
V65 65 0 DC 1.695
V66 66 0 DC 1.676
V67 67 0 DC 1.676
V68 68 0 DC 1.656
V69 69 0 DC 1.656
V70 70 0 DC 1.637
V71 71 0 DC 1.632
V72 72 0 DC 1.613
V73 73 0 DC 1.599
V74 74 0 DC 1.593
V75 75 0 DC 1.575
V76 76 0 DC 1.715
V77 77 0 DC 1.714
V78 78 0 DC 1.714
V79 79 0 DC 1.700
V80 80 0 DC 1.695
V81 81 0 DC 1.676
V82 82 0 DC 1.714
V83 83 0 DC 1.694
V84 84 0 DC 1.676
V85 85 0 DC 1.656
V86 86 0 DC 1.652
V87 87 0 DC 1.777
V88 88 0 DC 1.777
V89 89 0 DC 1.777
V90 90 0 DC 1.772
V91 91 0 DC 1.753
V92 92 0 DC 1.753
V93 93 0 DC 1.739
V94 94 0 DC 1.777
V95 95 0 DC 1.772
V96 96 0 DC 1.772
V97 97 0 DC 1.816
V98 98 0 DC 1.816
V99 99 0 DC 1.816
V100 100 0 DC 1.816
V101 101 0 DC 1.816
V102 102 0 DC 1.796
V103 103 0 DC 1.796
V104 104 0 DC 1.796
V105 105 0 DC 1.796
V106 106 0 DC 1.792
V107 107 0 DC 1.911
V108 108 0 DC 1.991
V109 109 0 DC 1.991
V110 110 0 DC 1.972
V111 111 0 DC 1.967
V112 112 0 DC 2.086
V113 113 0 DC 1.936
V114 114 0 DC 1.956
V115 115 0 DC 1.956
V116 116 0 DC 1.975
V117 117 0 DC 1.975
V118 118 0 DC 1.980
V119 119 0 DC 1.980
V120 120 0 DC 1.873
V121 121 0 DC 1.897
V122 122 0 DC 1.918
V123 123 0 DC 1.956
V124 124 0 DC 2.049
V125 125 0 DC 2.087
V126 126 0 DC 1.968
V127 127 0 DC 2.898
V128 128 0 DC 2.353
V129 129 0 DC 3.835
V130 130 0 DC 5.000
V131 131 0 DC 4.917
V132 132 0 DC 4.908
V133 133 0 DC 4.896
V134 134 0 DC 4.887
V135 135 0 DC 4.871
V136 136 0 DC 4.858
V137 137 0 DC 4.745
V138 138 0 DC 4.676
V139 139 0 DC 4.622
V140 140 0 DC 4.553
V141 141 0 DC 4.497
V142 142 0 DC 4.438
V143 143 0 DC 2.469
V144 144 0 DC 2.473
V145 145 0 DC 4.268
V146 146 0 DC 4.230
V147 147 0 DC 4.174
V148 148 0 DC 4.098
V149 149 0 DC 4.059
V150 150 0 DC 4.021
V151 151 0 DC 3.964
V152 152 0 DC 3.923
V153 153 0 DC 3.870
V154 154 0 DC 3.831
V155 155 0 DC 3.778
V156 156 0 DC 3.752
V157 157 0 DC 3.716
V158 158 0 DC 3.677
V159 159 0 DC 3.661
V160 160 0 DC 3.640
V161 161 0 DC 3.625
V162 162 0 DC 3.563
V163 163 0 DC 3.524
V164 164 0 DC 3.485
V165 165 0 DC 3.432
V166 166 0 DC 3.392
V167 167 0 DC 3.353
V168 168 0 DC 3.338
V169 169 0 DC 3.299
V170 170 0 DC 3.278
V171 171 0 DC 3.258
V172 172 0 DC 3.223
V173 173 0 DC 3.203
V174 174 0 DC 3.202
V175 175 0 DC 3.182
V176 176 0 DC 3.181
V177 177 0 DC 3.147
V178 178 0 DC 3.124
V179 179 0 DC 3.089
V180 180 0 DC 3.049
V181 181 0 DC 3.010
V182 182 0 DC 2.995
V183 183 0 DC 2.974
V184 184 0 DC 2.954
V185 185 0 DC 2.919
V186 186 0 DC 2.899
V187 187 0 DC 2.878
V188 188 0 DC 2.878
V189 189 0 DC 2.859
V190 190 0 DC 2.857
V191 191 0 DC 2.859
V192 192 0 DC 2.859
V193 193 0 DC 2.880
V194 194 0 DC 2.841
V195 195 0 DC 2.787
V196 196 0 DC 2.747
V197 197 0 DC 2.707
V198 198 0 DC 2.653
V199 199 0 DC 2.629
V200 200 0 DC 2.608
V201 201 0 DC 2.588
V202 202 0 DC 2.573
V203 203 0 DC 2.553
V204 204 0 DC 2.553
V205 205 0 DC 2.552
V206 206 0 DC 2.553
V207 207 0 DC 2.572
V208 208 0 DC 2.595
V209 209 0 DC 2.574
V210 210 0 DC 2.539
V211 211 0 DC 2.500
V212 212 0 DC 2.495
V213 213 0 DC 2.459
V214 214 0 DC 2.440
V215 215 0 DC 2.419
V216 216 0 DC 2.399
V217 217 0 DC 2.384
V218 218 0 DC 2.378
V219 219 0 DC 2.364
V220 220 0 DC 2.363
V221 221 0 DC 2.378
V222 222 0 DC 2.382
V223 223 0 DC 2.382
V224 224 0 DC 2.384
V225 225 0 DC 2.403
V226 226 0 DC 2.380
V227 227 0 DC 2.343
V228 228 0 DC 2.304
V229 229 0 DC 2.265
V230 230 0 DC 2.249
V231 231 0 DC 2.230
V232 232 0 DC 2.209
V233 233 0 DC 2.210
V234 234 0 DC 2.210
V235 235 0 DC 2.209
V236 236 0 DC 2.209
V237 237 0 DC 2.209
V238 238 0 DC 2.209
V239 239 0 DC 2.209
V240 240 0 DC 2.230
V241 241 0 DC 2.230
V242 242 0 DC 2.210
V243 243 0 DC 2.189
V244 244 0 DC 2.189
V245 245 0 DC 2.169
V246 246 0 DC 2.148
V247 247 0 DC 2.133
V248 248 0 DC 2.113
V249 249 0 DC 2.113
V250 250 0 DC 2.113
V251 251 0 DC 2.113
V252 252 0 DC 2.113
V253 253 0 DC 2.113
V254 254 0 DC 2.113
V255 255 0 DC 2.113
V256 256 0 DC 2.133
Это сопротивления на моторах, которые управляют перемещением машинки взад-вперед, влево-направо
сопротивления на моторах, которые управляют перемещением машинки
Это сопротивления, которые подключены к моторам машинок для управления перемещения. Они нужны для того, чтобы эволюции было легче связать батарейку на камере, например V255, с выходным сопротивлением на моторе.
сопротивления, которые подключены к моторам машинок для управления перемещения
V269 952 0 DC 2.510
V270 953 0 DC 2.613
V271 954 0 DC 2.709
V272 955 0 DC 2.768
V273 956 0 DC 2.788
V274 957 0 DC 2.768
V275 958 0 DC 2.690
V276 959 0 DC 2.593
V277 960 0 DC 2.471
V278 961 0 DC 2.355
V279 962 0 DC 2.244
V280 963 0 DC 2.162
V281 964 0 DC 2.083
V282 965 0 DC 2.006
V283 966 0 DC 1.963
V284 967 0 DC 1.764
V285 968 0 DC 2.593
V286 969 0 DC 2.690
V287 970 0 DC 2.768
V288 971 0 DC 2.807
V289 972 0 DC 2.609
V290 973 0 DC 2.589
V291 974 0 DC 2.530
V292 975 0 DC 2.438
V293 976 0 DC 2.337
V294 977 0 DC 2.239
V295 978 0 DC 2.142
V296 979 0 DC 2.079
V297 980 0 DC 2.006
V298 981 0 DC 1.948
V299 982 0 DC 1.904
V300 983 0 DC 1.870
V301 984 0 DC 2.410
V302 985 0 DC 2.510
V303 986 0 DC 2.589
V304 987 0 DC 2.632
V305 988 0 DC 2.632
V306 989 0 DC 2.613
V307 990 0 DC 2.536
V308 991 0 DC 2.453
V309 992 0 DC 2.337
V310 993 0 DC 2.239
V311 994 0 DC 2.020
V312 995 0 DC 1.943
V313 996 0 DC 1.904
V314 997 0 DC 1.865
V315 998 0 DC 1.827
V316 999 0 DC 1.807
V317 1000 0 DC 2.356
V318 1001 0 DC 2.453
V319 1002 0 DC 2.510
V320 1003 0 DC 2.530
V321 1004 0 DC 2.530
V322 1005 0 DC 2.477
V323 1006 0 DC 2.293
V324 1007 0 DC 2.221
V325 1008 0 DC 2.138
V326 1009 0 DC 2.061
V327 1010 0 DC 2.001
V328 1011 0 DC 1.943
V329 1012 0 DC 1.886
V330 1013 0 DC 1.865
V331 1014 0 DC 1.827
V332 1015 0 DC 1.807
V333 1016 0 DC 2.134
V334 1017 0 DC 2.254
V335 1018 0 DC 2.356
V336 1019 0 DC 2.293
V337 1020 0 DC 2.307
V338 1021 0 DC 2.274
V339 1022 0 DC 2.254
V340 1023 0 DC 1.959
V341 1024 0 DC 1.915
V342 1025 0 DC 1.857
V343 1026 0 DC 1.823
V344 1027 0 DC 1.803
V345 1028 0 DC 1.803
V346 1029 0 DC 1.803
V347 1030 0 DC 1.682
V348 1031 0 DC 1.687
V349 1032 0 DC 1.157
V350 1033 0 DC 1.196
V351 1034 0 DC 1.234
V352 1035 0 DC 1.235
V353 1036 0 DC 1.190
V354 1037 0 DC 1.214
V355 1038 0 DC 1.099
V356 1039 0 DC 1.098
V357 1040 0 DC 1.078
V358 1041 0 DC 1.039
V359 1042 0 DC 1.019
V360 1043 0 DC 0.918
V361 1044 0 DC 1.827
V362 1045 0 DC 1.788
V363 1046 0 DC 1.764
V364 1047 0 DC 1.745
V365 1048 0 DC 0.000
V366 1049 0 DC 0.000
V367 1050 0 DC 0.000
V368 1051 0 DC 0.000
V369 1052 0 DC 0.000
V370 1053 0 DC 0.000
V371 1054 0 DC 0.000
V372 1055 0 DC 0.000
V373 1056 0 DC 0.000
V374 1057 0 DC 0.000
V375 1058 0 DC 0.000
V376 1059 0 DC 0.000
V377 1060 0 DC 1.663
V378 1061 0 DC 1.682
V379 1062 0 DC 1.682
V380 1063 0 DC 1.682
V381 1064 0 DC 0.000
V382 1065 0 DC 2.300
V383 1066 0 DC 3.513
V384 1067 0 DC 2.484
V385 1068 0 DC 2.464
V386 1069 0 DC 2.328
V387 1070 0 DC 2.368
V388 1071 0 DC 0.000
V389 1072 0 DC 0.000
V390 1073 0 DC 1.035
V391 1074 0 DC 1.935
V392 1075 0 DC 1.896
V393 1076 0 DC 1.740
V394 1077 0 DC 1.740
V395 1078 0 DC 1.740
V396 1079 0 DC 1.586
V397 1080 0 DC 0.000
V398 1081 0 DC 2.375
V399 1082 0 DC 4.755
V400 1083 0 DC 4.759
V401 1084 0 DC 4.773
V402 1085 0 DC 4.791
V403 1086 0 DC 4.791
V404 1087 0 DC 0.000
V405 1088 0 DC 0.000
V406 1089 0 DC 2.376
V407 1090 0 DC 4.763
V408 1091 0 DC 4.790
V409 1092 0 DC 4.785
V410 1093 0 DC 4.771
V411 1094 0 DC 4.776
V412 1095 0 DC 4.757
V413 1096 0 DC 4.150
V414 1097 0 DC 4.150
V415 1098 0 DC 4.150
V416 1099 0 DC 4.150
V417 1100 0 DC 4.136
V418 1101 0 DC 4.136
V419 1102 0 DC 4.132
V420 1103 0 DC 2.071
V421 1104 0 DC 2.065
V422 1105 0 DC 2.051
V423 1106 0 DC 4.082
V424 1107 0 DC 4.079
V425 1108 0 DC 4.062
V426 1109 0 DC 4.061
V427 1110 0 DC 4.044
V428 1111 0 DC 4.060
V429 1112 0 DC 3.741
V430 1113 0 DC 3.757
V431 1114 0 DC 3.741
V432 1115 0 DC 3.737
V433 1116 0 DC 3.723
V434 1117 0 DC 3.718
V435 1118 0 DC 3.702
V436 1119 0 DC 3.683
V437 1120 0 DC 3.681
V438 1121 0 DC 3.662
V439 1122 0 DC 3.645
V440 1123 0 DC 3.639
V441 1124 0 DC 3.622
V442 1125 0 DC 3.603
V443 1126 0 DC 3.605
V444 1127 0 DC 3.604
V445 1128 0 DC 3.474
V446 1129 0 DC 3.474
V447 1130 0 DC 3.456
V448 1131 0 DC 3.454
V449 1132 0 DC 3.436
V450 1133 0 DC 3.416
V451 1134 0 DC 3.415
V452 1135 0 DC 3.400
V453 1136 0 DC 3.380
V454 1137 0 DC 3.359
V455 1138 0 DC 3.358
V456 1139 0 DC 3.338
V457 1140 0 DC 3.323
V458 1141 0 DC 3.316
V459 1142 0 DC 3.300
V460 1143 0 DC 3.299
V461 1144 0 DC 3.264
V462 1145 0 DC 3.244
V463 1146 0 DC 3.211
V464 1147 0 DC 3.205
V465 1148 0 DC 3.185
V466 1149 0 DC 3.170
V467 1150 0 DC 3.152
V468 1151 0 DC 3.131
V469 1152 0 DC 3.129
V470 1153 0 DC 3.110
V471 1154 0 DC 3.094
V472 1155 0 DC 3.089
V473 1156 0 DC 3.073
V474 1157 0 DC 3.086
V475 1158 0 DC 3.090
V476 1159 0 DC 3.089
V477 1160 0 DC 3.072
V478 1161 0 DC 3.072
V479 1162 0 DC 3.052
V480 1163 0 DC 3.037
V481 1164 0 DC 3.033
V482 1165 0 DC 3.017
V483 1166 0 DC 2.998
V484 1167 0 DC 2.978
V485 1168 0 DC 2.977
V486 1169 0 DC 2.956
V487 1170 0 DC 2.940
V488 1171 0 DC 2.921
V489 1172 0 DC 2.919
V490 1173 0 DC 2.918
V491 1174 0 DC 2.898
V492 1175 0 DC 2.883
V493 1176 0 DC 2.943
V494 1177 0 DC 2.923
V495 1178 0 DC 2.904
V496 1179 0 DC 2.898
V497 1180 0 DC 2.898
V498 1181 0 DC 2.883
V499 1182 0 DC 2.863
V500 1183 0 DC 2.848
V501 1184 0 DC 2.842
V502 1185 0 DC 2.827
V503 1186 0 DC 2.821
V504 1187 0 DC 2.801
V505 1188 0 DC 2.785
V506 1189 0 DC 2.765
V507 1190 0 DC 2.765
V508 1191 0 DC 2.764
V509 1192 0 DC 2.842
V510 1193 0 DC 2.828
V511 1194 0 DC 2.824
V512 1195 0 DC 2.822
V513 1196 0 DC 2.803
V514 1197 0 DC 2.787
V515 1198 0 DC 2.768
V516 1199 0 DC 2.766
V517 1200 0 DC 2.747
V518 1201 0 DC 2.727
V519 1202 0 DC 2.727
V520 1203 0 DC 2.706
V521 1204 0 DC 2.691
V522 1205 0 DC 2.671
V523 1206 0 DC 2.670
V524 1207 0 DC 2.650
Это комментарий,начиная с которого происходят мутации и скрещивания в схеме
комментарий,начиная с которого происходят мутации и скрещивания в схеме
*start scheme
Дальше идет код случайных мутаций и скрещиваний
код случайных мутаций и скрещиваний
R717 260 1 100
C1 254 340 100m
X2 180 877 593 TRANS_SAFE
X3 781 280 DIODE_SAFE
L1 331 340 223m
L2 607 435 100m
R718 822 21 100m
R719 3 112 100m
L3 555 479 776m
L4 815 304 100m
C2 135 503 100m
L5 56 581 100m
L6 785 268 100m
R720 845 947 100m
L7 776 2 100m
L8 3 765 100m
C3 505 784 100m
X4 632 561 DIODE_SAFE
C4 914 350 100m
L9 241 890 100m
X5 669 467 208 TRANS_SAFE
L10 131 913 100m
X6 286 138 126 TRANS_SAFE
R721 190 707 100m
Выбираем тип анализа цепи
тип анализа цепи
.TRAN 1 1.5 1 1
Происходит запуск анализа цепи и вывод исходных данных в текстовые файлы
запуск анализа цепи и вывод исходных данных в текстовые файлы
Давайте посмотрим, как в программе устроены случайные изменения («мутации»).
Это как "генератор случайных изменений" для машинки - он может добавить новую часть, убрать существующую, изменить её свойства или переставить в другое место.
public string mutationCurcuit(string circuit)
{
Int a = UnityEngine.Random.Range(0,100);
string s = “”;
If(a<70)
{
s=mutationAddCurcuit(circuit);
}
else if(a<80)
{
s=mutationDeleteCurcuit(circuit);
}
else if(a<90)
{
s=mutationChangeCurcuit(circuit);
}
else
{
s=mutationMoveCurcuit(circuit);
}
return s;
}
Рассмотрим каждую функцию по отдельности. Первой будет рассмотрена функция,которая перемещает радиоэлемент в случайное место Этот код описывает номер трансформатора. Находится максимальный номер трансформатора
код описывает номер трансформатора.
public string mutationMoveCurcuit(string circuit)
{
List<string> arr = new List<string>(circuit.Split('n'));
List<string> code = new List<string>();
Dictionary<char, int> radioElement = new Dictionary<char, int>();
List<char> element = new List<char>() { 'R', 'C', 'L', 'K','X' };
radioElement['R'] = 0;
radioElement['C'] = 0;
radioElement['L'] = 0;
radioElement['K'] = 0;
radioElement['X'] = 0;
int k = 0;
bool flag2 = false;
int max = 0;
for(int i = 0;i<arr.Count;i++)
{
if (string.IsNullOrEmpty(arr[i]))
break;
if (arr[i][0] == 'K')
{
int value = Convert.ToInt32(arr[i].Split(' ')[0].Substring(1));
if (radioElement[arr[i][0]] < value)
radioElement[arr[i][0]] = value;
}
Этот код находит максимальный номер транзистора или диода(которые загружаются через .subckt в связке с резисторами)
код находит максимальный номер транзистора или диода
if (arr[i][0] == 'X')
{
int value = Convert.ToInt32(arr[i].Split(' ')[0].Substring(1));
if (radioElement[arr[i][0]] < value)
radioElement[arr[i][0]] = value;
string[] r = arr[i].Split(' ');
if (r[r.Length - 1] == "TRANS_SAFE")
{
if (max < Convert.ToInt32(arr[i].Split(' ')[1]))
max = Convert.ToInt32(arr[i].Split(' ')[1]);
if (max < Convert.ToInt32(arr[i].Split(' ')[2]))
max = Convert.ToInt32(arr[i].Split(' ')[2]);
if (max < Convert.ToInt32(arr[i].Split(' ')[3]))
max = Convert.ToInt32(arr[i].Split(' ')[3]);
}
if (r[r.Length - 1] == "DIODE_SAFE")
{
if (max < Convert.ToInt32(arr[i].Split(' ')[1]))
max = Convert.ToInt32(arr[i].Split(' ')[1]);
if (max < Convert.ToInt32(arr[i].Split(' ')[2]))
max = Convert.ToInt32(arr[i].Split(' ')[2]);
}
}
Этот код находит максимальный номер сопротивления, конденсатора, индуктивности.
код находит максимальный номер сопротивления, конденсатора, индуктивности.
if (arr[i][0] == 'R' || arr[i][0] == 'C' || arr[i][0] == 'L')
{
int value = Convert.ToInt32(arr[i].Split(' ')[0].Substring(1));
if (radioElement[arr[i][0]] < value)
radioElement[arr[i][0]] = value;
if (max < Convert.ToInt32(arr[i].Split(' ')[1]))
max = Convert.ToInt32(arr[i].Split(' ')[1]);
if (max < Convert.ToInt32(arr[i].Split(' ')[2]))
max = Convert.ToInt32(arr[i].Split(' ')[2]);
}
Этот код считывает схему после строки *start scheme
код считывает схему после строки *start scheme
if (arr[i][0] == '.' &&
arr[i][1] == 'T' &&
arr[i][2] == 'R' &&
arr[i][3] == 'A' &&
arr[i][4] == 'N')
{
max++;
}
}
for (int i = 0; i < arr.Count; i++)
{
if (arr[i] == "*start scheme")
{
k = i + 1;
flag2 = true;
continue;
}
if (!string.IsNullOrEmpty(arr[i]))
{
if (arr[i][0] == '.' &&
arr[i][1] == 'T' &&
arr[i][2] == 'R' &&
arr[i][3] == 'A' &&
arr[i][4] == 'N')
{
break;
}
}
if (flag2) code.Add(arr[i]);
Этот код меняет расположение резистора,конденсатора,индуктивности
код меняет расположение резистора,конденсатора,индуктивности
}
int move = UnityEngine.Random.Range(0, code.Count);
string val = "";
if (code.Count > 0)
{
int r1 = 0, r2 = 0,r3=0;
while (true)
{
r1 = UnityEngine.Random.Range(0, max);
r2 = UnityEngine.Random.Range(0, max);
r3 = UnityEngine.Random.Range(0, max);
if (r1 == r2||r2==r3)
continue;
else
break;
}
string[] a = arr[move + k].Split(' ');
if (a[0][0] != 'K')
{
if (a[0][0] != 'X')
{
a[a.Length - 2] = r1.ToString();
a[a.Length - 3] = r2.ToString();
}
Этот код считывает радиосхему,начиная с комментария start scheme
public string mutationChangeCurcuit(string circuit)
{
List<string> arr = new List<string>(circuit.Split('n'));
List<string> code = new List<string>();
int k = 0;
bool flag2 = false;
for (int i = 0; i < arr.Count; i++)
{
if (arr[i] == "*start scheme")
{
k = i + 1;
flag2 = true;
continue;
}
Эта программа случайно изменяет характеристики резисторов, конденсаторов или катушек
программа случайно изменяет характеристики резисторов, конденсаторов или катушек
if (!string.IsNullOrEmpty(arr[i]))
{
if (arr[i][0] == '.' &&
arr[i][1] == 'T' &&
arr[i][2] == 'R' &&
arr[i][3] == 'A' &&
arr[i][4] == 'N')
{
break;
}
}
if (flag2) code.Add(arr[i]);
}
int changed = UnityEngine.Random.Range(0, code.Count-1);
string val="";
if (code.Count > 0)
{
string[] a = arr[changed + k].Split(' ');
if (a[0][0] != 'K' && a[0][0]!='X')
{
int value = UnityEngine.Random.Range(1, 1000);
a[a.Length - 1] = value.ToString() + "m";
val = string.Join(" ", a);
arr[changed + k] = val;
}
Это конец функции
конец функции mutationChangeCurcuit
}
return string.Join("n", arr.ToArray());
}
Это функция mutationDeleteCurcuit.Она удаляет случайный радиоэлемент Этот код читает описание схемы после строки *start scheme
код читает описание схемы после строки *start scheme
public string mutationDeleteCurcuit(string circuit)
{
List<string> arr = new List<string>(circuit.Split('n'));
List<string> code = new List<string>();
int k = 0;
bool flag2 = false;
for (int i = 0; i < arr.Count; i++)
{
if (arr[i] == "*start scheme")
{
k = i + 1;
flag2 = true;
continue;
}
if (!string.IsNullOrEmpty(arr[i]))
{
if (arr[i][0] == '.' &&
arr[i][1] == 'T' &&
arr[i][2] == 'R' &&
arr[i][3] == 'A' &&
arr[i][4] == 'N')
{
break;
}
}
if (flag2) code.Add(arr[i]);
Эта функция добавляет случайный радиоэлемент Этот код считает номер трансформатора
код считает номер трансформатора
public string mutationAddCurcuit(string circuit)
{
List<string> arr = new List<string>(circuit.Split('n'));
Dictionary<char,int> radioElement = new Dictionary<char, int>();
List<char> element = new List<char>() { 'R', 'C','L','K','X' };
radioElement['R'] = 0;
radioElement['C'] = 0;
radioElement['L'] = 0;
radioElement['K'] = 0;
radioElement['X'] = 0;
//find max value
//add 1
//random
int max = 0;
for (int i = 0; i < arr.Count; i++)
{
if (!string.IsNullOrEmpty(arr[i]))
{
if (arr[i][0] == 'K')
{
int value = Convert.ToInt32(arr[i].Split(' ')[0].Substring(1));
if (radioElement[arr[i][0]] < value)
radioElement[arr[i][0]] = value;
Этот код определяет, сколько всего транзисторов и диодов в схеме.
код определяет, сколько всего транзисторов и диодов в схеме.
}
if (arr[i][0] == 'X')
{
int value = Convert.ToInt32(arr[i].Split(' ')[0].Substring(1));
if (radioElement[arr[i][0]] < value)
radioElement[arr[i][0]] = value;
string[] r = arr[i].Split(' ');
if (r[r.Length - 1] == "TRANS_SAFE")
{
if (max < Convert.ToInt32(arr[i].Split(' ')[1]))
max = Convert.ToInt32(arr[i].Split(' ')[1]);
if (max < Convert.ToInt32(arr[i].Split(' ')[2]))
max = Convert.ToInt32(arr[i].Split(' ')[2]);
if (max < Convert.ToInt32(arr[i].Split(' ')[3]))
max = Convert.ToInt32(arr[i].Split(' ')[3]);
}
if (r[r.Length - 1] == "DIODE_SAFE")
{
if (max < Convert.ToInt32(arr[i].Split(' ')[1]))
max = Convert.ToInt32(arr[i].Split(' ')[1]);
if (max < Convert.ToInt32(arr[i].Split(' ')[2]))
max = Convert.ToInt32(arr[i].Split(' ')[2]);
}
Этот код определяет: Сколько всего деталей (резисторов, конденсаторов, катушек) К каким узлам схемы они подключены
определяет:сколько всего деталей
}
if (arr[i][0] == 'R' || arr[i][0] == 'C' || arr[i][0] == 'L')
{
int value = Convert.ToInt32(arr[i].Split(' ')[0].Substring(1));
if (radioElement[arr[i][0]] < value)
radioElement[arr[i][0]] = value;
if (max < Convert.ToInt32(arr[i].Split(' ')[1]))
max = Convert.ToInt32(arr[i].Split(' ')[1]);
if (max < Convert.ToInt32(arr[i].Split(' ')[2]))
max = Convert.ToInt32(arr[i].Split(' ')[2]);
Мы заранее резервируем номера для будущих деталей, чтобы схему можно было свободно расширять.
Здесь мы добавляем случайно радиоэлемент в радиосхему(от транформаторов я позже отказался,но их добавлять также можно)
добавляем случайно радиоэлемент в радиосхему
int r1 = 0, r2 = 0,r3=0;
while (true)
{
r1 = UnityEngine.Random.Range(0, max);
r2 = UnityEngine.Random.Range(0, max);
r3 = UnityEngine.Random.Range(0, max);
if (r1 == r2||r2==r3)
continue;
int index = UnityEngine.Random.Range(0, element.Count);
char e = element[index];
int value = radioElement[e] + 1;
if (e == 'K')
{
continue;
/*
if (radioElement['L'] >= 2)
{
int k1 = 0, k2 = 0;
while (true)
{
k1 = UnityEngine.Random.Range(1, radioElement['L'] + 1);
k2 = UnityEngine.Random.Range(1, radioElement['L'] + 1);
if (k1 == k2)
continue;
arr.Insert(i, e.ToString() + value + " " + "L" + k1.ToString() + " " + "L" + k2.ToString() + " " + "0.99");
break;
}
}
*/
}
else if (e == 'L' || e == 'C' || e == 'R')
{
arr.Insert(i, e.ToString() + value + " " + r1.ToString() + " " + r2.ToString() + " " + "100m");
}
else if (e == 'X')
{
int rnd = UnityEngine.Random.Range(0, 2);
if (rnd == 0)
{
arr.Insert(i, e.ToString() + value + " " + r1.ToString() + " " + r2.ToString() + " " + "DIODE_SAFE");
}
else
{
arr.Insert(i, e.ToString() + value + " " + r1.ToString() + " " + r2.ToString() + " " + r3.ToString() + " " + "TRANS_SAFE");
}
}
break;
}
break;
}
}
}
print("max value" + max);
string result = string.Join("n", arr.ToArray());
return result;
После того как мы научились изменять отдельные элементы, переходим к объединению целых схем(скрещиванию). Вот как устроена эта функция: Программа произвольно соединяет радиодетали между собой.
произвольно соединям радиодетали между собой.
string summoming(string data, string data2,string output)
{
List<string> arr = new List<string>(data.Split('n'));
List<string> arr2 = new List<string>(data2.Split('n'));
List<string> arr3 = new List<string>();
List<string> temp = new List<string>();
int r=1, c=1, l=1, k=1;
for (int i = 0; i < arr.Count; i++)
{
if (!string.IsNullOrEmpty(arr[i]))
{
if (arr[i][0] == '.' &&
arr[i][1] == 'T' &&
arr[i][2] == 'R' &&
arr[i][3] == 'A' &&
arr[i][4] == 'N')
{
break;
}
}
temp.Add(arr[i]);
}
arr = temp;
temp.Clear();
for (int i = 0; i < arr2.Count; i++)
{
if (!string.IsNullOrEmpty(arr2[i]))
{
if (arr2[i][0] == '.' &&
arr2[i][1] == 'T' &&
arr2[i][2] == 'R' &&
arr2[i][3] == 'A' &&
arr2[i][4] == 'N')
{
break;
}
}
temp.Add(arr2[i]);
}
arr2 = temp;
int max = arr.Count;
if (max < arr2.Count)
max = arr2.Count;
int min = 0;
if (min > arr2.Count)
min = arr2.Count;
for (int i = 0; i < min; i++)
{
if (arr[i] != arr2[i])
{
int j = UnityEngine.Random.Range(0, 2);
if (j == 0)
{
arr3.Add(arr[i]);
}
else
{
arr3.Add(arr2[i]);
}
}
else
{
arr3.Add(arr[i]);
}
}
Программа проверяет, нет ли ошибок в нумерации деталей.
проверка нет ли ошибок в нумерации деталей.
for (int i = min; i < max; i++)
{
if(arr.Count==max && arr2.Count==max)
{
arr3.Add(arr[i]);
}
if (arr.Count==min && arr2.Count == max)
{
arr3.Add(arr2[i]);
}
}
for (int j = 0; j < arr3.Count; j++)
{
if(!string.IsNullOrEmpty(arr3[j]))
{
if (arr3[j][0] == 'R')
{
int val = Convert.ToInt32((arr3[j].Split(' ')[0].Substring(1)));
if (val != r)
{
string s = "R" + r.ToString();
string[] ss = arr3[j].Split(' ');
ss[0] = s;
arr3[j]=string.Join(" ", ss);
}
r++;
}
if (arr3[j][0] == 'L')
{
int val = Convert.ToInt32((arr3[j].Split(' ')[0].Substring(1)));
if (val != l)
{
string s = "L" + l.ToString();
string[] ss = arr3[j].Split(' ');
ss[0] = s;
arr3[j] = string.Join(" ", ss);
}
l++;
}
if (arr3[j][0] == 'C')
{
int val = Convert.ToInt32((arr3[j].Split(' ')[0].Substring(1)));
if (val != c)
{
string s = "C" + c.ToString();
string[] ss = arr3[j].Split(' ');
ss[0] = s;
arr3[j] = string.Join(" ", ss);
}
c++;
}
if (arr3[j][0] == 'K')
{
int val = Convert.ToInt32((arr3[j].Split(' ')[0].Substring(1)));
if (val != k)
{
string s = "K" + k.ToString();
string[] ss = arr3[j].Split(' ');
ss[0] = s;
arr3[j] = string.Join(" ", ss);
}
k++;
}
}
}
Завершаем создание схемы и добавляем инструкции для её моделирования.
Что можно сделать дальше (продолжение эксперимента):
Техническое улучшение: Переписать симулятор схем (NGspice) прямо на C# для Unity, чтобы всё работало быстрее и стабильнее в моей версии программы.
Обучение рисованию: Сделать из кубиков фигуры в виде цифр и букв и попробовать научить машинки тыкать в них или даже рисовать их самим.
Эксперимент "как у собаки Павлова": Суть: Давать машинке сигнал (например, звук) каждый раз, когда она хватает определённую часть буквы или цифры.
Цель: Со временем, просто услышав этот звук, машинка должна "вспомнить" и нарисовать ту самую букву или цифру. Это будет как условный рефлекс у собак Павлова, но у машинок.
Идея, если эксперимент сработает: Если машинки смогут так научиться, то этот метод можно применить и для людей. Например, строить в парках большие памятники в виде букв и цифр, которые складываются в строки из важных книг (художественных или технических).
Как это поможет: Человек, прогуливаясь мимо таких памятников, будет невольно запоминать информацию. Это будет работать как тренажёр для памяти, использующий пространство вокруг.
Я понимаю, что моя идея не единственно возможная. Для эволюции робомашинок можно использовать нейросети или цифровую логику — но я сознательно выбрал аналоговые радиосхемы. Мой подход лучше, потому что аналоговые схемы зависят от времени. Это значит, что машинка может использовать ощущение времени для ориентирования в пространстве. Нейросети и цифровые схемы так не умеют — они не привязаны ко времени так же тесно. Оказалось, что живые нервные клетки (нейроны) работают очень похоже на аналоговые схемы из транзисторов и конденсаторов — такие же, как в моём проекте.
Список литературы: 1.Книга по изучению радиоэлектроники Гололобов В.Н. радиоэлектроника от азов до создания практических устройств 2.Мануал по ngspice -> Ngspice 3.Книга по изучению юнити Бонд Д.Г. unity и c# геймдизайн от идеи до реализации. 4.Мануал по созданию машинки с колесиками в среде unity3d Wheel collider 5.Омельяненко Я. – Эволюционные нейросети на языке python 6.Статья на хабре – как я нейроны паял.