Vidicon / camsense-X1

unofficial reverse engineering of a Chinese LiDAR. Discussed this on my discord: https://discord.gg/zRGJcqa
GNU General Public License v3.0
54 stars 10 forks source link

how you infer the protocol? #1

Closed rossihwang closed 4 years ago

rossihwang commented 4 years ago

especially this part

...
Vidicon commented 4 years ago

I first used a logic analyzer to find out the baud rate. this is 115200 baud

After examining the protocol I found the following structure:


packages are 36 bytes.

<0x55><0xAA><0x03><0x08> (always the same start) \\ (Rotation speed, should be around 19900 units unknown) \ \ (Contains an counter but it randomly skips a counts and does more strange stuff ) \\\ (distance in mm (uint16)) \\\ \\\ \\\ \\\ \\\ \\\ \\\ \ \ (similar counter as unknown1) \ \ ___ To combine high and low bytes I use: `uint16 value = (byteH << 8) | byteL;` Reading the measurements: ```c++ for(var i = 0; i < 8; i++) { uint8_t byte0 = data[8+(i*3)]; uint8_t byte1 = data[9+(i*3)]; uint8_t byte2 = data[10+(i*3)]; distanceArray[index*8 + i] = ((uint16_t) byte1 << 8) | byte0; qualityArray[index*8 + i] = byte2; if(byte2 == 0x00) // measurement failed when quality is 0 { distanceArray[index*8 + i] = -1; } } ``` ___ the problem is that I have not found a reliable method to find the package index. (start position of the 8 distance values within the 360 degrees) I expected the counter in \ to be the index but that does not seem to be accurate. ___ If someone has more information, please let me know.
Vidicon commented 4 years ago

An tool that was very usefull for reverse engineering the protocol is lorris toolbox

rossihwang commented 4 years ago

thanks a lot, i'm gonna put the lidar in a static environment, plot the distance data to see the period underhood, and try to figure out the angle index

Vidicon commented 4 years ago

Yea, have tried the same but until now without success.

rossihwang commented 4 years ago

After visualizing the data, I found that every (about) 50 frames will be a complete package(360-degree data). so that the degree gap between the data will be 0.9 degree. But I still not figure out how the data index, currently I only start collecting data after the counter(unknown1) reset. Please check the code https://gist.github.com/rossihwang/0b3f76164bf454cda5d62ad18b6538b7

image

Vidicon commented 4 years ago

Looks good,

I found this repository today: https://github.com/anhui1995/Camsense_X1

According to that, it gives back a start and end angle:

    float startAngle = (data [7] << 8 | data [6]) / 64.0 - 640.0;
    float endAngle = (data [33] << 8 | data [32]) / 64.0 - 640.0;
    float step = 0.0;
    if(endAngle > startAngle)
    {
        step = (endAngle-startAngle); 
    }
    else
    {
        step = (endAngle - (startAngle - 360)); 
    }

    step /= 8;

it looks promising but the angle values are jumping around. That makes it difficult to put them in a normal array.

rossihwang commented 4 years ago

We can use a static array data[360] to store the data. Firstly we initialize all the value to the maximum measurement "00 80". Then use the index rule above but round the angle to int and fill them in the array. I may try this later

rossihwang commented 4 years ago

In addition, post-processing may help. say, median filter to remove some of the maximum measurement and subsample to shrink the data size

rossihwang commented 4 years ago

update the gist https://gist.github.com/rossihwang/0b3f76164bf454cda5d62ad18b6538b7 and port to ros2 https://github.com/rossihwang/ros2_camsense_x1

fabiogua commented 4 years ago

36 Bytes, 115200 baud. 00-03 Always 0x55 0xAA 0x03 0x08 04-05 Rotational Speed 06 unknown 07 Start angle/2 + 160 08-09 Reading 0 (Low, High) 0a Strength 0 (0-255 reflected power, 0=no reading, higher=better) 0b-0c Reading 1 0d Strength 1 0e-0f Reading 2 10 Strength 2 11-12 Reading 3 13 Strength 3 14-15 Reading 4 16 Strength 4 17-18 Reading 5 19 Strength 5 1a-1b Reading 6 1c Strength 6 1d-1e Reading 7 1f Strength 7 20 Unknown 21 Finish angle/2 + 160 22 Unknown 23 Unknown e.g. 55 AA 03 08 3E 4E 1C B3 C8 02 91 CB 02 1A C9 02 3D CB 02 13 C5 02 4C C4 02 33 C0 02 A3 BE 02 07 AE B4 88 5E 07: B3 = 179. (179-160)2 = 38. Readings start at 38 degrees. 08-09: C802 = 712mm to target (Reading 0) 0a: 0x91 = 145, a good strength signal. 14-15: C502 = 709mm to target (Reading 4) 16: 4C = 76, good signal, but not as shiny as reading 0. 21: B4 = 180. (180-160)2 = 40. Readings end at 40 degrees.

