
Kod źródłowy projektu (system wizyjny).
W projekcie „Linia przenośnikowa” jednym z kluczowych elementów jest system wizyjny – „oczy” całej instalacji. Jego zadaniem jest wykrywanie okrągłych elementów (krążków / podkładek) na taśmie oraz rozpoznanie ich koloru. Na tej podstawie reszta systemu (sterowanie, sortowanie, statystyki) podejmuje decyzje, co dalej zrobić z danym elementem.
System został napisany w Pythonie z użyciem biblioteki OpenCV i pracuje przy dedykowanym oświetleniu LED zamontowanym nad taśmą. Stałe, kontrolowane światło z LED-ów znacząco poprawia powtarzalność pomiarów i stabilność klasyfikacji kolorów.
Jak wygląda pipeline wizyjny?
W uproszczeniu, dla każdej klatki z kamery robimy:
- Pobranie obrazu z kamery,
- Wykrycie okrągłych elementów (krążków) – algorytm Hougha dla okręgów,
- Odfiltrowanie duplikatów i elementów spoza interesującego nas obszaru taśmy,
- Dla każdego wykrytego okręgu:
- analiza kolorów w jego wnętrzu (HSV),
- decyzja, jaki to kolor,
- zapisanie wyniku,
- inkrementacja statystyk.
Na końcu funkcja detect_circles(…) zwraca listę słowników takich jak:
{„x”: a, „y”: b, „r”: r, „color”: „zielony”, „hsv”: [H, S, V]}
To gotowa informacja dla reszty systemu sterowania linii.
Hardware: kamera + oświetlenie LED

Aby system wizyjny działał stabilnie, potrzebuje nie tylko dobrego algorytmu, ale też powtarzalnych warunków oświetleniowych. W naszym projekcie nad taśmą zamontowaliśmy pasek / moduły LED o stałej temperaturze barwowej, skierowany na obszar roboczy kamery (ROI). Dzięki temu:
- redukujemy zmiany jasności wynikające z oświetlenia hali,
- kolory są bardziej przewidywalne w przestrzeni HSV,
- możemy stosować stałe progi do klasyfikacji barw. Nawet najlepszy algorytm będzie się gubił, jeśli scena raz jest bardzo ciemna, a raz prześwietlona, więc stabilne LED-y są kluczowym elementem całego systemu.

Wykrywanie okręgów – HoughCircles w praktyce

Pierwszy krok po pobraniu klatki to przygotowanie obrazu. Najpierw konwersja do skali szarości, potem filtr medianowy:
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
gray = cv.medianBlur(gray, 5)
Następnie używamy funkcji
cv.HoughCircles do wykrycia okręgów na podstawie krawędzi:
detected_circles = cv.HoughCircles(
gray,
cv.HOUGH_GRADIENT,
dp=1,
minDist=CIRCLE_MIN_RADIUS * 2,
param1=param1,
param2=param2,
minRadius=CIRCLE_MIN_RADIUS,
maxRadius=CIRCLE_MAX_RADIUS,
)
Krótko o parametrach:
- dp – skala akumulatora względem obrazu; 1 oznacza pełną rozdzielczość (dokładniej, wolniej)
- minDist – minimalna odległość między środkami wykrytych okręgów (eliminuje duplikaty)
- param1 – górny próg dla detektora krawędzi Canny (ile krawędzi widzimy)
- param2 – próg akumulatora
- minRadius / maxRadius – zakres promieni okręgów odpowiadających naszym fizycznym elementom na taśmie
Po wykryciu okręgów sortujemy je od największego promienia do najmniejszego i eliminujemy te, które leżą wewnątrz już zaakceptowanych większych okręgów. Dodatkowo filtrujemy po prostokątnym obszarze interesu (ROI) określonym przez FRAME_LEFT_MARGIN, FRAME_TOP_MARGIN, FRAME_WIDTH i FRAME_HEIGHT – dzięki temu ignorujemy elementy spoza taśmy.
Za klasyfikację kolorów odpowiada funkcja get_circle_color_info(a, b, r, frame). Pierwszy krok to przejście z BGR do HSV:
hsv_frame = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
HSV (Hue, Saturation, Value) dzieli kolor na odcień (H), nasycenie (S) i jasność (V). W tej przestrzeni dużo łatwiej jest zdefiniować „czerwony”, „zielony” czy „niebieski” jako zakresy wartości H.
Następnie przechodzimy po pikselach znajdujących się wewnątrz okręgu i zliczamy średnie HSV oraz wszystkie wartości H:
average = np.array([0.0, 0.0, 0.0])
count = 0
hue_list = []
for y in range(b - floor(r), b + floor(r)):
for x in range(a - floor(r), a + floor(r)):
if 0 <= y < hsv_frame.shape[0] and 0 <= x < hsv_frame.shape[1]:
if (x - a) ** 2 + (y - b) ** 2 <= r**2:
average += hsv_frame[y, x, :]
count += 1
hue_list.append(hsv_frame[y, x, 0])
Jeśli zebraliśmy jakiekolwiek piksele, dzielimy średnią przez count, a następnie bierzemy najczęściej występującą wartość H jako reprezentatywny odcień dla całego okręgu:
if count > 0:
average /= count
if hue_list:
hue_value = int(np.bincount(np.array(hue_list, dtype=np.uint8)).argmax())
else:
hue_value = 0
average[0] = hue_value
Zastosowanie najczęściej występującego odcienia (a nie średniej H) ogranicza wpływ pojedynczych, zaszumionych pikseli.
Mapowanie HSV → nazwa koloru

