webots icon indicating copy to clipboard operation
webots copied to clipboard

Inconsistent behaviour of lidar in python

Open miquelmassot opened this issue 2 years ago • 6 comments

Hi,

I am trying to code a simple driver in Python using a lidar. So far, I've tried the range image and the point cloud outputs but neither of them work - the console reports that "The process crashed sometime after starting successfully".

The code to test this is attached: webots.zip

Also, the documentation and the API do not match here: https://github.com/cyberbotics/webots/blob/cb1af9e3bbe43a138b70fe67552cfcc8c7fe1e11/lib/controller/python/controller/lidar.py#L142 https://cyberbotics.com/doc/reference/lidar?tab-language=python#wb_lidar_get_point_cloud

where the argument data_type in getPointCloud() is missing in the code.

miquelmassot avatar Jun 13 '23 05:06 miquelmassot

Which version are you using? I would recommend to try the latest nightly build of Webots R2022a-rev1 from here as it contains fixes for many bugs and inconsistencies in the Python API.

omichel avatar Jun 13 '23 06:06 omichel

Hi @omichel,

I've just tried with R2023a-rev1 in Ubuntu amd64 and there's no change in behaviour. Retrieving a pointcloud crashes the controller, and retrieving a range image returns:

ValueError: NULL pointer access
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "/home/miquel/git/imr_webots/controllers/slave/slave.py", line 106, in <module>
    controller.run()
  File "/home/miquel/git/imr_webots/controllers/slave/slave.py", line 94, in run
    points = self.lidar.getRangeImage()
SystemError: _PyEval_EvalFrameDefault returned a result with an exception set

miquelmassot avatar Jun 13 '23 08:06 miquelmassot

I modified the python lidar example and added the following code from line 47:

            point_cloud = self.lidar.getPointCloud()
            print(str(type(point_cloud)) + ' of ' + str(type(point_cloud[0])) + ' value[0].x = ' + str(point_cloud[0].x))
            range_image = self.lidar.getRangeImage()
            print(str(type(range_image)) + ' of ' + str(type(range_image[0])) + ' value[0] = ' + str(range_image[0]))      

When I run it in lidar.wbt after removing the C binary controller from the controller folder, I am getting the following output:

<class 'list'> of <class 'controller.lidar_point.LidarPoint'> value[0].x = 2.1800849437713623
<class 'list'> of <class 'float'> value[0] = 2.1806445121765137
<class 'list'> of <class 'controller.lidar_point.LidarPoint'> value[0].x = 2.1800849437713623
<class 'list'> of <class 'float'> value[0] = 2.1806445121765137
<class 'list'> of <class 'controller.lidar_point.LidarPoint'> value[0].x = 2.1800849437713623
<class 'list'> of <class 'float'> value[0] = 2.1806445121765137
<class 'list'> of <class 'controller.lidar_point.LidarPoint'> value[0].x = 2.1800849437713623
<class 'list'> of <class 'float'> value[0] = 2.1806445121765137

I did not observe any crash. Can you try the same on your side and report if you are getting the same results as I do?

omichel avatar Jun 13 '23 09:06 omichel

Hi @omichel, the example you pointed out works well - I even modified it to just 1 layer and fixed it instead of rotating, and it works flawlessly. I would like to know why the simple Lidar I set is not working - do you happen to have any clue?

miquelmassot avatar Jun 13 '23 10:06 miquelmassot

I don't have any clue. I would recommend you to modify the working example step-by-step until it fully resembles your world and controller. At some point, it should break and you will understand what is the cause of the problem. Once you identified it, please post the information here so that we can help with this case.

omichel avatar Jun 13 '23 11:06 omichel

I might have a clue : your code could be reduced to

        while True:
            points = self.lidar.getPointCloud()
            if self.step(self.timeStep) == -1:
                break

but during the first loop, you try to access the pointCloud BEFORE any step() have been called, hence no data is available if I modify your code to

        while self.step(self.timeStep) != -1:
            points = self.lidar.getPointCloud()

it work flawlessly ! And the pattern of not using an infinite loop is more friendly :)

That being said, it don't explain why the code crash, I would expect that getPointCloud() return an empty cloud in that case.

The fact that getNumberOfPoints() give the "correct" answer is also confusing, it in fact give the number of point setup in the Lidar Node, and not the length of the cloudPoint currently ready to be processed.

I don't know how the C API behave in that case.

But the basic Lidar sample @omichel used make a step() before retrieving the cloudPoint, so it work indeed well !

ShuffleWire avatar Apr 29 '24 08:04 ShuffleWire