d358ca1b32
The various C Webots controllers are replaced by a single Python controller - More readable (in my opinion) - Does not require compilation - Easily modifiable to run user code - Can be blackboxed and configured via arguments when designing a robot model - Optionally provides the ability to stream camera images over TCP - Generalizable to copters and rovers (and probably more) - Supports multi-vehicle simulation (including of multiple types) - Requires no non-standard libraries (neither does current) Higher fidelity example worlds - Iris quadcopter demo world similar to gazebo - Crazyflie quadcopter demo world (crazyflie models baked into webots) - Pioneer3at rover demo world (pioneer models baked into webots)
84 lines
3.1 KiB
Python
Executable File
84 lines
3.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
#
|
|
# An example script that receives images from a WebotsArduVehicle on port 5599
|
|
# and displays them overlayed with any ArUco markers using OpenCV.
|
|
# Requires opencv-python (`pip3 install opencv-python`)
|
|
#
|
|
|
|
import cv2
|
|
import socket
|
|
import struct
|
|
import numpy as np
|
|
|
|
# connect to WebotsArduVehicle
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.connect(("127.0.0.1", 5599))
|
|
|
|
# ArUco setup
|
|
aruco_dict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_4X4_50)
|
|
aruco_params = cv2.aruco.DetectorParameters_create()
|
|
|
|
header_size = struct.calcsize("=HH")
|
|
while True:
|
|
# receive header
|
|
header = s.recv(header_size)
|
|
if len(header) != header_size:
|
|
print("Header size mismatch")
|
|
break
|
|
|
|
# parse header
|
|
width, height = struct.unpack("=HH", header)
|
|
|
|
# for CV applications we may want camera intrinsics such as focal length:
|
|
# https://stackoverflow.com/questions/61555182/webot-camera-default-parameters-like-pixel-size-and-focus
|
|
# cam_focal_length = 2 * np.arctan(np.tan(cam_fov * 0.5) / (cam_width / cam_height))
|
|
|
|
# receive image
|
|
bytes_to_read = width * height
|
|
img = bytes()
|
|
while len(img) < bytes_to_read:
|
|
img += s.recv(min(bytes_to_read - len(img), 4096))
|
|
|
|
# convert to numpy array
|
|
img = np.frombuffer(img, np.uint8).reshape((height, width))
|
|
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
|
|
|
|
# detect ArUco markers
|
|
(corners, ids, rejected) = cv2.aruco.detectMarkers(img, aruco_dict, parameters=aruco_params)
|
|
if len(corners) > 0:
|
|
# flatten the ArUco IDs list
|
|
ids = ids.flatten()
|
|
# loop over the detected ArUCo corners
|
|
for (markerCorner, markerID) in zip(corners, ids):
|
|
# extract the marker corners (which are always returned in
|
|
# top-left, top-right, bottom-right, and bottom-left order)
|
|
corners = markerCorner.reshape((4, 2))
|
|
(topLeft, topRight, bottomRight, bottomLeft) = corners
|
|
# convert each of the (x, y)-coordinate pairs to integers
|
|
topRight = (int(topRight[0]), int(topRight[1]))
|
|
bottomRight = (int(bottomRight[0]), int(bottomRight[1]))
|
|
bottomLeft = (int(bottomLeft[0]), int(bottomLeft[1]))
|
|
topLeft = (int(topLeft[0]), int(topLeft[1]))
|
|
|
|
# draw the bounding box of the ArUCo detection
|
|
cv2.line(img, topLeft, topRight, (0, 255, 0), 2)
|
|
cv2.line(img, topRight, bottomRight, (0, 255, 0), 2)
|
|
cv2.line(img, bottomRight, bottomLeft, (0, 255, 0), 2)
|
|
cv2.line(img, bottomLeft, topLeft, (0, 255, 0), 2)
|
|
# compute and draw the center (x, y)-coordinates of the ArUco
|
|
# marker
|
|
cX = int((topLeft[0] + bottomRight[0]) / 2.0)
|
|
cY = int((topLeft[1] + bottomRight[1]) / 2.0)
|
|
cv2.circle(img, (cX, cY), 4, (0, 0, 255), -1)
|
|
# draw the ArUco marker ID on the image
|
|
cv2.putText(img, str(markerID),
|
|
(topLeft[0], topLeft[1] - 15), cv2.FONT_HERSHEY_SIMPLEX,
|
|
0.5, (0, 255, 0), 2)
|
|
|
|
# display image
|
|
cv2.imshow("image", img)
|
|
if cv2.waitKey(1) == ord("q"):
|
|
break
|
|
|
|
s.close() |