Na podstawie hue_value oraz jasności i nasycenia wybieramy nazwę koloru. W kodzie wygląda to następująco:
if average[2] < 80 or (average[2] < 150 and average[1] < 100):
color = "czarny"
elif hue_value < 7.5:
color = "czerwony"
elif hue_value < 19:
color = "pomaranczowy"
elif hue_value < 35:
color = "zolty"
elif hue_value < 80:
color = "zielony"
elif hue_value < 122.5:
color = "niebieski"
elif hue_value < 140:
color = "fioletowy"
elif hue_value < 162.5:
color = "rozowy"
else:
color = "czerwony"
Progi zostały dobrane eksperymentalnie pod konkretne elementy i warunki oświetleniowe na linii. Czerń rozpoznajemy głównie po niskiej jasności (V) i niskim nasyceniu (S), a pozostałe kolory na podstawie zakresów H.
Integracja z resztą systemu
System wizyjny nie działa w próżni – wyniki z detect_circles(…) są dalej wykorzystywane m.in. do sterowania elementami wykonawczymi (siłowniki, sortowniki, wyrzutniki), synchronizacji z położeniem na taśmie, wyświetlania podglądu na aplikacji webowej oraz logowania zdarzeń.
Dla każdej klatki mamy zestaw obiektów z pozycją (x, y), promieniem r i pewnym kolorem, na podstawie którego reszta systemu podejmuje decyzje. System wizyjny nie działa w próżni – wyniki z detect_circles(…) są dalej wykorzystywane m.in. do sterowania elementami wykonawczymi (siłowniki, sortowniki, wyrzutniki), synchronizacji z położeniem na taśmie, wyświetlania podglądu na aplikacji webowej oraz logowania zdarzeń.
Dla każdej klatki mamy zestaw obiektów z pozycją (x, y), promieniem r i pewnym kolorem, na podstawie którego reszta systemu podejmuje decyzje.
Co dalej?
Rozwinięcia systemu wizyjnego, nad którymi można pracować w ramach koła, obejmują m.in.:
- wykrywanie innych kształtów (np. prostokątnych etykiet),
- integrację z sieciami neuronowymi (CNN) do bardziej złożonych zadań.
System wizyjny z projektu linii przenośnikowej pokazuje, że relatywnie prostymi metodami (Hough + HSV) można zbudować stabilne, działające w czasie rzeczywistym rozwiązanie, które realnie współpracuje z automatyką przemysłową w warunkach koła naukowego.
Autor: Marvin Ruciński
0 komentarzy