File: plot-ibvs-control-law.py

package info (click to toggle)
visp 3.6.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 119,296 kB
  • sloc: cpp: 500,914; ansic: 52,904; xml: 22,642; python: 7,365; java: 4,247; sh: 482; makefile: 237; objc: 145
file content (128 lines) | stat: -rw-r--r-- 5,312 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
from typing import List, Optional, Callable
import numpy as np
import json
from matplotlib import pyplot as plt
import argparse
from pathlib import Path

# Use latex in plot legends and labels
plt.rc('text', usetex=True)
plt.rc('text.latex', preamble=r'\usepackage{amsmath}')

def basic_plot(data: np.ndarray, title: Optional[str] = None, legend: List[str] = None, ylabel: str = None, fontsize=16, titlefontsize=24,
              before_fn: Optional[Callable[[plt.Figure, np.ndarray], None]] = None,
              after_fn: Optional[Callable[[plt.Figure, np.ndarray], None]] = None) -> plt.Figure:
  fig = plt.figure()
  if before_fn is not None:
      before_fn(fig, data)

  plt.plot(data)
  if legend is not None:
    plt.legend(legend, fontsize=fontsize)
  if ylabel is not None:
    plt.ylabel(ylabel, fontsize=fontsize)
  plt.xlabel('Iterations', fontsize=fontsize)
  plt.grid()
  if title is not None:
    plt.title(title, fontsize=titlefontsize)

  if after_fn is not None:
      after_fn(fig, data)
  return fig

if __name__ == '__main__':
  parser = argparse.ArgumentParser(description='Plot the data related to a basic control law. Input data should be generated with the tutorial located in tutorials/visual-servo/ibvs/tutorial-ibvs-4pts-json')
  parser.add_argument('--data', type=str, help='The JSON file containing the data')
  parser.add_argument('--plot_folder', type=str, help='The folder in which to save the plots')
  args = parser.parse_args()

  json_path = Path(args.data).absolute()
  assert json_path.exists(), f'JSON file {json_path} not found'
  json_data = None
  with open(json_path, 'r') as json_file:
      json_data = json.load(json_file)

  plot_folder_path = Path(args.plot_folder).absolute()
  plot_folder_path.mkdir(exist_ok=True)

  def bounds(_fig, data):
      xmin = 0
      xmax = len(data)
      ymin = min(0, np.min(data))
      ymax = np.max(data)
      plt.xlim((xmin, xmax))
      plt.ylim((ymin, ymax))
  def make_hline(_fig, data):
      plt.axhline(0, 0, len(data), c='k')

  def sequence_fn(*fns) -> Callable:
      def multi(*args):
        for fn in fns:
            fn(*args)
      return multi

  if 'errorNorm' in json_data:
    print('Generating error norm plot...')
    error_norm = np.array(json_data['errorNorm'])
    fig_error = basic_plot(error_norm, legend=None,
                          title='Visual error norm',
                          ylabel=r'$\lVert\mathbf{e}\rVert^2_2$',
                          before_fn=sequence_fn(bounds, make_hline))
    plt.tight_layout()
    plt.savefig(plot_folder_path / 'error.pdf')
    plt.close()

  if 'velocities' in json_data:
    print('Generating velocity plot...')
    velocities = np.array([o['data'] for o in json_data['velocities']])
    velocities_legend = [fr'$\mathbf{{\upsilon_{axis}}}$' for axis in ['x', 'y', 'z']] + \
    [fr'$\mathbf{{\omega_{axis}}}$' for axis in ['x', 'y', 'z']]
    fig_velocities = basic_plot(velocities, legend=velocities_legend,
                          title='Camera velocity',
                          ylabel=r'$\mathbf{v}$',
                          before_fn=sequence_fn(bounds, make_hline))
    plt.tight_layout()
    plt.savefig(plot_folder_path / 'velocity.pdf')
    plt.close()

  if 'trajectory' in json_data:
    print('Generating 2D and 3D trajectory plots...')
    poses = np.array([o['data'] for o in json_data['trajectory']])
    cdMo = np.array(json_data['parameters']['cdMo']['data']).reshape(4, 4)
    poses_legend = [fr'$\mathbf{{t_{axis}}}$' for axis in ['x', 'y', 'z']] + \
    [fr'$\theta\mathbf{{u_{axis}}}$' for axis in ['x', 'y', 'z']]
    fig_poses = basic_plot(poses, legend=poses_legend,
                          title='Camera pose',
                          ylabel=r'$\mathbf{r}$',
                          before_fn=bounds)
    plt.tight_layout()
    plt.savefig(plot_folder_path / 'cMo.pdf')
    plt.close()

    ax = plt.figure().add_subplot(projection='3d')
    ax.scatter(poses[0, 0], poses[0, 1], poses[0, 2], marker='x', c='r', label='Initial position')
    ax.scatter(cdMo[0, -1], cdMo[1, -1], cdMo[2, -1], marker='x', c='g', label='Desired position')
    ax.plot(poses[:, 0], poses[:, 1], zs=poses[:, 2], label='Camera trajectory')
    plt.title('Camera trajectory in world space', fontsize=24)
    plt.legend()
    plt.tight_layout()
    plt.savefig(plot_folder_path / 'cMo_3d.pdf')
    plt.close()

  if 'features' in json_data and 'desiredFeatures' in json_data:
    print('Generating features plot...')
    desired_features_2d = np.array([[f['x'], f['y']] for f in json_data['desiredFeatures']])
    fig = plt.figure()
    plt.scatter(desired_features_2d[:, 0], desired_features_2d[:, 1], marker='x')
    trajectories_2d = []
    point_count = len(desired_features_2d)
    for i in range(point_count): # Each iteration we have a set of point_count points, iterate on every point to get the 2d trajectory of each point
       trajectories_2d.append(np.array([[points[i]['x'], points[i]['y']] for points in json_data['features']]))
    for trajectory_2d in trajectories_2d:
       plt.plot(trajectory_2d[:, 0], trajectory_2d[:, 1])
    plt.title('IBVS feature trajectories')
    plt.tight_layout()
    plt.savefig(plot_folder_path / 'trajectories_2d.pdf')
    plt.close()

  print(f'Plots were saved to {plot_folder_path}')