Hình 37. Giao diện màn hình Vận tải
Trên đây là thiết kế giao diện màn hình Vận tải - Lên lộ trình giao hàng. Giao diện
gồm các thành phần sau:
- Màn hình bản đồ: đây chính là bản đồ của Google hỗ trợ định tuyến đường đi
của
các lộ trình giao hàng.
- Pop-up Lên lộ trình giao hàng: Để lên lộ trình cho các Đơn hàng, điều kiện
cần:
là người dùng cần chọn Tổ chức ở đây là Chi nhánh và điều kiện đủ là Ngày chính là Ngày đơn hàng. Nếu thiếu một trong hai điều kiện, Lộ trình giao hàng sẽ không được tạo lên.
Trong phạm vi bài toán nghiên cứu, Kho được quản lý bởi Chi nhánh hay nói cách
khác Chi nhánh là Tổ chức cấp trên của Kho mà các Đơn hàng thuộc Kho. Vì vậy, để lên lộ trình giao hàng cho các Đơn hàng thì cần chọn Chi nhánh, như vậy tất cả các Đơn
hàng thuộc Kho được quản lý bởi Chi nhánh đó sẽ được lên lộ trình. Và Ngày đơn hàng để quyết định những Đơn hàng của ngày nào sẽ được lên lộ trình.
Hình 38. Giao diện màn hình Tối ưu lộ trình
Trên đây là thiết kế giao diện màn hình Vận tải - Tạo lộ trình tối ưu. Giao diện gồm các thành phần sau:
- Màn hình bản đồ Lộ trình: Hiển thị các Lộ trình giao hàng, có bao nhiêu Lộ
trình
trên Thanh lộ trình sẽ có bấy nhiêu Lộ trình được hiển thị trên bản đồ Lộ trình. Các chỉ số 1, 2, 3.. .tương ứng với một điểm giao hàng và biểu tượng hình ngôi nhà chính là Kho.
- Bảng Dòng thời gian - Thanh Lộ trình: Hiển thị các lộ trình được tối ưu cùng
các
phương tiện đã được gán để phân phối các đơn hàng.Bao gồm: thông tin lộ trình, thông tin phương tiện, thông tin tài xế, thông tin khách hàng tại mỗi điểm giao.
- Button Chốt lộ trình: để thực hiện các tác vụ giao hàng, người dùng cần chốt
các
lộ trình đã lên. Khi người dùng nhấn Chốt lộ trình các tác vụ giao hàng được sinh ra và tài xế giao hàng có thể bắt đầu lộ trình giao hàng của mình.
- Button Gỡ chốt: Đối với những Lộ trình đã chốt, nếu cần bất cứ sự chỉnh sửa
nào,
khi người dùng nhấn Gỡ chốt, các đơn hàng thuộc Lộ trình đó mới được phép chỉnh sửa.
- Button Khởi tạo: Khi các đơn hàng đã được chỉnh sửa, để tối ưu lại lộ trình cho
các đơn hàng đó người dùng nhấn Khởi tạo. Khi đó, người dùng cần chọn lại Tổ chức Chi nhánh và Ngày đơn hàng để tối ưu lộ trình.
CHƯƠNG 3
GIẢI PHÁP TỐI ƯU LỘ TRÌNH GIAO HÀNG 3.1. Một số giải pháp giúp tối ưu lộ trình.
Để giải quyết các bài toán tối ưu trong quản lý vận tải, phần mềm Abivin vRoute đã sử dụng OR Tools - một phần mềm mã nguồn mở chuyên để giải các bài toán tối ưu của Google.
Giới thiệu về OR Tools:
- OR Tools là phần mềm mã nguồn mở giúp giải quyết các bài toán tối ưu hoá tổ
hợp, hỗ trợ tìm kiếm giải pháp tốt nhất cho một vấn đề trong số rất nhiều các giải pháp khả thi. Các vấn đề tối ưu mà OR Tools giải quyết như:
+ Định tuyến phương tiện: Tìm các tuyến đường tối ưu cho đội xe nhận và giao hàng bị ràng buộc bởi các điều kiện như tải trọng của xe (ví dụ: xe không thể chứa nhiều
hơn 20 tạ) hoặc tất cả việc giao hàng phải thực hiện trong khung thời gian cụ thể (ví dụ: các đơn hàng trên lộ trình phải giao trong khung thời gian 2h -3h).
+ Lên kế hoạch: Lên kế hoạch tối ưu cho một nhóm các tác vụ phức tạp, một số tác vụ cần phải được thực hiện trước một tác vụ khác.
+ Đóng gói hàng: Đóng gói càng nhiều các loại hàng hoá khác nhau càng tốt, vào một thùng có dung lượng tối đa.
- Trong hầu hết các trường hợp, những vấn đề như thế này có rất nhiều các giải
pháp khả thi vì vậy có quá nhiều kết quả tìm kiếm. Để khắc phục điều này, OR Tools đã sử dụng các thuật toán tiên tiến để thu hẹp bộ tìm kiếm, nhằm tìm ra các giải pháp tối ưu.
- OR Tools bao gồm các thư viện bộ giải như:
+ Constraint Programing - Tối ưu ràng buộc: Một tập hợp các kỹ thuật để tìm ra các giải pháp khả thi cho một vấn đề được thể hiện dưới dạng các ràng buộc.
+ Linear and Mixed-Integer Programming - Tối ưu tuyến tính và hỗn hợp: Tìm giá trị tối ưu của hàm mục tiêu tuyến tính.
+ Vehicle Routing - Định tuyến xe: Xác định các tuyến tối ưu từ các ràng buộc. + Graph Algorithms - Thuật toán đồ thị: Tìm đường đi ngắn nhất trong biểu đồ, luồng chi phí tổi thiểu.
[(45 6,
320), # location 0 - the depot
(228 , 0), # location 1 (912 , 0), # location 2 (0. # location 3 (114, 80), # location 4 (570 , 160), # location 5 (798 , 160), # location 6 (342 , 24B), # location 7 (684 , 24B), # location B (570 , 400). # location 9 (912 , 400). # location 19 (114, 480). # location 11 (228 , 480), # location 12 (342 , 560), # location 13 (684 , 560), # location 14 (0, 640), # location 15 (798 , 64B) ] # location 16
Abivin vRoute đã sử dụng bộ thư viện Vehicle Route - Định tuyến xe để giải quyết
bài toán tối ưu lộ trình, các giải pháp được nêu ra dưới đây:
3.1.1. Vehicle Routing Problem — Vấn đề định tuyến xe
Trong Bài toán định tuyến xe (VRP), mục tiêu là tìm ra các lộ trình tối ưu cho
nhiều
phương tiện ghé thăm một tập hợp các địa điểm giao hàng.
Nhưng ý nghĩa của "các lộ trình tối ưu" đối với VRP là gì? Một câu trả lời là các tuyến đường có tổng khoảng cách bé nhất. Giả định, không có ràng buộc nào khác, giải pháp tối ưu lộ trình là giảm thiểu chiều dài của các tuyến đường trong số tất cả các phương tiện với mục tiêu là hoàn thành tất cả việc giao hàng càng sớm càng tốt hay nói cách khác đó là định tuyến quãng đường của xe đến các cửa hàng sao cho tổng quãng đường của các xe là bé nhất.
Dưới đây là cách thức giải quyết bài toán định tuyến phương tiện - Vehicle Routing
Problem (VRP) bằng OR Tools như sau:
Giả sử, mỗi điểm giao được biểu thị bằng các chỉ số 1, 2, 3... tương ứng với một khách hàng và vị trí của Kho là 0 (Hình 39).
Hình 39. Sơ đồ minh hoạ các điểm giao hàng
Thứ nhất, tạo dữ liệu đầu vào, bao gồm:
- distance matrix: một mảng ma trận khoảng cách hai chiều giữa các vị trí kho,
khách hàng theo mét
- num locations: số các vị trí - địa điểm khách hàng
52
- Imiiiveliicles: số lượng phương tiện đang hoạt động thuộc Kho
- depot: chỉ số của Kho - vị trí nơi mà tất cả các phương tiện bắt đầu và kết thúc
lộ trình giao hàng
C ɪn
static class DataModel {
public final long[][] distanceMatrix - {
{θ, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}. {548, 0, 684, 308, 194 502,730, 354, 696, 742, 1084,594, 480, 674, 1016, 868, 121θ}, {776, 684, 0, 992, 878, 502,274, 810, 468, 742, 400, 1278,1164, 1138, 788, 1552, 754}, {696, 308, 992, 0, 114, 658,878, 502, 844, 890, 1232,514, 628, 822, 1164,560,1358}, {582, 194, 878, 114, 8 536,764, 388, 730, 776, 1118,488 514, 708, 1050, 674 1244}, {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, {5Θ2, 730, 274, 878, 764,228, 0, 536, 194, 468, 354,1884, 89θ, 856, 514, 1278, 48θ}, {194, 354, 810, 502, 388,308, 536, 0, 342, 388, 73β,468, 354, 320, 662, 742, 856}. {3Θ8, 696, 468 844, 730,194, 194^ 342, 0, 274, 388 810, 696, 662, 320, 1884, 514}, {194, 742,742, 890, 776, 248, 468, 388, 274, 8, 342,536, 422, 388, 274, 810, 468), {536, 1884, 488, 1232, 1118, 582, 354, 738, 388, 342, 8, 878, 764, 738, 388, 1152, 354}, {5Θ2, 594, 1278, 514, 488, 776, 1884, 468, 818, 536, 878, θl 114, 388, 658, 274, 844}, {388, 488, 1164, 628, 514, 662, 898, 354, 696, 422, 764, 114, 8, 194, 536, 388, 738}, {354, 674, 1138, 822,788, 628, 856, 328, 662, 388, 738, 388, 194, 8, 342, 422, 536}, {468, 1816, 788 1164, 1858, 514, 514, 662, 328, 274, 388, 658, 536, 342, 8, 764, 194}, {776, 868, 1552 568, 674, 1858, 1278, 742, 1884, 818, 1152, 274, 388, 422, 764, 8, 798}, {662, 1218, 754, 1358, 1244, 788, 488, 856, 514, 468, 354, 844, 738, 536, 194, 798, θ}, }.
public final Int VehicleNumber - 4;
public final int depot = 8;
Trong ví dụ này mảng ma trận khoảng cách cho 16 địa điểm khách hàng tương ứng
với 16 x 16 = 256 khoảng cách giữa các địa điểm. Số phương tiện được truyền vào là 4 và chỉ số của Kho là 0.
Thứ hai, truyền toạ độ vị trí:
Để tính toán ma trận khoảng cách, công cụ OR-Tools đã định ra các toạ độ vị trí x,y được truyền vào tương ứng với các vị trí được hiển thị, ví dụ như sau:
Truyền dữ liệu về các vị trí, OR-Tools sẽ định nghĩa các vị trí từ đó biểu thị bằng các chỉ số (0, 1, 2...). Sau đó tính khoảng cách giữa các vị trí bằng cách xác định khoảng
cách giữa hai điểm (x1, y1) và (x2, y2), được tính bằng công thức: |x1-x2| + |y1-y2|.
Thứ ba, xác định distance callback:
Distance callback function là một hàm gọi lại khoảng cách. Callback finction được
dùng đề thực hiện các tác vụ bất đồng bộ. Khi nó được truyền vào một function khác dưới dạng tham số sẽ có tác dụng trả về khoảng cách giữa các vị trí. Nó cũng được dùng
để xác định chi phí đi lại dựa trên khoảng cách của quãng đường.
C C
final int transitcallbackindex =
routing.register!ransitCallback((long fromlndex, long tolndex) -> {
// Convert from routing variable Index to user Nodeindex.
int fromNode = manager.index!oNode(fromlndex);
int toNode = manager.IndexToNode(tolndex);
return data.distanceMatrix[fromNode][toNode];
});
routing .setArcCostEValuatorOfAHVehicles(transitcallbackindex) ;
Thứ tư, thêm kích thước khoảng cách:
Để giải quyết bài toán định tuyến phương tiện, cần tạo thêm một kích thước khoảng
cách, tính toán các khoảng cách giữa Kho tới khách hàng và giữa khách hàng tới khách hàng dọc theo lộ trình của phương tiện.
I routing.addDimeπsioπ(transitcallbackindex, 0, 300θ,
true, /Ị start Mill to zero
"Distance’);
RoutingDiiIieriSion (IistanceDiIIiension = routing .getMutableDimension("Distance");
d LStanceDiniension. SetGlobaispanCostCoefficient(jIDD) ;
Cuối cùng là thực hiện in ra lộ trình giao hàng đến các cửa hàng của các phương
tiện, chính là output - đầu ra với tổng quãng đường nhỏ nhất từ số lượng xe và số lượng các vị trí kho và khách hàng đã truyền vào.
e ŋ
III Φbrief Print the solution.
static void printSol□tion(
DataModel data, RoutingModel routing, RoutingIndexManager manager. Assignment solution) {
Il Inspect solution.
long InaxRouteDistance = 0;
I
for (int i = θ; i < data.VehicleNumber; **i) {
long index = routing.start(i);
logger.info("Route for Vehicle ” + i +
long TOUteDistance = 0;
String route = "";
while (!routing.IsEnd(Index)) {
route += manager.IndexToNode(index) + " -> ";
long PreviousIndex = index;
index = solution.value(routing.nextVar(index));
TOUteDistance += routing .getArcCostForVehicle(previousIndex, index, i);
}
logger.info(route + manager.IndexToNode(Index));
logger.info( "Distance of the route: '' + TOUteDistance + "m'');
IiiaxRouteDistance = Math.max(routeDistance, InaxRouteDistance);
}
logger.info("Maximum of the route distances: ,,+ IiiaxRouteDistance + "m");
Sau khi chạy chương trình thuật toán, kết quả đầu ra là các lộ trình có tổng quãng đường là bé nhất. Một lộ trình giao hàng được bắt đầu tại Kho và cũng kết thúc tại Kho.
Dưới đây là các lộ trình được lên kế hoạch cho 4 phương tiện như sau:
Route for vehicle 0:
a -> 8 -> 6 -> 2 -> 5 -> 0 Distance of route: 1552m Route for vehicle 1: a -> 7 -> 1 -> 4 -> 3 -> 0 Distance of route: 1552m Route for vehicle 2:
B -> 9 -> IB -> 16 -> 14 -> 0 Distance of route: 1552m Route for vehicle 3:
B -> 12 -> 11 -> 15 -> 13 -> B
Distance of route: 1552m
Total distance of all routes: 6208ιr∣
Lộ trình của Phương tiện 0: bắt đầu từ vị trí Kho có chỉ số là 0 đến vị trí khách hàng có chỉ số là 8, rồi đến vị trí khách hàng có chỉ số là 6, tiếp đến đến vị trí khách hàng có chỉ số là 2, đến vị trí khách hàng có chỉ số là 5 và trở về Kho. Chiều dài quãng đường lộ trình của Phương tiện 0 là 1552m.
Tương tự, với các lộ trình của Phương tiện 1,2 và 3. Từ đó, có tổng quãng đường ngắn nhất của 4 phương tiện là 6208 m.
Dưới đây là sơ đồ mô phỏng lộ trình đã được tạo ra sau quá trình tối ưu:
Hình 40. Sơ đồ mô phỏng các lộ trình tối ưu VRP
3.1.2. Capacity Constraints — Hạn chế tải trọng
Đối với các Doanh nghiệp lớn, mỗi ngày có hàng trăm đơn hàng cần đi giao và tương ứng với hàng trăm khách hàng nhận hàng. Vì vậy số lượng mặt hàng càng lớn thì thể tích hoặc/và khối lượng của đơn hàng càng nhiều mà các phương tiện có khả năng chuyên chở hạn chế nên vấn để đặt ra là làm thế nào để việc giao nhận các mặt hàng đáp
ứng số lượng xe ít nhất nhưng lại giao đến nhiều khách hàng nhất mà không vượt quá sức chứa của các phương tiện với mục tiêu giảm thiểu chi phí vận tải.
Dưới đây là cách thức giải quyết bài toán định tuyến phương tiện với hạn chế về tải trọng - Capacitated Vehicle Routing Problem (CVRP) bằng OR Tools như sau:
Bài toán CVRP được mở rộng từ bài toán VRP trước đó. Ví dụ, tại mỗi địa điểm sẽ có thêm nhu cầu tương ứng với số lượng mặt hàng đã đặt và mỗi chiếc xe có tải trọng
là 15 (Hình 41)
Hình 41. Sơ đồ các điểm giao hàng tương ứng với tải trọng
Thứ nhất, tạo dữ liệu đầu vào:
- distance matrix: một mảng ma trận khoảng cách hai chiều giữa các vị trí kho,
khách hàng theo mét
- num locations: số các vị trí - địa điểm khách hàng
- num_vehicles: số lượng phương tiện đang hoạt động thuộc Kho
- depot: chỉ số của Kho - vị trí nơi mà tất cả các phương tiện bắt đầu và kết thúc
lộ trình giao hàng
- demands: mỗi vị trí có nhu cầu về mặt hàng tương ứng với số lượng đã đặt
- capacity: sức chứa tối đa của phương tiện
Các dữ liệu đầu vào được giữ nguyên như đối với bài toán VRP, ở bài toán CVRP OR Tools thêm hai dữ liệu đầu vào đó là demands và capacity:
public final Lor⅜g∣] demands = <θ, 1r 1r2, 4. 2, 4, S, B, 1.2, 1, 2, 4, 4r 8, 8};
public final loπg[] VehicleCapacities = {1Ξ, 15, 15, 15};
Trong ví dụ này mảng ma trận khoảng cách cho 16 địa điểm khách hàng tương ứng
với 16 x 16 = 256 khoảng cách giữa các địa điểm. Số phương tiện được truyền vào là 4 và chỉ số của Kho là 0. Tương ứng với 16 khách hàng là 16 nhu cầu số lượng hàng hoá. Sức chứa tối đa của 4 xe là bằng nhau bằng 15.
Route for vehicle 0:
0 Load (15) -> 1 Load (14) -> 4 Load (10) -> 3 Load (8) -> 15 Load (0) -> 0 Load (0)
Distance of the route: 2192m Load of the route: 15
Route for vehicle 1:
0 Load (15) -> 14 Load (11) -> 16 Load (3) -> 10 Load (1) -> 2 Load (0) -> 0 Load (0) Distance of the route: 2192m
Load of the route: 15
Thứ hai, thêm demand callback và capacity constraint: các hàm này sẽ trả về nhu
cầu tức là khối lượng và/hoặc thể tích của hàng hoá tại mỗi vị trí và các ràng buộc về tải
trọng của phương tiện.
. . e l∏
final int demandcallbackindex = routing.FegisterUnaryTransitCallback((long fromlndex) -> {
// Convert from routing variable Index to user Nodeindex,
int fromNode = manager.indexT0N0de(fromlndex);
return data.demands[fromNode];
}): ' '
routing.addDimensionWithVehicleCapacity(demandCallbackIndex, 0, Ilnull capacity slack data.vehiclecapacities, // vehicle maximum capacities
true, Ilstart cumul to zero
"Capacity");
Cuối cùng, in ra được lộ trình tối ưu cho các xe trong đó tải trọng của mỗi xe là
tối đa.
C C
III i⅛rief Print the solution.
static void printSolution(
DataModel data, RoutingModel routing, RoutingIndexManager manager. Assignment solution) {
IlInspect solution.
long totalDιstance = e;
long totalLoad = θ;
I
for (int i = 0; i < data.VehicleNumber; ++1) {
long index = routing.start(i);
logger.info("Route for Vehicle ■ + i +
long routeDistance = 0;
long TOUteLoad = θ;
String route = "";