Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
pbvs-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 FeatureTranslation
58from visp.visual_features import FeatureThetaU
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] // 2): # Note: Use // to divide an int and return an int
111 plot_x.plot(self.vector_x[:,2*i], self.vector_x[:,2*i+1])
112 plot_x.legend(['$x_1$','$x_2$','$x_3$','$x_4$'])
113 for i in range(self.vector_x.shape[1] // 2): # Note: Use // to divide an int and return an int
114 plot_x.plot(self.vector_xd[2*i], self.vector_xd[2*i+1],'o')
115
116 # Create output folder it it doesn't exist
117 output_folder = os.path.dirname(fig_filename)
118 if not os.path.exists(output_folder):
119 os.makedirs(output_folder)
120 print("Create output folder: ", output_folder)
121
122 print(f"Figure is saved in {fig_filename}")
123 plt.savefig(fig_filename)
124
125 # Plot 3D camera trajectory
126 plot_traj = plt.figure().add_subplot(projection='3d')
127 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')
128 # Hack to ensure that the scale is at minimum between -0.5 and 0.5 along X and Y axis
129 min_s = np.min(self.vector_w_t_c, axis=0)
130 max_s = np.max(self.vector_w_t_c, axis=0)
131 for i in range(len(min_s)):
132 if (max_s[i] - min_s[i]) < 1.:
133 max_s[i] += 0.5
134 min_s[i] -= 0.5
135 plot_traj.axis(xmin=min_s[0], xmax=max_s[0])
136 plot_traj.axis(ymin=min_s[1], ymax=max_s[1])
137
138 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')
139 plot_traj.set_title('Camera trajectory w_t_c in world space')
140 plot_traj.legend()
141 filename = os.path.splitext(fig_filename)[0] + "-traj-w_t_c.png"
142 print(f"Figure is saved in {filename}")
143 plt.savefig(filename)
144
145 plt.show()
146
147if __name__ == '__main__':
148 parser = argparse.ArgumentParser(description='The script corresponding to TP 4, IBVS on 4 points.')
149 parser.add_argument('--initial-position', type=int, default=2, dest='initial_position', help='Initial position selection (value 1, 2, 3 or 4)')
150 parser.add_argument('--interaction', type=str, default="current", dest='interaction_matrix_type', help='Interaction matrix type (value \"current\" or \"desired\")')
151 parser.add_argument('--plot-log-scale', action='store_true', help='Plot norm of the error using a logarithmic scale')
152 parser.add_argument('--no-plot', action='store_true', help='Disable plots')
153
154 args, unknown_args = parser.parse_known_args()
155 if unknown_args:
156 print("The following args are not recognized and will not be used: %s" % unknown_args)
157 sys.exit()
158
159 print(f"Use initial position {args.initial_position}")
160
161 # Position of the reference in the camera frame
162 c_T_w = HomogeneousMatrix()
163
164 if args.initial_position == 1:
165 # - CASE 1
166 thetau = ThetaUVector(0.0, 0.0, 0.0)
167 c_R_w = RotationMatrix(thetau)
168 c_t_w = TranslationVector(0.0, 0.0, 1.3)
169 c_T_w.insert(c_R_w)
170 c_T_w.insert(c_t_w)
171 elif args.initial_position == 2:
172 # - CASE 2
173 thetau = ThetaUVector(Math.rad(10), Math.rad(20), Math.rad(30))
174 c_R_w = RotationMatrix(thetau)
175 c_t_w = TranslationVector(-0.2, -0.1, 1.3)
176 c_T_w.insert(c_R_w)
177 c_T_w.insert(c_t_w)
178 elif args.initial_position == 3:
179 # - CASE 3 : 90 rotation along Z axis
180 thetau = ThetaUVector(0.0, 0.0, Math.rad(90))
181 c_R_w = RotationMatrix(thetau)
182 c_t_w = TranslationVector(0.0, 0.0, 1.0)
183 c_T_w.insert(c_R_w)
184 c_T_w.insert(c_t_w)
185 elif args.initial_position == 4:
186 # - CASE 4 : 180 rotation along Z axis
187 thetau = ThetaUVector(0.0, 0.0, Math.rad(180))
188 c_R_w = RotationMatrix(thetau)
189 c_t_w = TranslationVector(0.0, 0.0, 1.0)
190 c_T_w.insert(c_R_w)
191 c_T_w.insert(c_t_w)
192 else:
193 raise ValueError(f"Wrong initial position value. Values are 1, 2, 3 or 4")
194
195 # Position of the desired camera in the world reference frame
196 cd_T_w = HomogeneousMatrix()
197 thetau = ThetaUVector(0, 0, 0)
198 cd_R_w = RotationMatrix(thetau)
199 cd_t_w = TranslationVector(0.0, 0.0, 1.0)
200 cd_T_w.insert(cd_R_w)
201 cd_T_w.insert(cd_t_w)
202
203 # 3D point in the reference frame in homogeneous coordinates
204 wX = []
205 wX.append(Point(-0.1, 0.1, 0.0))
206 wX.append(Point( 0.1, 0.1, 0.0))
207 wX.append(Point( 0.1, -0.1, 0.0))
208 wX.append(Point(-0.1, -0.1, 0.0))
209
210 # Begin just for point trajectory display, compute the coordinates of the points in the image plane
211 x = np.zeros(8) # Current coordinates of the points [x1,y1,x2,y2,x3,y3,x4,y4]
212 xd = np.zeros(8) # desired coordinates of the points [xd1,yd1,xd2,yd2,xd3,yd3,xd4,yd4]
213 # Update the coordinates of the 4 desired points
214 for i in range(len(wX)):
215 wX[i].track(cd_T_w)
216 xd[2*i:2*i+2] = [wX[i].get_x(), wX[i].get_y()]
217 # End just for point trajectory display
218
219 # Creation of the current (t and tu) and desired (td and tud) features vectors
220 t = FeatureTranslation(FeatureTranslation.cdMc)
221 tu = FeatureThetaU(FeatureThetaU.cdRc)
222 td = FeatureTranslation(FeatureTranslation.cdMc)
223 tud = FeatureThetaU(FeatureThetaU.cdRc)
224
225 # Create the visual servo task
226 task = Servo()
227 task.setServo(Servo.EYEINHAND_CAMERA)
228 task.setLambda(0.1) # Set the constant gain
229 task.addFeature(t, td)
230 task.addFeature(tu, tud)
231
232 iter = 0
233
234 # Control loop
235 while (iter == 0 or norm_e > 0.0001):
236 print(f"---- Visual servoing iteration {iter} ----")
237 # Considered vars:
238 # e: error vector
239 # norm_e: norm of the error vector
240 # v: velocity to apply to the camera
241 # x: current visual feature vector
242 # xd: desired visual feature vector
243 # c_T_w: current position of the camera in the world frame
244
245 # Compute current features
246 cd_T_c = cd_T_w * c_T_w.inverse()
247 t.buildFrom(cd_T_c)
248 tu.buildFrom(cd_T_c)
249
250 # Begin just for point trajectory display, compute the coordinates of the points in the image plane
251 for i in range(len(wX)):
252 wX[i].track(c_T_w)
253 x[2*i:2*i+2] = [wX[i].get_x(), wX[i].get_y()]
254 # End just for point trajectory display
255
256 if args.interaction_matrix_type == "current":
257 # Set interaction matrix type
258 task.setInteractionMatrixType(Servo.CURRENT, Servo.PSEUDO_INVERSE)
259 elif args.interaction_matrix_type == "desired":
260 # Set interaction matrix type
261 task.setInteractionMatrixType(Servo.DESIRED, Servo.PSEUDO_INVERSE)
262 else:
263 raise ValueError(f"Wrong interaction matrix type. Values are \"current\" or \"desired\"")
264
265 # Compute the control law
266 v = task.computeControlLaw()
267 e = task.getError()
268 norm_e = e.frobeniusNorm()
269 Lx = task.getInteractionMatrix()
270
271 if not args.no_plot:
272 if iter == 0:
273 plot = PlotPbvs(e, norm_e, v, x, xd, c_T_w, args.plot_log_scale)
274 else:
275 plot.stack(e, norm_e, v, x, xd, c_T_w)
276
277 # Compute camera displacement after applying the velocity for delta_t seconds.
278 c_T_c_delta_t = ExponentialMap.direct(v, 0.040)
279
280 # Compute the new position of the camera
281 c_T_w = c_T_c_delta_t.inverse() * c_T_w
282
283 print(f"e: \n{e}")
284 print(f"norm e: \n{norm_e}")
285 print(f"Lx: \n{Lx}")
286 print(f"v: \n{v}")
287 print(f"c_T_w: \n{c_T_w}")
288
289 # Increment iteration counter
290 iter += 1
291
292 print(f"\nConvergence achieved in {iter} iterations")
293
294 if not args.no_plot:
295 # Display the servo behavior
296 plot.display("results/fig-pbvs-four-points-initial-position-" + str(args.initial_position) + ".png")
297
298 print("Kill the figure to quit...")
__init__(self, e, norm_e, v, x, xd, c_T_w, plot_log_scale)
display(self, fig_filename)
stack(self, e, norm_e, v, x, xd, c_T_w)