Does this help you?

rossihwang commented 4 years ago

Thanks, @fabiogua. For the start/stop angle, I think the solution above(the code than @Vidicon provided) is more reasonable, for other parts, it's almost the same as we have known.

Vidicon commented 4 years ago

@rossihwang After looking more into the data I did notice that the angle resets after 50 or 51 packages this would mean that on average there are about 50*8= 400 measurements per resolution.

What I'm currently use in the ros1 node I'm writing:

const float anglePerIndex = 400 / 360.0;
const float angleOffset = 180.0; 

 float measurementAngle = (startAngle + step * i)+angleOffset;
 float fIndex = measurementAngle * IndexMultiplier;
 int index = round(fIndex);
 index = index % 400; // limit index between 0 and 399
 index = 399-index; // invert to match ROS
Vidicon commented 4 years ago

rotation speed is as follows:

uint16_t rawRotationValue = ((uint16_t) (raw_bytes[5] << 8) | raw_bytes[4]);
float rotationRPM = rawRotationValue / 64.0;
float rotationHz = rotationRPM / 60.0;
Vidicon commented 4 years ago

ROS1 driver:

https://github.com/Vidicon/camsense_driver

rossihwang commented 4 years ago

@rossihwang After looking more into the data I did notice that the angle resets after 50 or 51 packages this would mean that on average there are about 50*8= 400 measurements per resolution.

What I'm currently use in the ros1 node I'm writing:

const float anglePerIndex = 400 / 360.0;
const float angleOffset = 180.0; 

 float measurementAngle = (startAngle + step * i)+angleOffset;
 float fIndex = measurementAngle * IndexMultiplier;
 int index = round(fIndex);
 index = index % 400; // limit index between 0 and 399
 index = 399-index; // invert to match ROS

great idea! previously I just quantize the data to 360 degree. I wonder what the "angleOffset" for?

rossihwang commented 4 years ago

ROS1 driver:

https://github.com/Vidicon/camsense_driver

some suggestions

  1. when sync failed, it should be reset to the first sync state
  2. the invalid range data should be the maximum value. it seems the convention in the SLAM system(such as amcl)
RuairiSpain commented 4 years ago

About to buy this on AliExpress. Is the code/driver able to read acceptable reads to setup SLAM and start prototyping a navigation robot?

rossihwang commented 4 years ago

About to buy this on AliExpress. Is the code/driver able to read acceptable reads to setup SLAM and start prototyping a navigation robot?

yes it can, you can use @Vidicon 's ros1 package or my ros2 package directly

Vidicon commented 4 years ago

@RuairiSpain I have successfully made a (crappy) map by walking around with the LiDAR and running Hector_mapping. navigation will most likely work as well.

Vidicon commented 4 years ago

ROS1 driver: https://github.com/Vidicon/camsense_driver

some suggestions

  1. when sync failed, it should be reset to the first sync state
  2. the invalid range data should be the maximum value. it seems the convention in the SLAM system(such as amcl)

yes, both are good ideas to add. will do that very soon

Vidicon commented 4 years ago

@rossihwang angleOffset is used to rotate the forward direction. So the motor is to the back (-X) this seems to be the convention for similar lidars (RP lidar A1, YDlidar, XV-11, etc)

rossihwang commented 4 years ago

thanks, i will update it soon!😁

Bram Fenijn notifications@github.com 于2020年7月24日周五 上午1:35写道:

@rossihwang https://github.com/rossihwang angleOffset is used to rotate the forward direction. So the motor is to the back (-X) this seems to be the convention for similar lidars (RP lidar A1, YDlidar, XV-11, etc)

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Vidicon/camsense-X1/issues/1#issuecomment-663137770, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABOTZUPEHPC7W3PXJ6RO5NLR5BYGFANCNFSM4NWBW5XQ .

-- regards

HUANG RUOXI

Vidicon commented 4 years ago

@rossihwang I see you still have some 360 that need to be changed to 400.

Line 136 & Line 160

rossihwang commented 4 years ago

@rossihwang I see you still have some 360 that need to be changed to 400.

Line 136 & Line 160

thanks for pointing out the bug

fabiogua commented 4 years ago

Hey guys I dont know if you still need my Information, but I found this.

https://github.com/roundsToThree/Camsense-X1-Previewer

Vidicon commented 4 years ago

@fabiogua No I think the protocol is fully known (with exception of the CRC).

I think it used this repository to get it to work as they link to me in that repository:

Credit: Bram Fenijn (https://github.com/vidicon)

I will close this issue.