# Lesson C: Static range-and-bearing localization

In this lessson, we still focus on the perception of landmarks clearly identified: each observation is related to a known position.
The problem is similar to the localization of the previous lesson, but now we also consider the bearing, *i.e.* the angular position of the rock with respect to the heading of the robot.

We will see how to handle this constraint. From this example, we will understand the principles of *decomposition of constraints* and the *polar equations*.

## Formalism

For the moment, the map is made of one landmark. The localization problem corresponds to the following observation function:

where \(\mathbf{x}\) is the unknown state vector: the robot is located at \(\left(x_{1},x_{2}\right)^\intercal\) with a heading \(x_{3}\). The output measurement vector is \(\mathbf{y}\), made of the range \(y_{1}\) and the bearing \(y_{2}\), as illustrated by the figure. It is assumed that the measurement is bounded: \(\mathbf{y}\in\left[\mathbf{y}\right]\), contrary to \(\mathbf{x}\) that is completely unknown: \(\mathbf{x}\in[\mathbf{x}]=[-\infty,\infty]^2\). We say that \([\mathbf{x}]\) is an *unbounded* set.

Often, the observation equation also appears in the following implicit form:

In our *range-and-bearing* case, the observation equation is then defined as:

*range-only*case where the robot only measures the distance \(y\) to the landmark, we obtain the following relation:

\[g(\mathbf{x},y)=\left(x_{1}-m_1\right)^{2}+\left(x_{2}-m_2\right)^{2}-y^{2}.\]

In a set-membership approach, the uncertainties related to the state \(\mathbf{x}\), the measurement \(\mathbf{y}\) and the landmark \(\mathbf{m}\) will be implicitly represented by interval-vectors (boxes): \(\mathbf{x}\in[\mathbf{x}]\), \(\mathbf{y}\in[\mathbf{y}]\) and \(\mathbf{m}\in[\mathbf{m}]\).

We will solve this problem using the constraint programming approach.

## Decomposition of constraints

Problems involving complex equations can be broken down into a set of **primitive constraints**. Here, *primitive* means that the constraints cannot be decomposed anymore. For instance, in a range-only localization problem (see Lessons A and B), the observation constraint \(\mathcal{L}_{\textrm{dist}}\) can be broken down into:

where \(a,b,\dots,e\) are intermediate variables used for the decomposition. This constitutes a network made of the \(\mathcal{L}_{-}\), \(\mathcal{L}_{+}\), \(\mathcal{L}_{(\cdot)^2}\), and \(\mathcal{L}_{\sqrt{\cdot}}\) elementary constraints.

**elementary contractors**such as \(\mathcal{C}_{+}\), \(\mathcal{C}_{(\cdot)^2}\), \(\mathcal{C}_{\sqrt{\cdot}}\) and build the complex contractor modeling the constraint \(\mathcal{L}_{\textrm{dist}}\).

```
ctc_dist = CtcFunction(Function("x[2]", "b[2]", "y",
"sqrt((x[0]-b[0])^2+(x[1]-b[1]))-y"))
```

```
CtcFunction ctc_dist(Function("x[2]", "b[2]", "y",
"sqrt((x[0]-b[0])^2+(x[1]-b[1]))-y"));
```

## Optimality of contractors

Depending on the properties of the equation, the resulting contractor can be **optimal**. It means that the contracted box will perfectly enclose the set of feasible solutions **without pessimism**. This enables an efficient resolution.

In other cases, for instance because of dependencies between the variables, the resulting operator may not be optimal.
For instance, looking at the equation depicting the above problem of *range-and-bearing* localization, the formula

could be implemented by

```
ctc_g = CtcFunction(Function("x[3]", "y[2]", "m[2]",
"(x[0]+y[0]*cos(x[2]+y[1])-m[0] ; x[1]+y[0]*sin(x[2]+y[1])-m[1])"))
```

```
CtcFunction ctc_g(Function("x[3]", "y[2]", "m[2]",
"(x[0]+y[0]*cos(x[2]+y[1])-m[0] ; x[1]+y[0]*sin(x[2]+y[1])-m[1])"));
```

However, this involves a multi-occurrence of variables which leads to pessimism. For instance, the sum \((x_3+y_2)\) appears twice in functions \(\cos\) and \(\sin\), which is hardly handled by a classical decomposition.

We may instead use a **dedicated contractor** that deals with the **polar constraint**, appearing in the above function \(\mathbf{g}\). This will allow us to avoid multi-occurrences and then, pessimism.

## The polar constraint

The range-and-bearing problem is usually related to the *polar constraint* that links Cartesian coordinates to polar ones. This constraint is often encountered in robotics and it is important to be able to deal with it in an optimal way.

This simple and elementary constraint is expressed by:

*elementary*in the sense that it cannot be decomposed anymore without losing its optimality. In addition, a dedicated contractor already exists to deal with it: the

`CtcPolar`

.Technical documentation

The CtcPolar page of the user manual provides more details about the contractor `CtcPolar`

and the related publication.

This constraint appears in the expression of \(\mathbf{g}\).

Exercise

