Processing 模拟生态系统

  • 博文简介
  • 生物知识
    • 生态系统
  • 实现内容
  • 效果展示
  • 操作说明
  • 相关类
  • 技术讲解
    • 遗传
    • 自治智能体
      • 群集行为
      • 个体行为
  • 问题与思考
  • 参考
  • 其他作品推荐
    • 布料模拟
    • 融入动画技术的交互应用
    • 《Magic Network》:一个小孩都能玩的神经网络交互系统

博文简介

使用Processing实现了简单的池塘生态系统。
主要是鱼——水蚤——水草三类生物的行为。
关键技术是遗传算法和自治智能体

生物知识

生态系统

生态系统指在自然界的一定的空间内,生物与环境构成的统一整体,在这个统一整体中,生物与环境之间相互影响、相互制约,并在一定时期内处于相对稳定的动态平衡状态。

组成成分:非生物的物质和能量、生产者、消费者、分解者。

这里模拟了简单的池塘生态系统。忽略掉非生物的物质和能量,还有分解者。只有 生产者–水草、低级消费者–水蚤(以水草为食)、高级消费者–鱼(以水蚤为食)

核心规则:
1、·能量传递的过程中逐级递减,传递率为10%~20%
这点问题不大,简单来说就是 鱼:水蚤:水草 =100:10:1,当然实际上没有这么简单,实现的过程中没有严格按照这点,大概就是注意鱼的数量少于水蚤少于水草,并且鱼的寿命大于水蚤大于水草,生命值也不同。
2、生物进化
这里用到了遗传算法,实现这点的方法是寿命,生物活得越久繁殖概率越大,DNA也就会传递下去
3、动态平衡
生态系统通常具有自我调节能力,可以实现动态平衡。

生物进化和动态平衡在实现的过程中都出现了问题,比我一开始想的出入很大,在下文 问题与思考 中详细阐述。

实现内容

DNA
寿命 (有最大最小限制)
速度 (进化需有一定随机性 、突变性)
大小

交叉
复制
变异

水草:
生命值(被吃会变透明)
寿命
大小(与寿命相关)
速度

死亡
无性繁殖
种子扩散
有微弱的移动能力

水蚤(指低级消费者):
寿命(性成熟)
性别
大小
速度

躲避行为(躲鱼)
寻偶
群集行为(主要是对齐)
吸收养分(吃植物)
有性繁殖

鱼(指高级消费者):
寿命(性成熟)
性别
大小
速度
性别
大小

捕食(追水蚤)
寻偶
群集行为(主要是分离)
吸收养分(吃虾)
有性繁殖

效果展示

完整视频:https://www.bilibili.com/video/av50958125

操作说明

可以通过单击鼠标左键、右键、滑轮添加鱼、水蚤、水草,或单击左上角得“-”号减少
拖拽可以持续生成(后期生物过多,单击生成减少太慢)
通过交互人为调节生态平衡。

相关类

类间关系如图

下面是各类的域和方法,具体实现和全部代码见 https://github.com/sssal/EcoSystem

主类

