USB lidar skill for Hitachi-LG LDS-01: scans to NMS, globals, obstacle zones, and live view for navigation.
How to add the Hitachi Lg Lds Lidar robot skill
- Load the most recent release of ARCx on a compatible operating system (Get ARCx).
- Press the + Skills menu from the top menu bar in ARCx.
- Choose the Navigation category tab.
- Press the Hitachi Lg Lds Lidar icon to add the robot skill to your project.
Don't have a robot yet?
Follow the Getting Started Guide to build a robot and use the Hitachi Lg Lds Lidar robot skill.
How to use the Hitachi Lg Lds Lidar robot skill
Reads the Hitachi-LG LDS 360 degree lidar and feeds it to the Navigation Messaging System (NMS), global variables, an obstacle zone and a live scan view.
The Hitachi-LG LDS (model HLS-LFCD2, sold as the LDS-01 with the ROBOTIS TurtleBot3) is a low cost laser distance sensor that turns five times a second and measures one distance per degree, from 12 cm out to 3.5 m. This robot skill talks to it directly over its USB adapter, a UART, or a serial to network bridge, on Windows, Linux and macOS alike, with nothing extra to install.
Each revolution is turned into readings around the robot (corrected for how the lidar is mounted, and filtered), then sent to the NMS where a navigator such as Another Navigator builds its map from it. The same readings drive a set of global variables for scripts, an optional obstacle zone that can pause navigation or stop the robot, and the scan view in the window.
The window
- Start / Stop - connect to the lidar and start it scanning, or stop the scan and the motor and close the connection.
- Port - the serial port the lidar is on. The refresh button looks for ports again, which is how to find a lidar that was plugged in after ARCx started. When the lidar is behind a network bridge this shows its address instead.
- Status - Stopped, Connecting, Scanning or Reconnecting, and Obstacle while something is inside the obstacle zone.
- Readouts - where the lidar is connected and whether its checksums are being verified, how many revolutions a second arrive, the motor speed the lidar reports, how many readings and packets made it into the last revolution (and how many packets were dropped as damaged), the closest thing and where it is, the range of reflection strengths, and the closest thing in front, to the right, behind and to the left. The last line says whether the scans are going to the NMS and whether a position placeholder is being published (see below).
- Scan view - the robot seen from above, facing up the screen. Green dots are the readings, the blue lines join readings on the same surface, the red circle marks the closest reading, the orange triangle is the robot, and the grey rings are distance markers (their spacing is shown in the corner). Exclusion zones are outlined in grey and the obstacle zone in orange, turning red when something is in it.
- The mouse wheel zooms the view. Clicking it reports, in the log, the direction and distance of the spot clicked and what the lidar reads in that direction. That is the quickest way to find the angles for an exclusion zone or the obstacle zone.
- Log - what the skill is doing, and when something goes wrong, what the likely cause is.
The scan view costs nothing while it is not on screen, and the readouts are redrawn on their own twice a second, so leaving the window open on a small computer is fine.
Angles
Every angle in this skill is measured around the robot as seen from above: 0 is straight ahead, and it increases counter-clockwise, so 90 degrees is the robot's left, -90 its right and 180 behind it. This is ROS's convention (REP 103), and the same one the NMS and the navigators use. A range of angles always runs counter-clockwise from the first to the second, so -10 to 10 is the 20 degrees around straight ahead and 170 to -170 the 20 degrees behind.
The units depend on who is reading:
- Settings and the window are for people, so they use degrees and centimetres: the configuration, the readouts, the scan view and the log. Any angle you type is accepted (270 is the same as -90), and angles shown are between -180 and 180.
- Scripts and the NMS use ROS's units, metres and radians: every global variable, every control command, and every scan point sent to the NMS. Angles are radians between -PI and PI (PI/2 is the robot's left, -PI/2 its right), and a script may pass any angle.
Connecting
The lidar has a 6 pin connector. It runs from 5 V and talks 3.3 V serial at 230400 baud.
| Pin | Name | Connect to |
|---|---|---|
| 6 | Vcc (+5 V) | 5 V supply that can deliver 1 A as the motor starts (400 mA running) |
| 5 | TX | The adapter's RX |
| 4 | PWM | Nothing; the lidar drives its own motor |
| 3 | GND | Ground |
| 2 | RX | The adapter's TX |
| 1 | BOOT0 | Nothing |
The TurtleBot3's USB2LDS adapter is wired this way already: plug it in, choose its port, and press Start. Any other USB serial adapter works as long as it is a 3.3 V one and can supply the current. On Linux the adapter appears as /dev/ttyUSB0 or /dev/ttyACM0, and the user running ARCx needs to be in the dialout group to open it.
When it starts, the skill sends the lidar its "begin" command, the motor spins up, and the first revolution arrives within a second or two. Stopping sends "end", which stops the motor. A lidar that streams by itself from power on works too.
A lidar wired to a serial to network bridge (an ESP32 or similar passing the bytes through a TCP socket) is reached with the Network bridge, TCP connection.
When the connection is lost - the cable is pulled, the lidar stops sending, or the port cannot be opened - the skill keeps trying to reconnect every few seconds until it is stopped, unless that is turned off.
Checksums
Every packet the lidar sends carries a checksum, and a packet that fails it (a noisy cable, a USB adapter dropping bytes) is dropped rather than drawn as a wall that is not there. The readouts count the dropped packets.
The datasheet marks this lidar's protocol as tentative, so the skill does not take the checksum on trust: if a lidar's packets arrive in perfect order but never match the documented checksum, it notices within two revolutions, says so in the log, and accepts them unchecked for the rest of that session.
The navigation messaging system
Each revolution is sent to the NMS as 360 scan points, measured from the robot's centre, each with a range in metres (0 where nothing was seen) and an angle in radians. A navigator only receives the scan points when the robot's position is also reported, which is the job of a positioning robot skill such as wheel encoders or a tracking camera.
A robot with a lidar and nothing else would therefore never get its scans to a navigator, even one that works out where the robot is from the lidar alone. The Position placeholder setting fills that gap: after each revolution it repeats the last known position, so the scan is delivered. On its recommended setting it watches for other skills reporting the position and stands aside the moment one does, resuming two seconds after that skill goes quiet. The readouts and the log say which is happening. Always is the ARC version's "fake pose hint" option.
Global variables
With Set global variables on, these are published with the configured prefix (Lidar by default), so LidarFrontDistance and so on. Distances are in metres from the robot's centre (to the millimetre), and 0 means nothing was seen. Angles are in radians counter-clockwise from straight ahead, between -PI and PI. Only values that have changed are written.
| Variable | Meaning |
|---|---|
| IsRunning | True while the skill is running (always published) |
| NearestDistance, NearestAngle | The closest thing the lidar sees (metres), and its direction (radians) |
| FurthestDistance, FurthestAngle | The furthest thing (metres), and its direction (radians) |
| FrontDistance, RightDistance, RearDistance, LeftDistance | The closest thing in each direction, within the configured sector width (metres) |
| ScanRate | Revolutions per second arriving |
| MotorRPM | The motor speed the lidar reports (300 when healthy) |
| PointCount | Readings in the last revolution |
| Obstacle, ObstacleDistance, ObstacleAngle | Whether something is in the obstacle zone, and where it was when it entered, in metres and radians (only with the obstacle zone on) |
Give each lidar in a project its own prefix so their variables do not overwrite each other. Like the variables, the skill's control commands take and return metres and radians.
The obstacle zone
The obstacle zone is a slice of the circle around the robot - by default the 60 degrees in front of it, out to 40 cm. When enough readings fall inside it (three by default, so one stray reflection does not count) the skill can pause a navigation in progress, stop the robot, and run a script. When the zone has stayed clear for the clear time, a paused navigation carries on and a second script runs.
The lidar cannot see anything closer than 12 cm to itself, so keep the zone's distance comfortably beyond that plus however far the lidar sits from the robot's edge.
If the skill is stopped while it has navigation paused, navigation stays paused: carrying on with the lidar no longer watching would be the wrong surprise.
AI assistants (MCP)
An AI connected to ARCx through the Model Context Protocol (Options Preferences AI connections) gets a get_scan tool from this skill: the latest scan divided into sectors around the robot (12 by default), each with the nearest obstacle in centimetres, plus the single nearest obstacle and its direction in degrees, and the motor speed. The per-degree scan is also readable as a resource. Ask the AI what is around the robot or is the way ahead clear.
Configuration
| Setting | What it does |
|---|---|
| Connection | Serial port, or a serial to network bridge over TCP |
| Serial port | The port the lidar's USB adapter appears as |
| Baud rate | 230400, the lidar's only rate. Change it only for an adapter or bridge that converts it |
| RTS line on, DTR line on | Raise these serial lines while connected, for adapters that use them to power or enable the lidar. RTS is on by default, as in the ARC version |
| Address, Port | A network bridge's IP address and TCP port |
| Timeout | How long to wait for the next revolution before the connection is treated as lost. The first revolution gets two seconds more while the motor spins up |
| Verify checksum | Drop damaged packets (see Checksums above) |
| Start when the project loads | Connect and scan as soon as the project has loaded |
| Reconnect automatically, Reconnect every | Keep trying when the connection is lost, and how often |
| Setting | What it does |
|---|---|
| Rotation | Which way the lidar's zero direction faces, in degrees counter-clockwise from the robot's front: 90 when it faces the robot's left, -90 its right, 180 backwards. Stand in front of the robot: you should appear at the top of the scan view |
| Mounted upside down | Reverses the direction the readings go round. If walking past the robot's right shows you on its left, tick this |
| Forward of centre, Left of centre | Where the lidar sits relative to the point the robot turns around, in centimetres (negative for behind or right; left is positive, as in ROS). Readings are moved by this much, because a navigator expects them measured from the robot's centre |
| Ignore closer than | Drops readings closer than this to the robot's centre. Set it just past the robot's own body |
| Nothing beyond | Readings further than this count as "nothing seen". The lidar is rated to 350 cm |
| Minimum intensity | Drops readings with a weaker reflection than this. Dark and glancing surfaces reflect weakly and read least reliably. The window shows the range of the last revolution as a guide |
| Exclusion zones | Slices of the circle whose readings are dropped - chassis posts, a mast, an arm. Each runs counter-clockwise from its first angle to its second, so 170 to -170 is the 20 degrees behind the robot. A distance limits a zone to readings closer than it, so a post can be ignored without blinding the lidar to everything beyond it |
| Setting | What it does |
|---|---|
| Publish to the NMS | Send every revolution to the navigation messaging system |
| Position placeholder | When nothing else reports one (recommended), Always, or Never. See above |
| Set global variables, Variable prefix | Publish the readings as global variables, and the start of their names |
| Update variables every | The least time between variable updates |
| Sector width | How wide the front, right, rear and left sectors are |
| Setting | What it does |
|---|---|
| Watch for obstacles | Turns the obstacle zone on |
| Zone direction, Zone width | Where the zone points (0 ahead, 90 left, -90 right, 180 behind) and how wide it is. 360 wide watches all the way round |
| Closer than | How close counts as an obstacle |
| Readings needed | How many readings must be in the zone before it counts. The lidar takes one per degree |
| Clear after | How long the zone must stay empty before the obstacle is gone |
| Pause navigation | Pause a navigation in progress while there is an obstacle |
| Stop moving | Stop the robot the moment something enters the zone |
| When an obstacle appears, When the zone is clear | Scripts run as the zone fills and empties |
| Setting | What it does |
|---|---|
| View range | How far the scan view shows when it opens |
| Frame interval | The least time between frames of the scan view. 200 ms is five frames a second, one per revolution |
| Show open space | Join neighbouring readings on the same surface with a line |
Custom UI
The skill offers a Lidar radar widget for Custom UI screens: the same scan view, with a start/stop button and the distance to the closest thing. Its settings choose the caption and whether the button and the readout are shown.
Troubleshooting
The log explains a failed start from what did arrive:
- Nothing at all arrived - check the wiring (the lidar's TX to the adapter's RX), that the 5 V supply can deliver the motor's starting current, and the port. A lidar whose motor does not turn is usually short of power.
- Data arrived but none of it was a lidar packet - the baud rate is wrong, or it is a different lidar. The LDS-02 (LDROBOT LD08) that later TurtleBot3s ship with speaks a different protocol and is not supported by this skill.
- Every packet failed its checksum - a noisy line or the wrong baud rate.
- The dropped packet count climbs steadily - a noisy cable, a long or unshielded lead, or a USB adapter dropping bytes.
- The motor speed is well away from 300 RPM - the log says so once. The lidar is usually short of power.
- Readings are mirrored or rotated - set Rotation and Mounted upside down so that standing in front of the robot puts you at the top of the scan view, and walking to its right puts you on the right.
- The navigator never receives the scans - with no positioning skill in the project, set Position placeholder to "When nothing else reports one".
Control Commands for the Hitachi Lg Lds Lidar robot skill
There are Control Commands available for this robot skill which allows the skill to be controlled programmatically from scripts or other robot skills. These commands enable you to automate actions, respond to sensor inputs, and integrate the robot skill with other systems or custom interfaces. If you're new to the concept of Control Commands, we have a comprehensive manual available here that explains how to use them, provides examples to get you started and make the most of this powerful feature.
Control Command ManualThis robot skill has 11 control commands, listed below. Send them from a JavaScript or Python script, from Blockly, or from another robot skill.
The first value is always the window title, which is the name in the title bar of the robot skill on your workspace. It starts out as Hitachi Lg Lds Lidar, which is what every example below uses. If you rename the robot skill, or add a second copy of it to the project, use the title as it appears on that window instead.
ControlCommand() can be shortened to CC(), and both spellings work in JavaScript and in Python. The examples are JavaScript; Python is the same call, except that true and false are written True and False.
GetDistance
The distance in metres from the robot's centre to the closest thing within a degree of the given direction, from the latest revolution. angle is in radians counter-clockwise from straight ahead (0 ahead, PI/2 left, -PI/2 right, PI behind; any value is accepted). 0 when nothing is seen there.
Syntax
ControlCommand("Hitachi Lg Lds Lidar", "GetDistance", (Number)angle)
Parameters
| # | Name | Type | What to pass |
|---|---|---|---|
| 1 | angle | Number | A number, without quotes |
Returns: Float [1.5e45 to 3.4e38]
Example
var result = ControlCommand("Hitachi Lg Lds Lidar", "GetDistance", 0);
GetMotorRPM
The motor speed the lidar reported in its latest revolution, in revolutions per minute. 300 (plus or minus 10) is healthy. 0 when not scanning.
Syntax
ControlCommand("Hitachi Lg Lds Lidar", "GetMotorRPM")
Returns: Float [1.5e45 to 3.4e38]
Example
var result = ControlCommand("Hitachi Lg Lds Lidar", "GetMotorRPM");
GetNearest
The closest thing the lidar sees as [distance in metres, direction in radians counter-clockwise from straight ahead (-PI to PI: PI/2 left, -PI/2 right)]. [0, 0] when nothing is seen.
Syntax
ControlCommand("Hitachi Lg Lds Lidar", "GetNearest")
Returns: Array of Float
Example
var result = ControlCommand("Hitachi Lg Lds Lidar", "GetNearest");
GetScan360
The latest revolution as 360 distances in metres (the closest reading in each degree), one per whole degree counter-clockwise around the robot: index i covers i to i + 1 degrees, so index 0 is straight ahead, 90 its left, 180 behind and 270 its right. The index is in degrees even though the other commands take radians. 0 where nothing was seen.
Syntax
ControlCommand("Hitachi Lg Lds Lidar", "GetScan360")
Returns: Array of Float
Example
var result = ControlCommand("Hitachi Lg Lds Lidar", "GetScan360");
GetSectorDistance
The distance in metres from the robot's centre to the closest thing between two directions, going counter-clockwise from startAngle to endAngle. Both are in radians from straight ahead (PI/2 is left, -PI/2 right; any value is accepted). -0.1745 to 0.1745 is the 20 degrees around straight ahead; 2.9671 to -2.9671 the 20 degrees behind; -PI to PI all the way round. 0 when nothing is seen there.
Syntax
ControlCommand("Hitachi Lg Lds Lidar", "GetSectorDistance", (Number)startAngle, (Number)endAngle)
Parameters
| # | Name | Type | What to pass |
|---|---|---|---|
| 1 | startAngle | Number | A number, without quotes |
| 2 | endAngle | Number | A number, without quotes |
Returns: Float [1.5e45 to 3.4e38]
Example
var result = ControlCommand("Hitachi Lg Lds Lidar", "GetSectorDistance", 0, 0);
GetStatistics
How the connection is doing, as text: where the lidar is connected, bytes received, good and bad packets, revolutions, and whether checksums are being verified. Empty when not running.
Syntax
ControlCommand("Hitachi Lg Lds Lidar", "GetStatistics")
Returns: String
Example
var result = ControlCommand("Hitachi Lg Lds Lidar", "GetStatistics");
IsObstacleDetected
Whether something is inside the obstacle zone right now. Always false when the obstacle zone is turned off.
Syntax
ControlCommand("Hitachi Lg Lds Lidar", "IsObstacleDetected")
Returns: Boolean [true or false]
Example
var result = ControlCommand("Hitachi Lg Lds Lidar", "IsObstacleDetected");
IsRunning
Whether the skill is running (connecting, scanning or waiting to reconnect).
Syntax
ControlCommand("Hitachi Lg Lds Lidar", "IsRunning")
Returns: Boolean [true or false]
Example
var result = ControlCommand("Hitachi Lg Lds Lidar", "IsRunning");
SetPublishToNMS
Turn sending the scans to the Navigation Messaging System on or off. Returns the new setting.
Syntax
ControlCommand("Hitachi Lg Lds Lidar", "SetPublishToNMS", (Bool)publish)
Parameters
| # | Name | Type | What to pass |
|---|---|---|---|
| 1 | publish | True or false | true or false, without quotes |
Returns: Boolean [true or false]
Example
var result = ControlCommand("Hitachi Lg Lds Lidar", "SetPublishToNMS", true);
Start
Connect to the lidar and start scanning. Returns false if it was already running.
Syntax
ControlCommand("Hitachi Lg Lds Lidar", "Start")
Returns: Boolean [true or false]
Example
var result = ControlCommand("Hitachi Lg Lds Lidar", "Start");
Stop
Stop scanning, stop the lidar's motor and close the connection. Returns false if it was not running.
Syntax
ControlCommand("Hitachi Lg Lds Lidar", "Stop")
Returns: Boolean [true or false]
Example
var result = ControlCommand("Hitachi Lg Lds Lidar", "Stop");
This list is read out of the robot skill itself each time its package is uploaded, so it matches the published version (v2).







This robot skill has been updated to v1.
What has changed
initial release
This robot skill has been updated to v2.
What has changed
updated to use some new ARCx UI features for improved performance.