WEBVTT

00:00.230 --> 00:06.560
Now that we know how to create and use an action server and also how to interact with it through the

00:06.560 --> 00:14.480
Ros2 command line interface, let's see how we can create a Python action client node that use the Fibonacci

00:14.480 --> 00:18.380
action server that we created in the last laboratory lesson.

00:19.080 --> 00:24.990
If you are only interested in the C plus plus development, then you can skip directly to the next lesson

00:24.990 --> 00:27.330
in which we are going to implement the same node.

00:27.330 --> 00:30.060
So the same action client in C plus plus.

00:30.860 --> 00:33.440
Just as we did with the server node.

00:33.470 --> 00:39.270
Let's also place the client node inside the Arduino board Pi examples and so inside the Arduino board

00:39.320 --> 00:40.790
by example sub folder.

00:40.790 --> 00:48.710
And here let's create a new file called Simple Action Client Dot Pi.

00:49.250 --> 00:49.760
Here.

00:49.760 --> 01:00.050
Let's start by importing the Pi library and also from the Pi library and from the node modules.

01:00.560 --> 01:03.590
Let's import the node class.

01:03.800 --> 01:14.540
And then in order to create a Ros two node, let's create a new class called Simple Action Client that

01:14.540 --> 01:18.470
inherits from the node class that is defined in the node module.

01:18.470 --> 01:24.050
So the simple action client inherits from the node class.

01:24.050 --> 01:26.270
And now let's define the constructor.

01:28.580 --> 01:32.780
So this is a member of the Simple Action Client class.

01:32.780 --> 01:38.270
And within the constructor, let's start by initializing the object of the node class.

01:38.270 --> 01:40.190
So of the base class.

01:42.500 --> 01:49.250
So let's call the init function on the object of the base class and let's pass a string as input.

01:49.250 --> 01:51.560
That is the name that we want to give to the node.

01:51.590 --> 01:57.440
So let's call it simple action client.

01:58.570 --> 02:02.670
Next, let's initialize and create the actual action client.

02:02.680 --> 02:07.570
And so in order to do this, let's import another library.

02:07.570 --> 02:16.900
So still from the Pi library, this time from the action module, let's import the definition of the

02:16.900 --> 02:21.160
action client class, and now let's use it to create a new object.

02:21.160 --> 02:30.040
So here, still within the constructor, let's create a new object and let's call this one action client.

02:30.040 --> 02:35.410
And this is an instance of the action client class.

02:36.210 --> 02:42.540
The constructor of this class takes the instance of the Ros2 node where the action client is implemented,

02:42.540 --> 02:45.750
which is the current simple action client class.

02:45.750 --> 02:49.440
So we can refer to this one with the self variable.

02:49.800 --> 02:54.810
It also takes as input the type of the interface that we want to use for the communication with the

02:54.810 --> 02:55.830
action server.

02:55.830 --> 03:00.210
And we define this interface in the Arduino bot messages package.

03:00.420 --> 03:12.360
So from this package, from the Arduino bot messages and from the action module, let's import the Fibonacci

03:12.360 --> 03:13.110
interface.

03:13.110 --> 03:18.810
And now let's use this interface, the Fibonacci for the communication with the action server.

03:18.810 --> 03:23.880
So basically we are saying that we want to create an action client which communicates with a certain

03:23.880 --> 03:26.970
action server using the Fibonacci interface.

03:27.090 --> 03:33.660
And also finally, let's specify the name of the action server that is the Fibonacci.

03:34.050 --> 03:39.640
At this point, we could already use the action client to send the goals to the server.

03:39.790 --> 03:45.730
However, before doing that, let's first verify that the action server is actually running using the

03:45.730 --> 03:46.540
function.

03:46.720 --> 03:52.870
So on the action client object, let's use the function, wait for server.

03:52.870 --> 03:55.690
And so this function will block the execution of the client.

03:55.690 --> 04:02.410
So of this script until an action server that is called Fibonacci and that uses the Fibonacci interface

04:02.410 --> 04:03.460
is available.

04:03.460 --> 04:05.110
So it becomes available in Ros2.

04:05.590 --> 04:13.060
So after this function, if we insert any instruction after this function, we are sure that an action

04:13.060 --> 04:14.560
server exists.

04:14.560 --> 04:17.380
And so it means that we can send a goal to this action server.

04:17.380 --> 04:19.720
So requesting its execution.

04:19.840 --> 04:22.540
So let's start by creating a new goal variable.

04:23.110 --> 04:31.630
Let's call this one goal and this is an instance of the Fibonacci interface and of the goal message.

04:31.630 --> 04:37.600
And now let's, for example, request the calculation of the Fibonacci sequence up to the order ten.