World world;Boolean isOpenIntroduce;
void setup() {size(800, 600);frameRate(60);world = new World(100, 20, 0);textFont(createFont("KaiTi-48.vlw", 48));isOpenIntroduce = true;
}void draw() {background(200);world.update();textSize(15);fill(50);text("Plants:" + (int)world.getPlantNum(), 20, 20);text("Fishes:" + (int)world.getFishNum(), 20, 40);text("Fleas:" + (int)world.getFleaNum(), 20, 60);rect(100, 7, 20, 12);rect(100, 27, 20, 12);rect(100, 47, 20, 12);fill(250);rect(105, 12, 10, 3);rect(105, 32, 10, 3);rect(105, 52, 10, 3);//说明界面if (isOpenIntroduce) {fill(0, 200);rect(width/4, height/4, width/2, height/2, 20);textSize(30);fill(250);text("说明", width/2-50, height/4+40);textSize(20);text("按 “ i ” 退出/显示说明界面", width/4+20, height/4+70);text("左上角表示示植物、水蚤、鱼数量", width/4+20, height/4+100);text("鼠标单击左键生成鱼,右键水蚤,中键植物", width/4+20, height/4+130);text("鼠标拖拽持续生成",width/4+20,height/4+160);text("鼠标单击左上角“-”减少",width/4+20,height/4+190);text("鼠标在左上角“-”处拖拽持续减少",width/4+20,height/4+220);
}
}void mouseDragged() {//print(0);if (mouseX>100&&mouseX<120&&mouseY>7&&mouseY<19) {world.reducePlant();} else if (mouseX>100&&mouseX<120&&mouseY>27&&mouseY<39) {world.reduceFish();} else if (mouseX>100&&mouseX<120&&mouseY>47&&mouseY<59) {world.reduceFlea();} else {if (mouseButton == LEFT) {print(1);world.addFish(new PVector(mouseX, mouseY));} else if (mouseButton == RIGHT) {world.addFlea(new PVector(mouseX, mouseY));} else if (mouseButton == CENTER) {world.addPlant(new PVector(mouseX, mouseY));}}
}void mouseClicked() {//print(0);if (mouseX>100&&mouseX<120&&mouseY>7&&mouseY<19) {world.reducePlant();} else if (mouseX>100&&mouseX<120&&mouseY>27&&mouseY<39) {world.reduceFish();} else if (mouseX>100&&mouseX<120&&mouseY>47&&mouseY<59) {world.reduceFlea();} else {if (mouseButton == LEFT) {print(1);world.addFish(new PVector(mouseX, mouseY));} else if (mouseButton == RIGHT) {world.addFlea(new PVector(mouseX, mouseY));} else if (mouseButton == CENTER) {world.addPlant(new PVector(mouseX, mouseY));}}
}void keyPressed() {if (key == 'i') {if (isOpenIntroduce) {isOpenIntroduce = false;} else {isOpenIntroduce = true;}}
}
class World {ArrayList<Flea> fleas;ArrayList<Plant> plants;ArrayList<Fish> fishes;World(int plantsNum, int fleasNum, int fishNum) void update() float getFishNum() float getFleaNum()float getPlantNum() void addFish(PVector pvector) void addFlea(PVector pvector)void addPlant(PVector pvector) void reduceFish() void reducePlant()void reduceFlea()
}
class Creature {//生物类 所有生物的父类PVector position;  //位置PVector acceleration; //加速度PVector velocity;  //速度float lifetime;     //寿命 float maxspeed;     //速度float maxforce;    //转向力float size;         //大小float r;            //画图大小float maxLifetime; //用来保存生物的最大生命和尺寸float maxSize;     float health;      //生命值float maxHealth;DNA dna;          DNA fatherDNA;//设置不同行为的权重float separateWeight;float cohesionWeight;float alignWeight;float breedProbability; //繁殖概率float matingProbability; //交配概率float xoff;float yoff;  //控制随机移动速度float periphery = PI/2; //视野角度Boolean gender;  //性别Boolean isRut;   //是否处于发情期Boolean isPregnancy; //怀孕color col;      //颜色Creature(PVector pos, DNA initDNA)//更新void update()//移动void move() //画图void display() //添加力改变加速度void applyForce(PVector force)//三种群集规则//分离  避免碰撞//对齐  转向力与邻居一致//聚集  朝邻居中心转向 (留在群体内)void flock(ArrayList<? extends Creature> Creatures) //寻找PVector seek(PVector target) // Cohesion 聚集行为PVector cohesion (ArrayList<? extends Creature> creatures) //分离行为PVector separate (ArrayList<? extends Creature> creatures) //对齐行为PVector align (ArrayList<? extends Creature> creatures)  //设置发情期void rut()//寻偶PVector mating(ArrayList<? extends Creature> creatures)//觅食PVector foraging(ArrayList<? extends Creature> creatures)//繁殖public Creature  breed() // 避免超出画板范围void borders()//判断死亡boolean dead()
}
class Plant extends Creature {//水草类Plant(PVector pos, DNA initDNA)@Overridevoid display() @OverridePlant breed()//移动@Overridevoid move()//判断死亡@Overrideboolean dead()
class Flea extends Creature {//水蚤类Flea(PVector pos, DNA initDNA)//更新@Overridevoid update() @Overridevoid display()@Override //加入寻偶行为void flock(ArrayList<? extends Creature> Creatures)//躲避动作void moveElude(ArrayList<Fish> fishes)//躲避PVector elude(ArrayList<Fish> fishes)@Override //有性繁殖Flea breed()//移动@Overridevoid move()//对齐行为@Override  //添加视野角度PVector align (ArrayList<? extends Creature> creatures)//吃水草void eat(ArrayList<Plant> plants)//判断死亡@Overrideboolean dead()
class Fish extends Creature {//鱼类Fish(PVector pos, DNA initDNA)//更新@Overridevoid update()@Overridevoid display()@Override   //有性繁殖Fish breed()@Override //加入寻偶行为void flock(ArrayList<? extends Creature> Creatures)//捕食运动void moveForaging(ArrayList<Flea> fleas)//移动@Overridevoid move() //吃水蚤void eat(ArrayList<Flea> fleas)//判断死亡@Overrideboolean dead()
class DNA {private HashMap<String, Float> genes=new HashMap<String,Float>();DNA()DNA(HashMap newgenes)//复制public DNA dnaCopy()//交叉public DNA dnaCross(DNA fatherDNA)//变异public void mutate(float m)

技术讲解

向量、力之类的比较基础,这里主要说一下遗传和自治智能体。

遗传

这部分的代码比较简单。只需要把遗传相关的属性和方法实现就可以了。
在DNA类中,使用 HashMap<String, Float> 用作储存基因型,键(String)保存基因型的名字,值(FLoat)保存基因间的差别,在Creature类中实例化DNA类并通过值来实现表现型。

之后事基因的复制、交叉和变异。
复制主要用在无性繁殖,直接复制DNA就可以了