**C.1.** On a sheet of paper, write a decomposition of function \(\mathbf{g}\) that involves \(\mathcal{L}_\textrm{polar}\), other constraints and intermediate variables.

## Initialization

A robot depicted by the state \(\mathbf{x}=\left(2,1,\pi/6\right)^\intercal\) is perceiving a landmark \(\mathbf{m}=\left(5,6.2\right)^\intercal\) at a range \(y_1=6\) and a bearing \(y_2=\pi/6\). We assume that the position of the robot is not known and that the uncertainties related to the measurement and the landmark are:

**Measurement.**For \(y_1\): \([-0.3,0.3]\), for \(y_2\): \([-0.1,0.1]\).**Landmark.**For each component of the 2d dimension: \([-0.2,0.2]\).

Exercise

**C.2.** Create a new project (Python or C++) and create the vectors `x_truth`

, `y_truth`

, `m_truth`

representing the actual but unknown values of \(\mathbf{x}=\left(2,1,\pi/6\right)^\intercal\), \(\mathbf{y}=\left(6,\pi/6\right)^\intercal\) and \(\mathbf{m}=\left(5,6.2\right)^\intercal\).

```
# We recall that in Python, a mathematical vector (not an IntervalVector)
# can be represented with native objects, for instance:
x_truth = (2,1,math.pi/6) # actual state vector (pose)
```

```
// We recall that in C++, we can use the Vector class for
// representing mathematical vectors. For instance:
Vector x_truth({2.,1.,M_PI/6}); // actual state vector (pose)
```

**C.3.** Create the bounded sets related to the state, the measurement and the landmark position: \([\mathbf{x}]\in\mathbb{IR}^3\), \([\mathbf{y}]\in\mathbb{IR}^2\), \([\mathbf{m}]\in\mathbb{IR}^2\). We can for instance use the `.inflate(float radius)`

method on intervals or boxes. The heading of the robot is assumed precisely known (for instance thanks to a compass); the actual heading \(x_3\) is represented by `x_truth[2]`

.

**C.4.** Display the vehicle and the landmark with:

```
beginDrawing()
fig_map = VIBesFigMap("Map")
fig_map.set_properties(100,100,500,500)
fig_map.axis_limits(0,7,0,7)
fig_map.draw_vehicle(x_truth, size=1)
fig_map.draw_box(m, "red")
fig_map.draw_box(x.subvector(0,1)) # does not display anything if unbounded
fig_map.show()
endDrawing()
```

```
vibes::beginDrawing();
VIBesFigMap fig_map("Map");
fig_map.set_properties(100,100,500,500);
fig_map.axis_limits(0,7,0,7);
fig_map.draw_vehicle(x_truth, 1);
fig_map.draw_box(m, "red");
fig_map.draw_box(x.subvector(0,1)); // does not display anything if unbounded
fig_map.show();
vibes::endDrawing();
```

**C.5.** Display the range-and-bearing measurement with its uncertainties. For this, we will use the `fig_map.draw_pie(x,y,interval_rho,interval_theta)`

to display a portion of a ring \([\rho]\times[\theta]\) centered on \((x,y)^\intercal\). Here, we add in \([\theta]\) the robot heading \(x_3\) and the bounded bearing \([y_2]\).

You should obtain this figure:

As one can see, intervals are not limited to axis-aligned boxes: we sometimes perform rotational mapping to better fit the set to represent. This polar constraint is a case in point.

## State estimation

We will implement the decomposition of Question **C.1** using the `ContractorNetwork`

, that will manage the constraint propagation for us.

Exercise

*The following must be made before the graphical part.*

**C.6.** Create the contractors related to the decomposition of Question **C.1**.

- The contractor \(\mathcal{C}_\textrm{polar}\) is given by the class
`CtcPolar`

. Note that you do not need to create an object of this class, since one is already instantiated in the library (it is named`ctc.polar`

in Python,`ctc::polar`

in C++). The other contractors can be built with several

`CtcFunction`

objects, as we did in the previous Lessons A and B. We recall that these constraints have to be expressed in the form \(f(\mathbf{x})=0\). See more.

**C.7.**Create a

`ContractorNetwork`

(CN) to solve the problem by adding the contractors and the domains from the decomposition.```
cn = ContractorNetwork()
cn.add(<ctc_object>, [<dom1>,<dom2>,...])
```

```
ContractorNetwork cn;
cn.add(<ctc_object>, {<dom1>,<dom2>,...});
```

**C.8.**Create the intermediate variables introduced in Question

**C.1**. They are

`Interval`

and `IntervalVector`

objects, as for the other variables.**C.9.** Use `cn.contract()`

to solve the problem. You should obtain this figure:

The black box \([\mathbf{x}]\) cumulates all the uncertainties of the problem:

uncertainties of the measurement

uncertainties of the position of the landmark

If we remove the uncertainties related to the measurement \([\mathbf{y}]\), then the width of \([\mathbf{x}]\) should be exactly the same as the one of \([\mathbf{m}]\), because we used optimal contractors. The width of a box is given by the `.diam()`

method.