MeARM UPV QuickStart LaserCutting
MeARM UPV QuickStart LaserCutting
4 28 25
29 3
16
10 24
27
1
6
21 22 9 19
26 14
13 11 12 2 5
7
18 17
20
23
30
1x Arduino + USB Cable 1x Extension Shield 4x Servo motor 1x Li-Ion 16340 Battery
1
6
21 22 9 19
26 14
13 11 12 2 5
7
18 17
20
23
30
1x ESP-01S with 10x DuPont Cables F-F 1x Servo Controller PCA9685 4x Servo motor
USB Programmer
1x DIY 16340 Powerbank 1x Li-Ion 16340 Battery Spaces, Scews, Nuts Selftap screws
2mm
Select the Arduino Nano board in ToolsYBoard. In ToolsYProcessor, you must take into
consideration that if you are using a cloned version, you must use the ATmega328P (old
bootloader) version. If the Arduino board is original, then you must use ATmega328P.
Once the board and the processor have been selected, you must select the communication
port in ToolsYPort. Connect the Arduino to the PC and a new serial port will appear that
you must select. If no new port appears, then it probably means that you have not properly
installed the driver. In the cloned versions of Arduino, the CH340G chip is used, so you must
install the driver for this chip following the instructions indicated here, if that’s the
case:https://sparks.gogo.co.nz/ch340.html
Connect the USB cable, with an empty program (with the "setup" and "loop" functions that
come by default), try uploading the code to the Arduino board by accessing ProgramYUpload.
Usually, it is convenient to previously compile the program, to verify that you do not have any
errors in the code. In that case, to compile the program (do not upload the code), go to
ProgramYVerify/Compile.
The ESP-01S is a development board based on the ESP8266 processor. In order to program
this development board it is convenient to use a USB programmer. The ESP-01S must be
connected to the USB programmer as shown in the figure. The USB programmer has
duplicated the connector, so that on the one hand it allows you to connect the ESP-01S to
the programmer and at the same time it allows you to connect the device to other devices
through the processor signals (RX, TX, GPIO0 and GPIO2) and to supply external power the
ESP -01S processor through the power pins (VCC and GND).
ESP-01S Flash Memory
Pins
In order to use the Servo controller board, you need to install Adafruit PWM Servo Driver
library. You can install this library from ProgramYInclude LibraryYManage Libraries and
search for ‘PWM Servo’.
Connect the USB cable, with an empty program (with the "setup" and "loop" functions that
come by default), try uploading the code to the Arduino board by accessing ProgramYUpload.
Usually, it is convenient to previously compile the program, to verify that you do not have any
errors in the code. In that case, to compile the program (do not upload the code), go to
ProgramYVerify/Compile.
Component Arduino PIN
Servo motor 1 7
Servo motor 2 6
Servo motor 3 5
Servo motor 4 4
Powerbank (5V and GND) +5V
Gnd
Previous to assembling the robot, it is convinient to set all servos at 90 position. This is an
intermediate position between 0 to 180 servo range. Brown cable is for ground signal and
yellow for signal, while red is for Vcc (a voltage between 4V and 6V approx.).
<
<
180
Los servos tienen un tiempo de ciclo de
20ms, con lo que no debéis mandar
comandos de movimiento por debajo de
este periodo. Utilizad instrucciones de
retardo (delay) para garantizar que se
cumple con este periodo.
0
#include <Servo.h>
#define SERVO4_PIN 4
#define SERVO4_OFFSET 0
#define SERVO4_MIN_POS 0
#define SERVO4_MAX_POS 180
typedef struct
{
uint8_t pin;
int offset;
int min_pos;
int max_pos;
} RobotServo_t;
void setup() {
writeServo(servo4,90); //Sets the position of the servo to 90º
delay(1000);
detachServo(servo4);
}
void loop() {
}
q4=90
q2=90
q4=55 Joint 2
q2=50 q3=20
q2=170 q3=90
q1=0
q3=160 Joint 3
q1=90
Joint 1
q1=180
Base0 fija
Link Link 1 Link 2 Link 3 (left)
q1
d2
X
d5
d1
Y
q3,O
l3,O
l3 l4
l2
q3 q2
Dimensiones
Dimensions mm l3,I
d1 75 Z
d2 83.7
l0 5 h1
h1 64.5
l1 15.1
l2 80
l3 80 X
l3,I 23.9
l3,O 35
l4 80 l0
l5 36
52
d5 5
q1=0
q1=90
q1=180
q4=70
q2=65
q3=110 q4=90
q2=90
q3=90
q2=160
q3=25
Fix four M3x10mm spacers to the base (1) of the robot with M3 nuts.
Now screw a servo (SG90 or MG90S) to the base of servo 1. Use two M3x8mm screws and a
servo bracket (2) for fixing. Then, fix the assembly to the robot base (3) with four M3x16mm
(metal) screws and their corresponding M3 nuts. To fix the height, the screws are screwed
against the wood itself. Note that the direction of the servo cable should be at the front of
the robot.
2 3
Fix a servo (SG90 or MG90S) to the right side of link 1 (4). Use two M3x8mm screws and a
servo bracket (6) to fix it (the screws are screwed against the wood itself). Note that the
direction of the servo cable should be as shown in the figure. Then, screw the base of link 1
(5) to the right side with a M3x10mm screw and a M3 nut. At the bottom of link 1, screw the
double servo horn with four M2x6mm sheet metal screws.
4
6
5
Screw a simple servo handle to the right link 2 (7) with an
M2x6mm sheet metal screw. Then, with the servo positioned at
90, screw the link onto the servo so that it is in the vertical
position, as shown in the figure. If you don't know how to
position the servo at 90, please see the Servo Control section.
8
Now, screw the support for
link 2 (8) to the right link 2
(7), as shown in the figure.
Use an M3x10mm screw and
M3 nut.
Screw the lower right beam of link 3 (9) and the triangle to the
right link 2 (10) from the outside. When screwing, use two
washers between the pieces to reduce friction (see the figure
9
with the enlarged view). The screw is screwed against the
wood itself. The M3x10mm screw must be tight enough so that 10
the robot does not have too much slack, but at the same time,
the parts must rotate easily.
11
Screw the left link 2 (13) and the vertical support (14)
that joins the link 2 (7) with the link 1 (3). Use a small 13
M3x6mm screw with a washer against the wood itself.
14
Subsequently, screw, using M3x10mm screws and M3
nuts, the two pieces on one side to link 1 (3) and on
the other side to the support of link 2 (7). Make sure
that when screwing the M3x6mm screw that it has no
slack, but allows movement.
15
16 17
18
19
26
27 28
𝑞
𝑞
𝑞
𝑞
𝑞
𝑞
0 T
We define a smooth trajectory, 𝑞 𝑡 , in the configuration space with a polynomial
expression (the derivative of the trajectory is 𝑞̇ 𝑡 ) and we set the contour conditions,
𝑞 (0) and 𝑞 𝑇 and their derivates, 𝑞̇ (0) y 𝑞̇ 𝑇 . In particular, for a cubic trajectory, we
cant set as contour conditions for the trajectory the initial and final configurations 𝑞 and
𝑞 , respectively; and the initial and final speeds will be set to zero. Thus, the cubic
expression is defined as follows:
Trajectory (speed): 𝑞̇ 𝑡 = 3𝑎 𝑡 + 2𝑏 𝑡 + 𝑐
Contour conditions: 𝑞 0 =𝑞 𝑞 𝑇 =𝑞
𝑞̇ 0 = 0 𝑞̇ 𝑇 = 0
From contour conditions, we can get the values of the cubic trajectory parameters:
2 𝑞 −𝑞 3 𝑞 −𝑞 𝑐 =0 𝑑 =𝑞
𝑎 =− 𝑏 =
𝑇 𝑇
The parameters of the path are different for each servo and must be recalculated each
time the start, end position, speeds or the time of the path is modified.
In order to evaluate a trajectory over time, we must know the time of the microprocessor
clock. In this case, the most appropriate instruction in Arduino code is millis(), which
provides the clock time in milliseconds. On the other hand, to ensure that we evaluate the
trajectory at least every 20ms, which is the minimum time that the servo needs to
generate the control signal, we must use the delay(…) instruction, which will generate a
wait for the time indicated in milliseconds. Keep in mind that when calling the millis()
function for the first time, the return time does not have to be zero.
Move the robot to three consecutive configurations with a Point To Point trajectory (cubic)
control starting from home configuration. Once the configurations have been reached,
move the robot back to the home configuration.
If the input information is given in the Cartesian space (a position in the Cartesian space), then
we must use the inverse kinematics to compute the joint configurations. The computation Will
be done in four steps: :
1) Joint 𝑞 is computed from coordinates x and y of gripper point 𝑝 . Due to the non-zero term
𝑑 , the computation of 𝑞 must be obtained from the sum of two angles: the first as a
consequence of the angle of 𝑝 w.r.t. the reference frame 0 (shown in the figure). Then, we
need to compensate such angle due to distance 𝑑 from a simple trigonometric relation. On
the other hand, the point of the gripper wrist with respect to the robot, 𝑝 , can be computed
as:
𝑝 −𝑙 𝑑
l5 𝑞 = tan
,
+ sin
−𝑝 ,
𝑝 −𝑙 +𝑝
d5 l3 , ,
q1
𝑙 sin 𝑞 − 𝑙
𝑝 = 𝑝 + −𝑙 cos 𝑞
l1
X 0
x0
y0
l0
Y
2) Joint coordinates 𝑞 and 𝑞 , are computed from the triangle formed by links 2 and 3 of a
conventional robot arm:
𝑟= 𝑝 , +𝑝 , −𝑙 q3,O
l3 g
𝑧 =𝑝 , −ℎ
l2 q2
𝛼 = tan
𝑧 s
𝑟 ze b
a
Z
𝑠= 𝑟 +𝑧 r
h1
𝛾 = cos
𝑙 +𝑙 −𝑠
𝑞 =𝜋−𝛾 z0
,
2𝑙 𝑙
X x0
𝑠 +𝑙 −𝑙
𝛽 = cos 𝑞 =𝜋−𝛼−𝛽
2𝑠𝑙 l1
3) Finally, the angle of the third joint 𝑞 is computed from the inverse kinematics of the four-
bar mechanism:
l3,O
q3,O
e l4
l2 q2
y
q3
f
l3,I Z
𝑙 , sin 𝑞 ,
𝑒= 𝑙 , + 𝑙 − 2𝑙 , 𝑙 cos 𝑞 , 𝜓 = sin
𝑒
𝑒 +𝑙 , −𝑙 𝜋
𝜙 = cos 𝑞 = 𝜓+𝜙+ −𝑞
2𝑒𝑙 , 2
The following code proposes the definition of a new data type in a struct to include all required
robot geometric parameters, RobotParams_t and it also proposes another data type to indicate
a target position to reach in RobotPosition_t. Also, defines the function inverseKin so that from
a target configuration and the robot parameters, returns a goal configuration to reach (in an
array). Function moveJ combines the inverse kinematic with the point to point trajectory
control.
…
typedef struct
{
double l0;
double h1;
double l1;
double l2;
double l3;
double l3I;
double l3O;
double l4;
double l5;
} RobotParams_t;
typedef struct
{
double x;
double y;
double z;
} RobotPosition_t;
…
RobotParams_t params={…,…,…,…,{…}};
RobotPosition_t pT={…};
Compute three robot configurations from three target positions provided in mm and move
the robot to those configurations using the Point to Point trajectory control. Start from the
home position, where all servos are in a 90 position.
Linear motion control with a trajectory can be done with a cubic trajectory, where the
trajectory displacement 𝑠 𝑡 is computed as before. The actual trajectory is computed
from a unitary vector 𝑝⃗ from the initial position, 𝑝 , that reaches the final position 𝑝 . The
initial speed will be zero at the beginning and the end of the trajectory, while in the
intermediate points the speed will be greater, as shown in the following figure:
𝑠(𝑡) 𝑝 𝑝⃗
𝑠
𝑠
0 T 𝑝
To implement the linear control you need to compute the position of the gripper from a
given configuration, that is, the servo angles. As before, we explain the required
computations to implement the forward kinematics of the robot.
The separation of the wrist with respect to the joint 1 axis is computed as follows (in the
figure, when 𝑞 = and 𝑞 , = , the distance value is 𝑙 + 𝑙 ):
𝑟 = −𝑙 cos 𝑞 − 𝑙 cos 𝑞 + 𝑞 ,
On the other hand, the height of the gripper is computed from:
𝑝 , = ℎ + 𝑙 sin 𝑞 + 𝑙 sin 𝑞 + 𝑞 ,
l3,O
q3,O m
t f l4
l2
q2
q3
j
l3,I Z
𝜋
𝜑 =𝑞 +𝑞 −
2
𝑙 , sin 𝜑
𝑓= 𝑙 , + 𝑙 − 2𝑙 , 𝑙 cos 𝜑 𝜏 = sin
𝑓
𝑓 +𝑙 , −𝑙 𝑞 =𝜏+𝜇
𝜇 = cos ,
2𝑓𝑙 ,
The following code proposes an implementation of two functions forwardKin y moveL.
Function forwardKin must implement the forward kinematics of the robot, while function
moveL, must implement the linear control with a cubic trajectory.
…
void forwardKin(const double q[JOINTS], const RobotParams_t ¶ms, RobotPosition_t *target);
void moveL(const RobotServo_t robotServos[JOINTS], const double q0[JOINTS], const RobotPosition_t target, const float T, const
RobotParams_t ¶ms);
…
void moveL(const RobotServo_t robotServos[JOINTS], const float q0[JOINTS], const RobotPosition_t target, const float T, const
RobotParams_t ¶ms)
{
//TODO: Call forwardKin to compute the current gripper position from the given configuration. Then compute a target position and
move the gripper along the trajectory by sending proper servos commands using the inverse kinematics.
}
void forwardKin(const double q[JOINTS], const RobotParams_t ¶ms, RobotPosition_t *target)
{
//TODO: Return in target the values of the position of the gripper.
}
Implement now the three consecutive movement using a linear control. At the end, the
robot must return to the initial configuration, also with a linear movement.
In order to move the robot with a circular movement in the 3D Cartesian space, we need
the position of three points. The first point, 𝑃 , corresponds to the actual position of robot,
while the other two points, 𝑃 and 𝑃 must be given. Obviously, the points must met some
conditions so that they belong to a 3D circle, such as they must not be aligned or repeated,
so we will assume that these conditions are met (checking those conditions is out of the
scope). The procedure is actually quite simple, we need to define a reference frame with
origin 𝑃 and with axis X pointing towards the direction 𝑃 = 𝑃 − 𝑃 and axis Z
perpendicular to vectors 𝑃 and 𝑃 = 𝑃 − 𝑃 . By doing this, we can express the original
points in the new reference frame, where the z coordinate is zero and therefore all points
will be contained in a 2D plane. If we extract the circle center 𝐶 and undo the
transformation to obtain the original 3D center, 𝐶. Once the center is obtained, we can
create a trajectory starting from 𝑃 and finishing in 𝑃 passing through point 𝑃 . The
temporal implementation for this trajectory can use a polynomic expression for the
travelled distance.
The maths with all these computations are explained below. The transformation of the
points is expressed with the following homogenous transformation matrix:
𝑋 𝑌 𝑍 𝑃
𝑇=
0 0 0 1
×
with 𝑋 = ,𝑍 = , Y = 𝑍 × 𝑋, being × the cross product.
×
The points transformed are:
𝑃 =𝑇 𝑃 𝑃
, 𝑃 =𝑇
1 1 1 1
Therefore, the position of the cernter in the XY plane of the new reference frame and its
corresponding 3D position are computed as:
𝑃 𝑃,
𝐶= 𝑃, 𝑃 − 2, +𝑃 − 𝐶
=𝑇 𝐶
, , 2 0 𝑅= 𝐶
2 2𝑃 1 1
,
being R, the radius of the circle, that is, the distance of the center to the origin of the new
reference frame.
𝑃
Finally, the circular trajectory 𝑃(𝑡) is obtained from:
cos 𝜃(𝑡) 𝜃(0)
𝑃(𝑡)
𝑃 𝑡 = 𝑅 sin 𝜃(𝑡) + 𝐶 = 𝑇 𝑃(𝑡)
1 1
0 𝑃
being 𝜃(𝑡) the angle along the circular trajectory evaluated 𝜃(𝑇) 𝑃
from 0 to T. This trajectory is not necessary linear and we
can demand some initial and final conditions for the speed
of the trajectory using, for instance a cubic expresión as
previously seen. In addition to this, we set the conditions
that the trajectory must start from 𝑃 and end at 𝑃 :
𝜃 𝑡 = 𝑎𝑡 + 𝑏𝑡 + 𝑐𝑡 + 𝑑 𝜃̇ 𝑡 = 3𝑎𝑡 + 2𝑏𝑡 + 𝑐
−𝐶
𝜃 0 = tan 𝜃̇ 0 = 0 𝜃̇ 𝑇 = 0
−𝐶
𝑃 −𝐶 ⋅𝐶 𝑃 −𝐶 ⋅𝐶
𝜃 𝑇 = cos − + 𝜃(0) or 𝜃 𝑇 = 2𝜋 − cos − +𝜃 0
𝑅 𝑅
2 𝜃 𝑇 −𝜃 0 3 𝜃 𝑇 −𝜃 0
with 𝑎 = − 𝑏= 𝑐=0 𝑑 = 𝜃(0)
𝑇 𝑇
Remark: One of the two solutions Will pass through point 𝑃 while the other condition will
not. Choose the right choice for your case.
Now, you are in position for implementing a function moveC, that generates a circular
trajectory from the actual robot configuration and two target points: target1 and target2,
given by their Cartesian coordinates. The reminder of input arguments of the function are
similar to the previous functions.
…
void moveC(const RobotServo_t robotServos[JOINTS], const float q0[JOINTS], const RobotPosition_t target1, const RobotPosition_t
target2, const float T, const RobotParams_t ¶ms);
…
void moveC(const RobotServo_t robotServos[JOINTS], const float q0[JOINTS], const RobotPosition_t target1, const RobotPosition_t
target2, const float T, const RobotParams_t ¶ms)
{
//TODO: Compute a ciruclar trajectory based on the current configuration, and target points 1 and 2
}
…
Now implement a circular movement in the XY, YZ or XZ plane and with a free-choice
radius, as well as the amount of rotation within the circle. If you want to make a complete
circle, you must do it in two steps.
From the inverse kinematics, we know that:
𝑝 −𝑙, 𝑑 𝑝 𝑑
𝑞 = tan + sin = tan + sin
−𝑝 , −𝑝
𝑝 , −𝑙 +𝑝 ,
𝑝 +𝑝
𝑝 , 𝐽 , +𝑝 , 𝐽 , 𝑝 , 𝐽 , +𝑝 , 𝐽 ,
𝑟= 𝑝 , +𝑝 , −𝑙 𝑟̇ = 0 ⋅ 𝑝̇ = 𝐽 ⋅ 𝑝̇
𝑟+𝑙 𝑟+𝑙
𝑧 𝐽 , 𝑧 𝐽 , 𝑟
𝑧 𝛼̇ = − − ⋅ 𝑝̇ = 𝐽 ⋅ 𝑝̇
𝑧 =𝑝 , −ℎ 𝛼 = tan 𝑠 𝑠 𝑠
𝑟
𝑟𝐽 , 𝑟𝐽 , 𝑧
𝑙 +𝑙 −𝑠 𝑠̇ = ⋅ 𝑝̇ = 𝐽 ⋅ 𝑝̇
𝑠= 𝑟 +𝑧 𝛾 = cos 𝑠 𝑠 𝑠
2𝑙 𝑙
𝑠
𝛾̇ = ⋅ 𝐽 ⋅ 𝑝̇ = 𝐽 ⋅ 𝑝̇
𝑠 +𝑙 −𝑙 𝑙 𝑙 sin 𝛾
𝛽 = cos
2𝑠𝑙 1 1
𝛽̇ = − ⋅ 𝐽 ⋅ 𝑝̇ = 𝐽 ⋅ 𝑝̇
𝑠 tan 𝛽 𝑙 sin 𝛽
𝑞 , =𝜋−𝛾
𝑞 =𝜋−𝛼−𝛽 𝑞̇ = − 𝐽 + 𝐽 ⋅ 𝑝̇ = 𝐽 ⋅ 𝑝̇
𝑙 , 𝑙 sin 𝑞 ,
𝑒= 𝑙 , + 𝑙 − 2𝑙 , 𝑙 cos 𝑞 , 𝑒̇ = − ⋅ 𝐽 ⋅ 𝑝̇ = 𝐽 ⋅ 𝑝̇
𝑒
𝑙 sin 𝑞 1
𝜓 = sin
, , 𝜓̇ = − ⋅ 𝑙 , cos 𝑞 , 𝐽 + sin 𝜓 𝐽 ⋅ 𝑝̇ = 𝐽 ⋅ 𝑝̇
𝑒 𝑒 cos 𝜓
𝑙 , cos 𝜙 − 𝑒
𝑒 +𝑙 , −𝑙 𝜙̇ = ⋅ 𝐽 ⋅ 𝑝̇ = 𝐽 ⋅ 𝑝̇
𝜙 = cos 𝑒𝑙 , sin 𝜙
2𝑒𝑙 ,
𝜋 𝑞̇ = 𝐽 + 𝐽 − 𝐽 ⋅ 𝑝̇ = 𝐽 ⋅ 𝑝̇
𝑞 =𝜓+𝜙+ −𝑞
2
In summary, we can compute the velocities of the servos bsaed on the velocities of the end
effector by computing its jacobian. In this way, we can implement a kinematic control law so
that the end effector follows a trajectory given by 𝑝 ∗ 𝑡 and its velocity 𝑝̇ ∗ 𝑡 , being 𝑘 the
proportional gain of the controller:
𝑞̇ 𝑡 = 𝐽 𝑝 𝑡 ⋅ 𝑘 𝑝∗ 𝑡 − 𝑝 𝑡 + 𝑝̇ ∗ 𝑡
Please, note that since we have computed the jacobian from inverse kinematic expressions,
we do not need to compute the inverse of the jacobian. It is also important to remark that
since the robot uses position servos, we need to integrate the control law on evary stpe to
actually compute the reference to set the servo position.
A trajectory XY describing an infinite shape of size wxxwy and centre cx,cy,cz and duration T
seconds would have the following timed expression:
𝑤 4𝜋𝑡 𝜋 2𝑤 𝜋 4𝜋𝑡 𝜋
cos − +𝑐 − sin −
2 𝑇 2 𝑇 𝑇 2
𝑝∗ 𝑡 = 𝑤 2𝜋𝑡 𝑝̇ ∗ 𝑡 = 𝑤 𝜋 2𝜋𝑡
sin −𝜋 +𝑐 cos −𝜋
2 𝑇 𝑇 𝑇
0 0
The following code proposes the implementation of three function infiniteTraj,
inverseDiffKin and kinControl. Function infiniteTraj is a function that must provide with
the position and velocity of the trajectory for a given time instant, function inverseDiffKin
must return the velocity of servos for a given velocity of the end effector, while function
kinControl computes the proportional control law with velocity feed-forward.
…
typedef struct
{
double vx;
double vy;
double vz;
} RobotVelocity_t;
typedef struct
{
double cx;
double cy;
double cz;
double wx;
double wy;
double wz;
double T;
} TrajectoryParams_t;
typedef struct
{
double gain;
} ControlParams_t;
typedef void (*trajectoryPtr)(const double t , const TrajectoryParams_t &trajParams, RobotPosition_t* target, RobotVelocity_t
*vel_target);
void infiniteTraj(const double t , const TrajectoryParams_t &trajParams, RobotPosition_t* target, RobotVelocity_t *vel_target)
{
//TODO: Compute position and velocity of the infinite shape trajectory for the given time.
}
void inverseDiffKin(const RobotPosition_t &end_effector, const RobotVelocity_t &vel_end_effector, const RobotParams_t ¶ms,
double *dq);
void kinControl(const RobotServo_t robotServos[JOINTS], const double q0[JOINTS], const trajectoryPtr trajectory, const double T,
const RobotParams_t ¶ms, const TrajectoryParams_t &trajParams, const ControlParams_t &controlParams);
…
void setup()
{
…
kinControl(robotServos, q0, &infiniteTraj, T, params,trajParams,controlParams); //How to call “kinControl”
…
}
…
void kinControl(const RobotServo_t robotServos[JOINTS], const double q0[JOINTS], const trajectoryPtr trajectory, const double T,
const RobotParams_t ¶ms, const TrajectoryParams_t &trajParams, const ControlParams_t &controlParams)
{
//TODO: Call forward kinematics to compute end effector position based on the current joint position. Call “trajectory” to evaluate
the target position and velocity at each time instant and compute the desired end effector velocity. Compute joint velocities using
“inverseDiffKin” and apply next joint angles considering the computed velocities. Repeat until the time has ellapsed.
}
Implement the movements so that the robot follows a trajectory with the ∞ shape. To
validate the control, the robot and the trajectories must start at different positions.