  //复制public DNA dnaCopy() {HashMap<String, Float> childGenes = (HashMap<String,Float>)genes.clone();return new DNA(childGenes);}

交叉则是有性繁殖,父亲把DNA传递给母亲,然后生成孩子的DNA

  //交叉public DNA dnaCross(DNA fatherDNA){HashMap<String, Float> childGenes = (HashMap<String,Float>)genes.clone();HashMap<String, Float> fatherGenes = fatherDNA.genes;float lifetime = (fatherGenes.get("lifetime") + genes.get("lifetime"))/2;childGenes.put("lifetime",lifetime);float speed = (fatherGenes.get("speed") + genes.get("speed"))/2;childGenes.put("speed",speed);float size = (fatherGenes.get("size") + genes.get("size"))/2;childGenes.put("size",size);return new DNA(childGenes);}

变异在有性繁殖和无性繁殖中都会出现

  //变异public void mutate(float m) {for (String key : genes.keySet()) {if (random(1)<m) {genes.put(key, random(0, 1));}}}

自治智能体

这部分是代码的核心,控制生物的行为。
生物的行为可以分成两类,一类是群集行为,一类是个体行为。
运动会受到不同权重,生物的视野和角度影响。

群集行为

群集行为包括聚集,分离,对齐。
水蚤的对齐行为权重更大,会与视野内的水蚤速度保持一致。
统计视野内其他水蚤的速度均值,处理后作为加速度。

  //对齐行为@Override  //添加视野角度PVector align (ArrayList<? extends Creature> creatures) {float neighbordist = size * 2;PVector sum = new PVector(0, 0);int count = 0;for (Creature other : creatures) {//从一个生物到另一个生物的向量PVector comparison = PVector.sub(other.position, position);//距离float d = PVector.dist(position, other.position);//角度float  diff = PVector.angleBetween(comparison, velocity);if ((diff < periphery) && (d > 0) && (d < neighbordist)) {sum.add(other.velocity);count++;}}if (count > 0) {sum.div((float)count);sum.normalize();sum.mult(maxspeed);PVector steer = PVector.sub(sum, velocity);steer.limit(maxforce);return steer;} else {return new PVector(0, 0);}}

聚集则是计算视野内其他生物的位置的均值,生成从当前位置指向均值的向量处理后用作加速度

