Skip to content

Commit 67edaf8

Browse files
authored
Enhance rectangle fitting docs (AtsushiSakai#848)
* enhance rectangle fitting docs * enhance rectangle fitting docs * Update rectangle_fitting_main.rst * Update rectangle_fitting_main.rst * Update rectangle_fitting_main.rst * Update rectangle_fitting_main.rst * Update rectangle_fitting_main.rst * Update rectangle_fitting_main.rst * Update rectangle_fitting_main.rst * Update rectangle_fitting_main.rst * enhance rectangle fitting docs
1 parent d7fdcad commit 67edaf8

File tree

3 files changed

+128
-44
lines changed

3 files changed

+128
-44
lines changed

Mapping/rectangle_fitting/rectangle_fitting.py

Lines changed: 65 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,47 @@
3030

3131

3232
class LShapeFitting:
33+
"""
34+
LShapeFitting class. You can use this class by initializing the class and
35+
changing the parameters, and then calling the fitting method.
36+
37+
"""
3338

3439
class Criteria(Enum):
3540
AREA = 1
3641
CLOSENESS = 2
3742
VARIANCE = 3
3843

3944
def __init__(self):
40-
# Parameters
45+
"""
46+
Default parameter settings
47+
"""
48+
#: Fitting criteria parameter
4149
self.criteria = self.Criteria.VARIANCE
42-
self.min_dist_of_closeness_criteria = 0.01 # [m]
43-
self.d_theta_deg_for_search = 1.0 # [deg]
44-
self.R0 = 3.0 # [m] range segmentation param
45-
self.Rd = 0.001 # [m] range segmentation param
50+
#: Minimum distance for closeness criteria parameter [m]
51+
self.min_dist_of_closeness_criteria = 0.01
52+
#: Angle difference parameter [deg]
53+
self.d_theta_deg_for_search = 1.0
54+
#: Range segmentation parameter [m]
55+
self.R0 = 3.0
56+
#: Range segmentation parameter [m]
57+
self.Rd = 0.001
4658

4759
def fitting(self, ox, oy):
60+
"""
61+
Fitting L-shape model to object points
62+
63+
Parameters
64+
----------
65+
ox : x positions of range points from an object
66+
oy : y positions of range points from an object
67+
68+
Returns
69+
-------
70+
rects: Fitting rectangles
71+
id_sets: id sets of each cluster
4872
73+
"""
4974
# step1: Adaptive Range Segmentation
5075
id_sets = self._adoptive_range_segmentation(ox, oy)
5176

@@ -60,56 +85,53 @@ def fitting(self, ox, oy):
6085

6186
@staticmethod
6287
def _calc_area_criterion(c1, c2):
63-
c1_max = max(c1)
64-
c2_max = max(c2)
65-
c1_min = min(c1)
66-
c2_min = min(c2)
67-
88+
c1_max, c1_min, c2_max, c2_min = LShapeFitting._find_min_max(c1, c2)
6889
alpha = -(c1_max - c1_min) * (c2_max - c2_min)
69-
7090
return alpha
7191

7292
def _calc_closeness_criterion(self, c1, c2):
73-
c1_max = max(c1)
74-
c2_max = max(c2)
75-
c1_min = min(c1)
76-
c2_min = min(c2)
93+
c1_max, c1_min, c2_max, c2_min = LShapeFitting._find_min_max(c1, c2)
7794

7895
# Vectorization
79-
D1 = np.minimum(c1_max - c1, c1 - c1_min)
80-
D2 = np.minimum(c2_max - c2, c2 - c2_min)
81-
d = np.maximum(np.minimum(D1, D2), self.min_dist_of_closeness_criteria)
96+
d1 = np.minimum(c1_max - c1, c1 - c1_min)
97+
d2 = np.minimum(c2_max - c2, c2 - c2_min)
98+
d = np.maximum(np.minimum(d1, d2), self.min_dist_of_closeness_criteria)
8299
beta = (1.0 / d).sum()
83100

84101
return beta
85102

86103
@staticmethod
87104
def _calc_variance_criterion(c1, c2):
88-
c1_max = max(c1)
89-
c2_max = max(c2)
90-
c1_min = min(c1)
91-
c2_min = min(c2)
105+
c1_max, c1_min, c2_max, c2_min = LShapeFitting._find_min_max(c1, c2)
92106

93107
# Vectorization
94-
D1 = np.minimum(c1_max - c1, c1 - c1_min)
95-
D2 = np.minimum(c2_max - c2, c2 - c2_min)
96-
E1 = D1[D1 < D2]
97-
E2 = D2[D1 >= D2]
98-
V1 = - np.var(E1) if len(E1) > 0 else 0.
99-
V2 = - np.var(E2) if len(E2) > 0 else 0.
100-
gamma = V1 + V2
108+
d1 = np.minimum(c1_max - c1, c1 - c1_min)
109+
d2 = np.minimum(c2_max - c2, c2 - c2_min)
110+
e1 = d1[d1 < d2]
111+
e2 = d2[d1 >= d2]
112+
v1 = - np.var(e1) if len(e1) > 0 else 0.
113+
v2 = - np.var(e2) if len(e2) > 0 else 0.
114+
gamma = v1 + v2
101115

102116
return gamma
103117

118+
@staticmethod
119+
def _find_min_max(c1, c2):
120+
c1_max = max(c1)
121+
c2_max = max(c2)
122+
c1_min = min(c1)
123+
c2_min = min(c2)
124+
return c1_max, c1_min, c2_max, c2_min
125+
104126
def _rectangle_search(self, x, y):
105127

106-
X = np.array([x, y]).T
128+
xy = np.array([x, y]).T
107129

108130
d_theta = np.deg2rad(self.d_theta_deg_for_search)
109131
min_cost = (-float('inf'), None)
110132
for theta in np.arange(0.0, np.pi / 2.0 - d_theta, d_theta):
111133

112-
c = X @ rot_mat_2d(theta)
134+
c = xy @ rot_mat_2d(theta)
113135
c1 = c[:, 0]
114136
c2 = c[:, 1]
115137

@@ -129,8 +151,8 @@ def _rectangle_search(self, x, y):
129151
sin_s = np.sin(min_cost[1])
130152
cos_s = np.cos(min_cost[1])
131153

132-
c1_s = X @ np.array([cos_s, sin_s]).T
133-
c2_s = X @ np.array([-sin_s, cos_s]).T
154+
c1_s = xy @ np.array([cos_s, sin_s]).T
155+
c2_s = xy @ np.array([-sin_s, cos_s]).T
134156

135157
rect = RectangleData()
136158
rect.a[0] = cos_s
@@ -151,28 +173,28 @@ def _rectangle_search(self, x, y):
151173
def _adoptive_range_segmentation(self, ox, oy):
152174

153175
# Setup initial cluster
154-
S = []
176+
segment_list = []
155177
for i, _ in enumerate(ox):
156-
C = set()
157-
R = self.R0 + self.Rd * np.linalg.norm([ox[i], oy[i]])
178+
c = set()
179+
r = self.R0 + self.Rd * np.linalg.norm([ox[i], oy[i]])
158180
for j, _ in enumerate(ox):
159181
d = np.hypot(ox[i] - ox[j], oy[i] - oy[j])
160-
if d <= R:
161-
C.add(j)
162-
S.append(C)
182+
if d <= r:
183+
c.add(j)
184+
segment_list.append(c)
163185

164186
# Merge cluster
165187
while True:
166188
no_change = True
167-
for (c1, c2) in list(itertools.permutations(range(len(S)), 2)):
168-
if S[c1] & S[c2]:
169-
S[c1] = (S[c1] | S.pop(c2))
189+
for (c1, c2) in list(itertools.permutations(range(len(segment_list)), 2)):
190+
if segment_list[c1] & segment_list[c2]:
191+
segment_list[c1] = (segment_list[c1] | segment_list.pop(c2))
170192
no_change = False
171193
break
172194
if no_change:
173195
break
174196

175-
return S
197+
return segment_list
176198

177199

178200
class RectangleData:

docs/modules/mapping/rectangle_fitting/rectangle_fitting_main.rst

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,65 @@ This is an object shape recognition using rectangle fitting.
55

66
.. image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/Mapping/rectangle_fitting/animation.gif
77

8+
This example code is based on this paper algorithm:
9+
10+
- `Efficient L\-Shape Fitting for Vehicle Detection Using Laser Scanners \- The Robotics Institute Carnegie Mellon University <https://www.ri.cmu.edu/publications/efficient-l-shape-fitting-for-vehicle-detection-using-laser-scanners>`_
11+
12+
The algorithm consists of 2 steps as below.
13+
14+
Step1: Adaptive range segmentation
15+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16+
17+
In the first step, all range data points are segmented into some clusters.
18+
19+
We calculate the distance between each range data and the nearest range data, and if this distance is below a certain threshold, it is judged to be in the same cluster.
20+
This distance threshold is determined in proportion to the distance from the sensor.
21+
This is taking advantage of the general model of distance sensors, which tends to have sparser data distribution as the distance from the sensor increases.
22+
23+
The threshold range is calculated by:
24+
25+
.. math:: r_{th} = R_0 + R_d * r_{origin}
26+
27+
where
28+
29+
- :math:`r_{th}`: Threashold range
30+
- :math:`R_0, R_d`: Constant parameters
31+
- :math:`r_{origin}`: Distance from the sensor for a range data.
32+
33+
Step2: Rectangle search
34+
~~~~~~~~~~~~~~~~~~~~~~~~~~
35+
36+
In the second step, for each cluster calculated in the previous step, rectangular fittings will be applied.
37+
In this rectangular fitting, each cluster's distance data is rotated at certain angle intervals.
38+
It is evaluated by one of the three evaluation functions below, then best angle parameter one is selected as the rectangle shape.
39+
40+
1. Rectangle Area Minimization criteria
41+
=========================================
42+
43+
This evaluation function calculates the area of the smallest rectangle that includes all the points, derived from the difference between the maximum and minimum values on the x-y axis for all distance data points.
44+
This allows for fitting a rectangle in a direction that encompasses as much of the smallest rectangular shape as possible.
45+
46+
47+
2. Closeness criteria
48+
======================
49+
50+
This evaluation function uses the distances between the top and bottom vertices on the right side of the rectangle and each point in the distance data as evaluation values.
51+
If there are points on the rectangle edges, this evaluation value decreases.
52+
53+
3. Variance criteria
54+
=======================
55+
56+
This evaluation function uses the squreed distances between the edges of the rectangle (horizontal and vertical) and each point.
57+
Calculating the squared error is the same as calculating the variance.
58+
The smaller this variance, the more it signifies that the points fit within the rectangle.
59+
60+
API
61+
~~~~~~
62+
63+
.. autoclass:: Mapping.rectangle_fitting.rectangle_fitting.LShapeFitting
64+
:members:
65+
66+
References
67+
~~~~~~~~~~
68+
69+
- `Efficient L\-Shape Fitting for Vehicle Detection Using Laser Scanners \- The Robotics Institute Carnegie Mellon University <https://www.ri.cmu.edu/publications/efficient-l-shape-fitting-for-vehicle-detection-using-laser-scanners>`_

docs/modules/slam/FastSLAM1/FastSLAM1_main.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ represent the initial uncertainty. At each time step we do:
6565

6666
The following equations and code snippets we can see how the particles
6767
distribution evolves in case we provide only the control :math:`(v,w)`,
68-
which are the linear and angular velocity repsectively.
68+
which are the linear and angular velocity respectively.
6969

7070
:math:`\begin{equation*} F= \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \end{equation*}`
7171

0 commit comments

Comments
 (0)