基于Matlab元胞自动机的城市扩张模拟与实践

发布时间:2026/9/1 6:49:25
基于Matlab元胞自动机的城市扩张模拟与实践 简介项目使用Matlab构建元胞自动机模型以印度艾哈迈达巴德地区为实验对象模拟并预测未来城市增长与土地利用变化。模型中的元胞可处于未开发土地、住宅、商业、工业等多种状态通过当前状态和周围邻域规则逐时间步迭代反映人口迁移、交通改善与政策干预等因素。面向城市规划、地理信息系统相关专业学生和研究者尤其适合希望掌握CA建模方法的初学者压缩包共12个文件体积仅105KB核心为7个m脚本包括总控主程序、道路距离计算、邻域滤波、影像读取与矩阵转换等函数另附txt运行指南和jpg、png示例图便于快速运行和对照结果。当前已有264人浏览学习通过研读代码读者可以理解元胞状态定义、转换规则和邻域影响机制掌握将遥感影像转换为元胞数组、设置不同发展参数、输出可视化分布图等具体操作还可自行调整规则权重评估不同规划政策或区位条件对城市扩张的影响是一份轻量完整的入门实践资料。1. 一张网格如何长出一座城市元胞自动机在Matlab里的落地逻辑先说点实际的。去年我拿到一个项目需求——模拟某个片区未来二十年的城市扩张趋势输入数据是一张土地利用栅格图和历史建成区变化记录输出是若干年后最可能的城市增长区域分布。调研了一圈现成工具最后决定自己用Matlab搭建模型底层原理就是今天要聊的元胞自动机Cellular Automata简称CA。为什么选CA而不是传统统计分析模型一句话解释就是城市扩张本质上是地-地相互作用的涌现结果每一个地块是否被开发跟它周边地块的状态强相关而这种相邻影响恰恰是CA天然能表达的东西。传统回归模型往往把空间关系当独立变量处理忽略了邻居效应导致模拟结果在空间形态上太平均跟现实里那种沿道路轴向蔓延、围绕城市核心圈层式的扩张形态差距很大。CA的核心思想也不复杂把研究区域划分成规则的格网每个格子就是一个元胞每个元胞有一个状态比如城市或非城市定义一条状态转换规则规则的关键输入是当前元胞自身属性以及邻域内其他元胞的状态然后按时间步迭代整张格网同步更新。城市增长模拟里每个时间步对应一年多次迭代之后就能看到城市从初始斑块慢慢向外扩张的全过程。在Matlab里实现这套逻辑比想象中顺手。矩阵是Matlab的原生数据结构一张土地利用栅格本身就是二维矩阵邻域提取用数组切片就能完成状态更新用逻辑索引批量操作完全不需要逐格循环代码效率高得离谱。相比之下用Python写也行但Matlab在矩阵操作上的语法更直接可视化工具箱也省事imagesc一调用出空间分布图配合循环写frame还能输出gif动画。简单梳理一下这个项目里元胞自动机的四要素映射关系CA要素城市增长场景下的对应物数据形式元胞地块栅格单元状态是否已城市化0/1 矩阵邻域周边地块对中心地块的影响Moore邻域窗口转换规则地块开发概率与判定公式计算与阈值判断后面几节就把每一部分掰开说清楚从状态定义、邻域取法、规则设计到Matlab代码实现最后附上我实测调参踩过的坑。2. 模型构建第一步状态定义、邻域取法与初始化2.1 栅格状态设计项目里我把每个元胞的状态分为三大类0非城市用地可开发1已开发的城市用地2不可开发用地比如水体、生态保护区、地质灾害禁止建设区用整数矩阵存状态好处是后续计算转换概率时可以直接用逻辑矩阵做掩膜。例如landuse 2就能一次性取出所有不可开发区的位置把这些位置的开发概率直接置0防止模型把湖面或保护区也开发了。区域范围用m x n的矩阵表示每个格子对应实际地面大小比如 50m × 50m 或 100m × 100m。栅格分辨率会影响模拟结果粒度这个后面单独讲。初始城市的分布我一般从遥感分类结果或土地利用现状图里读Matlab里用readgeoraster读GeoTIFF或者直接读ASCII网格转成矩阵后重新编码。没有实测数据验证的话也可以手动指定几个核心斑块作为城市种子点观察扩张形态。2.2 邻域窗口选择CA领域最经典的是Moore邻域也就是以目标元胞为中心的3×3窗口内除自身外的8个邻居。城市扩张模拟里扩展半径可以调大一些我用的是半径 r 2 的扩展Moore邻域也就是5×5窗口共24个邻居。为什么要加大因为道路、基础设施对周边地块开发的影响范围远超一个格子的距离用3×3窗口会导致城市边缘只能逐格推进扩张形态过于缓慢、平滑缺少现实中那种跳跃式飞地开发。Matlab里提取邻域状态不需要写成多重循环。用conv2做二维卷积直接统计邻域内城市用地数量是最优雅的写法kernel ones(5, 5); kernel(3, 3) 0; % 排除中心元胞自身 neighborCount conv2(urbanMap, kernel, same);卷积结果neighborCount里每个元素就是对应位置周围5×5窗口内城市元胞的个数一次矩阵运算算完整张图比嵌套for循环快了几个数量级。中心元胞本身是否为城市从urbanMap里单独判断即可。边界处理要注意conv2默认对图像边界补零所以靠近研究区边界的元胞邻域内邻居数会偏少导致边界区域开发概率被系统性低估。城市增长模拟里一般对研究区边界做无邻居假设也说得通因为边界外的数据本来就缺失。如果边界外其实还有真实城区建议先用逻辑掩膜把研究区外的网格剔除或者对边界单独处理别让它干扰结果。2.3 初始化城市种子与不可开发区初始化代码段我习惯写成函数initLanduse(m, n, seedMode)方便不同场景复用。手动设置种子城市时我常用zeros创建一个全零矩阵然后往指定坐标写1。真实数据导入时写一个重编码逻辑% 假设 landuseRaw 是读入的原始分类栅格 % 分类值 1城市, 2水体, 3林地, 4农田 urbanMap landuseRaw 1; unsuitable (landuseRaw 2) | (landuseRaw 3); landuseMap zeros(size(landuseRaw)); landuseMap(urbanMap) 1; landuseMap(unsuitable) 2;初始化完之后可以画一张图检查三区分布是否合理这一步别跳过——我遇到过数据坐标翻转导致城市斑块跑到水体里的情况如果不在初始化阶段肉眼检查后面所有模拟结果都是错的而且极难排查。3. 状态转换规则发展概率从哪里来CA模型的灵魂在于状态转换规则这一节写的公式和参数是整个模型的核心也是本项目的重点所在。3.1 综合发展概率的计算城市增长模拟里我采用多因子加权 邻域效应 随机扰动的综合概率模型。每个非城市元胞在某一时间步的开发概率 P_dev 由四个部分构成P_dev P_gravity × P_neighbor × P_suit × P_randomP_gravity基于到城市核心或道路的引力衰减反映基础设施可达性P_neighbor邻域城市密度效应由上文neighborCount归一化得到P_suit土地适宜性指数来自坡度、规划用途、生态限制等空间因子P_random随机扰动项用来引入不确定性单个因子看都符合直觉。P_gravity 用反距离衰减公式计算距离主要道路网或城市中心越近开发概率越高P_neighbor 跟邻域城市元胞数量正相关模拟城市集聚效应P_suit 是空间变量比如坡度过大的区域开发成本高生态敏感区概率压低P_random 让模型在多次运行时产生不同方案这正是城市增长模拟需要的情景不确定性。3.2 邻域效应函数P_neighbor 我用的是归一化指数形式而不是简单的线性比例P_neighbor (neighborCount / maxNeighbors)^alpha其中maxNeighbors是邻域窗口内最大邻居数5×5窗口去掉中心就是24alpha 是邻域强度系数通常取0.5~1.5。alpha 小于1时低密度邻域就有较强吸引力城市扩张更分散alpha 大于1时只有高密度城市邻域才能显著促进开发扩张更紧凑。下图是我测试不同alpha对扩张形态影响的大致规律建议实际项目里做敏感性分析。3.3 约束条件与适宜性因子土地适宜性 P_suit 我定义为多个约束因子的乘积。每一类因子都是0到1之间的权重值1代表完全适合开发0代表禁止开发。常用的约束因子有坡度约束坡度 25度时 P_suit 趋近0生态保护区掩膜保护区内 P_suit 0基本农田保护区P_suit 0 或低值距离水系距离河湖缓冲区内设为低概率Matlab里处理多个栅格因子用逐像元乘法即可P_suit slopeFactor .* ecoFactor .* farmlandFactor .* waterBufferFactor;注意约束因子必须是同尺寸矩阵且最终 P_suit 的取值范围压到 [0,1]。实际项目中我从GIS软件导出这些因子栅格再在Matlab里统一重采样到同一分辨率这一步务必对齐地理坐标否则错位一个像元产生的偏差会直接污染所有后续计算。3.4 阈值判定与随机扰动算出 P_dev 之后怎么决定这个元胞到底转不转成城市业内常用做法是对每个非城市元胞生成一个均匀分布的随机数 rand比较 P_dev 与 rand 的大小关系满足条件就开发newUrban (P_dev rand) (landuseMap 0) (landuseMap ~ 2);rand 矩阵用rand(m, n)一次生成向量化比较无需逐格循环。这个机制叫蒙特卡洛阈值判定法本质上是一种概率采样高概率的元胞更容易被选中开发低概率的元胞也可能被随机选中模拟了现实中不可预见的偶发开发行为。这里有个容易踩的坑P_dev 的值通常很小比如0.001~0.05直接跟 uniform(0,1) 比较会导致每步开发量过少迭代几十年城市也没长几块模拟结果全是星点状分布完全不现实。我实际项目中的做法是把开发总量控制也纳入判定逻辑比如设定每年新建城市元胞数量目标为某个动态范围如果当年随机判定的开发量超出目标区间就按 P_dev 排序取前K个元胞进行开发。这样既保留了随机性又能控制扩张速度在合理范围。代码示意candidate find((landuseMap 0) (landuseMap ~ 2)); devProb P_dev(candidate); [~, idx] sort(devProb, descend); K min(targetNewCells, numel(candidate)); selected candidate(idx(1:K)); landuseMap(selected) 1;如果更想要随机情景也可以在这些候选元胞里按概率加权抽样两种方式各有利弊。总量控制更贴近规划场景中每年新增建设用地指标的逻辑概率抽样则适合研究自然扩散过程。4. Matlab完整实现与可视化输出4.1 主循环代码框架把上面的逻辑整合成一个完整的主循环代码结构如下% 参数初始化 rows 200; cols 200; landuseMap initLanduse(rows, cols); years 30; targetNewCells 80; % 每年新增城市元胞数 alpha 0.9; % 邻域强度系数 gravityMap computeGravity(); % 基于道路/中心的引力场 suitMap computeSuitability(); % 适宜性因子乘积层 for t 1:years urbanMap landuseMap 1; % 邻域城市密度 kernel ones(5,5); kernel(3,3) 0; neighborCount conv2(double(urbanMap), kernel, same); P_neighbor (neighborCount / 24) .^ alpha; % 综合发展概率 P_dev gravityMap .* P_neighbor .* suitMap; % 排除已开发与不可开发区 P_dev(landuseMap 1) 0; P_dev(landuseMap 2) 0; % 候选元胞总量控制选取 candidate find(P_dev 0); [~, idx] sort(P_dev(candidate), descend); selected candidate(idx(1:min(targetNewCells, numel(candidate)))); landuseMap(selected) 1; % 输出/可视化每一帧 if mod(t, 5) 0 visualize(landuseMap, t); end end这段代码里computeGravity和computeSuitability是项目里自定义的函数分别计算基础设施可达性栅格和适宜性栅格。真实项目中这两个函数的数据准备和计算通常很花时间我就遇到过原始数据是矢量格式从shp文件转栅格就折腾了小半天。另一个需要留意的迭代顺序问题CA理论框架要求所有元胞根据上一时间步的状态同步更新也就是同步CA。我在代码里先完整算完所有元胞的 P_dev再统一更新landuseMap这符合同步CA语义。如果边算边更新会让同一时间步内先更新的元胞影响后更新的元胞产生路径依赖破坏CA模型的时空一致性。很多初学CA的开发者容易忽略这一点结果模拟结果出现诡异的扫把形方位偏向。4.2 交互式动画输出模拟Visual展示用imagesc比较直观区分三种土地状态如图例城市、非城市蓝绿到黄的渐变色、不可开发灰色。写循环叠加drawnow和pause(0.1)就能形成动画但我实际写过更强的方案——组合成GIF动态图这样我能把结果直接放进项目汇报PPT里评审和客户不需要装Matlab也能看。figure; for t 1:years imagesc(landuseMap); colormap([0.5 0.8 0.5; 0.9 0.2 0.1; 0.4 0.4 0.4]); title(sprintf(Year %d, t)); axis equal tight; drawnow; frame getframe(gcf); [A, map] rgb2ind(frame2im(frame), 256); if t 1 imwrite(A, map, urbanGrowth.gif, gif, LoopCount, Inf, DelayTime, 0.15); else imwrite(A, map, urbanGrowth.gif, gif, WriteMode, append, DelayTime, 0.15); end end如果只关心末态分布画一张imagesc静态图就够了但动态图能直观展示城市形态演化过程尤其在模拟出现了飞地式开发、轴向蔓延等典型现象时特别有说服力。5. 实测案例一个小片区的30年城市扩张模拟5.1 数据准备与参数标定为了测试模型和给客户演示我用了一个简化但真实的案例数据某片区现状土地利用栅格分辨率100米格网200×200共4万格。现状城区面积约占5%主要分布在西南侧和中部。区域内有一条主干道斜穿中部可开发面积约60%其余为山体和基本农田。参数上我给 alpha 设了0.8邻居窗口用5×5每年目标新增元胞数量设为80约等于0.8平方公里/年大致符合案例片区近年年均新增建设用地规模。道路引力场用gravityMap exp(-distanceToRoad / 2000)表示单位是米衰减距离2000米。5.2 模拟结果分析运行30年后城市斑块从初始的西南和中部分别向外扩张在道路两侧形成明显的指状延伸主干道交叉口附近的非城市元胞变成了新的开发热点。这正好体现了CA模型对真实城市扩张形态的复现能力城市不是均匀摊大饼而是沿着高可达性走廊选择性蔓延。统计指标方面我计算了每年新增城市元胞的空间中心变化发现重心整体向东北方向偏移偏移速度前10年较快后20年趋缓。这个现象和案例片区近年的中心城区-外围新区土地供应节奏基本吻合说明模型具备一定解释力。多情景模拟对比也很有价值。我把目标新增元胞数量从每年60调到120模拟结果显示了两种明显不同的城市形态低增长情景下城市紧凑连绵被山体约束边界清晰高增长情景下出现大量飞地式新城生态廊道被切割。这个对比正好提供给规划部门做情景讨论用。5.3 模型初始状态敏感性实际使用CA模型时初始城市分布对长期模拟结果影响很大。我做过一组实验把初始城市斑块沿主干道稍微平移若干格模拟30年后的城市总面积虽然接近但空间分布差异显著尤其在前10年新增城市元胞纷纷贴附在初始斑块周边扩散差异要等模拟后期才会逐渐弥合。这表明CA模型的路径依赖很强初始数据的准确性直接决定模拟质量。如果你的初始土地利用数据来源不可靠或者分类精度不够建议先用历史数据做校正再预测未来情景。6. 参数敏感性分析哪些参数需要花时间标定模型好建参数难调。这一节专门聊参数设计对输出结果的影响以及我实际调参沉淀的经验。6.1 参数一览表参数含义取值范围对结果的影响alpha邻域强度系数0.5~1.5值越大城市扩张越紧凑值越小越分散邻域窗口邻里影响半径3×3~7×7窗口越大跳跃式开发越明显targetNewCells年新增城市元胞数据驱动控制城市扩张速度影响总面积引力衰减距离道路/中心影响半径500~3000m距离衰减越快沿路轴向蔓延越明显alpha 是最让人头疼的参数之一因为它和网格分辨率还有交互作用。越粗的栅格上alpha 效应越弱越细的栅格上alpha 对形态的影响越被放大。我做过敏感性分析alpha 从0.5增加到1.5模拟结果的聚合度指标比如城市斑块数量、边界密度变化了约40%这个数字说明它不是可有可无的参数必须结合历史数据率定。6.2 历史数据率定方法经验上最可靠的调参方式是拿历史数据做回溯模拟。比如已知2010年和2020年两期土地利用分布用2010年数据初始化模型模拟到2020年再拿模拟结果跟2020年真实分布对比。常用评价指标是Kappa系数和FOM指数Figure of Merit即模拟新增城市元胞与真实新增城市元胞的重合比例。Kappa达到0.75以上FOM在0.2~0.3之间算是模拟效果比较合理的水平。调参方法我推荐先粗调后细调先固定其他参数改变目标参数跑5~10组模拟对比统计指标找到局部最优区间再做多参数网格搜索比如 alpha ∈ {0.7, 0.9, 1.1} 与 neighborRadius ∈ {2, 4} 的交叉组合。总共6组模拟每组跑一遍Matlab多核并行用parfor能节省不少时间。6.3 过拟合风险率定参数时要控制度别把模型调成只能复现历史数据但对未来预测意义不大的状态。如果为提高Kappa值把参数调到极端区间比如 alpha 设到2.5模拟结果形态会严重聚集新增城市全部堆在现有城区边缘完全丧失了模拟可能性的价值。我的原则是参数要保持在一定物理意义范围内宁可Kappa低一点也要让模型有足够空间表达多种增长形态。7. 局限性反思与改进方向7.1 CA模型的固有缺陷CA模型的缺点也很明显。第一转换规则通常是静态的整个模拟周期内参数不随时间变化但现实中城市规划政策、经济周期、土地供应策略都在动态演变静态规则很难表达这些非平稳变化。第二CA只关注空间状态转换缺少对城市增长背后的社会经济驱动机制的显式表达比如人口增长、产业转移、房价梯度等并不会直接出现在模型里。第三CA转换规则里 P_random 的随机扰动虽然引入了不确定性但它没有方向性对空间格局的解释力接近噪声。真实城市增长中的不确定性往往表现为大型项目落地带来的跳跃式开发更加结构化这需要靠外部情景事件来驱动而不是纯随机项。7.2 混合模型改进方向我实践的改进方向有三个。一是让参数随时间变化比如把 alpha 设计成时间函数模拟城市发展从核聚变到蔓延再到填充的阶段特征。二是引入多情景外生变量例如土地利用规划约束、新增建设用地指标的空间分配把规划方案叠加成动态约束层让模型从 纯自组织模拟 变成 规划干预下的模拟。三是跟系统动力学 / 多智能体模型耦合补上人口、产业、交通需求等模块让城市扩张的社会经济驱动力能够内生表达。第一个方向最容易实现——只需要把 alpha 从标量改成一个随时间变化的向量。第二个方向适用于跟规划部门配合的实际项目技术上也不难难的是数据获取和规划情景的标准化。第三个方向工程量最大但也是目前学术研究和产业项目中价值最高的。回到开头那个项目我最后交付给用户的模型除了CA主模型还额外输出了一组情景规划对比图无约束自然增长、基本农田保护约束增长、生态红线与骨架路网约束增长三种情景。用户拿着这组结果跟规划方案做空间叠加分析直接识别出了若干模型预测高风险扩张但缺乏规划管控的重点片区。这个价值已经超出了单一模拟器本身变成了一个支撑空间决策的量化分析工具。如果你手头也有Metlab环境建议直接把手上的土地利用数据跑一遍这个框架先观察城市增长形态是否符合直觉再逐步校准参数。动手比看教程有用得多。本文还有配套的精品资源点击获取