  // Cohesion 聚集行为PVector cohesion (ArrayList<? extends Creature> creatures) {float neighbordist = r * 5; //视野  ????PVector sum = new PVector(0, 0);   // Start with empty vector to accumulate all positionsint count = 0;for (Creature other : creatures) {float d = PVector.dist(position, other.position);if ((d > 0) && (d < neighbordist)) {sum.add(other.position); // 将其他对象位置相加count++;}}if (count > 0) {sum.div(count);return seek(sum);  //寻找邻居的平均坐标} else {return new PVector(0, 0);}}

分离统计远离其他生物的向量均值,可以用于避免生物间距离过近重叠。

  //分离行为PVector separate (ArrayList<? extends Creature> creatures) {float desiredseparation = r*1.5; //分离视野PVector steer = new PVector(0, 0, 0);int count = 0;// For every boid in the system, check if it's too closefor (Creature other : creatures) {float d = PVector.dist(position, other.position);// If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)if ((d > 0) && (d < desiredseparation)) {// Calculate vector pointing away from neighborPVector diff = PVector.sub(position, other.position);diff.normalize();diff.div(d);        // Weight by distancesteer.add(diff);count++;            // Keep track of how many}}// Average -- divide by how manyif (count > 0) {steer.div((float)count);}// As long as the vector is greater than 0if (steer.mag() > 0) {// Implement Reynolds: Steering = Desired - Velocitysteer.normalize();steer.mult(maxspeed);steer.sub(velocity);steer.limit(maxforce);}return steer;}

个体行为

个体的行为是求偶、觅食、躲避

当个体处于发情期会寻找视野内处于发情期的异性,找到后传递DNA,并结束发情期状态

  //寻偶PVector mating(ArrayList<? extends Creature> creatures) {float neighbordist = r * 15;if (isRut) {for (Creature other : creatures) {if (other.isRut && gender != other.gender) {//都处于发情期且性别不同//PVector comparison = PVector.sub(other.position, position);//距离float d = PVector.dist(position, other.position);//角度//float  diff = PVector.angleBetween(comparison, velocity);if ( (d < neighbordist) && (d>r)) {return seek(other.position);} else if (d<r) { //当两者足够靠近//print(3);isRut = false;other.isRut = false;if (gender) {col = color(255, 0, 0);other.col = color(0, 255, 0);isPregnancy = true;fatherDNA = other.dna;} else {col = color(0, 255, 0);other.col = color(255, 0, 0);other.isPregnancy = true;other.fatherDNA = dna;}}}}}return new PVector(0, 0);}

觅食是鱼的行为,主动寻找水蚤

  //觅食PVector foraging(ArrayList<? extends Creature> creatures) {float neighbordist = r * 10;for (Creature c:creatures) {//从一个生物到另一个生物的向量PVector comparison = PVector.sub(c.position, position);//距离float d = PVector.dist(position, c.position);//角度float  diff = PVector.angleBetween(comparison, velocity);if ((diff < periphery) && (d < neighbordist)) {return seek(c.position);}}return new PVector(0, 0);}

躲避与觅食相反,水蚤主动躲避鱼

