Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
ibvs-four-points.py
35
36import numpy as np
37import argparse
38import sys
39
40# For plots
41import matplotlib.pyplot as plt
42import os
43
44# Use latex in plot legends and labels
45plt.rc('text', usetex=True)
46plt.rc('text.latex', preamble=r'\usepackage{amsmath}')
47
48# ViSp Python bindings
49from visp.core import ExponentialMap
50from visp.core import HomogeneousMatrix
51from visp.core import Math
52from visp.core import Point
53from visp.core import RotationMatrix
54from visp.core import ThetaUVector
55from visp.core import TranslationVector
56
57from visp.visual_features import FeatureBuilder
58from visp.visual_features import FeaturePoint
59
60from visp.vs import Servo
61
63 def __init__(self, e, norm_e, v, x, xd, c_T_w, plot_log_scale):
64 self.vector_e = e
65 self.vector_ne = norm_e
66 self.vector_v = v
67 self.vector_x = x
68 self.vector_xd = xd
69 self.vector_w_t_c = c_T_w.inverse().getTranslationVector()
70 self.plot_log_scale = plot_log_scale
71
72 def stack(self, e, norm_e, v, x, xd, c_T_w):
73 self.vector_e = np.vstack((self.vector_e, e))
74 self.vector_ne = np.vstack((self.vector_ne, norm_e))
75 self.vector_v = np.vstack((self.vector_v, v))
76 self.vector_x = np.vstack((self.vector_x, x))
77 self.vector_w_t_c = np.vstack((self.vector_w_t_c, c_T_w.inverse().getTranslationVector()))
78
79 def display(self, fig_filename):
80 plt.figure(figsize=(10,10))
81
82 plot_e = plt.subplot(2, 2, 1)
83 plot_v = plt.subplot(2, 2, 2)
84 plot_ne = plt.subplot(2, 2, 3)
85 plot_x = plt.subplot(2, 2, 4)
86
87 plot_e.set_title('error')
88 plot_v.set_title('camera velocity')
89 plot_x.set_title('point trajectory in the image plane')
90
91 if self.plot_log_scale:
92 plot_ne.set_title('log(norm error)')
93 plot_ne.plot(np.log(self.vector_ne))
94 else:
95 plot_ne.set_title('norm error')
96 plot_ne.plot(self.vector_ne)
97
98 plot_ne.grid(True)
99 plot_e.grid(True)
100 plot_v.grid(True)
101 plot_x.grid(True)
102
103 plot_e.plot(self.vector_e)
104 plot_e.legend(['$x_1$','$y_1$','$x_2$','$y_2$','$x_3$','$y_3$','$x_4$','$y_4$',])
105
106 for i in range(self.vector_v.shape[1]): # Should be 6
107 plot_v.plot(self.vector_v[:,i])
108 plot_v.legend(['$v_x$','$v_y$','$v_z$','$\omega_x$','$\omega_y$','$\omega_z$'])
109
110 for i in range(self.vector_x.shape[1]):
111 pts = np.asarray([[p.get_x(), p.get_y()] for p in self.vector_x[:, i]])
112 plot_x.plot(pts[:, 0], pts[:, 1])
113 plot_x.legend(['$x_1$','$x_2$','$x_3$','$x_4$'])
114
115 for i in range(self.vector_x.shape[1] ):
116 plot_x.plot(self.vector_xd[i].get_x(), self.vector_xd[i].get_y(),'o')
117
118 # Create output folder it it doesn't exist
119 output_folder = os.path.dirname(fig_filename)
120 if not os.path.exists(output_folder):
121 os.makedirs(output_folder)
122 print("Create output folder: ", output_folder)
123
124 print(f"Figure is saved in {fig_filename}")
125 plt.savefig(fig_filename)
126
127 # Plot 3D camera trajectory
128 plot_traj = plt.figure().add_subplot(projection='3d')
129 plot_traj.scatter(self.vector_w_t_c[0][0], self.vector_w_t_c[0][1], self.vector_w_t_c[0][2], marker='x', c='r', label='Initial position')
130 # Hack to ensure that the scale is at minimum between -0.5 and 0.5 along X and Y axis
131 min_s = np.min(self.vector_w_t_c, axis=0)
132 max_s = np.max(self.vector_w_t_c, axis=0)
133 for i in range(len(min_s)):
134 if (max_s[i] - min_s[i]) < 1.:
135 max_s[i] += 0.5
136 min_s[i] -= 0.5
137 plot_traj.axis(xmin=min_s[0], xmax=max_s[0])
138 plot_traj.axis(ymin=min_s[1], ymax=max_s[1])
139
140 plot_traj.plot(self.vector_w_t_c[:, 0], self.vector_w_t_c[:, 1], zs=self.vector_w_t_c[:, 2], label='Camera trajectory')
141 plot_traj.set_title('Camera trajectory w_t_c in world space')
142 plot_traj.legend()
143 filename = os.path.splitext(fig_filename)[0] + "-traj-w_t_c.png"
144 print(f"Figure is saved in {filename}")
145 plt.savefig(filename)
146
147 plt.show()
148
149if __name__ == '__main__':
150 parser = argparse.ArgumentParser(description='The script corresponding to TP 4, IBVS on 4 points.')
151 parser.add_argument('--initial-position', type=int, default=2, dest='initial_position', help='Initial position selection (value 1, 2, or 3)')
152 parser.add_argument('--interaction', type=str, default="current", dest='interaction_matrix_type', help='Interaction matrix type (value \"current\" or \"desired\")')
153 parser.add_argument('--plot-log-scale', action='store_true', help='Plot norm of the error using a logarithmic scale')
154 parser.add_argument('--no-plot', action='store_true', help='Disable plots')
155
156 args, unknown_args = parser.parse_known_args()
157 if unknown_args:
158 print("The following args are not recognized and will not be used: %s" % unknown_args)
159 sys.exit()
160
161 print(f"Use initial position {args.initial_position}")
162
163 # Position of the reference in the camera frame
164 c_T_w = HomogeneousMatrix()
165
166 if args.initial_position == 1:
167 # - CASE 1
168 thetau = ThetaUVector(0.0, 0.0, 0.0)
169 c_R_w = RotationMatrix(thetau)
170 c_t_w = TranslationVector(0.0, 0.0, 1.3)
171 c_T_w.insert(c_R_w)
172 c_T_w.insert(c_t_w)
173 elif args.initial_position == 2:
174 # - CASE 2
175 thetau = ThetaUVector(Math.rad(10), Math.rad(20), Math.rad(30))
176 c_R_w = RotationMatrix(thetau)
177 c_t_w = TranslationVector(-0.2, -0.1, 1.3)
178 c_T_w.insert(c_R_w)
179 c_T_w.insert(c_t_w)
180 elif args.initial_position == 3:
181 # - CASE 3 : 90 rotation along Z axis
182 thetau = ThetaUVector(0.0, 0.0, Math.rad(90))
183 c_R_w = RotationMatrix(thetau)
184 c_t_w = TranslationVector(0.0, 0.0, 1.0)
185 c_T_w.insert(c_R_w)
186 c_T_w.insert(c_t_w)
187 else:
188 raise ValueError(f"Wrong initial position value. Values are 1, 2 or 3")
189
190 # Position of the desired camera in the world reference frame
191 cd_T_w = HomogeneousMatrix()
192 thetau = ThetaUVector(0, 0, 0)
193 cd_R_w = RotationMatrix(thetau)
194 cd_t_w = TranslationVector(0.0, 0.0, 1.0)
195 cd_T_w.insert(cd_R_w)
196 cd_T_w.insert(cd_t_w)
197
198 # 3D point in the reference frame in homogeneous coordinates
199 wX = []
200 wX.append(Point(-0.1, 0.1, 0.0))
201 wX.append(Point( 0.1, 0.1, 0.0))
202 wX.append(Point( 0.1, -0.1, 0.0))
203 wX.append(Point(-0.1, -0.1, 0.0))
204
205 # Creation of the current and desired features vectors respectively in x and xd
206 x = [FeaturePoint(), FeaturePoint(), FeaturePoint(),FeaturePoint()] # Initialize current visual feature
207 xd = [FeaturePoint(), FeaturePoint(), FeaturePoint(), FeaturePoint()] # Initialize desired visual feature
208
209 # Create the visual servo task
210 task = Servo()
211 task.setServo(Servo.EYEINHAND_CAMERA)
212 task.setLambda(0.1) # Set the constant gain
213
214 # For each point
215 for i in range(len(wX)):
216 # Create current visual feature
217 wX[i].track(c_T_w)
218 FeatureBuilder.create(x[i], wX[i])
219
220 print(f"Visual features at initial position for point {i}:")
221 print(f" wX[{i}]: {wX[i].get_oX()} {wX[i].get_oY()} {wX[i].get_oZ()}")
222 print(f" cX[{i}]: {wX[i].get_X()} {wX[i].get_Y()} {wX[i].get_Z()}")
223 print(f" x[{i}]: {x[i].get_x()} {x[i].get_y()} {x[i].get_Z()}")
224
225 # Create desired visual feature
226 wX[i].track(cd_T_w)
227 FeatureBuilder.create(xd[i], wX[i])
228
229 print(f"Visual features at desired position for point {i}:")
230 print(f"cdX[{i}]: {wX[i].get_X()} {wX[i].get_Y()} {wX[i].get_Z()}")
231 print(f"xd[{i}]: {xd[i].get_x()} {xd[i].get_y()} {xd[i].get_Z()}")
232
233 # Add current and desired features to the visual servo task
234 task.addFeature(x[i], xd[i])
235
236 iter = 0
237
238 # Control loop
239 while (iter == 0 or norm_e > 0.0001):
240 print(f"---- Visual servoing iteration {iter} ----")
241 # Considered vars:
242 # e: error vector
243 # norm_e: norm of the error vector
244 # v: velocity to apply to the camera
245 # x: current visual feature vector
246 # xd: desired visual feature vector
247 # c_T_w: current position of the camera in the world frame
248 if args.interaction_matrix_type == "current":
249 # Set interaction matrix type
250 task.setInteractionMatrixType(Servo.CURRENT, Servo.PSEUDO_INVERSE)
251 # Update visual features in x for each point
252 for i in range(len(wX)):
253 # Update current visual feature
254 wX[i].track(c_T_w);
255 FeatureBuilder.create(x[i], wX[i])
256 elif args.interaction_matrix_type == "desired":
257 # Set interaction matrix type
258 task.setInteractionMatrixType(Servo.DESIRED, Servo.PSEUDO_INVERSE)
259 # Update visual features in xd
260 for i in range(len(wX)):
261 # Update current visual feature
262 wX[i].track(c_T_w);
263 FeatureBuilder.create(x[i], wX[i])
264 # Update desired visual feature
265 wX[i].track(cd_T_w);
266 FeatureBuilder.create(xd[i], wX[i])
267 else:
268 raise ValueError(f"Wrong interaction matrix type. Values are \"current\" or \"desired\"")
269
270 # Compute the control law
271 v = task.computeControlLaw()
272 e = task.getError()
273 norm_e = e.frobeniusNorm()
274 Lx = task.getInteractionMatrix()
275
276 xplot = []
277
278 if not args.no_plot:
279 for f in x:
280 p = FeaturePoint()
281 p.buildFrom(f.get_x(), f.get_y(), f.get_Z())
282 xplot.append(p)
283
284 if iter == 0:
285 plot = PlotIbvs(e, norm_e, v, xplot, xd, c_T_w, args.plot_log_scale)
286 else:
287 plot.stack(e, norm_e, v, xplot, xd, c_T_w)
288
289 # Compute camera displacement after applying the velocity for delta_t seconds.
290 c_T_c_delta_t = ExponentialMap.direct(v, 0.040)
291
292 # Compute the new position of the camera
293 c_T_w = c_T_c_delta_t.inverse() * c_T_w
294
295 print(f"e: \n{e}")
296 print(f"norm e: \n{norm_e}")
297 print(f"Lx: \n{Lx}")
298 print(f"v: \n{v}")
299 print(f"c_T_w: \n{c_T_w}")
300
301 # Increment iteration counter
302 iter += 1
303
304 print(f"\nConvergence achieved in {iter} iterations")
305
306 if not args.no_plot:
307 # Display the servo behavior
308 plot.display("results/fig-ibvs-four-points-initial-position-" + str(args.initial_position) + ".png")
309
310 print("Kill the figure to quit...")
display(self, fig_filename)
__init__(self, e, norm_e, v, x, xd, c_T_w, plot_log_scale)
stack(self, e, norm_e, v, x, xd, c_T_w)