电梯交通量仿真分析3
3. 详细设计
3.1 建筑与电梯配置模块
3.1.1 建筑配置实现
建筑配置服务设计:
/**
* 建筑配置服务,实现PDF中描述的建筑特性建模
* "在交通仿真中,起点和终点楼层通常根据模拟建筑的假设人口分布定义"
*/
public class BuildingConfigurationService {
/**
* 创建建筑配置,基于PDF中描述的建筑类型特性
*
* @param config 建筑配置参数
* @return 配置完成的建筑对象
*/
public Building createBuilding(BuildingConfig config) {
// 验证基本参数
validateBasicParameters(config);
// 创建楼层列表
List<Floor> floors = createFloors(config);
// 配置电梯组
List<ElevatorGroup> elevatorGroups = configureElevatorGroups(config, floors);
// 配置人口分布和交通特性
configurePopulationAndTraffic(config, floors);
return new Building(
UUID.randomUUID().toString(),
config.getName(),
config.getTotalFloors(),
config.getFloorHeight(),
config.getBuildingType(),
floors,
elevatorGroups
);
}
/**
* 配置建筑人口分布和交通特性
* 实现PDF中描述的三种交通成分比例
*/
private void configurePopulationAndTraffic(BuildingConfig config, List<Floor> floors) {
// 根据建筑类型设置默认交通模式
TrafficPatternConfig defaultPattern = determineDefaultTrafficPattern(config.getBuildingType());
// 为每层配置人口分布
for (Floor floor : floors) {
Map<TimePeriod, PopulationDistribution> distributionMap = new EnumMap<>(TimePeriod.class);
for (TimePeriod period : TimePeriod.values()) {
// 获取该时段的交通模式配置
TrafficPatternConfig patternConfig = getPatternConfig(config, period, floor);
// 计算该时段的人口分布
PopulationDistribution distribution = calculatePopulationDistribution(
floor,
period,
patternConfig,
config.getBuildingType()
);
distributionMap.put(period, distribution);
}
// 更新楼层对象
floor.setPopulationDistribution(distributionMap);
}
}
/**
* 计算特定楼层、特定时段的人口分布
* 实现PDF中描述的"进楼、出楼和楼层间交通"三种交通成分
*/
private PopulationDistribution calculatePopulationDistribution(
Floor floor,
TimePeriod period,
TrafficPatternConfig patternConfig,
BuildingType buildingType
) {
// 根据楼层用途和建筑类型确定基础人口
int population = determinePopulation(floor, buildingType);
// 计算三种交通成分比例
double incomingRatio = patternConfig.getIncomingRatio();
double outgoingRatio = patternConfig.getOutgoingRatio();
double interfloorRatio = 1.0 - incomingRatio - outgoingRatio;
// 验证比例总和
validateTrafficRatios(incomingRatio, outgoingRatio, interfloorRatio);
// 获取该时段的批次到达特性
BatchArrivalProfile batchProfile = determineBatchProfile(
floor,
period,
buildingType,
patternConfig
);
return new PopulationDistribution(
population,
incomingRatio,
outgoingRatio,
interfloorRatio,
batchProfile
);
}
/**
* 确定特定楼层的批次到达特性
* 基于PDF中"批次到达可以建模为时间非齐次泊松过程"的研究
*/
private BatchArrivalProfile determineBatchProfile(
Floor floor,
TimePeriod period,
BuildingType buildingType,
TrafficPatternConfig patternConfig
) {
// 根据PDF研究,早高峰和午餐高峰的批次特性不同
if (period.isPeakHour()) {
// 高峰时段有更高的批次到达概率和更大的平均批次大小
return createPeakHourBatchProfile(buildingType, patternConfig);
} else {
// 非高峰时段批次特性
return createOffPeakBatchProfile(buildingType);
}
}
// 其他辅助方法...
}关键实现细节:
建筑类型交通特性:
- 办公楼:早高峰以上行为主(进楼交通比例高),晚高峰以下行为主(出楼交通比例高),午餐高峰双向流量大
- 商场:楼层间交通比例高(通常50%以上),各层流量相对均衡
- 住宅:交通模式较为分散,无明显高峰特征
- 混合用途建筑:根据各层用途动态调整交通模式比例
人口分布配置:
- 实现PDF中描述的"在交通仿真中,起点和终点楼层通常根据模拟建筑的假设人口分布定义"
- 支持自定义人口分布,或基于建筑类型自动推导
- 考虑楼层用途对人口分布的影响(如办公楼的办公层、商场的零售层)
批次到达模型:
- 实现PDF中描述的"批次到达可以建模为时间非齐次泊松过程"
- 按时段配置不同的批次特性参数
- 支持自定义批次大小分布(基于PDF中的实验数据)
3.1.2 电梯配置实现
电梯组配置服务设计:
/**
* 电梯组配置服务,实现不同控制方式的电梯组配置
*/
public class ElevatorGroupConfigurationService {
/**
* 配置电梯组,基于PDF中描述的电梯控制策略
*
* @param building 建筑对象
* @param groupConfig 电梯组配置
* @return 配置完成的电梯组
*/
public ElevatorGroup configureElevatorGroup(
Building building,
ElevatorGroupConfig groupConfig
) {
// 验证配置参数
validateGroupConfig(groupConfig, building);
// 创建电梯列表
List<Elevator> elevators = createElevators(groupConfig);
// 配置服务楼层
List<Integer> servedFloors = configureServedFloors(groupConfig, building);
// 创建电梯组
return new ElevatorGroup(
UUID.randomUUID().toString(),
groupConfig.getControlType(),
elevators,
servedFloors,
TrafficPattern.initialPattern()
);
}
/**
* 创建单台电梯实例
*/
private Elevator createElevator(ElevatorConfig config) {
// 计算开门时间(基于PDF中门宽与乘客流动时间的关系)
double doorOpenTime = calculateDoorOpenTime(config.getDoorWidth(), config.getCapacity());
// 创建电梯状态
ElevatorStatus initialStatus = new ElevatorStatus(
config.getStandbyFloor() * building.getFloorHeight(),
ElevatorDirection.STOPPED,
0.0,
false,
0,
new HashSet<>(),
new ArrayList<>(),
0,
0,
0
);
return new Elevator(
UUID.randomUUID().toString(),
config.getCapacity(),
config.getRatedSpeed(),
config.getAcceleration(),
config.getDeceleration(),
config.getDoorWidth(),
config.getStandbyFloor(),
new HashSet<>(config.getServiceFloors()),
initialStatus
);
}
/**
* 配置电梯服务楼层
* 实现PDF中描述的电梯服务区域划分
*/
private List<Integer> configureServedFloors(
ElevatorGroupConfig config,
Building building
) {
if (config.getServiceFloors() != null && !config.getServiceFloors().isEmpty()) {
// 使用用户指定的服务楼层
return config.getServiceFloors();
} else {
// 根据控制类型自动配置服务楼层
return determineDefaultServiceFloors(config, building);
}
}
/**
* 计算开门时间,基于PDF中门宽与乘客流动的关系
* "同一层站如上下乘客较多,受开门宽度影响会增加电梯停靠等待时间"
*/
private double calculateDoorOpenTime(double doorWidth, int capacity) {
// 基础开门时间(与门宽相关)
double baseOpenTime = 2.0 + (1.5 * (1.0 - doorWidth / 1.2));
// 乘客流动时间系数
double flowCoefficient = 0.8 + (0.4 * (capacity / 20.0));
return baseOpenTime * flowCoefficient;
}
}关键实现细节:
控制类型配置:
- 单控:每台电梯独立配置,不共享召唤信号
- 并联:两台电梯配置为共享召唤信号,通常服务相同楼层范围
- 群控:三台及以上电梯配置为协同工作,可实现动态分区
- 目的选层:三台及以上电梯配置为支持目的楼层选择,需特殊UI支持
电梯物理参数计算:
- 实现PDF中描述的电梯运行时间计算模型
- 停站时间 = 开门时间 + 乘客流动时间 + 关门时间
- 乘客流动时间 = 乘客数量 × (上/下时间) × 拥挤系数
- 运行时间 = √(2×距离/加速度) + (距离-加速段-减速段)/额定速度 + √(2×距离/减速度)
服务区域配置:
- 支持固定分区和动态分区
- 高层建筑可配置高低区服务
- 目的选层系统可实现更精细的分区策略
3.2 乘客流量生成模块
3.2.1 批次到达模型实现
批次到达生成器:
/**
* 批次到达生成器,实现PDF中描述的"批次到达可以建模为时间非齐次泊松过程"
*/
public class BatchArrivalGenerator implements PassengerFlowService {
private final Random random = new Random();
@Override
public List<PassengerArrivalEvent> generatePassengerFlow(
Building building,
long startTime,
long duration
) {
List<PassengerArrivalEvent> events = new ArrayList<>();
// 按楼层生成到达事件
for (Floor floor : building.getFloors()) {
// 获取当前时段
TimePeriod currentPeriod = determineCurrentTimePeriod(startTime);
// 获取该楼层该时段的人口分布
PopulationDistribution distribution = floor.getPopulationDistribution(currentPeriod);
// 生成该楼层的批次到达
List<BatchArrivalEvent> floorEvents = generateBatchArrivals(
floor,
distribution.getBatchProfile(),
startTime,
duration
);
// 转换为乘客到达事件
events.addAll(convertToPassengerEvents(floorEvents, building));
}
// 按时间排序所有事件
events.sort(Comparator.comparingLong(SimulationEvent::getTimestamp));
return events;
}
@Override
public List<BatchArrivalEvent> generateBatchArrivals(
Floor floor,
BatchArrivalProfile batchProfile,
long startTime,
long duration
) {
List<BatchArrivalEvent> batchEvents = new ArrayList<>();
// 计算批次到达率(批次/秒)
double batchRate = 1.0 / batchProfile.getInterArrivalMean();
// 使用泊松过程生成批次到达时间
long currentTime = startTime;
while (currentTime < startTime + duration) {
// 生成指数分布的到达间隔
double interval = -Math.log(1.0 - random.nextDouble()) / batchRate;
currentTime += (long)(interval * 1000); // 转换为毫秒
if (currentTime >= startTime + duration) {
break;
}
// 确定批次大小
int batchSize = determineBatchSize(batchProfile);
// 创建批次到达事件
BatchArrivalEvent event = new BatchArrivalEvent(
currentTime,
building.getId(),
floor.getFloorNumber(),
batchSize
);
batchEvents.add(event);
}
return batchEvents;
}
/**
* 确定批次大小,基于PDF中描述的批次大小分布
*/
private int determineBatchSize(BatchArrivalProfile profile) {
double rand = random.nextDouble();
double cumulativeProb = 0.0;
for (BatchSizeProbability sizeProb : profile.getBatchSizeDistribution()) {
cumulativeProb += sizeProb.getProbability();
if (rand <= cumulativeProb) {
return sizeProb.getBatchSize();
}
}
// 默认返回最小批次大小
return profile.getBatchSizeDistribution().get(0).getBatchSize();
}
/**
* 将批次到达事件转换为乘客到达事件
*/
private List<PassengerArrivalEvent> convertToPassengerEvents(
List<BatchArrivalEvent> batchEvents,
Building building
) {
List<PassengerArrivalEvent> passengerEvents = new ArrayList<>();
for (BatchArrivalEvent batchEvent : batchEvents) {
List<Passenger> passengers = generatePassengersForBatch(
batchEvent,
building
);
passengerEvents.add(new PassengerArrivalEvent(
batchEvent.getTimestamp(),
building.getId(),
batchEvent.getFloor(),
passengers
));
}
return passengerEvents;
}
/**
* 为批次生成乘客,包括目的地分配
*/
private List<Passenger> generatePassengersForBatch(
BatchArrivalEvent batchEvent,
Building building
) {
List<Passenger> passengers = new ArrayList<>(batchEvent.getBatchSize());
Floor floor = building.getFloor(batchEvent.getFloor());
// 生成OD矩阵(如果尚未计算)
ODMetricMatrix odMatrix = building.getOdMatrix();
if (odMatrix == null) {
odMatrix = odEstimationService.estimateBuildingODMatrix(building);
building.setOdMatrix(odMatrix);
}
// 为每个乘客分配目的地
for (int i = 0; i < batchEvent.getBatchSize(); i++) {
int destination = determineDestination(floor, odMatrix);
PassengerType type = determinePassengerType();
passengers.add(new Passenger(
"P-" + UUID.randomUUID(),
batchEvent.getFloor(),
destination,
batchEvent.getTimestamp(),
-1, // boardingTime - will be set later
-1, // alightingTime - will be set later
type,
batchEvent.getBatchSize()
));
}
return passengers;
}
// 其他辅助方法...
}关键实现细节:
批次到达过程:
- 实现PDF中描述的"批次到达可以建模为时间非齐次泊松过程"
- 使用指数分布生成批次间到达时间
- 基于PDF实验数据配置批次大小分布
交通模式实现:
- 进楼交通(从底层大厅进入建筑)
- 出楼交通(离开建筑返回底层大厅)
- 楼层间交通(建筑内部不同楼层间移动)
- 根据建筑类型和时段动态调整三种交通模式比例
目的地分配:
- 基于OD矩阵分配乘客目的地
- 考虑楼层用途对目的地选择的影响
- 支持特殊建筑配置(如商场内部流量较大)
3.2.2 OD矩阵生成实现
OD矩阵生成服务:
/**
* OD矩阵生成服务,实现PDF中描述的电梯行程OD矩阵估计
* "电梯行程OD矩阵估计问题可以表述为网络流问题"
*/
public class ODMatrixGenerationService implements ODEstimationService {
private static final double EXECUTION_TIME_LIMIT_MS = 500.0; // 0.5秒
@Override
public ODMetricMatrix estimateWithLP(List<ElevatorTrip> elevatorTrips, Building building) {
// 构建线性规划问题
LinearProblem lpProblem = buildLPProblem(elevatorTrips, building);
// 求解LP问题
LPResult result = solveLPProblem(lpProblem);
// 转换为OD矩阵
return convertToODMatrix(result, building);
}
@Override
public ODMetricMatrix estimateWithBILS(
List<ElevatorTrip> elevatorTrips,
Building building,
boolean withRandomization
) {
// 构建BILS问题
BILSProblem bilsProblem = buildBILSProblem(elevatorTrips, building);
// 求解BILS问题
BILSResult result;
if (withRandomization) {
result = solveBILSProblemWithRandomization(bilsProblem);
} else {
result = solveBILSProblem(bilsProblem);
}
// 处理复杂情况:如果在时限内未找到解,使用连续解的舍入
if (result == null || result.getExecutionTime() > EXECUTION_TIME_LIMIT_MS) {
ContinuousSolution continuousSolution = bilsProblem.getContinuousSolution();
result = roundContinuousSolution(continuousSolution);
}
// 转换为OD矩阵
return convertToODMatrix(result, building);
}
@Override
public ODMetricMatrix estimateWithCP(List<ElevatorTrip> elevatorTrips, Building building) {
// 构建约束规划问题
CPProblem cpProblem = buildCPProblem(elevatorTrips, building);
// 求解CP问题
CPResult result = solveCPProblem(cpProblem);
// 转换为OD矩阵
return convertToODMatrix(result, building);
}
/**
* 构建BILS问题,实现PDF中描述的"Box-Constrained Integer Least Squares"
*/
private BILSProblem buildBILSProblem(List<ElevatorTrip> elevatorTrips, Building building) {
// 构建目标函数
Matrix A = buildCoefficientMatrix(elevatorTrips, building);
Vector b = buildObservationVector(elevatorTrips);
// 设置约束
Matrix bounds = buildBoundsMatrix(building);
return new BILSProblem(A, b, bounds);
}
/**
* 使用随机化方法求解BILS问题
* "Publication[III] presents two new algorithms to find all solutions to the BILS formulation"
*/
private BILSResult solveBILSProblemWithRandomization(BILSProblem problem) {
List<BILSResult> allSolutions = new ArrayList<>();
long startTime = System.currentTimeMillis();
// 在时限内尽可能多地寻找解
while (System.currentTimeMillis() - startTime < EXECUTION_TIME_LIMIT_MS) {
BILSResult solution = solveBILSProblemWithRandomStart(problem);
if (solution != null) {
allSolutions.add(solution);
}
}
// 如果找到多个解,随机选择一个
if (!allSolutions.isEmpty()) {
int randomIndex = random.nextInt(allSolutions.size());
return allSolutions.get(randomIndex);
}
return null;
}
/**
* 将连续解舍入为整数解
* "在复杂实例中,通过将连续解舍入到最近的整数快速获得可能次优但可行的解"
*/
private BILSResult roundContinuousSolution(ContinuousSolution continuousSolution) {
// 将连续解舍入到最近的整数
Vector roundedSolution = continuousSolution.getSolution().round();
// 计算目标函数值
double objectiveValue = calculateObjectiveValue(roundedSolution);
return new BILSResult(
roundedSolution,
objectiveValue,
System.currentTimeMillis() - continuousSolution.getStartTime(),
true // 标记为舍入解
);
}
/**
* 验证OD矩阵的一致性
* "OD矩阵必须满足流量守恒原则"
*/
@Override
public ValidationResult validateODMatrix(ODMetricMatrix matrix, Building building) {
List<ValidationIssue> issues = new ArrayList<>();
// 检查流量守恒
for (int floor = 1; floor <= building.getTotalFloors(); floor++) {
double totalOut = matrix.getTotalPassengersFrom(floor);
double totalIn = matrix.getTotalPassengersTo(floor);
// 允许小的数值误差
if (Math.abs(totalOut - totalIn) > 0.001 * Math.max(totalOut, totalIn)) {
issues.add(new ValidationIssue(
"FLOW_CONSERVATION_VIOLATION",
String.format("流量守恒违反: 楼层 %d, 出流量=%.1f, 入流量=%.1f",
floor, totalOut, totalIn)
));
}
}
// 检查非负性
for (ODMetric metric : matrix.getMetrics()) {
if (metric.getPassengerCount() < 0) {
issues.add(new ValidationIssue(
"NEGATIVE_PASSENGER_COUNT",
String.format("负乘客数: (%d->%d)=%.1f",
metric.getOriginFloor(),
metric.getDestinationFloor(),
metric.getPassengerCount())
));
}
}
return new ValidationResult(issues.isEmpty(), issues);
}
}关键实现细节:
三种OD估计方法:
- LP(线性规划):将问题表述为网络流问题
- BILS(带界约束的整数最小二乘):包括上界约束,提高解的质量
- CP(约束规划):基于不同假设,可能产生与BILS不同的解
随机化搜索:
- 实现PDF中描述的"Publication[III] presents two new algorithms to find all solutions to the BILS formulation"
- 在时限内尽可能多地寻找解,随机选择一个作为结果
- "在复杂实例中,通过将连续解舍入到最近的整数快速获得可能次优但可行的解"
一致性验证:
- 验证流量守恒原则(进入某楼层的乘客数 = 离开该楼层的乘客数)
- 检查非负性约束
- 识别并报告不一致问题
3.3 仿真引擎核心逻辑
3.3.1 仿真调度器设计
离散事件仿真调度器:
/**
* 电梯交通仿真调度器,实现离散事件仿真
* "支持实时仿真(1秒模拟时间≤100ms真实时间)"
*/
public class ElevatorSimulationScheduler {
private final PriorityQueue<SimulationEvent> eventQueue = new PriorityQueue<>(
Comparator.comparingLong(SimulationEvent::getTimestamp)
);
private final Building building;
private final SimulationConfig config;
private final SimulationClock clock;
private SimulationState currentState;
public ElevatorSimulationScheduler(
Building building,
SimulationConfig config,
SimulationClock clock
) {
this.building = building;
this.config = config;
this.clock = clock;
this.currentState = new SimulationState(building);
}
/**
* 初始化仿真,添加初始事件
*/
public void initialize() {
// 添加初始乘客到达事件
List<PassengerArrivalEvent> initialEvents = generateInitialPassengerFlow();
eventQueue.addAll(initialEvents);
// 添加电梯初始化事件
for (ElevatorGroup group : building.getElevatorGroups()) {
for (Elevator elevator : group.getElevators()) {
eventQueue.add(new ElevatorInitializationEvent(
clock.getCurrentTime(),
building.getId(),
elevator.getId()
));
}
}
}
/**
* 运行仿真直到指定时间
*/
public SimulationResult runUntil(long endTime) {
long startTime = System.currentTimeMillis();
while (!eventQueue.isEmpty() && clock.getCurrentTime() < endTime) {
SimulationEvent event = eventQueue.poll();
// 更新仿真时钟
clock.setCurrentTime(event.getTimestamp());
// 处理事件
handleEvent(event);
// 记录时间切片数据(如果需要)
if (shouldRecordTimeline()) {
recordTimelineSnapshot();
}
}
// 生成仿真结果
return generateSimulationResult(startTime);
}
/**
* 处理仿真事件
*/
private void handleEvent(SimulationEvent event) {
if (event instanceof PassengerArrivalEvent arrivalEvent) {
handlePassengerArrival(arrivalEvent);
}
else if (event instanceof CallRegistrationEvent callEvent) {
handleCallRegistration(callEvent);
}
else if (event instanceof ElevatorStopEvent stopEvent) {
handleElevatorStop(stopEvent);
}
else if (event instanceof PassengerBoardingEvent boardingEvent) {
handlePassengerBoarding(boardingEvent);
}
else if (event instanceof PassengerAlightingEvent alightingEvent) {
handlePassengerAlighting(alightingEvent);
}
else if (event instanceof ElevatorDirectionChangeEvent directionEvent) {
handleElevatorDirectionChange(directionEvent);
}
else if (event instanceof ElevatorInitializationEvent initEvent) {
handleElevatorInitialization(initEvent);
}
// 其他事件类型...
}
/**
* 处理乘客到达事件
*/
private void handlePassengerArrival(PassengerArrivalEvent event) {
// 更新状态
currentState.recordPassengerArrival(event);
// 根据控制类型处理召唤请求
for (Passenger passenger : event.getPassengers()) {
CallRequest request = createCallRequest(passenger);
// 对于目的选层系统,乘客先输入目的地
if (isDestinationControlSystem(event.getFloor())) {
handleDestinationRequest(request);
}
// 对于传统系统,乘客直接注册召唤
else {
handleStandardCallRequest(request);
}
}
// 生成后续事件(如果需要)
generateFollowUpEvents(event);
}
/**
* 处理电梯停靠事件
*/
private void handleElevatorStop(ElevatorStopEvent event) {
Elevator elevator = getElevator(event.getElevatorId());
Floor floor = building.getFloor(event.getFloor());
// 更新电梯状态
ElevatorStatus newStatus = elevator.getStatus().withDoorOpen(true);
elevator = elevator.withStatus(newStatus);
// 记录停靠开始时间
long stopStartTime = clock.getCurrentTime();
// 计算乘客流动时间
double boardingTime = calculateBoardingTime(
event.getBoardingCount(),
elevator.getDoorWidth()
);
double alightingTime = calculateAlightingTime(
event.getAlightingCount(),
elevator.getDoorWidth()
);
// 确定总停站时间
long stopDuration = determineStopDuration(
boardingTime,
alightingTime,
elevator
);
// 创建停站结束事件
eventQueue.add(new ElevatorStopCompletionEvent(
clock.getCurrentTime() + stopDuration,
building.getId(),
elevator.getId(),
event.getFloor(),
stopStartTime
));
// 更新状态
currentState.updateElevatorStop(event, stopDuration);
}
/**
* 计算乘客流动时间,考虑开门宽度影响
* "同一层站如上下乘客较多,受开门宽度影响会增加电梯停靠等待时间"
*/
private double calculateBoardingTime(int passengerCount, double doorWidth) {
if (passengerCount == 0) return 0;
// 基础上车时间(每乘客)
double baseTimePerPassenger = 1.2; // 秒
// 门宽影响系数(门越宽,流动越快)
double widthFactor = 1.0 / (0.8 + 0.4 * (doorWidth / 1.2));
// 拥挤系数(乘客越多,效率越低)
double congestionFactor = 1.0 + 0.2 * Math.log(1 + passengerCount);
return passengerCount * baseTimePerPassenger * widthFactor * congestionFactor;
}
// 其他辅助方法...
}关键实现细节:
离散事件仿真:
- 使用优先队列管理仿真事件,按时间戳排序
- 只在事件发生时推进仿真时间,提高效率
- 支持可配置的仿真速度和时间比例
事件处理流程:
- 乘客到达 → 召唤注册 → 电梯分配 → 电梯移动 → 电梯停靠 → 乘客上下 → 电梯继续运行
- 每个环节都有精确的时间模型,基于PDF中的物理参数
停站时间计算:
- 实现"停站时间 = 开门时间 + 乘客流动时间 + 关门时间"公式
- 乘客流动时间 = 乘客数量 × (上/下时间) × 拥挤系数
- 考虑开门宽度对乘客流动的影响
3.3.2 电梯控制策略实现
电梯控制策略工厂:
/**
* 电梯控制策略工厂,根据控制类型创建相应的策略实现
*/
public class ElevatorControlStrategyFactory {
public ElevatorControlStrategy createStrategy(
ElevatorGroup group,
Building building
) {
return switch (group.getControlType()) {
case SINGLE_CONTROL -> new SingleControlStrategy(group, building);
case PARALLEL_CONTROL -> new ParallelControlStrategy(group, building);
case GROUP_CONTROL -> new GroupControlStrategy(group, building);
case DESTINATION_CONTROL -> new DestinationControlStrategy(group, building);
};
}
}
/**
* 电梯控制策略接口
*/
public interface ElevatorControlStrategy {
/**
* 处理召唤请求
*/
AssignmentResult handleCallRequest(CallRequest request);
/**
* 处理目的楼层请求(仅目的选层系统)
*/
default AssignmentResult handleDestinationRequest(DestinationRequest request) {
throw new UnsupportedOperationException(
"This control strategy does not support destination requests"
);
}
/**
* 电梯状态更新
*/
void updateElevatorStatus(Elevator elevator, long currentTime);
/**
* 交通模式识别
*/
TrafficPattern recognizeTrafficPattern();
/**
* 获取当前控制参数
*/
ControlParameters getControlParameters();
}
/**
* 目的选层控制策略实现
* 实现PDF中描述的"目的地控制"系统
*/
public class DestinationControlStrategy implements ElevatorControlStrategy {
private final ElevatorGroup group;
private final Building building;
private final ODMatrixService odMatrixService;
private final TrafficPatternRecognizer trafficRecognizer;
public DestinationControlStrategy(
ElevatorGroup group,
Building building
) {
this.group = group;
this.building = building;
this.odMatrixService = new ODMatrixService(building);
this.trafficRecognizer = new TrafficPatternRecognizer();
}
@Override
public AssignmentResult handleDestinationRequest(DestinationRequest request) {
// 1. 将乘客添加到等待队列
addToWaitingQueue(request);
// 2. 识别当前交通模式
TrafficPattern trafficPattern = recognizeTrafficPattern();
// 3. 基于OD矩阵和聚类算法分配电梯
ElevatorAssignment assignment = assignElevator(
request,
trafficPattern
);
// 4. 更新状态
updateStateAfterAssignment(assignment);
return new AssignmentResult(
assignment.getElevatorId(),
assignment.getExpectedWaitTime()
);
}
/**
* 基于OD矩阵和聚类算法分配电梯
*/
private ElevatorAssignment assignElevator(
DestinationRequest request,
TrafficPattern trafficPattern
) {
// 获取当前电梯状态
List<Elevator> availableElevators = getAvailableElevators();
// 如果没有可用电梯,返回空分配
if (availableElevators.isEmpty()) {
return new ElevatorAssignment(null, Double.MAX_VALUE);
}
// 获取当前OD矩阵(用于聚类)
ODMetricMatrix currentOD = odMatrixService.getCurrentODMatrix();
// 计算每个电梯的响应时间
Map<String, Double> responseTimes = new HashMap<>();
for (Elevator elevator : availableElevators) {
double responseTime = calculateResponseTime(elevator, request, trafficPattern);
responseTimes.put(elevator.getId(), responseTime);
}
// 计算每个电梯的聚类适合度(将目的楼层相近的乘客分配到同一电梯)
Map<String, Double> clusterScores = calculateClusterScores(
request,
availableElevators,
currentOD
);
// 综合响应时间和聚类适合度
Map<String, Double> combinedScores = combineScores(
responseTimes,
clusterScores,
trafficPattern
);
// 选择最佳电梯
String bestElevatorId = findBestElevator(combinedScores);
return new ElevatorAssignment(
bestElevatorId,
responseTimes.get(bestElevatorId)
);
}
/**
* 计算聚类适合度
* "乘客聚类策略(将目的楼层相近的乘客分配到同一电梯)"
*/
private Map<String, Double> calculateClusterScores(
DestinationRequest request,
List<Elevator> elevators,
ODMetricMatrix currentOD
) {
Map<String, Double> scores = new HashMap<>();
for (Elevator elevator : elevators) {
// 获取电梯内已有乘客的目的楼层
List<Integer> existingDestinations = getExistingDestinations(elevator);
// 计算新请求与现有目的地的相似度
double similarity = calculateDestinationSimilarity(
request.getDestinationFloor(),
existingDestinations,
currentOD
);
// 相似度越高,聚类适合度越高(得分越低)
scores.put(elevator.getId(), 1.0 - similarity);
}
return scores;
}
/**
* 计算目的地相似度
*/
private double calculateDestinationSimilarity(
int newDestination,
List<Integer> existingDestinations,
ODMetricMatrix odMatrix
) {
if (existingDestinations.isEmpty()) {
return 0.0; // 无现有目的地,相似度为0
}
// 计算新目的地与现有目的地的平均距离
double totalDistance = 0.0;
for (int dest : existingDestinations) {
totalDistance += calculateFloorDistance(newDestination, dest);
}
double avgDistance = totalDistance / existingDestinations.size();
// 转换为相似度(距离越小,相似度越高)
double similarity = 1.0 / (1.0 + avgDistance);
// 考虑OD矩阵中的流量模式
double trafficFactor = 1.0;
if (odMatrix != null) {
// 如果新目的地与现有目的地在OD矩阵中有高流量,提高相似度
for (int dest : existingDestinations) {
double prob = odMatrix.getProbability(newDestination, dest);
trafficFactor = Math.max(trafficFactor, prob * 2.0);
}
}
return similarity * trafficFactor;
}
// 其他辅助方法...
}关键实现细节:
策略模式实现:
- 使用策略模式实现四种控制策略
- 每种策略实现特定的调度算法
- 策略间可互换,便于比较不同控制方式的性能
目的选层系统实现:
- 实现"乘客聚类策略(将目的楼层相近的乘客分配到同一电梯)"
- 使用OD矩阵增强聚类效果
- 多目标优化(最小化停站次数、平衡负载等)
交通模式适应:
- 实时识别当前交通模式(上行主导/下行主导/双向)
- 根据交通模式动态调整控制参数
- 实现高峰时段特殊策略
3.4 OD矩阵估计实现
3.4.1 BILS算法实现
BILS问题求解器:
/**
* BILS(带界约束的整数最小二乘)问题求解器
* 实现PDF中描述的"Box-Constrained Integer Least Squares"方法
*/
public class BILSSolver {
private static final double TIME_LIMIT_MS = 500.0; // 0.5秒执行时间限制
/**
* 求解BILS问题
*/
public BILSResult solve(BILSProblem problem) {
long startTime = System.currentTimeMillis();
// 1. 计算连续松弛解
ContinuousSolution continuousSolution = solveContinuousRelaxation(problem);
// 2. 检查连续解是否满足整数约束
if (isIntegerSolution(continuousSolution.getSolution())) {
return createIntegerResult(continuousSolution, startTime);
}
// 3. 使用分支定界法寻找最优整数解
IntegerSolution integerSolution = branchAndBound(
problem,
continuousSolution,
startTime
);
// 4. 如果在时限内未找到解,返回舍入解
if (integerSolution == null) {
return roundContinuousSolution(continuousSolution, startTime);
}
return new BILSResult(
integerSolution.getSolution(),
integerSolution.getObjectiveValue(),
System.currentTimeMillis() - startTime,
false
);
}
/**
* 求解连续松弛问题
*/
private ContinuousSolution solveContinuousRelaxation(BILSProblem problem) {
// 使用Cholesky分解求解Ax = b
Matrix A = problem.getA();
Vector b = problem.getB();
// 计算A^T*A和A^T*b
Matrix AtA = A.transpose().multiply(A);
Vector Atb = A.transpose().multiply(b);
// 使用Cholesky分解求解
CholeskyDecomposition cholesky = new CholeskyDecomposition(AtA);
Vector x = cholesky.solve(Atb);
return new ContinuousSolution(x, System.currentTimeMillis());
}
/**
* 使用分支定界法寻找最优整数解
*/
private IntegerSolution branchAndBound(
BILSProblem problem,
ContinuousSolution continuousSolution,
long startTime
) {
// 初始化分支定界
PriorityQueue<Node> queue = new PriorityQueue<>(Comparator.comparingDouble(Node::getLowerBound));
queue.add(createRootNode(problem, continuousSolution));
IntegerSolution bestSolution = null;
double bestObjective = Double.POSITIVE_INFINITY;
while (!queue.isEmpty()) {
// 检查时间限制
if (System.currentTimeMillis() - startTime > TIME_LIMIT_MS) {
break;
}
// 获取下界最小的节点
Node node = queue.poll();
// 如果下界大于当前最优解,剪枝
if (node.getLowerBound() >= bestObjective) {
continue;
}
// 检查是否为整数解
if (isIntegerSolution(node.getSolution())) {
bestObjective = node.getObjectiveValue();
bestSolution = new IntegerSolution(
node.getSolution(),
node.getObjectiveValue()
);
continue;
}
// 分支:创建两个子节点
int branchVar = selectBranchingVariable(node);
queue.addAll(createChildNodes(node, branchVar));
}
return bestSolution;
}
/**
* 将连续解舍入为整数解
* "在复杂实例中,通过将连续解舍入到最近的整数快速获得可能次优但可行的解"
*/
private BILSResult roundContinuousSolution(
ContinuousSolution continuousSolution,
long startTime
) {
// 将连续解舍入到最近的整数
Vector roundedSolution = continuousSolution.getSolution().round();
// 计算目标函数值
Vector residual = problem.getA().multiply(roundedSolution).subtract(problem.getB());
double objectiveValue = residual.norm2();
return new BILSResult(
roundedSolution,
objectiveValue,
System.currentTimeMillis() - startTime,
true // 标记为舍入解
);
}
/**
* 创建根节点
*/
private Node createRootNode(
BILSProblem problem,
ContinuousSolution continuousSolution
) {
// 获取变量边界
Matrix bounds = problem.getBounds();
// 创建根节点,初始时无变量固定
return new Node(
continuousSolution.getSolution(),
0.0, // 下界(连续松弛解的目标值)
bounds
);
}
// 内部类和辅助方法...
}关键实现细节:
BILS问题求解流程:
- 先求解连续松弛问题
- 如果连续解满足整数约束,直接返回
- 否则使用分支定界法寻找最优整数解
- 如果在时限内未找到解,返回舍入解
分支定界优化:
- 实现PDF中描述的"real relaxation based branch and bound method"
- 使用优先队列选择最有希望的节点
- 有效剪枝策略提高求解效率
随机化搜索:
- 实现PDF中"Publication[III] presents two new algorithms to find all solutions"的描述
- 在时限内尽可能多地寻找解
- 用于提高统计质量
3.4.2 CP算法实现
CP问题求解器:
/**
* CP(约束规划)问题求解器
* 实现PDF中描述的"Constraint Programming"方法
*/
public class CPSolver {
/**
* 求解CP问题
*/
public CPResult solve(CPProblem problem) {
long startTime = System.currentTimeMillis();
// 1. 创建CP模型
Model model = createCPModel(problem);
// 2. 求解模型
Solver solver = model.getSolver();
boolean solved = solver.solve();
// 3. 获取解
if (solved) {
Vector solution = extractSolution(model, problem);
double objectiveValue = calculateObjectiveValue(solution, problem);
return new CPResult(
solution,
objectiveValue,
System.currentTimeMillis() - startTime
);
} else {
// 无解情况处理
return handleNoSolution(problem, startTime);
}
}
/**
* 创建CP模型
*/
private Model createCPModel(CPProblem problem) {
Model model = new Model("Elevator OD Matrix Estimation");
// 创建决策变量
IntVar[] variables = createDecisionVariables(model, problem);
// 添加约束
addConstraints(model, variables, problem);
// 设置目标函数
setObjectiveFunction(model, variables, problem);
return model;
}
/**
* 创建决策变量
*/
private IntVar[] createDecisionVariables(Model model, CPProblem problem) {
int n = problem.getNumberOfVariables();
IntVar[] variables = new IntVar[n];
for (int i = 0; i < n; i++) {
// 根据边界设置变量范围
int lb = problem.getLowerBound(i);
int ub = problem.getUpperBound(i);
variables[i] = model.intVar("x_" + i, lb, ub);
}
return variables;
}
/**
* 添加约束
* "CP formulation is based on the assumption that passengers do not give false requests"
*/
private void addConstraints(Model model, IntVar[] variables, CPProblem problem) {
// 添加线性约束: Ax = b
for (int i = 0; i < problem.getConstraintCount(); i++) {
// 创建约束行
int[] coeffs = problem.getCoefficients(i);
int rhs = problem.getRightHandSide(i);
// 创建线性表达式
LinearExpression expr = model.scalProd(
Arrays.stream(coeffs).mapToObj(c -> model.intVar(c)).toArray(IntVar[]::new),
variables
);
// 添加等式约束
model.arithm(expr, "=", rhs).post();
}
// 添加流量守恒约束
addFlowConservationConstraints(model, variables, problem);
}
/**
* 添加流量守恒约束
*/
private void addFlowConservationConstraints(
Model model,
IntVar[] variables,
CPProblem problem
) {
int floors = problem.getNumberOfFloors();
for (int floor = 1; floor <= floors; floor++) {
// 计算从该楼层出发的流量
IntVar outFlow = calculateOutFlow(model, variables, problem, floor);
// 计算进入该楼层的流量
IntVar inFlow = calculateInFlow(model, variables, problem, floor);
// 添加守恒约束
model.arithm(outFlow, "=", inFlow).post();
}
}
/**
* 设置目标函数
*/
private void setObjectiveFunction(Model model, IntVar[] variables, CPProblem problem) {
// 最小化与连续解的偏差
IntVar[] deviations = calculateDeviations(model, variables, problem);
IntVar totalDeviation = model.sum(deviations, "");
// 设置目标: 最小化总偏差
model.setObjective(Model.MINIMIZE, totalDeviation);
}
// 其他辅助方法...
}关键实现细节:
约束规划模型:
- 基于Choco Solver等CP框架实现
- 正确建模PDF中描述的约束条件
- "CP formulation is based on the assumption that passengers do not give false requests"
流量守恒约束:
- 实现"进入某楼层的乘客数 = 离开该楼层的乘客数"约束
- 确保OD矩阵满足基本交通流原理
- 处理边界情况(如底层大厅的特殊流量)
目标函数设计:
- 最小化与连续解的偏差
- 平衡解的质量和计算效率
- 支持多目标优化
3.5 结果分析算法
3.5.1 性能指标计算
性能指标计算器:
/**
* 性能指标计算器,计算PDF中描述的各项性能指标
*/
public class PerformanceMetricsCalculator {
/**
* 计算仿真结果中的各项性能指标
*/
public SimulationResult calculateMetrics(
SimulationResult rawResult,
Building building
) {
// 1. 计算等待时间指标
WaitingTimeMetrics waitingMetrics = calculateWaitingTimeMetrics(
rawResult.getPassengerEvents(),
building
);
// 2. 计算运输时间指标
TravelTimeMetrics travelMetrics = calculateTravelTimeMetrics(
rawResult.getPassengerEvents(),
building
);
// 3. 计算系统性能指标
SystemPerformanceMetrics systemMetrics = calculateSystemPerformanceMetrics(
rawResult,
building
);
// 4. 计算5分钟输送能力
double handlingCapacity = calculateHandlingCapacity(
rawResult,
building
);
// 5. 创建最终结果
return new SimulationResult(
rawResult.getSimulationId(),
rawResult.getStartTime(),
rawResult.getEndTime(),
waitingMetrics,
travelMetrics,
systemMetrics,
handlingCapacity,
rawResult.getTimelineData()
);
}
/**
* 计算等待时间指标
* "最坏/平均等待时间及等待时间明细;乘客等待时间分布明细"
*/
private WaitingTimeMetrics calculateWaitingTimeMetrics(
List<PassengerEvent> passengerEvents,
Building building
) {
// 按楼层和时间段分组
Map<Integer, Map<TimePeriod, List<Double>>> waitingTimesByFloor = groupByFloorAndTime(
passengerEvents,
building
);
// 计算各统计量
Map<Integer, WaitingTimeStats> floorStats = new HashMap<>();
for (Map.Entry<Integer, Map<TimePeriod, List<Double>>> floorEntry : waitingTimesByFloor.entrySet()) {
int floor = floorEntry.getKey();
Map<TimePeriod, WaitingTimeStats.PeriodStats> periodStats = new EnumMap<>(TimePeriod.class);
for (Map.Entry<TimePeriod, List<Double>> periodEntry : floorEntry.getValue().entrySet()) {
periodStats.put(
periodEntry.getKey(),
calculatePeriodStats(periodEntry.getValue())
);
}
// 计算楼层总体统计
List<Double> allTimes = floorEntry.getValue().values().stream()
.flatMap(List::stream)
.collect(Collectors.toList());
WaitingTimeStats.FloorStats floorOverall = calculatePeriodStats(allTimes);
floorStats.put(floor, new WaitingTimeStats(floorOverall, periodStats));
}
// 计算建筑总体统计
List<Double> allWaitingTimes = waitingTimesByFloor.values().stream()
.flatMap(m -> m.values().stream())
.flatMap(List::stream)
.collect(Collectors.toList());
WaitingTimeStats.BuildingStats buildingStats = calculatePeriodStats(allWaitingTimes);
return new WaitingTimeMetrics(buildingStats, floorStats);
}
/**
* 计算单个时间段的统计量
*/
private WaitingTimeStats.PeriodStats calculatePeriodStats(List<Double> times) {
if (times.isEmpty()) {
return new WaitingTimeStats.PeriodStats(0, 0, 0, 0, new double[10]);
}
// 计算平均值
double sum = 0;
for (double time : times) {
sum += time;
}
double average = sum / times.size();
// 计算最大值
double max = Collections.max(times);
// 计算百分位数
List<Double> sorted = new ArrayList<>(times);
Collections.sort(sorted);
double[] percentiles = new double[10];
for (int i = 0; i < 10; i++) {
int index = (int)Math.ceil(i * 0.1 * sorted.size()) - 1;
index = Math.max(0, Math.min(index, sorted.size() - 1));
percentiles[i] = sorted.get(index);
}
return new WaitingTimeStats.PeriodStats(
times.size(),
average,
max,
calculateStandardDeviation(times, average),
percentiles
);
}
/**
* 计算5分钟输送能力(HC)
* "5分钟运输能力(%)"
*/
private double calculateHandlingCapacity(
SimulationResult result,
Building building
) {
// 获取早高峰时段的数据(通常为8:00-8:05)
List<PassengerEvent> peakEvents = filterPeakHourEvents(
result.getPassengerEvents(),
PeakHour.MORNING
);
// 计算5分钟内运送的乘客数量
int transportedIn5Min = countTransportedPassengers(peakEvents, 300);
// 获取建筑总人数
int totalPopulation = building.getTotalPopulation();
// 计算HC百分比
return (transportedIn5Min * 100.0) / totalPopulation;
}
/**
* 生成时间切片数据,用于前端可视化
* "按指定时间切片提供乘客及电梯数据以便于前端用动画进行可视化展示"
*/
public TimelineData generateTimelineData(
SimulationResult rawResult,
int timeSliceSeconds
) {
TimelineData timelineData = new TimelineData();
// 按时间切片组织数据
long currentTime = rawResult.getStartTime();
while (currentTime < rawResult.getEndTime()) {
long sliceEnd = currentTime + (timeSliceSeconds * 1000);
// 获取该时间切片内的电梯状态
List<ElevatorState> elevatorStates = getElevatorStatesAtTime(
rawResult.getTimelineData(),
currentTime,
sliceEnd
);
// 获取该时间切片内的乘客状态
List<PassengerState> passengerStates = getPassengerStatesAtTime(
rawResult.getPassengerEvents(),
currentTime,
sliceEnd
);
// 创建时间切片
TimeSlice timeSlice = new TimeSlice(
currentTime,
elevatorStates,
passengerStates
);
timelineData.addTimeSlice(timeSlice);
currentTime = sliceEnd;
}
return timelineData;
}
// 其他辅助方法...
}关键实现细节:
等待时间分析:
- 计算最坏/平均等待时间及明细(按楼层、时间段)
- 生成乘客等待时间分布(百分位数、直方图)
- 分析高峰时段等待时间特性
运输时间分析:
- 计算最坏/平均运输时间及明细
- 生成乘客运输时间分布
- 分析门到门时间特性
系统性能指标:
- 计算5分钟输送能力(HC)
- 计算电梯利用率
- 计算平均停站次数
- 估算能耗
时间切片数据生成:
- 按1秒/5秒/30秒时间切片组织数据
- 包含电梯位置、状态数据
- 包含乘客等待和运输过程跟踪数据
- 为前端动画提供结构化数据
3.5.2 PDF报告生成
PDF报告生成器:
/**
* PDF报告生成器,生成包含详细分析结果的PDF文档
*/
public class PdfReportGenerator {
private final PdfDocument pdfDocument;
private final DocumentContentFactory contentFactory;
public PdfReportGenerator() {
this.pdfDocument = new PdfDocument(new PdfWriter("simulation_report.pdf"));
this.contentFactory = new DocumentContentFactory();
}
/**
* 生成仿真报告
*/
public void generateReport(SimulationResult result, Building building) {
Document document = new Document(pdfDocument);
// 添加标题页
addTitlePage(document, building, result);
// 执行摘要
addExecutiveSummary(document, result, building);
// 详细性能指标
addPerformanceMetricsSection(document, result, building);
// 电梯利用率热力图
addElevatorUtilizationSection(document, result);
// 不同控制策略对比(如果是多策略仿真)
if (result.isMultiStrategy()) {
addStrategyComparisonSection(document, result);
}
// 优化建议
addOptimizationRecommendationsSection(document, result, building);
// 关闭文档
document.close();
}
/**
* 添加标题页
*/
private void addTitlePage(Document document, Building building, SimulationResult result) {
// 创建标题页内容
IBlockElement titleContent = contentFactory.createTitlePageContent(
building,
result
);
document.add(titleContent);
}
/**
* 添加执行摘要
*/
private void addExecutiveSummary(
Document document,
SimulationResult result,
Building building
) {
// 创建摘要内容
IBlockElement summaryContent = contentFactory.createExecutiveSummaryContent(
result,
building
);
document.add(summaryContent);
}
/**
* 添加性能指标部分
*/
private void addPerformanceMetricsSection(
Document document,
SimulationResult result,
Building building
) {
// 标题
document.add(new Paragraph("性能指标分析")
.setFontSize(16)
.setBold()
.setMarginTop(20));
// 等待时间分析
addWaitingTimeAnalysis(document, result);
// 运输时间分析
addTravelTimeAnalysis(document, result);
// 系统性能指标
addSystemPerformanceAnalysis(document, result, building);
}
/**
* 添加等待时间分析
*/
private void addWaitingTimeAnalysis(Document document, SimulationResult result) {
// 标题
document.add(new Paragraph("等待时间分析")
.setFontSize(14)
.setBold()
.setMarginTop(15));
// 创建等待时间图表
Image waitingTimeChart = contentFactory.createWaitingTimeChart(
result.getWaitingTimeMetrics()
);
// 添加图表
document.add(waitingTimeChart);
// 添加等待时间分布表格
Table waitingTimeTable = contentFactory.createWaitingTimeTable(
result.getWaitingTimeMetrics()
);
document.add(waitingTimeTable);
}
/**
* 添加电梯利用率热力图
*/
private void addElevatorUtilizationSection(Document document, SimulationResult result) {
// 标题
document.add(new Paragraph("电梯利用率热力图")
.setFontSize(16)
.setBold()
.setMarginTop(20));
// 创建热力图
Image utilizationHeatmap = contentFactory.createUtilizationHeatmap(
result.getSystemPerformanceMetrics()
);
// 添加热力图
document.add(utilizationHeatmap);
// 添加解释
document.add(new Paragraph("图中显示了各电梯在不同时间段的利用率情况。"
+ "颜色越深表示利用率越高。")
.setFontSize(10)
.setMarginTop(5)
.setItalic());
}
// 其他辅助方法...
}关键实现细节:
报告结构:
- 执行摘要和关键发现
- 详细的性能指标图表(等待时间、运输时间分布等)
- 电梯利用率热力图
- 不同控制策略的对比分析
- 优化建议
图表生成:
- 使用JFreeChart生成高质量图表
- 等待时间分布直方图
- 运输时间百分位数图表
- 电梯利用率热力图
多策略对比:
- 支持不同控制策略的并行仿真
- 生成策略对比表格和图表
- 提供统计显著性分析
专业排版:
- 符合工程文档标准的排版
- 清晰的标题层次结构
- 适当的图表标注和解释
以上是电梯交通量仿真分析软件后端开发文档的详细设计部分,涵盖了建筑与电梯配置、乘客流量生成、仿真引擎核心逻辑、OD矩阵估计实现和结果分析算法等关键模块。文档严格基于PDF研究资料和电梯工程最佳实践设计,确保了系统对真实电梯交通行为的准确模拟,特别是对批次到达模型、OD矩阵估计和交通模式识别的实现,直接参考了PDF中的研究成果。