“Anàlisi multiescala de l’oferta de transport públic (TMB i FGC) mitjançant dades GTFS i demanda horària”
Descripció tècnica del model complet de transport públic de Barcelona, redactada:
Model integral d’anàlisi i optimització del transport públic de Barcelona (TMB i FGC)
Aquest model en MATLAB permet fusionar les dades del sistema GTFS amb la demanda horària real estimada per a cada línia de transport públic. Està dissenyat per a avaluar i optimitzar la capacitat operativa de les línies gestionades per TMB i FGC segons diferents franges horàries.
Components principals del model:
1. Paràmetres bàsics i freqüències horàries:
Defineix la capacitat dels vehicles (ex. 150 passatgers) i la freqüència mitjana per hora segons el dia i la franja (pico, vall, etc.).
2. Importació de dades GTFS:
Llegeix els fitxers routes.txt i trips.txt per identificar les línies actives i associar-les al seu nom llarg.
3. Importació de demanda horària:
Carrega un fitxer Excel amb la demanda estimada per línia i franja horària (demanda_horaria.xlsx).
4. Classificació per operador:
Assigna automàticament cada línia a TMB o FGC segons el seu nom.
5. Modelatge de recursos disponibles:
Defineix el nombre total de vehicles i conductors disponibles per operador, i distribueix proporcionalment la flota per línia.
6. Càlcul d’oferta actual, òptima per arrodoniment, i òptima per programació lineal:
Es compara la demanda real amb tres tipus d’oferta:
Oferta actual (basada en la freqüència fixa).
Oferta recomanada per arrodoniment (demanda / capacitat).
Oferta òptima resolta amb optimització (linprog), tenint en compte les limitacions reals de flota i màxims per línia.
7. Visualització automàtica:
Per a cada franja, es genera una gràfica comparativa de demanda vs oferta (actual, arrodonida i òptima) i la diferència entre elles.
8. Exportació automàtica:
Per cada franja horària, el model exporta:
Una gràfica PNG amb les comparacions.
Un fitxer Excel txt o csv amb totes les dades, diferències i ofertes calculades.
Aplicacions pràctiques:
Planificació operativa i assignació de recursos.
Identificació de desequilibris entre demanda i oferta.
Simulació d’escenaris de reforç o reducció de servei.
Suport a la presa de decisions per millorar l’eficiència i sostenibilitat del sistema.
clc; clear;
%% PARÁMETROS BÁSICOS
capacidad_vehiculo = 150; % pasajeros por vehículo
frecuencia.Lunes_Pico = 6;
frecuencia.Lunes_Valle = 3;
frecuencia.Sabado_Pico = 4;
frecuencia.Sabado_Valle = 2;
frecuencia.Domingo_Total = 2;
franjas = fieldnames(frecuencia);
%% VALIDACIÓN DE ARCHIVOS
if ~exist('routes.txt', 'file') || ~exist('trips.txt', 'file') || ~exist('demanda_horaria.xlsx', 'file')
error('Faltan uno o más archivos requeridos.');
end
%% CARGA DE DATOS GTFS
routes = readtable('routes.txt');
trips = readtable('trips.txt');
lineas_unicas = unique(trips.route_id);
n = length(lineas_unicas);
nombre_linea = strings(n,1);
nombre_linea_norm = strings(n,1);
for i = 1:n
linea_id = lineas_unicas(i);
nombre = routes.route_long_name(routes.route_id == linea_id);
if isempty(nombre)
nombre_linea(i) = "Línea " + string(linea_id);
else
nombre_linea(i) = nombre(1);
end
nombre_linea_norm(i) = lower(strtrim(nombre_linea(i)));
end
%% CARGA DE DEMANDA HORARIA
demanda_horaria = readtable('demanda_horaria.xlsx');
lineas_excel = lower(strtrim(string(demanda_horaria.Linea)));
%% RECURSOS POR OPERADOR
recursos.TMB.flota_total = 800;
recursos.TMB.conductores = 1200;
recursos.FGC.flota_total = 150;
recursos.FGC.conductores = 300;
operador_linea = strings(n,1);
for i = 1:n
if contains(nombre_linea_norm(i), "metro") || contains(nombre_linea_norm(i), "bus")
operador_linea(i) = "TMB";
elseif contains(nombre_linea_norm(i), "fgc")
operador_linea(i) = "FGC";
else
operador_linea(i) = "TMB";
end
end
lineas_TMB = operador_linea == "TMB";
lineas_FGC = operador_linea == "FGC";
vehiculos_TMB = floor(recursos.TMB.flota_total / sum(lineas_TMB)) * ones(sum(lineas_TMB),1);
vehiculos_FGC = floor(recursos.FGC.flota_total / sum(lineas_FGC)) * ones(sum(lineas_FGC),1);
vehiculos_disponibles = zeros(n,1);
vehiculos_disponibles(lineas_TMB) = vehiculos_TMB;
vehiculos_disponibles(lineas_FGC) = vehiculos_FGC;
oferta_maxima = vehiculos_disponibles * capacidad_vehiculo;
fprintf('\n--- OFERTA TEÓRICA POR LÍNEA ---\n');
for i = 1:n
fprintf('%s (%s): Máx Vehículos = %d | Máx Oferta = %d pas/h\n', ...
nombre_linea(i), operador_linea(i), vehiculos_disponibles(i), oferta_maxima(i));
end
%% BUCLE POR FRANJAS HORARIAS
for f = 1:length(franjas)
franja = franjas{f};
freq = frecuencia.(franja);
demanda_franja = zeros(n,1);
oferta_franja = freq * capacidad_vehiculo * ones(n,1);
for i = 1:n
match = strcmp(lineas_excel, nombre_linea_norm(i));
if any(match)
demanda_franja(i) = demanda_horaria{match, franja};
else
demanda_franja(i) = 0;
end
end
diferencia = demanda_franja - oferta_franja;
x_opt_round = round(demanda_franja / capacidad_vehiculo);
oferta_opt_round = x_opt_round * capacidad_vehiculo;
diferencia_round = demanda_franja - oferta_opt_round;
%% OPTIMIZACIÓN AVANZADA CON LINPROG
f_lp = abs(demanda_franja / capacidad_vehiculo - 1);
A = [
double(lineas_TMB)';
double(lineas_FGC)';
eye(n)
];
b = [
recursos.TMB.flota_total;
recursos.FGC.flota_total;
vehiculos_disponibles
];
lb = zeros(n,1);
ub = vehiculos_disponibles;
options = optimoptions('linprog','Display','none');
[x_lin, ~] = linprog(f_lp, A, b, [], [], lb, ub, options);
x_opt_lin = round(x_lin);
oferta_opt_lin = x_opt_lin * capacidad_vehiculo;
diferencia_opt = demanda_franja - oferta_opt_lin;
%% VISUALIZACIÓN Y GUARDADO
figure('Name', franja, 'NumberTitle', 'off');
bar([demanda_franja oferta_franja oferta_opt_round oferta_opt_lin diferencia_round diferencia_opt]);
legend('Demanda','Oferta Actual','Oferta Round','Oferta LP','Dif Round','Dif LP');
xticklabels(nombre_linea);
xtickangle(45);
ylabel('Pasajeros por hora');
title(['Oferta vs Demanda - ' strrep(franja,'_',' ')]);
grid on;
saveas(gcf, ['oferta_vs_demanda_' franja '.png']);
%% EXPORTAR EXCEL
resumen = table(nombre_linea, operador_linea, demanda_franja, ...
oferta_franja, oferta_opt_round, oferta_opt_lin, ...
diferencia_round, diferencia_opt, ...
'VariableNames', {'Linea','Operador','Demanda','Oferta_Actual','Oferta_Round','Oferta_LP','Dif_Round','Dif_LP'});
writetable(resumen, ['resumen_' franja '.xlsx']);
%% INFORME CONSOLA
fprintf('\n=== %s ===\n', strrep(franja, '_', ' '));
for i = 1:n
fprintf('%s (%s): Demanda = %d | Actual = %d | Round = %d | LP = %d | DifLP = %+d\n', ...
nombre_linea(i), operador_linea(i), demanda_franja(i), ...
oferta_franja(i), oferta_opt_round(i), oferta_opt_lin(i), diferencia_opt(i));
end
end
Comments
Post a Comment