04:38.350 --> 04:43.750
So let's access to the goal message and then to the order variable.

04:43.840 --> 04:46.030
And let's set this 1 to 10.

04:46.150 --> 04:50.950
And now in order to send the goal to the action server, we can use the function.

04:51.220 --> 05:00.160
So still using the action client, let's use the function, send goal async and this receives as input

05:00.160 --> 05:02.440
the goal that we want to send to the server.

05:03.040 --> 05:06.790
And so basically the variable goal that we have just created.

05:07.360 --> 05:14.440
And also here in this end goal async function, we need also to pass the name of the function that we

05:14.440 --> 05:18.600
want to execute when a new feedback message is received by the client.

05:18.610 --> 05:24.910
So every time the action server will send a feedback message, the client will execute a certain function

05:24.910 --> 05:27.970
in order to inform about the progress of the action.

05:28.060 --> 05:30.370
And for example, let's call this function.

05:30.370 --> 05:39.010
So the feedback callback function, let's call this one again feedback callback.

05:39.010 --> 05:45.340
And we are going to define this feedback callback function still within the simple action client node.

05:45.440 --> 05:48.670
Now the send goal async function.

05:48.670 --> 05:54.790
This one will send a goal to the action server and returns a future variable.

05:54.790 --> 06:04.570
And so let's store this one within a variable called future that takes the output of the send goal async.

06:05.320 --> 06:06.190
This variable.

06:06.190 --> 06:12.310
So the future variable provides a mechanism to access the result of an asynchronous operation.

06:12.310 --> 06:17.920
And so, for example, we can also add a callback to this variable in order to be notified when the

06:17.920 --> 06:20.380
action server completes its execution.

06:20.500 --> 06:23.110
So on the future variable.

06:24.140 --> 06:30.050
Let's add a new callback function by using the add done callback function.

06:30.050 --> 06:36.410
And so when the server is done, so basically when the action client gets notified that the server completed

06:36.410 --> 06:43.460
its execution, we want to execute the response callback function.

06:43.460 --> 06:46.400
And so now we need to define this function.

06:46.400 --> 06:55.040
So we need to define the response callback function and let's define it as a member still of the simple

06:55.040 --> 06:56.300
action client node.

06:56.570 --> 06:59.030
And so it receives the self variable.

06:59.030 --> 07:04.040
And also this function receives as input the future variable.

07:04.040 --> 07:10.490
So the future mechanism that allows to access to the result of the action server once it is done.

07:11.030 --> 07:17.120
So first of all, let's save the result of the future variable into a new variable that we can call

07:17.150 --> 07:19.370
goal handle.

07:19.550 --> 07:29.610
And so let's take the future and let's use the function result in order to get the result of this server.

07:29.880 --> 07:35.910
Now we can check if the goal that we sent has been accepted or not by the action server.

07:35.910 --> 07:42.060
So let's check if not the goal handle variable.

07:43.140 --> 07:47.700
So the goal handle is accepted.

07:47.940 --> 07:51.780
So if this is not accepted, let's print an informative message.

07:52.440 --> 08:03.270
So with the get logger function, let's print an informative message that says goal rejected.

08:03.690 --> 08:09.660
And in this case, let's print a sad face and then let's also return.

08:09.660 --> 08:16.310
So let's terminate the execution of the action client with the return statement otherwise.

08:16.320 --> 08:24.980
So if we go here, if the goal was accepted, let's print again a new log message.

08:24.980 --> 08:32.360
So an informative message that says goal accepted.

08:32.660 --> 08:35.960
And in this case, let's print an face.

08:36.770 --> 08:43.220
Then, since the goal was accepted and the action server has started its execution, we can use the

08:43.250 --> 08:44.750
goal and all variable.

08:44.750 --> 08:49.400
So this one here to get the result of the action server execution.

08:49.820 --> 08:51.380
And so let's use this function.

08:51.380 --> 08:59.420
So the goal endl and the function get result async.

08:59.450 --> 09:06.320
So the response callback function, this one will be executed when we receive a response from the action

09:06.320 --> 09:06.980
server.

09:06.980 --> 09:14.930
And this basically informs us just if the server has accepted or not, our goal and now with this other

09:14.930 --> 09:15.440
function.

09:15.440 --> 09:22.040
So with the get result async, we are basically at the point in which we are sure we know that the action

09:22.040 --> 09:25.850
server accepted our goal and now we want the result.

09:25.850 --> 09:29.900
So we want the result of the execution of the logic of the action server.

09:30.440 --> 09:37.290
So as this is also an asynchronous function, as you can say from the name, it also returns a future

09:37.290 --> 09:38.040
variable.

09:38.040 --> 09:40.980
So let's store this one in the variable future.