  //躲避PVector elude(ArrayList<Fish> fishes) {float neighbordist = r * 10;for (Fish f:fishes) {//从一个生物到另一个生物的向量PVector comparison = PVector.sub(f.position, position);//距离float d = PVector.dist(position, f.position);//角度float  diff = PVector.angleBetween(comparison, velocity);if ((diff < periphery) && (d < neighbordist)) {PVector result = seek(f.position);result = new PVector(-result.x, -result.y);return result;}}return new PVector(0, 0);}

问题与思考

1、遗传
遗传算法是计算数学中用于解决最佳化的搜索算法,曾经使用过用来计算函数的最大值,这里考虑把生物的寿命作为适应度,活得越长适应度越大,这样迭代一段时间后就能够得到寿命最长也就是最适应环境的个体,但在尝试的过程中发现这样太理想化了。
一开始想要把各项参数都放在DNA中,例如不同行为的权重,繁殖率等,但是发现变量太多,效果不好。
在系统中只有水蚤和水草时,出现过一种情况是水蚤越来越大,但速度却越来越慢,我猜测因为没有给水蚤添加觅食行为,所以走得慢反而更容易生存,越大觅食范围越大,并且水草不会被直接吃掉,生存概率也更高。但是这种情况我后来并没有复现出来。
这里放了一种能够体现遗传进化的情况:
初始为水草:100 水蚤:20 无鱼

一段时间后的情况:

发现水蚤在往大的方向进化,速度并没有越来越慢。

这时候我意识到 适应度 的问题,我希望的适应度是寿命,但实际上想要让水蚤因为各项属性影响寿命是很困难的事,我只把参数修改一点点就可能导致生物灭绝或者过度繁殖,所以寿命的随机性其实挺高的。而真正的适应度其实是交配的概率,吃得多活得长,交配的概率高,但影响交配的概率最大的其实是体积,越大越可能和异性接触并交配。

2、动态平衡
正常的生态系统是具有自我调节的能力的,就像狼-兔-草模型中,兔子多了,狼也会多,草减少,这样兔子就会减少,一开始我也是想让鱼-水蚤-水草达到平衡状态,在只有水蚤-水草时还行,加入鱼后我发现这是不可能的,不添加外在条件时不可能的。

自然界中其实还存在一些规律,实际上当一种生物增多时,首先受影响的是它的下级,生态系统的调节是具有滞后性的,就像兔子多了,首先是草大量减少,然后才是狼多,需要一定的时间跨度,才能恢复平衡。但在我模拟的生态系统中,当存在鱼-水蚤-水草时几乎无法调节平衡,会持续地往极端发展,并最终全部灭绝。

当然这应该有我对生物的模拟不够真实的原因,并且环境太小,没有足够的时间和空间让模拟生态系统运行下去。

参考

参考案例:https://www.openprocessing.org/sketch/687983
参考书籍:《The nature of code》/《代码本色》

其他作品推荐

布料模拟

布料模拟

这个作品一下子就吸引了我,很简洁又真实,确实有布料运动的感觉,这个技术的实现让我想到曾经制作的愤怒的小鸟的弹弓。
布料的整体运动取决于部分的运动,每一个局部运动逻辑是相似的,但整体表现出来的效果又很棒。

融入动画技术的交互应用

博文地址

一开始实现的跳一跳看起来就很不错,运动、交互的效果看起来很舒服。后边还看到一些有趣的效果,processing实现的融入动画的漫画场景,很好看。最后还分享了一些动画,特别是这个水生物的自然形态模拟,很喜欢,我自己做的生态系统看上去特别丑,早点看到这个例子,或许能让我的作品更具艺术性。

《Magic Network》:一个小孩都能玩的神经网络交互系统

Magic Netwo

界面画风很符合主题,在学神经网络、人工智能的时候就经常感觉很抽象,很多东西的中间过程难以理解,这个作品非常好,即便是没有学过相关知识的人也可以操作,帮助理解神经网络。

Processing 模拟池塘生态系统相关推荐

  1. processing 模拟arduino ide串口监视器发送命令信息(string类型)

