cyberbotics / webots

Webots Robot Simulator
https://cyberbotics.com
Apache License 2.0
3.31k stars 1.72k forks source link

Inconsistent behaviour of lidar in python #6194

Open miquelmassot opened 1 year ago

miquelmassot commented 1 year ago

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.

omichel commented 1 year ago

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.

miquelmassot commented 1 year ago

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
omichel commented 1 year ago

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?

miquelmassot commented 1 year ago

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?

omichel commented 1 year ago

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.

ShuffleWire commented 7 months ago

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 !