1
1
#!/usr/bin/env python3
2
2
3
3
# This node is used to visualize the trajectory of lidarbot by tracking the ArUco marker
4
- # on it
4
+ # on it and drawing lines through the center of the marker in subsequent image frames
5
5
6
6
# Adapted from: https://automaticaddison.com/estimate-aruco-marker-pose-using-opencv-gazebo-and-ros-2/
7
7
8
8
# Import Python libraries
9
9
import cv2
10
10
import numpy as np
11
11
import argparse
12
- from scipy .spatial .transform import Rotation as R
13
12
14
13
# Import ROS2 libraries
15
14
import rclpy
16
15
from rclpy .node import Node
17
16
from rclpy .qos import qos_profile_sensor_data # Uses Best Effort reliability for camera
18
17
from cv_bridge import CvBridge # Package to convert between ROS and OpenCV Images
19
- from geometry_msgs .msg import TransformStamped # Handles TransformStamped message
20
18
from sensor_msgs .msg import Image # Image is the message type
21
- from tf2_ros import TransformBroadcaster
22
19
23
20
# Construct argument parser and parse the arguments
24
21
ap = argparse .ArgumentParser ()
@@ -62,7 +59,6 @@ def __init__(self):
62
59
"/home/noobinventor/lidarbot_ws/src/lidarbot_aruco/config/chessboard_calibration.yaml" ,
63
60
)
64
61
self .declare_parameter ("image_topic" , "/image_raw" )
65
- self .declare_parameter ("aruco_marker_name" , "aruco_marker_frame" )
66
62
67
63
# Read parameters
68
64
aruco_dictionary_name = (
@@ -83,9 +79,6 @@ def __init__(self):
83
79
image_topic = (
84
80
self .get_parameter ("image_topic" ).get_parameter_value ().string_value
85
81
)
86
- self .aruco_marker_name = (
87
- self .get_parameter ("aruco_marker_name" ).get_parameter_value ().string_value
88
- )
89
82
90
83
# Check that we have a valid ArUco marker
91
84
if ARUCO_DICT .get (aruco_dictionary_name , None ) is None :
@@ -118,18 +111,18 @@ def __init__(self):
118
111
)
119
112
self .subscription # prevent unused variable warning
120
113
121
- # Initialize the transform broadcaster
122
- self .tfbroadcaster = TransformBroadcaster (self )
123
-
124
114
# Used to convert between ROS and OpenCV images
125
115
self .bridge = CvBridge ()
126
116
117
+ # List of points to draw curves through
118
+ self .points = []
119
+
127
120
# Callback function
128
121
def listener_callback (self , data ):
129
122
# Display the message on the console
130
123
self .get_logger ().info ("Receiving video frame" )
131
124
132
- # Convert ROS Image message to OpenCV image
125
+ # Convert ROS image message to OpenCV image
133
126
current_frame = self .bridge .imgmsg_to_cv2 (data , desired_encoding = "bgr8" )
134
127
135
128
# Detect ArUco markers in the video frame
@@ -142,9 +135,6 @@ def listener_callback(self, data):
142
135
# Check that at least one ArUco marker was detected
143
136
if marker_ids is not None :
144
137
145
- # Draw a square around detected markers in the video frame
146
- cv2 .aruco .drawDetectedMarkers (current_frame , corners , marker_ids )
147
-
148
138
# Get the rotation and translation vectors
149
139
rvecs , tvecs , obj_points = cv2 .aruco .estimatePoseSingleMarkers (
150
140
corners ,
@@ -153,71 +143,36 @@ def listener_callback(self, data):
153
143
self .dist_coeffs ,
154
144
)
155
145
156
- # The pose of the marker is with respect to the camera lens frame.
157
- # Imagine you are looking through the camera viewfinder,
158
- # the camera lens frame's:
159
- # x-axis points to the right
160
- # y-axis points straight down towards your toes
161
- # z-axis points straight ahead away from your eye, out of the camera
162
- for i , marker_id in enumerate (marker_ids ):
163
-
164
- # Create the coordinate transform
165
- t = TransformStamped ()
166
- t .header .stamp = self .get_clock ().now ().to_msg ()
167
- t .header .frame_id = "webcam_frame"
168
- t .child_frame_id = self .aruco_marker_name
169
-
170
- # Store the translation (i.e. position) information
171
- t .transform .translation .x = tvecs [i ][0 ][0 ]
172
- t .transform .translation .y = tvecs [i ][0 ][1 ]
173
- t .transform .translation .z = tvecs [i ][0 ][2 ]
174
-
175
- # Store the rotation information
176
- rotation_matrix = np .eye (4 )
177
- rotation_matrix [0 :3 , 0 :3 ] = cv2 .Rodrigues (np .array (rvecs [i ][0 ]))[0 ]
178
- r = R .from_matrix (rotation_matrix [0 :3 , 0 :3 ])
179
- quat = r .as_quat ()
180
-
181
- # Quaternion format
182
- t .transform .rotation .x = quat [0 ]
183
- t .transform .rotation .y = quat [1 ]
184
- t .transform .rotation .z = quat [2 ]
185
- t .transform .rotation .w = quat [3 ]
186
-
187
- # Send the transform
188
- self .tfbroadcaster .sendTransform (t )
189
-
190
- # Draw the axes on the marker
191
- cv2 .drawFrameAxes (
192
- current_frame ,
193
- self .camera_matrix ,
194
- self .dist_coeffs ,
195
- rvecs [i ],
196
- tvecs [i ],
197
- 0.05 ,
146
+ # Loop over the detected ArUCo corners
147
+ for markerCorner , markerID in zip (corners , marker_ids ):
148
+ # Extract the marker corners (which are always returned in
149
+ # top-left, top-right, bottom-right, and bottom-left order)
150
+ corners = markerCorner .reshape ((4 , 2 ))
151
+ (topLeft , topRight , bottomRight , bottomLeft ) = corners
152
+
153
+ # Convert the (x,y) coordinates pairs to integers
154
+ topRight = (int (topRight [0 ]), int (topRight [1 ]))
155
+ bottomRight = (int (bottomRight [0 ]), int (bottomRight [1 ]))
156
+ bottomLeft = (int (bottomLeft [0 ]), int (bottomLeft [1 ]))
157
+ topLeft = (int (topLeft [0 ]), int (topLeft [1 ]))
158
+
159
+ # Compute and draw the center (x, y)-coordinates of the ArUco
160
+ # marker
161
+ cX = int ((topLeft [0 ] + bottomRight [0 ]) / 2.0 )
162
+ cY = int ((topLeft [1 ] + bottomRight [1 ]) / 2.0 )
163
+ self .points .append ([cX , cY ])
164
+ cv2 .circle (current_frame , (cX , cY ), 4 , (0 , 0 , 255 ), - 1 )
165
+
166
+ # Convert points list to numpy array
167
+ points_array = np .array (self .points )
168
+ points_array = points_array .reshape ((- 1 , 1 , 2 ))
169
+
170
+ # Connect center coordinates of detected marker in respective frames
171
+ # NOTE: this section works for only one marker
172
+ cv2 .polylines (
173
+ current_frame , [points_array ], False , (0 , 255 , 0 ), thickness = 5
198
174
)
199
175
200
- # Draw circle at the center of ArUco tag
201
- # x_sum = (
202
- # corners[0][0][0][0]
203
- # + corners[0][0][1][0]
204
- # + corners[0][0][2][0]
205
- # + corners[0][0][3][0]
206
- # )
207
- # y_sum = (
208
- # corners[0][0][0][1]
209
- # + corners[0][0][1][1]
210
- # + corners[0][0][2][1]
211
- # + corners[0][0][3][1]
212
- # )
213
- #
214
- # x_centerPixel = x_sum * 0.25
215
- # y_centerPixel = y_sum * 0.25
216
- #
217
- # cv2.circle(
218
- # current_frame, (x_centerPixel, y_centerPixel), 4, (0, 0, 255), -1
219
- # )
220
-
221
176
# Display image for testing
222
177
cv2 .imshow ("camera" , current_frame )
223
178
cv2 .waitKey (1 )
0 commit comments