WEBVTT 00:00.000 --> 00:11.760 Okay, so everyone, first thing first, as we say on social network, I'm thrilled to announce 00:11.760 --> 00:15.280 that that's the very first time we talk about this, okay? 00:15.280 --> 00:20.760 So, feel free to give us a lot of feedback after the meeting after the presentation, okay? 00:20.760 --> 00:27.360 So, today we will talk about the technical stack, so I will present bottlenecks really quickly 00:27.360 --> 00:33.080 the product as well, which is XE, and then we will dive directly into the technical things. 00:33.080 --> 00:39.120 First thing first, let me present the team, so I'm Antoine, I'm the CTO and co-founder of 00:39.120 --> 00:43.720 Botronics, so that's why I started with some gray air. 00:43.720 --> 00:51.680 Then you have Enzo, which joined Botronics early in 2024, is one of our monster in navigation 00:51.680 --> 00:58.440 and Rostou, and then we have David, who joined us in September, 2020, to divide straight 00:58.440 --> 01:03.640 from the ULB, so, from this university. 01:03.640 --> 01:12.080 Okay, so let me present Botronics, as you can see, with a slight template, we are a startup. 01:12.080 --> 01:19.560 We found Botronics in 2022, we are, of course, in Belgium, or it quarter is in Nivel, in Dutch 01:19.560 --> 01:25.880 which we say in Nivel, right, that's 40 minutes from Ian Vicar, and yeah, we just closed, 01:25.880 --> 01:32.600 so that you know, or situation, we just closed the seed round of 1.6 million rubles in September, 01:32.600 --> 01:39.560 2020, 25, we are only 10 people right now, out of them are engineers, and yeah, we basically 01:39.560 --> 01:43.320 love open source, we contribute to it in different ways. 01:43.320 --> 01:49.600 One of the ways is that we are a friend of NAF2, it means like, that's a pay-sponsorship 01:49.600 --> 01:56.160 of NAF2, that's a way to pay for, I mean, we pay for smart and people that I am to contribute 01:56.160 --> 02:03.360 to open source, then yeah, some of us are always a super thing individual, so it means 02:03.360 --> 02:12.080 you pay annually to sponsor the organization behind Rost, and then you contribute to open source 02:12.080 --> 02:20.080 as well, so we do code, so we participate to all the tutorial and tests, parties, we build 02:20.080 --> 02:24.760 some Rost packages, you saw Christoph and Nicolas before talking about some packages we 02:24.760 --> 02:30.720 built, and then yeah, in October last year, if you have from Belgium, you may be know 02:30.720 --> 02:39.760 that we create the very first Rost-Metop in Belgium, okay, then so it was Botronics and Botronics 02:39.760 --> 02:45.280 built XE, so this is the product, this is the first ever autonomous gold trolley, so you 02:45.280 --> 02:50.640 have to know that goldfills have to carry their bag when I play gold, that's 20 kilometers, 02:50.640 --> 02:55.880 that's five hours, so that's quite painful, and that's why when you are more than 50, that 02:55.880 --> 03:02.320 becomes quite difficult, so they play with a gold cart, that's not really a gold cart, 03:02.320 --> 03:08.600 that's a trolley, really a trolley, okay, we sold 300 units in codefunding last year, and 03:08.600 --> 03:16.680 so now we are in the industrialization phase, so bringing the prototype to product, okay, 03:16.680 --> 03:21.800 and the final price we are really on a premium product, so that's something like 5,000 03:21.800 --> 03:29.680 euros in the end, let's talk about now the product constraints as a robotics engineer, 03:29.680 --> 03:37.420 right, so you see, let's talk about the features of XE first, so that's a very first 03:37.420 --> 03:43.340 gold trolley with a large core of touch screen, okay, so that's really something important 03:43.340 --> 03:49.580 for this touch screen, and then you have some mechanical constraints as well that we want to 03:49.580 --> 03:55.100 push forward, like the stability on the golf course, there is a lot of steep slope on golf 03:55.100 --> 04:00.620 courses, so you have to deal with them, and then of course we have a camera, which is AI power, 04:01.100 --> 04:08.700 let's say so it means we can do a lot of stuff based on this camera, we have three main kind 04:08.700 --> 04:14.220 of features I would say, the first are the autonomous things, so it's able to go to some point 04:14.220 --> 04:18.860 of interest on the golf course, so they are able to go to the next E to the green and that kind 04:18.860 --> 04:24.460 of things, then you have a lot of seamless interaction things, so for example, if you want the golf 04:24.540 --> 04:30.620 trolley to follow you, you can put your end in your back, the trolley, recognize your end and try 04:30.620 --> 04:35.660 and start to follow you, okay, and then you have advanced features like filming your swing, 04:35.660 --> 04:41.900 giving you some advice and so on, and this is a B2C product, so it's really a constraint, 04:41.900 --> 04:49.180 it brings some constraint at least, so for example, the price, so the final price is 5,000 euros, 04:50.060 --> 04:57.260 the 18 included, right, so if you remove the VAT, you add something like 4,000, and if you consider 04:57.260 --> 05:02.860 after sales, if you consider stock and so on, while the bill of materials become something really 05:02.860 --> 05:11.020 important, right, so we can't afford expensive sensors or solution, we have to avoid initial setup, 05:11.020 --> 05:16.460 so let's imagine this is a B2B product, we saw it in the golf courses, we can go to the golf 05:16.460 --> 05:24.060 courses and say, okay, we need to push an airtica, an airtica, docking station, and then we can do 05:24.060 --> 05:31.020 a slam manually or that kind of thing, we can't do this because like, or trolley, 05:31.020 --> 05:36.620 they will bring the it to different golf courses, and then of course the user experience must be 05:37.820 --> 05:44.140 really, that's a premium product, so from day one, you have to provide something really reliable, 05:45.100 --> 05:51.260 that's also a node of robotics, so you have some constraints linked to this as well, okay, so 05:51.260 --> 05:58.780 now we talked about the product, it's dive into the technical stack, and so we solved, that's basically 05:58.780 --> 06:04.780 a technical stack, that's not a architectural diagram, right, that's a slide, but that's basically 06:04.780 --> 06:11.100 what we'll talk about today, we'll talk about the user interfaces, there is two kind of user 06:11.100 --> 06:15.500 interfaces, there is a mobile application of course, but there is also this embedded screen, 06:16.300 --> 06:20.300 and then of course we will talk about the co-application, the embedded application, 06:21.420 --> 06:27.260 we will talk about one important capability, we provide which is the remote update capability, 06:27.260 --> 06:31.420 and then some additional things like testing and observability, 06:32.380 --> 06:41.260 so we're going to start with the co-application, without too much surprise, it's based on Rostu, 06:41.260 --> 06:47.100 and so why the choice of Rostu, so first of all, it's open source, I think it will not be too 06:47.100 --> 06:52.940 difficult to convince you that it's a huge asset for a solution, for us it's mainly on the fact that 06:52.940 --> 06:57.740 we have control on the solution, we know what's inside, but other capabilities and the limit, 06:57.740 --> 07:03.980 and so we have a more possibility to master it, also Rostu has a really large and active 07:03.980 --> 07:09.180 community, and for us it's a huge asset, there is a lot of features, a lot of package available, 07:09.180 --> 07:15.340 and also it's really easy, I would say, easier to recruit and expand the team, 07:15.340 --> 07:19.500 that we have a fully custom solution, and you know, everyone needs to retrain and to be 07:19.500 --> 07:25.740 re-informed about it, and it's more durable, it's designed, and it really allows us to 07:25.740 --> 07:32.540 expand the stack and I think to skate it properly with a different update, as said in the previous 07:32.540 --> 07:40.060 talks, Rostu has some limits, but for example, in our use case, we don't have a real 07:40.060 --> 07:44.700 real-time constraints, I don't know if you have already seen a golf play, but it's not the 07:44.700 --> 07:50.620 constant sports to life, so I would say the odd real-time is not something really to blocking 07:50.620 --> 08:01.340 for us, the non-distarmistic behavior is not also a big default to the same reason, so yeah, 08:01.340 --> 08:07.740 so about the version of Rostu, so we are using Rostu jersey, and so we have done a release 08:07.740 --> 08:13.740 some migration, we start on Rost1, we have Rost Noetic, we migrate to Embell, and more recently 08:13.820 --> 08:19.180 we have done the migration to jersey, so the migration subject is something really important, 08:19.180 --> 08:23.900 because it can take some times, it's challenging every time, or you manage your dependencies, 08:23.900 --> 08:28.780 or you are able to, you know, make scale, you stuff, and so we have made the choice to make 08:28.780 --> 08:33.980 a frequent migration, to have access to the Rostu features, and also from the package, 08:33.980 --> 08:39.820 we have done some contribution in a different project by the past, but we are a bit limited, 08:39.820 --> 08:45.020 because, you know, for example on NAF2, if you want the last feature, you need to target the latest 08:45.020 --> 08:50.780 release, otherwise you need to backport a lot of things, and so in the end, the gain of time 08:50.780 --> 08:56.940 to stay on the same release, it's not always evident, so for the moment we only target the LTS, 08:57.180 --> 09:03.340 but because we have some, you know, we are working with CUDA, that gives us a lot of capabilities 09:03.420 --> 09:10.620 for AI models, but in the end it bind us a bit with Ubuntu, Jetpack version, that depend on Ubuntu 09:10.620 --> 09:17.020 version, and so we have explored your CID before, that with Pixi, we think that we could simplify 09:17.020 --> 09:21.980 your migration process for the next release, so that's why we are trying to do, and the next 09:21.980 --> 09:31.020 release to be able to target the LERIC cloud list for the next release LTS. So Rostu, as I said before, 09:31.020 --> 09:37.420 it provides a lot of out-of-the-box features, such as all the wrapper for Python, C++, 09:37.420 --> 09:42.060 also the Rostu controls suite, that's a really help us a lot to gain a lot of time to 09:42.060 --> 09:46.700 know, start experimenting with the robots, integrate the hardware, interface, and 09:46.700 --> 09:54.140 also the community project that you will see after in the presentation as some core of our stack, 09:54.140 --> 09:59.580 such as NAF2, a robot localization, and in the end the Rostu framework, it really gives us 10:01.020 --> 10:07.980 a kind of guide line and a standard way to integrate our custom business logic for the sense 10:07.980 --> 10:14.220 of driver, and also for all the business related to how we put our navigation 10:14.220 --> 10:24.460 behavior at the top of the stack. So yeah, there is a new monster in tone, right? Let's talk a 10:24.460 --> 10:31.660 little bit about RMW and our choice and in fact, our journey. But first, that's only based on 10:31.660 --> 10:39.500 our experience, okay? So we have an experience with the localhost only model, meaning that 10:39.500 --> 10:47.740 all our nodes are running on the same device, okay? When we start, so we migrate from Rostu 10:47.740 --> 10:55.660 from Rostu, to Rostum, and we start with the default DDS implementation, which is 10:55.660 --> 11:03.820 fast DDS. We try to use it, but in the end we run into a bug with the integration with Rostu 11:03.820 --> 11:10.780 control at some point it stopped, and so we try to figure out what's missing, and at some point 11:10.780 --> 11:14.940 we are like, okay, we open some issue and get up, but didn't find the solution, and at some point 11:14.940 --> 11:21.660 we say, okay, maybe we can just try cyclone DDS, that's what we did, and in fact, we cyclone 11:21.660 --> 11:26.860 DDS, we were really happy with it, we had some configuration issue at the very beginning, 11:26.860 --> 11:32.460 but then at some point it was working really cool, so we worked with this during like two years, 11:33.100 --> 11:42.140 but then we wanted to explore shared memory, okay? And at this point we found some issue to put 11:42.220 --> 11:49.100 shared memory in practice, and that's why in fact we decided, okay, maybe we should try with Zeno 11:49.100 --> 11:56.380 to see if it's easier with Zeno to use the shared memory, and yeah, it's quite easy, so that's 11:56.380 --> 12:02.300 four months ago we moved to Zeno and now we are using Zeno and we are really happy with it and we 12:02.300 --> 12:10.300 won't move anymore, I guess, right now, until the next solution. There is three important things, 12:10.380 --> 12:16.460 the first thing is that yeah, that's really easy to integrate the shared memory even for dynamic 12:16.460 --> 12:28.140 size messages with Zeno, then we win 10% CPU usage just by moving to Zeno with shared memory, 12:28.780 --> 12:36.380 and final thing is the initial setup is really easy, I mean you just have to change some 12:36.460 --> 12:41.420 environment variable, and then you have something working, of course we can still optimize it 12:41.420 --> 12:48.860 a little bit, I mean for no, for example the shared memory size is not optimized, but it definitely works, 12:48.860 --> 12:56.540 and so we can move on, and now that we talk about the value on top of this, you have everything 12:56.540 --> 13:02.860 regarding sensors, so let's share some guidance and experience we have, some people call it 13:02.940 --> 13:09.580 pain, but that's experience, we have with sensors, for some sensors you will have 13:10.380 --> 13:16.540 host two drivers provided, and that's great, that doesn't do everything, but that's great, okay, 13:17.260 --> 13:24.220 for other sensors we have some cheap sensors because of the final price, and in this case you don't have 13:24.220 --> 13:31.580 any host driver provided, in this case we recommend really a reader, a RIP, which are good guidance 13:31.580 --> 13:36.620 provided by the host community, to implement your host to driver, okay, so that's really 13:37.420 --> 13:44.780 something that's not easy, don't underestimate this, but do this with RIP, in mind it will be easier 13:44.780 --> 13:53.660 than to implement and to integrate the host to driver with your whole host tag, that's definitely 13:53.660 --> 14:01.020 not always plug-in play, even if you have the host to driver implemented, I mean at some point we had 14:01.500 --> 14:09.260 to configure the kernel to integrate a new sensor or to install some driver, so that's not 14:10.540 --> 14:18.460 a low level driver, the host to driver is a high level driver, yeah, we also had to 14:18.460 --> 14:25.980 patch the kernel, it was to integrate 12 Bluetooth working with multiple devices at once, so that's 14:26.060 --> 14:32.460 something you will have to do in the end, so really when you estimate the task of introducing your sensor, 14:32.460 --> 14:38.700 don't underestimate it, in any case, well what I suggest is before diving into the, okay, 14:38.700 --> 14:43.580 there is a host to driver, let's use it and it works, no, hit the datasheet, understand it, 14:43.580 --> 14:48.860 try some basic tests without choosing a host at all, and then go step by step, 14:49.500 --> 14:59.500 okay, thank you, Antoine, so I guess now everyone has a clear idea of the quote Hardware is hard 15:00.220 --> 15:08.060 and now let's talk about the fact that even though you have achieved that point where you can interact 15:08.060 --> 15:17.580 with your hardware, you're still not finished yet, so basically what we advise you to do after 15:18.540 --> 15:26.780 to check your data, for instance, we were working with GPS and we simply weren't understanding 15:26.780 --> 15:33.660 why our robot wasn't getting this data coming from our GPS, by looking back on the message that 15:33.660 --> 15:40.700 we were getting from the GPS, we were seeing that in the header message, actually the data was 15:40.700 --> 15:46.700 correct and the time was correct, at this time it was long time ago, we didn't have any RTC on 15:46.700 --> 15:53.500 our trolley and hence from our robot perspective, this message is coming from the future and it's 15:53.500 --> 16:01.500 simply this regard, this message, second tip, visualized your data, in our case we use plot 16:01.500 --> 16:09.180 juggler, shout out to David Fagonti, botronics love you, so if you don't know plot juggler, 16:09.180 --> 16:16.060 allows you to visualize your topic dynamically and even analyze them, it's really, really good, 16:16.220 --> 16:23.340 a really good plugin, and the last recommendation is don't be to bind it with your hardware, 16:23.340 --> 16:30.060 and when I mean what I mean here are two things, the first one is from the sensor perspective, 16:30.060 --> 16:34.300 don't get a sensor that is a jack of all trade, you will have a lot of trade-offs with this, 16:34.300 --> 16:38.780 for instance, at botronics, we were using a GPS that was also a magnetometer, 16:39.900 --> 16:44.780 having this GPS, so obviously the GPS has to be in the upper part of the trolley, 16:44.940 --> 16:51.740 but unfortunately we have clubs with iron, which implies that we will have magnetic disturbances 16:51.740 --> 17:00.300 or magnetometer listen to, to the magnetic field obviously, the second one is to avoid the 17:00.300 --> 17:07.100 all-in-one SDK, obviously take for instance cameras that come with a pre-made SDK that does 17:07.100 --> 17:12.700 object detection and tracking, we don't want to bind our software, we really want to have a 17:12.700 --> 17:19.740 hardware-agnostic software and isolate our piece of software every time and having a 17:19.740 --> 17:24.060 piece of software that depends on our hardware is not the best idea. 17:26.380 --> 17:31.260 Okay, now that our sensors are working well, we can think about the localization, 17:31.740 --> 17:36.780 and for this we rely on the robot localization package, so we will not dive into too much 17:36.860 --> 17:43.660 detents here, the big overview of the framework is that we have some relative 17:44.940 --> 17:49.660 measurements coming from certain sensor, let's think about the IMU, for example, or the 17:49.660 --> 17:57.740 odometry of what else. We will fuse those sensors into an extended 17:57.740 --> 18:02.380 Kalman filter to have a note put related to the displacement of our robot. 18:02.940 --> 18:10.860 Afterwards, we will have our GPS data that is being given in a lower frequency. 18:10.860 --> 18:15.980 We want to fuse all of these and make the use of the NAVSA transform in order to transform 18:15.980 --> 18:24.940 this position into UTM frame in order to have them in Cartesian frame, and as a product we have 18:25.100 --> 18:28.540 basically the position of our robot. 18:31.980 --> 18:37.980 And so more specifically, particularly on what we've been using and some advises with the sensor 18:37.980 --> 18:45.820 that we've been using. In our case, we use a stereo vision camera which implies that we can use 18:45.820 --> 18:52.300 visual odometry. In our case, didn't work quite well because of the fact that there are many 18:52.380 --> 18:57.900 features in golf fields. Obviously, we have some trees and nothing else. 19:00.300 --> 19:08.540 Yeah, we don't rely anymore really much on this visual odometry. Another fact is an important 19:08.540 --> 19:17.500 in our case is that we ship our trollies in many places on the world. Obviously, the magnetic field 19:17.500 --> 19:23.260 is not everywhere the same and we have to think about this magnetic declination, obviously. 19:23.260 --> 19:31.420 And so what we had to do at Botronics for XC is to dynamize a dynamic magnetic declination that 19:31.420 --> 19:37.740 is computed based on your position on the earth. And obviously, the magnetic field of the earth 19:37.740 --> 19:47.340 changed with time and obviously we have to recompute it each year. And the last part is related 19:47.580 --> 19:54.220 to the fact that we have a B2C product. As Anton mentioned, we cannot pre-install devices 19:54.220 --> 20:00.220 on the golf field. And so a challenge is to compute the initial pose of our trollies. So when 20:01.660 --> 20:07.100 consumer uses our trollie, if he wants, he doesn't want to calibrate anything, he just wants to play. 20:07.100 --> 20:15.180 And that's a big challenge. And so the big lesson, let's not say related to the localization, 20:15.260 --> 20:23.500 is make sure that your localization is reliable before diving directly onto navigation. Since navigation 20:23.500 --> 20:30.620 rely assumes that your localization is perfect. If you have issues with your navigation, it will 20:30.620 --> 20:43.100 probably be due to the fact that your localization is not good also. So for the navigation, we are using 20:43.100 --> 20:48.940 Neptune. It's a really complete framework. It provides a lot of advanced features out of the box, 20:48.940 --> 20:53.820 which is basically me when we switch to Ross 2. And so when I look at the Neptune instead of move 20:53.820 --> 21:00.620 base. And so it provides a lot of out of the box features such as planar controllers and collision 21:00.620 --> 21:08.060 monitors that provide safety software. So it's highly modular and it relies on Baviotry and it 21:08.060 --> 21:13.820 helps us a lot to manage the concurrency between the different Bavios and also provide the capabilities 21:13.820 --> 21:19.020 to use custom plugin. It's really important for us because in the end, at some point you have 21:19.020 --> 21:25.420 the choice to make all your custom logic, implements you know, every new Bavior from your part. 21:25.420 --> 21:31.180 And we make the choice to rely on the plugin, the plugin configuration of Neptune. And it gives us a lot 21:31.180 --> 21:36.380 of flexibility to manage our stack. And it's as it's a complete framework, it provides a lot of good 21:36.380 --> 21:42.220 practices. And to propel to that and to behave your trees in January, make something robust and 21:42.220 --> 21:48.460 really reliable. Also, it's a really well maintained project framework. It's quite popular. There 21:48.460 --> 21:54.380 is a lot of new features, update, backfix. So for us, it was definitely a good choice from our 21:54.380 --> 22:03.260 point of view to rely on Neptune. So how we use Ross to a Neptune in the XC case? So it's quite special 22:03.340 --> 22:10.060 because it's other robotics. We rely on GPS. I said David before. And we don't do slam. There are 22:10.060 --> 22:15.580 not some that much features on the golf course. And we have the chance to have a full data set 22:15.580 --> 22:21.660 of the different golf course around the world. So based on the loud static map, we process them, 22:21.660 --> 22:27.820 make them available for the robot. And as we know, basically, the GPS position of every of the 22:27.900 --> 22:32.860 circles on the golf course, such as the burn curves, the ponds. But also kind of a semantic 22:32.860 --> 22:38.940 of circles, such as the greens. And the starting areas is particularly not obstacle, but more restricted 22:38.940 --> 22:45.500 area. So based on that, we are able to manage our navigation only from that and from that too, 22:45.500 --> 22:54.140 to positions at Mesf map, based on the initial location of the robot. We also use obstacle avoidance 22:54.140 --> 22:59.900 based on vision. And as Antoine said, we cannot afford a 3D LiDAR of this kind of thing for the 22:59.900 --> 23:05.660 application. So we had to be smart about the computation and also reduce it. So we have only a 23:05.660 --> 23:12.060 front camera. So we have only a small field of view to detect the obstacles. And to save 23:12.060 --> 23:16.860 computation, we use depth to let us cancel. We compute the depth image from the camera to let us 23:16.860 --> 23:22.940 cancel to have it to the understanding of the environment. And we use a special temporal 23:23.260 --> 23:29.100 layer to make sense of it and take the most that we cannot this relatively limited field of view 23:29.100 --> 23:35.980 to understand the obstacles. And the custom plugin, we used it a lot during our development. 23:35.980 --> 23:41.500 I think the best example that we have is the follow feature. It's basically, you see, we have the golfer 23:41.500 --> 23:47.420 here, is a follow by the caddy because you don't want to take care of it all the time. And so 23:47.420 --> 23:53.980 based on vision, we perform this following. So we integrate it as a custom plugin. So we have a 23:53.980 --> 23:59.420 custom behavior tree and an navigator to be able to have no balance between going to goals. 23:59.420 --> 24:05.420 Following and don't have concurrency between this behaviors. And we take a lot of benefits 24:05.420 --> 24:10.460 from this choice because relying on that to it gives us a lot of feature integrated on it, 24:10.460 --> 24:15.660 such as the access to the cost map, to the localization and a lot of features that 24:15.660 --> 24:20.620 in the end really help us to integrate new features. For example, the collision monitor for specific 24:20.620 --> 24:26.620 behaviors, the fact to detect some features such as the bankers to be sure that even if you are 24:26.620 --> 24:31.420 supposed to be followed in the restricted area, it's going to stop. So in the end, you really 24:31.500 --> 24:34.620 expand what we can do on this custom plugins. 24:44.700 --> 24:52.460 Okay. So as Antoine said, for the follow, we're basing ourselves on a computer vision stack. 24:53.180 --> 24:59.500 So the idea here again is to have a hardware agnostic software. We know that specific is the case 25:00.380 --> 25:06.620 from specific cameras provide an object detection algorithm and a tracking algorithm. 25:06.620 --> 25:13.900 But in our case, we wanted to do it by ourselves. And for instance, for the object detection, 25:14.700 --> 25:20.460 we used the yellow model that we've tuned to by applying transfer learning. So we just 25:20.460 --> 25:26.380 trained the last layer of the model in order to fit our data, which is golf specific data. 25:26.380 --> 25:32.380 So we have, obviously, we have human. But we also have the backhand that triggers a follow. 25:32.380 --> 25:37.820 And we also have the better, which is some kind of golf club. 25:40.460 --> 25:46.300 Concerning the object tracking, obviously, your object detection model doesn't make any difference 25:46.300 --> 25:53.020 between Antoine, which is at the left and a quick stuff, which is at the right. We need to use 25:53.020 --> 25:58.700 an object tracking algorithm. In order to assign an ID to this person and also to estimate the 25:58.700 --> 26:05.020 next position at the next frame of this person. To do this, we rely on the open source, 26:05.020 --> 26:15.020 bots are tracking algorithm. So now, let's talk a little bit about remote update, which is called 26:15.100 --> 26:23.660 OTA update for Overture update, right. Let's talk about Ojourney again. So we rely on an 26:23.660 --> 26:32.060 envidiate JSON. So basically, envidiate JSON provide the jetpack, which you can see in a custom 26:32.860 --> 26:40.860 Ubuntu-like distribution provided and maintained by Nvidia. It provides some Overture capability. 26:41.020 --> 26:44.620 We start with it, but at some point, we are like, okay, we don't want to depend on 26:45.340 --> 26:51.820 Nvidia lifecycle for the jetpack. And we don't want to depend on the hardware, in fact, for the 26:51.820 --> 26:59.660 Overture capability. So we were looking for something else. We found the OCTO as a lot of people 27:00.380 --> 27:07.740 in this room. We start with the OCTO. It was nice, a full-time job, to maintain it. So at some point, 27:07.820 --> 27:12.460 we were like, okay, we capability with Mender, which allows us to do the Overture update. But at 27:12.460 --> 27:17.900 some point, we were like, okay, we started with, don't want to have a full-time job to work on this 27:17.900 --> 27:22.860 image. Of course, that's really optimized. That's really something interesting. You only 27:22.860 --> 27:28.540 ship when what you want to, to have a turn-time. That's really great. We had some issues with it, 27:28.540 --> 27:33.900 but that's okay. But we were like, okay, that's really, it takes too much time. And then, 27:33.980 --> 27:41.820 okay, we say that maybe we should create a simple YCTO image and install the Docker engine in it. 27:41.820 --> 27:48.140 So that we can render Docker in this YCTO simple image. That's, and at some point, we were looking 27:48.140 --> 27:54.620 on the internet. We find Ballena, which definitely, that's the purpose of Ballena. So that's why 27:54.620 --> 28:00.780 we, we know use Ballena. So Ballena is quite simple. That's a basic YCTO image. That's, this image is 28:00.780 --> 28:06.220 open, so you can modify it if you need. But at a finger cross, we don't have to right now. 28:07.660 --> 28:14.460 We don't want to return into this YCTO hell. And yeah, that definitely docker on top of it. 28:14.460 --> 28:18.860 So that's definitely what it provides. So that's really what we use now. And it provides also 28:18.860 --> 28:23.900 Ballena Cloud, which is not a free solution, that's because that's a Cloud solution. But it's 28:23.900 --> 28:31.740 relies on an open-end giant, which is open-source. So yeah, that's definitely all go to solution 28:31.740 --> 28:38.860 right now for this God-for-e. And the one important thing is that because of the Bitosy product, 28:38.860 --> 28:45.420 we have some requirement like the, we need the user to confirm when he wants to make the 28:45.420 --> 28:49.980 update. So that's really something we had to customize Ballena for this a little bit. 28:49.980 --> 28:58.220 So that's definitely a development flow today. So it's quite simple to understand. We rely a lot 28:58.220 --> 29:09.820 of on VS Code Defcontner for laptop or laptop as on Ubuntu, at least for the developers. And with NVIDIA GPU, 29:09.820 --> 29:15.020 so that we can test the model. We have ONN and X models, so other independent model, but in the 29:15.100 --> 29:20.300 N, we have to convert it to TensorFlow T, so that we are other dependent, but really optimized 29:20.300 --> 29:29.340 model. Then we deploy them to the Def-for-e, which is still based on the Defcontner in VS Code. 29:30.700 --> 29:37.820 But this fully is based on the Jetpack. And yeah, we have a clean SSH configuration we share together. 29:37.820 --> 29:42.940 And then only at this time we deploy to Ballena to make our test at this point, the only difference 29:43.020 --> 29:50.300 might be some small differences in the kernel itself, because yeah, of course, Docker relies on the kernel 29:51.180 --> 30:04.460 installed. So let's move on our testing pipeline. So this slide comes from the Roscon 2025. 30:04.460 --> 30:11.260 I'd love to have the best testing Pyramid pipeline with the cheap test is first. 30:11.820 --> 30:18.940 And at the end, we have the test on the real robot. Today at Botronics, unfortunately, 30:18.940 --> 30:26.300 we are following the right one, which is do a lot of things, not on the robot, 30:26.300 --> 30:32.860 after word test on the robot, and do a lot of tests on the robot. We obviously want to change this. 30:33.740 --> 30:38.620 We want to follow this Pyramid. The team is growing, it makes sense. 30:39.580 --> 30:45.260 Nowadays, to change it, and we're working on to change this testing pipeline. 30:46.300 --> 30:53.660 So obviously, to have a more robust testing pipeline, we need to have a robust simulation environment. 30:54.460 --> 31:01.420 As of today, we have localization and navigation that is implemented in to navigation. 31:01.420 --> 31:07.580 Unfortunately, the vision stack, so the computer vision stack isn't yet implemented in our 31:07.580 --> 31:16.140 simulation, so we cannot simulate for a loss. For example, as you can see on this gazebo screenshot, 31:16.140 --> 31:23.260 the dynamic of our robot is an really well explained on simulation, but we're working on it, 31:23.260 --> 31:31.740 obviously, and we want to upgrade all these specific points. So the test on the field, 31:31.820 --> 31:38.940 what do they look like? So today, we have two kinds of debugging. The first one are the 31:38.940 --> 31:47.340 live debugging, so we can use graphical user interfaces such as RVs and plug-jigler that it's 31:47.340 --> 31:57.500 forgotten. And the RQT over VNC, so that on laptops, we can directly visualize what the robot sees. 31:58.380 --> 32:05.260 Afterwards, what we also have since our CEO plays golf, he goes on the field and tells the 32:05.260 --> 32:13.900 trolley, what we've implemented is a button on our UI, on our mobile application. And by simply 32:13.900 --> 32:20.380 pushing this button, he can report a bug, and we've implemented custom bugs with rolling windows, 32:20.380 --> 32:25.580 so that we have a bug that takes what was happening sometimes before, and what was 32:25.660 --> 32:31.900 happening sometimes after the bug. Afterward, he only has to plug the trolley to 32:32.460 --> 32:39.020 internet, and we can directly download the bugs through an essential channel. 32:45.180 --> 32:52.780 So no observability. Well, basically, we implement the X-Tack, which is famous for telegraph, 32:52.780 --> 32:58.220 in FixDB, and Grafana. It is what happens in our trolley, so you have two different things, 32:58.220 --> 33:03.100 you have the embedded with what is returns on the trolley directly, and then you have cloud, 33:03.100 --> 33:10.300 which is what runs over the internet, right? So the main piece is on the embedded devices telegraph, 33:10.300 --> 33:17.580 telegraph, super, two kind of input for trolley, to big kind of input, which are telegraph input 33:17.660 --> 33:25.100 that are plug-in, that you set to collect data by default, like CPU, disk, and that kind of thing, 33:25.100 --> 33:31.340 and then we use ROS diagnostics, which is the famous package to collect other 33:31.340 --> 33:39.660 diagnostics, and we send it directly to the telegraph, and telegraph provide two kind of outputs, 33:39.660 --> 33:46.700 which are file, and then on the other end, directly to In FixDB, which is a time series that 33:47.260 --> 33:54.220 is, and then you have Grafana plugged on In FixDB for dashboard. There is several interesting 33:54.220 --> 34:02.140 thing here. About ROS diagnostics, it provides several things. First, a user interface, we can access 34:02.140 --> 34:09.820 directly offline over VNC on the trolley, so that we can see what's happened. Then we have two kind of 34:09.820 --> 34:18.140 diagnostics, with ROS diagnostics. One is common diagnostics provided directly by the package, 34:18.140 --> 34:25.260 that's some note that collects like the shared memory usage or the kind of thing, and then we 34:25.260 --> 34:31.980 implement custom diagnostics so that like covariance for GPS or status for a mandatory 34:31.980 --> 34:39.500 and that kind of metrics, and then we rely on a new ROS diagnostic package, which is the 34:39.500 --> 34:45.660 diagnostic remote logging that allows us to collect metrics and send them directly to telegraph. 34:46.620 --> 34:54.460 On the output, the In FixDB is quite normal, but we have also a file output, because at some point 34:54.460 --> 35:01.500 we have some trolleys without any internet connection on the field, so it allows us to have 35:02.140 --> 35:09.260 everything in a file when it's needed. In fact, that's quite an unusual way to use it, 35:09.260 --> 35:15.820 is that we give this file to an LLM, and it gives us what's wrong with it. It quite work well. 35:16.620 --> 35:24.060 That's really something like Artisanal, I don't know. But in fact, it helped us 35:24.060 --> 35:29.660 exploring tool by tool at the very beginning. I think at the end this file will disappear, 35:29.660 --> 35:35.900 but right now it allows us to set telegraph and then consider the internet connection, 35:35.900 --> 35:41.820 stuff issues, and so on. So that's really why we choose this. There is one important thing on 35:41.820 --> 35:46.940 why we choose the tick stack is also because of the pricing model, which is a pricing model 35:48.060 --> 35:56.140 oriented on dollar per megabytes or by queries or something and not by device, because of the 35:56.140 --> 36:00.940 B2C product, most of the time, a pricing by device is quite expensive for us. 36:07.580 --> 36:12.300 So we are building a B2C project, so there are the question of user interfaces, 36:12.780 --> 36:18.860 and we have two user interfaces, we have a mobile application and one running locally on the screen. 36:19.500 --> 36:24.540 So basically, the general takes that we have is a key crossover away from your UI. 36:25.180 --> 36:29.900 Because in the end, we are developing this application, we want to keep them as standard as possible, 36:29.900 --> 36:35.980 and we don't want to require robotics developer to maintain a web app. You see the logic behind. 36:36.540 --> 36:41.580 It's quite clear, it's easier to maintain, it keeps something as the architecture of the application 36:41.580 --> 36:47.820 standard, and in the end, we are able to separate the concern. We can change our solution 36:47.820 --> 36:53.260 and keep the different stacks separated, and just to rely on bridge to communicate between them. 36:53.820 --> 37:00.220 So for the mobile app, it's the first step that we have developed, and we have tried a lot of things. 37:00.220 --> 37:06.620 We start with the classic Rost Bridge Server plus Wi-Fi, and in the end, we have observed 37:06.620 --> 37:12.940 that it's not suited for B2C, because people don't point on the normal operation to connect 37:12.940 --> 37:17.980 on the not spot, don't have the 4G access on the iOS, it can be a bit complicated. 37:17.980 --> 37:25.420 So the not spot Wi-Fi solution in the end for our configuration was not optimal, so we tried Bluetooth, 37:25.420 --> 37:30.620 and so we went to Bluetooth Classic at first, and we have again iOS, there is a bit of issue, 37:30.700 --> 37:37.660 with a certificate, and it's not as well maintained as BLE, so we moved to the BLE, the Bluetooth 37:37.660 --> 37:43.100 low energy, and it provided really good performance of what we are doing, and for the moment 37:43.100 --> 37:50.540 we're using BLE, which is a Python library, but we're exploring a Rost option blue R, and maybe 37:50.540 --> 37:55.180 it could be the first use case for a R-cell address in our stack. So that's why we are deployed on 37:56.140 --> 38:01.900 and so we have basically by the end with this choice, something relatively classic, a 38:01.900 --> 38:08.300 rack native application running for the UI on the phone, and a kind of Bluetooth API to communicate 38:08.300 --> 38:13.580 with our stack, and so a Bluetooth API on the Rost site to communicate and just give the query. 38:14.380 --> 38:19.260 So we have also observed interestingly that there is no Rost from what we know, no Rost to package 38:19.260 --> 38:26.060 using Bluetooth, so we had to make something about something like that, and for the tactile application, 38:26.060 --> 38:31.740 we have well connected directly on the device, so we have just a web socket, it's wired communication, 38:31.740 --> 38:38.540 so we have a web socket, we have a stack with RGS and electron, so in the end is a desktop application 38:38.540 --> 38:45.020 running on the device. With this stack, we achieve to make something reliable, easy to deploy, 38:45.100 --> 38:51.260 we are running it into a Docker, so it's just another container on our Balena configuration, 38:51.260 --> 38:58.300 and based on the APMage, in the end, it's quite easy to deploy and push new images, so in the 38:58.300 --> 39:02.940 end we are quite happy with this stack. We have designed a custom web socket instead of relying on 39:02.940 --> 39:10.220 the Rost GGS for the reasons that I have seated before. So basically this is the end of the tour for 39:10.220 --> 39:15.580 stack, so we are up into question, as Antoine said, it's kind of the first time that we present it 39:15.580 --> 39:20.620 like that, so we are up into feedback and question to the top, so thank you in advance. 39:21.660 --> 39:30.220 Thank you, thank you, Enzo. Thank you, everyone. One last thing, thank you for being here, 39:30.220 --> 39:44.860 as a robotics enthusiast, we love seeing that much people engaged in a robotic, and we also 39:46.060 --> 39:59.020 want to contribute to this robotics ecosystem, especially in Belgium, and I can say that 39:59.980 --> 40:05.100 I have the honor to announce you that we are for the first time in Belgium, 40:06.780 --> 40:11.820 opening the Rost GGS. 40:19.820 --> 40:28.700 So save the date, it will be on 25 and 26 November, this year obviously, so we're Rost GGS, 40:29.180 --> 40:36.220 it will be held at Nivell, so as Antoine said, it's 40 minutes away from here, Brussels, 40:37.020 --> 40:44.060 and it's something really important, it will be held in English, because in Belgium we don't know 40:44.060 --> 40:51.580 which language to speak, we have. We've made a common agreement that English was nice, 40:52.460 --> 41:00.780 and we're obviously looking for speakers, feel free to contact Antoine, and we're obviously 41:00.780 --> 41:09.820 also looking for sponsors, again feel free to contact Antoine, and that's not finished yet, 41:10.780 --> 41:18.540 second announcement, we are hiring, so as a senior robotics engineer, you've seen another 41:18.540 --> 41:25.420 view of our stack, if you think that it might be interesting to join our team, feel free to apply 41:25.420 --> 41:31.980 on LinkedIn, and yeah, that's it, you can have a look at the job desk on LinkedIn, 41:32.860 --> 41:41.820 and obviously you can question every single member of the team concerning the job application. 41:42.780 --> 41:47.820 So yes, thank you everyone, we are open to any question. 41:58.380 --> 42:02.780 Hello, thanks for the presentation, thank you, just wondering what kind of 42:03.340 --> 42:06.140 certification was this required as a product? 42:08.220 --> 42:15.180 Well, I think that the basic CE certification, right, so there is not a lot of direct machine 42:15.180 --> 42:19.500 because we don't fall in the category, we fall in, of course, in the category of the machine 42:19.500 --> 42:25.820 directive, but we don't have a lot of big constraints, I don't remember the kind of category, 42:25.820 --> 42:33.180 because I'm not the safety expert, right, but that's not that big, yeah, we don't have to, 42:33.180 --> 42:36.140 we don't have real-time big constraints. 42:42.460 --> 42:49.180 Also, as a golf course, it's a private environment, and we don't fall in the configuration of a vehicle, 42:49.180 --> 42:53.340 for example, so we don't have, you know, I would say the difficulties that could have 42:53.420 --> 42:57.660 autonomous car, for example, on thermostatification, so it's way easier for us. 42:59.260 --> 43:01.500 I hope it understands the answer to the question. 43:02.780 --> 43:09.900 Okay, I had a question, did you ever have a problem with the trolley that's bricked? 43:12.540 --> 43:20.860 It can't, like, note a failed and it overwrote some boot sequence or something like that, 43:20.940 --> 43:28.940 and you had to go and fix it, do you have special, something special to prevent that sort of thing? 43:31.500 --> 43:40.140 In fact, we rely a lot of life cycle on life cycle node, so life cycle node, that's for the boot. 43:40.540 --> 43:52.300 Okay, yeah, we rely on life cycle node, we have a lot of diagnostic as well, so that we can understand 43:52.300 --> 43:59.260 what's going wrong, that's why we have some magnetometer status, for example, because magnetometer 43:59.260 --> 44:05.820 is one of the things that can fail quite easy, so we have to monitor those things, and so that we can 44:05.820 --> 44:11.340 and the same for the covariance of the GPS, the GPS covariance will allow us to say, okay, 44:11.340 --> 44:17.020 there is an issue with the GPS and so based on that, we have a node that is able to 44:17.020 --> 44:22.140 deal with it and to say, okay, I can't do autonomous navigation right now, that's an advantage, 44:22.140 --> 44:28.860 we have is that in fact, we are, the goal trolley is always with its golfer, it can be at 44:28.860 --> 44:33.260 100 meters from its golfer, but it's never something like, okay, it doesn't work and it 44:33.260 --> 44:38.300 is always an hours in place, no, because the golfer will say, okay, I can take it remotely, 44:38.300 --> 44:46.300 if needed. And also we have, it was described quite quickly, but we have also this all V and 44:46.300 --> 44:52.300 C debugging sort of stack that allows together data from the field, and so we can do basically 44:52.300 --> 44:57.820 remote debugging as a support, so it's really, you know, it's wasn't spared a bit by the 44:57.900 --> 45:03.660 all this like experience, we know if the remote support. Hi, just that a quick question, 45:04.860 --> 45:10.060 I mentioned using Belena for the OTA updates, but I was curious, do you have anything set up for, 45:10.060 --> 45:14.540 do you have a standardized kind of build environment for building releases or do you currently kind of 45:14.540 --> 45:20.380 do all the release management manually and how does that see I and release them inside work? 45:21.340 --> 45:28.220 Well, that's really linked to the testing Pyramid. Right now we are doing it on a server, 45:28.220 --> 45:36.860 we have, but yeah, in the end we will have to build it in a CI and GitHub, we'll use GitHub, 45:36.860 --> 45:41.820 but yeah, right now we are doing it on a separate server that's already something, but 45:41.820 --> 45:47.660 definitely we need to go one step further now, you know, that's start-up life after all. 45:47.660 --> 45:54.460 And yeah, we're a team of really few robotics developers, so next release.