This is the step two for this final project.
And in this step we are going to spawn some turtles on the turtle sim window here.
And actually the first step and the second step are completely independent.
Okay, you could have done the first step second.
And this step first okay.
Because for now the well the turtle controller and the turtle spawner that we will create are not communicating
with each other.
That's going to be for the step number three.
So I'm going to I'm going to stop that.
I'm going to I'm going to start the turtle sim again okay.
And let's first see what we want to do here.
So we want to spawn some turtles okay.
There's going to be a node called turtle spawner responsible for spawning a turtle every x amount of
time.
And then this master turtle will have to catch them one by one.
This time if we look at the services Ros2 service list.
So we've done that previously in this course.
And you see we have a spawn service.
So we're going to use this spawn service to service type with uh spawn.
You see we have the type Ros2 service uh, not service interface show okay.
And we see so what do we need to send as a request x coordinate y coordinate the theta.
So the orientation and an optional name here we're going to use the name.
And then what we receive the name.
So if you didn't provide the name you receive the generated name.
Or if you provided the name you receive the same thing.
Let's just test it from the terminal Ros2 service call with spawn.
And then the type is turtle sim service spawn.
And what I'm going to provide x, let's say 3.4 y 4.5 and then theta.
Well let's give.
So that's going to be in radians.
So for example in radians you have 3.14.
That's pi.
So the total should be not facing the right side but the left side okay.
Because that's pi that's 180 degrees and name.
And let's use quote like that.
ABC for testing.
And you see we have a new total with the x 3.4 y is 4.5.
Theta is 3.14.
So it's facing left.
And you see we have the name ABC and we get the name ABC as the response.
All right.
So that's the service we need to use in our node.
So it's a good thing to first try it and test it from the terminal if you can.
Now let's uh well where will we create our node?
We will need to create a new file.
Let's go to our package.
Turtle.
SIM, catch them all.
Turtle SIM, catch them all again.
And let's create a turtle
spawner.py.
Okay.
Also let's make it executable.
Turtle spawner.
All right.
And then we can open VSCode.
I don't need that one anymore.
Turtle spawner.
Let's take the well the node OOP template from here.
And it's going to go there.
Turtle spawner node here as well.
and let's name it Turtle Spawner.
Okay, I don't need that.
And as well.
Okay.
And so what do we want to do in this code.
Well we are going to create a service client so we can interact with the service server.
We have just seen the spawn service.
And we are simply going to call this every x amount of time.
So for example every second every two seconds.
And so let's start by creating a service client.
We will first import the interface.
What was the interface.
We go back here.
It's turtle sim srv spawn.
So from turtle sim turtle sim dot srv import spawn.
And we've already added turtle sim to the package dot XML, so no need to do that again.
And here I'm going to do self dot spawn client Is self create client spawn.
What is the name?
The exact name slash spawn.
So I'm going to use that exact name here.
And then well let's just create a function called spawn service for example.
Um we're going to give it the turtle name and then x y and theta coordinates okay.
That's what we need for the service.
And before we think about the logic of when we're going to call this service, let's just write the
code to call it and to get the response in a callback.
So for that you could open one node where we've done it before, for example the add to ins client.
Okay.
And we're just gonna get inspiration from that.
So first we can wait.
Let's do a do a weight while not self dot spawn client wait for service with let's say one second that's
going to be self logger.
Actually self get logger one waiting for spawn service.
When we know that the service has been found, then we can create a request.
So let's create a request which is going to be a spawn dot request object request.
In this request we have a text field which is going to be the x from the parameters here.
Then we have request dot y and then we have request Dot theta.
And then we have request dot name.
Okay.
So we actually here have name it.
Turtle name.
Okay.
We create a request.
And then we need to send the request to the service with call async.
So we do we're going to save it as a future self dot spawn client dot call async and we send the request.
After that we can register a callback.
Let's create a callback first.
So callback call spawn service for example.
So we're going to receive a future.
And we're going to pass the request as well.
So then we can later we can check the information that we have for x, y and theta.
And what do we do here?
For now, what we just do response is equal to future dot result.
And in this response we have one thing in this response we have you see a name.
So we have name one thing to know.
And that's something you can test on your own, is that if you try to spawn a turtle, for example,
that has a name that already exists, then it's going to return a empty string.
Okay, so if there is an error while creating the turtle while it's spawning, a new turtle is going
to return an empty string.
And that's how you can know that no turtle was spawned.
If you get a name, then there is a new turtle.
So we're just going to do an if response dot and we don't have any auto completion, maybe we can add
response.
It's going to be a spawn dot response okay.
So this is optional but makes the code a bit better if response dot Instancename is an empty string.
Actually is not an empty string.
Then we are going to do self dot get logger.
For now we just print.
We can say new alive turtle and plus response dot name which is a string.
Okay, so after we get the response from the server, we print the name of the turtle if it succeeded.
All right.
So now I need to do a future dot add done callback.
And if you remember here.
So I'm going to provide the callback.
But if I want to also provide an extra argument I need to use the partial function from the func tools.
So I'm going to do from func tools import partial.
And I will do here.
So the partial is just to check directly here.
Partial and then self callback call spawn service.
And then we need to put request.
That's the field is equal to request.
We don't use it now but we're going to use it later.
All right.
So this is well nothing special.
This is something we've already done.
And you can just basically kind of almost copy paste from the previous code that we've done.
And then before we go further, let's actually test this code.
So let's test that that function here called spawn service is correctly working.
How can we make a quick test.
Well you just here you see you create the node okay.
You create the client.
Then you can directly send a request and you're going to get the response when the node is spinning.
So here we could do node dot call spawn service with.
So we need to provide a name.
Let's do test and then a coordinate x.
Let's do 3.0 coordinate y 3.0 theta is going to be 0.0 okay.
Let's save.
And then we need to create an executable for that.
So I'm adding a comma here the executable.
Let's name it spawner or turtle spawner as you want.
And it's going to be turtle sim.
Catch them all dot turtle spawner.
And we want to execute the main function.
Let's save that.
Make sure you saved all the files and let's build click on build.
So even if we use Simulink install as you know already, we need to build again because we have a new
You file so packages select turtle sim, catch them all and we can use SIM link install.
That's going to apply to the existing files.
So the ones that we are going to build right now.
So what do we need to test this.
We need to start the turtle sim.
So Rosrun turtle sim and turtle sim node.
In fact for every test we do, that's going to be the first thing anyway we need to launch for our application.
And here I'm going to source the terminal because we have the new node Ros to run my no turtle sim catch
them all.
And this time it's going to be the spawner.
Let's see what we get.
And you see that we are spawning a new turtle here on the coordinate three, three and zero for the
orientation.
And we have a log new turtle test.
So that's the name of the turtle.
And you can see spawning turtle test here with x, y and theta.
So it's correctly working.
Now let's go back to the code.
So you see it's best to try to test stuff as soon as you can.
Okay.
You add a small functionality and you test.
That's going to be much easier for you to develop ros2 applications.
And now the question is, well, we're not going to do this.
I'm going to remove.
We're not going to spawn turtles like that.
We want to spawn a turtle every, let's say every two seconds.
So how do we do this.
Well, as I told you previously, in this course you can just call this and you can call the service
from anywhere you want.
Okay.
It could be from any callback you want.
So we can create a timer.
And so I'm going to create a timer here self dot Spawn.
Turtle timer is equal to self dot create timer.
Let's say two seconds to start.
And then what I'm going to do is I'm not going to call this directly.
I'm going to create an intermediate function.
You will see why.
So spawn new turtle with self.
And I'm going to put that here self dot spawn new turtle.
So this method is going to be called every two seconds when the node is spinning.
Now why do I do this.
Because we need to call that with a different name every time and also with different coordinates.
So in this function I'm going to compute different names and coordinates which I'm going to choose at
random.
And then I'm going to call that function here okay.
So let's start with the name.
We're going to choose a name and to choose a name.
Well we need the name that's different every time.
So we first need a string, let's say turtle, my turtle, whatever we want.
And then we could just add one.
So let's say turtle one, turtle two, turtle three.
There's going to be easier to do.
And I'm going to add here self dot turtle name prefix is equal to turtle by default okay.
So later on we will add this as a parameter.
So basically we're going to start the name with turtle.
But I just create a attribute in the class for that.
So self dot turtle name prefix.
Plus I will need here a number okay.
So I'm going to also create here a turtle counter.
Let's initialize to well let's initialize to zero.
And then the first thing we do is we just do plus one for example.
So self dot turtle hotel counter plus equals one.
So in this case it's going to start at one and then two and three etc..
And I'm going to add this.
So I need to cast it as a string because that's an integer self total counter okay.
So every time we just create a different name.
So you see here it's going to be turtle one and then turtle two, turtle three etc..
So that's it for the name.
Now we need an x and a y and a theta coordinate.
And how are we going to choose that.
Well I'm going to just choose a value at random.
And for that we can include the Python module here import random.
And then I can do random dot uniform between let's say 0.0.
And then let's say that the max value is 11.0 okay.
So that's going to simply return a random value a random float value between 0 and 11.
Let's do the same for y random dot uniform 0.0 11.0.
And then theta is going to be random dot uniform.
So we don't really need to provide an orientation.
Because the only thing that really matters in the end is the position of the target total.
But let's just make things complete.
And let's add also an orientation.
So it's going to be between zero and basically two times pi.
So we have a complete circle.
So we have 360 degrees.
And I'm just going to provide the exact value.
So I will import the math module.
And I will do two times math dot pi.
All right.
So with this I have a different name every time.
And I have a random coordinates.
And then I can do self dot call spawn service with the name first and then x y theta.
All right.
And that should be it for the code.
So to recap quickly in the constructor where we create some variables.
Here we create a service client and we create a timer that's going to call this method here every two
seconds.
So we create a different name every time we create random coordinates.
And then we call the service.
So we create the request we call asynchronously the service.
We add a callback and we get the response in that callback.
So we should be able to test that.
Let's go back to the terminal.
And we have already used Simulink install.
So let's run the total sim here.
Let's start the spawner and let's see what we get.
Okay.
You can see the first time we have an error because we are trying to create turtle one which already
exists.
So that's not really a problem here.
And then turtle two.
Turtle three.
You can see we have a new log every time.
And we have a new turtle on the screen.
All right.
Once every two seconds.
That is basically here you can see generated with random coordinates.
So it's correctly working and I can stop the turtle spawner.
And so the thing you can see at the beginning we got an error because turtle one is the turtle here
is called the well.
It's the main the first turtle that I will call maybe the master turtle that's going to have to catch
the other turtles.
So you could decide if you want to just increase the counter.
So not start at one here, but start at two.
It doesn't really matter here.
Okay.
If we have the prefix turtle we're just going to skip the first one.
It's not really a problem.
And then you see we have turtle two, turtle three, turtle four etc..
Now the next part of the challenge is how to actually control the main turtle so that we catch the different
turtles that we have spawned.