    1.Port.write 与单片机通讯是遇到的问题 2.解决方法:例如,要发送string类型信息:AE2 int a0 = 2: myport =new Serial(this,"COM1 ...

  2. 基于《代码本色》的processing学习与拓展

    目录 1. 第0章 引言 2. 第1章 向量 3. 第2章 力 4. 第3章 振荡 5. 第4章 粒子系统 1. 第0章 引言 <代码本色>在这一章节的主要内容是模拟随机游走. 讲了利用r ...

  3. Processing 编程学习指南 (丹尼尔·希夫曼 著)

    https://processing.org/reference/ 第1章 像素 (已看) 第2章 Processing (已看) 第3章 交互 (已看) 第4章 变量 (已看) 第5章 条件语句 ( ...

  4. 转:Processing 编程学习指南

    原文链接:http://www.cnblogs.com/revoid/p/9764535.html 书籍:由casey reas和ben fry编著的<爱上processing>适合零基础 ...

  5. 绘画系统-Processing版

    创作了两个版本的绘画系统,MATLAB版请移步https://blog.csdn.net/dont_like_jiemo/article/details/85345407 -------------- ...

  6. Processing 案例 | 圆圈的华尔兹

    文章目录 引言 代码 主要结构 交互 绘制圆 圆心 半径 颜色 小结 引言 生活中有很多积少成多的现象,很多微不足道的东西聚集起来会出现意想不到的效果.举个最常见的例子,比如一张普通的打印纸非常的薄, ...

  7. 瘟疫模拟相关知识总结(传染病模型+马尔可夫链)

    瘟疫模拟相关知识总结 总览 模型 SI模型 SIS模型 SIR模型 SEIR模型 马尔可夫链 简介 理解 举个栗子 需要注意 实验设计 实验群体:人 实验思路 代码搭建(暂) 社区类(Communit ...

  8. Processing编程学习指南导读

    前 言 Learning Processing:A Beginner抯 Guide to Programming Images, Animation, and Interaction, Second ...

  9. CASA——估算陆地生态系统植被净初级生产力(NPP)的经典模型

    文章目录 第一讲:CASA模型介绍 第二讲:CASA初步操作 第三讲:CASA数据制备(一) 第四讲:CASA数据制备(二) 第五讲:CASA数据制备(三) 第六讲:CASA数据制备(四) 第七讲:土 ...

最新文章

  1. zabbix安装php7.0,Centos 7.0安装Zabbix server详细步骤(示例代码)
  2. Shell语法—— while 条件语句
  3. Battle Encoder Shirase一款能限制进程CPU占有率的小东西
  4. caffeine 线程私有的ReadBuffer实现
  5. sde执行revoke SELECT ANY TABLE from sde导致报ora-29900 运算符连接不存在错误
  6. 电子电路分析与设计:数字电子技术_红外气体传感器电子电路设计
  7. 数据驱动安全:数据安全分析、可视化和仪表盘》一3.6 本章小结
  8. Mybatis3全面详解
  9. java webservice原理_轻松搞懂WebService工作原理
  10. RELU激活函数作用
  11. 背景学习模型 —》codebook算法
  12. LeetCode-55. 跳跃游戏
  13. 三星云服务S Cloud亮相 与苹果iCloud为敌
  14. 高德路径规划预估打车价格
  15. 未能打开这台计算机的策略组对象,Windows7 组策略错误:“未能打开这台计算机上的组策略对象。您可能没有合适的权限。”...
  16. GPGPU渲染GPU的工作原理和认知总结
  17. Uber新CEO:公司最早将于2019年IPO上市
  18. 系统体系结构-概念和框架
  19. 尼日利亚区块链专家认为加密货币可提振非洲经济
  20. 简单的手机蓝牙遥控智能小车

热门文章

  1. 【数据结构】一元多项式
  2. 台式计算机更改bios密码,台式机或笔记本忘记Bios密码解决方法
  3. 坚持真理的艰辛——罗巴切夫斯基创立非欧几何的艰难历程
  4. 平安科技2019校招后端工程师一面
  5. 【Jquery选择器】
  6. it可以拥有的证书含金量和途径
  7. CS61A fa2021 Composing Programs 2.8 Efficiency 效率
  8. MTK优美代码赏析2:MenuItemMask_flag
  9. 汉明码(海明码、hamming code)通俗易懂的解释!!!!
  10. Linux基本bash命令(持续更新)