{"id":989,"date":"2021-12-31T11:24:17","date_gmt":"2021-12-31T16:24:17","guid":{"rendered":"http:\/\/fll-freak.com\/blog\/?p=989"},"modified":"2025-04-21T00:54:35","modified_gmt":"2025-04-21T04:54:35","slug":"comments-on-an-ldrobot-ld-air-lidar","status":"publish","type":"post","link":"https:\/\/fll-freak.com\/blog\/?p=989","title":{"rendered":"Comments on an LDROBOT LD-Air Lidar"},"content":{"rendered":"<p>I purchased an LD-AIR LIDAR from LDROBOT as part of a Kickstarter campaign. I received it some time ago but did not have any time to play with it then. I just got some time this week and these are my notes.<\/p>\n<p><!--more--><\/p>\n<p>The unit looks and fells nice. It is not a chintzy unit. The molds for the parts all look very well made. The layout seems good and the size is great. It is not much bigger than a golf ball.When I purchased it, I did not buy the USB to serial adapter. By this point I must have a dozen. What I failed to realize was that with the adapter you also get the cable that plugs into the unit. So job one was to track down the connector and make\/buy a cable.<\/p>\n<p>What is lacking is documentation. There is a datasheet with things like scan rate, range, accuracy, wavelength, pinout, and such things, but nothing on the connector. I posted to their forum and got a reply with the type of cable. An Amazon purchase got me these <a href=\"https:\/\/www.amazon.com\/gp\/product\/B075CLCWBM\/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;psc=1\">JST ZH 1.5MM 4 Pin Cables<\/a><\/p>\n<p>So after a few minutes of soldering, I got a cable to power up the unit. Now the lack of documentation meant I had no idea the format of the data nor the baud rate. I got the baud rate from an oscilloscope (230400). I spent some time trying to reverse engineer the format but with little luck. My first break came when I looked at the documentation for the company&#8217;s other LIDAR units. It would appear that they used the same format. The data comes in packets of 12 measurements. Each measurement has a header followed by an array of measurements followed by a footer.\u00a0 The C structures I used to decode are:<\/p>\n<pre>#pragma pack(1)\r\ntypedef struct\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ 6 bytes\r\n{\r\n  uint8_t\u00a0 som;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Start of message (0x54)\r\n\u00a0 uint8_t\u00a0 size;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Low 5 bits is the number of measurements (12)\r\n\u00a0 uint16_t speed;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Rotation speed\r\n\u00a0 uint16_t start;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Start angle in units of 0.01 degrees\r\n } Ld19Hdr_t;\r\n#pragma pack(0)\r\n\r\n#pragma pack(1)\r\ntypedef struct\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ 3 bytes * 12 = 36\r\n{\r\n\u00a0 uint16_t\u00a0 dist;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Distance in mm\r\n\u00a0 uint8_t\u00a0\u00a0 confidence;\u00a0\u00a0 \/\/ Confidence value. Higher the better\r\n} Ld19Meas_t;\r\n#pragma pack(0)\r\n\r\n#pragma pack(1)\r\ntypedef struct\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ 5 bytes\r\n{\r\n\u00a0 uint16_t\u00a0 end;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ End angle in units of 0.01 degrees\r\n\u00a0 uint16_t\u00a0 time;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Time stamp in msec\r\n\u00a0 uint8_t\u00a0\u00a0 crc;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ CRC of bytes from SOM to time\r\n} Ld19Footer_t;\r\n#pragma pack(0)\r\n\r\n#pragma pack(1)\r\ntypedef union\r\n{\r\n  struct\r\n  {\r\n\u00a0   Ld19Hdr_t\u00a0\u00a0\u00a0 hdr;\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Header\r\n\u00a0   Ld19Meas_t\u00a0\u00a0 body[12]; \/\/ Body\r\n\u00a0   Ld19Footer_t footer;\u00a0\u00a0 \/\/ Footer\r\n  \r\n  } msg;\r\n\u00a0\u00a0   uint8_t\u00a0 buf[47];\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Array of bytes\r\n} Ld19Msg_t;\r\n#pragma pack(0)<\/pre>\n<p>&nbsp;<\/p>\n<p>I then wrote a simple Python program to record the data to a hex file and another one to decode the data and generate a CSV file as well as a series of images for each 360 degree sweep of the LIDAR. One small wrinkle is that the number of packets needed for a full sweep is not necessarily an integral number. You may change from 359+ degrees to 0 degrees in the middle of a packet of 12 measurements.<\/p>\n<p>I then took the series of images and combined them into a movie using ffmpeg. The visual representation makes it easy to visualize the large amount of data collected.<\/p>\n<p>The next step was to do the data collections using a microcontroller. My current unit of choice is the Teensy 4.1. It is ridiculously fast and has a built in microSD card for data storage. The downside is it does not have WiFi or Bluetooth transceivers.<\/p>\n<p>I wired up the LIDAR to the Teensy as well as an IR optical reflection sensor. A 3D printed part held the LIDAR,\u00a0 Teensy, a small power bank for power and the IR sensor. This was all mounted on top of a LEGO train car that ran on about 8 feet of straight train track. The IR sensor was mounted in such a way as to see the railroad ties. The contrast was enhanced with a small white 1*1 tile on each tie. Now as the train moved, I could also record the time that I saw each railroad tie pass underneath. Knowing that the ties are exactly 32 mm apart allowed me to know exactly where the LIDAR unit was during the motion path.<\/p>\n<p>My next steps are to continue to understand the limitations of the unit and to explore SLAM.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I purchased an LD-AIR LIDAR from LDROBOT as part of a Kickstarter campaign. I received it some time ago but did not have any time to play with it then. I just got some time this week and these are &hellip; <a href=\"https:\/\/fll-freak.com\/blog\/?p=989\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[24,37,14,38,1],"tags":[],"class_list":["post-989","post","type-post","status-publish","format-standard","hentry","category-avc","category-ld-air","category-personal","category-teensy","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/fll-freak.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/989","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fll-freak.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fll-freak.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fll-freak.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fll-freak.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=989"}],"version-history":[{"count":3,"href":"https:\/\/fll-freak.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/989\/revisions"}],"predecessor-version":[{"id":992,"href":"https:\/\/fll-freak.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/989\/revisions\/992"}],"wp:attachment":[{"href":"https:\/\/fll-freak.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=989"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fll-freak.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=989"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fll-freak.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=989"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}