09:43.920 --> 09:49.530
And now, as we did previously, let's also add a callback function to this future variable.

09:49.530 --> 09:54.480
In order to be notified whenever the server completes the execution of the goal.

09:54.930 --> 10:06.060
So still on the future variable, let's call the function add then callback and this receives the name

10:06.060 --> 10:06.840
of the function.

10:06.840 --> 10:12.210
So of the callback function that we want to execute when the server finishes its execution.

10:12.210 --> 10:19.140
In this case, let's execute the function result callback.

10:19.690 --> 10:24.090
So here is self result callback.

10:24.100 --> 10:29.530
And so still we are going to define this function as a member of the simple action client node.

10:30.190 --> 10:34.900
So let's define this function and it receives the self.

10:35.200 --> 10:42.730
And also this function receives the future variable that contains the mechanism to access the response.

10:42.730 --> 10:45.130
And so the result of the action server.

10:45.250 --> 10:52.450
So let's start with this one with taking the result of the action server and let's store it in a new

10:52.450 --> 10:54.190
variable called result.

10:54.280 --> 11:00.900
And so this takes the future variable and then the result function.

11:00.910 --> 11:04.240
And from this one, let's take the result.

11:04.240 --> 11:08.020
So here we are accessing to the result of the future variable.

11:08.020 --> 11:14.140
And here we are accessing to the result of the message interface of with the action server.

11:15.160 --> 11:16.570
Now this variable here.

11:16.570 --> 11:20.350
So the result variable contains the result of the action server.

11:20.350 --> 11:22.540
So the full Fibonacci sequence.

11:22.720 --> 11:25.930
Now let's print this sequence within the terminal.

11:26.710 --> 11:38.260
So with the get logger function, let's print an informative message that says result and then let's

11:38.260 --> 11:39.730
print the full vector.

11:40.180 --> 11:48.880
So let's format this string and let's print basically the result sequence.

11:48.880 --> 11:53.830
So let's print the full Fibonacci sequence that was calculated by the action server.

11:54.310 --> 11:58.330
Once we have received correctly the result from the action server.

11:58.420 --> 12:00.610
Now our simple example node.

12:00.610 --> 12:07.150
So the action client has finished basically its execution, so it doesn't need to execute any more actions

12:07.150 --> 12:14.960
and so we can actually shut down completely the node with the Pi shutdown function.

12:15.230 --> 12:19.340
Now, as you can see, we are only missing to declare one function.

12:19.340 --> 12:22.820
So to define the feedback callback function.

12:22.940 --> 12:25.640
Let's also declare this one as a member.

12:26.420 --> 12:29.720
So here of the simple action client.

12:30.170 --> 12:35.990
And so let's pass the self in order to indicate that this belongs to the simple action client class.

12:35.990 --> 12:41.990
And also this function receives as input the feedback message that was sent by the action server.

12:42.140 --> 12:47.210
So let's call this variable feedback message.

12:48.430 --> 12:49.750
Now when this happens.

12:49.750 --> 12:55.120
So when this function is executed, it means that we received a new feedback message from the action

12:55.120 --> 12:55.630
server.

12:55.630 --> 12:59.140
And so let's simply print in the terminal its content.

12:59.410 --> 13:09.820
So let's use the get logger function and then let's print an informative message that says received

13:10.690 --> 13:15.030
feedback and then followed by the content of the feedback message.

13:15.040 --> 13:20.650
So let's format this string and let's print from the feedback message.

13:20.770 --> 13:25.030
So let's access to the feedback variable.

13:25.030 --> 13:26.620
And here let's print the.

13:28.030 --> 13:30.970
Partial sequence.

13:31.330 --> 13:37.690
With this, the simple action client class is completed and now, since we have defined several callback

13:37.690 --> 13:41.260
function, let's quickly recap the purpose of each of them.

13:41.710 --> 13:43.930
The response callback function.

13:43.930 --> 13:51.580
So this one is executed after we send the goal to the action server and basically it checks whether

13:51.580 --> 13:55.360
or not the action server accepted our goal.

13:55.450 --> 14:01.930
Then the result callback function is executed at the end of the action server execution.

14:01.930 --> 14:06.460
So after the server has accepted and completed the action.

14:06.760 --> 14:12.550
And finally this one, this last one, the feedback callback function is executed repeatedly.

14:12.550 --> 14:18.490
So each time we receive a new feedback message from the action server during its execution.

14:19.000 --> 14:22.420
Now we only need to declare also the main function.

14:29.170 --> 14:38.500
And so here, let's call the main function and let's define this function here above and here.

14:38.500 --> 14:40.900
Let's start by initializing the ros2 node.

14:40.930 --> 14:45.790
So rql pi init in order to initialize the node.

