Sometimes utility standards or PLS-CADD graphical sagging requirements necessitate that the different transmission line phases be divided into different feature codes. However, depending on who processes the LiDAR data, the LiDAR received from transmission line surveys may or may not include the phases on different feature codes. Therefore, in these scenarios, it falls on the designer to separate the data themselves. In the event of large data sets or complex phase rolls, this process may be tedious and time consuming.

In order to determine some possible form of automation for this problem, I looked into the available clustering algorithms in the scikit-learn Python library. Based on my findings, the DBSCAN model seems to work for these purposes and is quite performant in comparison to some of the other clustering models that the library contains.

This post provides a sample of how the DBSCAN model can be leveraged to separate different wire phases within transmission line LiDAR.

Import Statements

The following statements import numpy for handling the numeric arrays in the sample; scikit-learn, which includes the DBSCAN model used for clustering the points; and matplotlib, which will be used for plotting the data.

%matplotlib notebook
import numpy as np
from sklearn import cluster
import matplotlib.pyplot as plt
import matplotlib.colors as colors

Sample Phase LiDAR Points

The following loads the phase LiDAR points, which were generated synthetically for this sample.

points = np.load("points.npy")

fig = plt.figure()
ax = fig.add_subplot(projection = "3d", title = "Sample Points")
ax.scatter(points[:,0], points[:,1], points[:,2], marker = "o");

Preprocess Points

The phase points must be normlized to the interval [0, 1] prior to fitting them to the DBSCAN model.

processed_points = points - points.min(axis = 0)
processed_points /= processed_points.max(axis = 0)

Fit The Model

The following creates a new DBSCAN model and fits the processed points to it. The eps parameter will need to be adjusted based on the particular points being fit. If you know that there are exactly $n$ clusters of wires, you may be able to tune the parameter by takings steps on eps until $n$ cluster labels are acquired.

model = cluster.DBSCAN(eps = 0.15)
model.fit(processed_points)
labels = sorted(set(model.labels_))
print(f"Labels: {labels}")
Labels: [0, 1, 2, 3]

Plot the Labeled Clusters

The following plots the LiDAR points with colors based on their identified cluster.

cmap = plt.cm.get_cmap("viridis")
bounds = np.linspace(labels[0] - 0.5, labels[-1] + 0.5, len(labels) + 1)
norm = colors.BoundaryNorm(bounds, cmap.N)

fig = plt.figure()
ax = fig.add_subplot(projection = "3d", title = "Clustered Points")
scatter = ax.scatter(points[:,0], points[:,1], points[:,2],
                     s = 5,
                     c = model.labels_,
                     norm = norm,
                     cmap = cmap)

fig.colorbar(scatter, ticks = labels);

Read Next