Ardupilot2/libraries/SITL/examples/Webots_Python/scripts/example_aruco_detection.py
Ian d358ca1b32 SITL: Add Webots 2023a support and examples
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)
2023-01-22 18:19:38 +11:00

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()