14:45.820 --> 14:54.340
Then let's create a new instance of the simple action client class and let's call this one action client

14:54.670 --> 14:57.640
as an instance of the simple action client.

14:57.640 --> 15:05.410
And then also let's keep this node up and running with the Pi dot spin function.

15:05.410 --> 15:08.800
And then with this we have done with our script.

15:08.920 --> 15:14.620
Now, as usual, we also need to add this script to the setup dot pi file.

15:14.620 --> 15:19.450
So to this one in which we have already installed, for example, the simple action server.

15:19.690 --> 15:29.680
So we can copy this instruction here also for the installation of the simple action client node that

15:29.680 --> 15:38.140
is still in the Arduino Pi examples folder and is in the simple action client script.

15:38.140 --> 15:41.740
And from this one we want still to execute the main function.

15:43.030 --> 15:45.250
Now, let's also save this file.

15:45.250 --> 15:51.450
And as we haven't added any new dependencies to our package, we don't need to modify the package dot

15:51.610 --> 15:56.050
XML and so we can directly proceed to build and run our node.

15:56.470 --> 15:58.720
To do so, let's open a new terminal.

15:59.410 --> 16:03.250
Let's go to the workspace and let's build it.

16:04.660 --> 16:08.710
Once the build is done, let's open a new window of the terminal.

16:09.350 --> 16:13.880
And here, let's start by launching the Simple Action Server.

16:13.880 --> 16:19.970
So let's start the action server by sourcing the workspace setup bash.

16:20.180 --> 16:26.150
And then we click on Ros to run from the Arduino Pi examples.

16:26.360 --> 16:29.300
Let's start the simple Action server.

16:29.300 --> 16:31.160
So let's press Enter.

16:31.550 --> 16:35.570
And now that the action server is running, we can also start the client.

16:35.570 --> 16:39.410
So in a new terminal, let's source the workspace.

16:39.410 --> 16:48.140
So if I set up Bash and here in order to launch the client node, let's use the command Ros to run and

16:48.140 --> 16:49.430
steal from the Arduino board.

16:49.730 --> 16:51.530
Pi Examples package.

16:51.560 --> 16:56.030
Let's start the simple action client node.

16:56.030 --> 16:57.980
So let's press enter.

16:59.280 --> 17:08.310
Here, we can see that there is an error as we need here in this function, to pass the name of the

17:08.310 --> 17:09.720
node that we want to spin.

17:09.990 --> 17:14.400
So here there was a small error, so let's save this one.

17:14.700 --> 17:19.620
But anyway, we can see that we were able to send the goal to the server.

17:19.710 --> 17:21.600
Now let's run this again.

17:21.600 --> 17:26.130
So let's build it and let's see again how it works.

17:26.520 --> 17:32.700
So now the node should remain in execution, so the actual client should remain in execution and also

17:32.700 --> 17:36.600
receive the feedback messages and the result messages from the server.

17:36.840 --> 17:40.890
Now let's start the client again and now correctly.

17:40.890 --> 17:44.880
As soon as we start the client, we see the message Goal accepted.

17:44.880 --> 17:49.710
So it means that we correctly send the goal and it was accepted by the server.

17:49.710 --> 17:57.360
And also we can see that we are starting to receive all the feedback messages that are sent by the action

17:57.380 --> 18:00.640
server and also at the end of the action.

18:00.640 --> 18:07.150
So here we can see that the last message that the action client receives before terminating its execution

18:07.150 --> 18:08.950
is the full Fibonacci sequence.

18:08.950 --> 18:11.350
So it's the result message.

18:11.350 --> 18:15.940
And here this is the result of the execution of the action server.

18:17.080 --> 18:22.210
Now if, for example, we terminate the execution of the action server.

18:22.360 --> 18:30.190
So now we can see that there is no action server with the command Ros2 action list.

18:30.880 --> 18:31.210
Okay.

18:31.210 --> 18:35.890
Now actually there aren't any action servers available in Ros2.

18:36.220 --> 18:41.530
And if, for example, we restart the action client, we can see that nothing happens.

18:41.530 --> 18:47.920
And so basically that this node is doing nothing, is idle and is waiting for an action server named

18:47.920 --> 18:50.080
Fibonacci to become available.

18:50.080 --> 18:55.840
And so this happened because here we use the function wait for server.

18:55.840 --> 19:03.430
So basically now our script is blocked, is stuck at this instruction until a Fibonacci action server

19:03.430 --> 19:06.790
that uses the Fibonacci interface becomes available.

19:07.000 --> 19:15.880
And so as soon as we restart the action server, actually the client is able to send the goal and also

19:15.880 --> 19:22.520
then to receive feedback messages and then at the end also to receive the result from the action server.
