Roboclaw ROS Driver: Odometry Calculation Reversal

rosorg-logo1I’m making good progress on integrating ROS into my TurtleBot variant called Phoebe. I’ve just configured RViz to plot Phoebe’s location as reported by Roboclaw ROS driver‘s odometry information topic /odom. On the same plot, I’ve also placed data reported by Phoebe’s scanning LIDAR. Everything looked good when I commanded Phoebe to drive forward, but the geometry went askew as soon as Phoebe started turning. The LIDAR dots rotated one way, and the odometry reporting arrow rotated another. Something is wrong in the geometry calculation code.

For this particular problem I knew exactly where to start looking. When I assembled Phoebe, I knew I needed to configure motors in the way expected by the Roboclaw ROS driver. The README didn’t explicitly say which motor went on which side, so I went into the source code and got conflicting answers. In the motor driving routine (cmd_vel_callback) it performed calculation for right wheel motion and sent it to motor 1, and left wheel motion went to motor 2.

However, this doesn’t match encoder calculation code. The method EncodeOdom::update_publish expects its parameters to be (enc_left, enc_right). In method Node::run, it retrieves encoder values from motor 1 and saves it in variable enc1, and enc2 for motor 2, then calls update_publish(enc1, enc2). Which would treat encoder value of motor 1 as enc_left instead of enc_right, the opposite of what we’d want.

So the fact data on /odom is going the wrong way was not a surprise, but why didn’t the LIDAR transform also suffer the same problem? This was traced down to an earlier change submitted as a fix for the reverse transform problem – that commit added a negative sign in front of the calculated angle when broadcasting odom -> base_link transform. But that only masked and didn’t fix the underlying problem. The transform might look right, but /odom data is still all wrong, and variables like self.last_enc_right is actually holding the left side value.

The correct fix would be to reverse the parameter order when calling EncodeOdom::update_publish which correctly assigns encoder 1 count the right motor, and encoder 2 the left motor. And with the underlying problem fixed, we no longer need the negative sign so that can be deleted.

With this fix, Phoebe’s laser plot and odometry plot in RViz appear to agree with each other and both correspond correctly to the direction Phoebe is turning. I’ve submitted this fix as a pull request on the Roboclaw ROS driver.

Driving Miss Phoebe (Not Self-Driving… Yet)

Now that the Roboclaw ROS node is configured for Phoebe TurtleBot’s physical layout, and the code is running reliably instead of crashing, I can drive it around as a remote control car. This isn’t the end goal, of course. If all I wanted was a remote control car there were far cheaper and easier ways to get here. Right now the goal is just to verify functionality of the drive system and that it is properly integrated into the ROS infrastructure.

In order to send driving commands to Phoebe, I need something that publishes to the command & velocity topic /cmd_vel. On an autonomous robot, the navigation code would be in charge of sending these commands. But since it’s a pretty common task to move a robot around manually, there are standard ROS packages to drive a robot via human commands. The most basic is teleop_twist_keyboard which allows driving by pressing keys on a keyboard. Alternatively there is teleop_twist_joy for driving via a joystick.

Those remote control (or ‘tele operation’ in ROS parlance) nodes worked right off the bat, which is great. But I quickly got bored with driving Phoebe around on the carpet in front of me. First I launched RViz to visualize scanning LIDAR data as I did before. This was enough for me to drive Phoebe beyond my line of sight, watching surroundings in the form of the laser scan dots. After I verified that still worked, I stepped up the difficulty: I wanted RViz to plot laser data on top of odometry data in order to verify that Roboclaw ROS node is generating odometry data correctly.

To do this, I needed to participate in the ROS coordinate transform stack, the infrastructure to track all the frames of reference for relating robot components to each other in physical space. The Roboclaw ROS node publishes a transform to relate a robot’s position reference frame base_link to the odometry origin reference frame odom. The LIDAR ROS node publishes its data relative to its own neato_laser reference frame. As the robot builder, it was my job to write a transform to relate neato_laser frame to Phoebe’s base_link frame. Fortunately, ROS transform tutorials covered this exact task and I quickly got my desired RViz plot.

RViz with LaserScan and Odometry

It looks like the LIDAR scan plot from earlier, but now there’s an arrow indicating Phoebe’s position and direction. The bigger change not visible in this screen shot is that RViz is now plotting in the odometry frame. This means we no longer watch strictly from Phoebe’s viewpoint  where robot stays in the center of screen. The plot is now in odometry frame, and Phoebe should be moving relative to the map.

I drove Phoebe forward, and was happy to see the laser scan stayed still relative to the map and the red arrow representing Phoebe moved forward. But when I started turning Phoebe, the red arrow turned one way and the LIDAR plot moved the opposite way.

Something is wrong in the coordinate transform data, time for some debugging…

Roboclaw ROS Driver: Add Thread Synchronization

rosorg-logo1I now turn my attention to finding the root cause of random sporadic failures when the Roboclaw ROS driver makes calls into Roboclaw API. The most common symptom of these failures is the TypeError I addressed earlier, but avoiding crash by TypeError is only a band-aid. There were still other issues. Sometimes Phoebe’s movement stutters. And even more mysteriously, sometimes when Phoebe was supposed to be standing still, it would twitch for a fraction of a second.

Looking into ROS logs, the node never intended to send a movement. But it does send a continuous stream of “run at speed X” commands at a rate of ten per second, whether it is trying to move the robot or not. When staying still, that stream continues at the same rate constantly sending “run at speed zero”. The fact that my robot twitches when it’s supposed to be standing still tells me that “run at speed zero” command is occasionally corrupted into a “run at speed X” command. Which starts the motor moving for 1/10th of a second until it is stopped by the next non-corrupted “run at speed zero” command.

Any time there is a random sporadic failure, my instinct is to look for places where threading collisions may be taking place. Programming for multi-threaded environments can get tricky. When I wrote code for SGVHAK Rover, the intent was to make that code easy to understand and pick up. In that spirit, I explicitly kept everything on a single thread to avoid multi-threading issues.

But now we’re playing in the major leagues and there’s no avoiding multiple threads. ROS itself runs across multiple processes and threads, so it could scale to robots that have multiple on board computers, each of which has multiple processing cores. Fortunately, ROS implementation takes care of almost everything, each ROS node just has to make sure they’re doing the right thing with their own private data.

There are at least three different ROS topics involved in this Roboclaw ROS node:

  1. Driving commands received via /cmd_vel
  2. Odometry computation broadcast to /odom
  3. Diagnostics information

Each ROS topic is processed in own thread, so given three topics, we should expect at least three different threads who might all call into Roboclaw API at the same time to perform their tasks. Armed with this knowledge, I looked for code managing cross-thread access. My plan was to review that code to see if I can find any obvious problems with it.

That plan was changed when I found no code managing cross-thread access. I guess its absence qualifies as an obvious problem and it would certainly explain the kind of behavior I saw.

Not being a Python expert, I cruised StackOverflow for a pattern I could use to implement Python thread synchronization. I decided it was most straightforward to use the with keyword described on one of the later replies on this “Semaphores on Python” thread. Using this pattern makes the code change delta very straightforward to read.

There were a few initial calls into Roboclaw API to set things up, I left those alone. But as soon as the code started kicking off events that would have other threads (specifically, when it started the diagnostics thread) every following call into Roboclaw API is synchronized by a threading.Lock() object. With this modification, we can guarantee that only one thread will be performing serial communication to Roboclaw motor controller at any given time, and avoid data corruption by multiple threads trying to talk to the serial port at the same time.

Phoebe ran smoothly and reliably after this work. No more stutter in motion, and no more twitching when standing still. I’ve submitted the fix as a pull request.

Roboclaw ROS Driver: Encoder Count Logging Error

rosorg-logo1Once I fixed the ticks_per_meter error, I could drive Phoebe around by sending movement command messages to the /cmd_vel topic. However, I could not do this for very long (sometimes minutes, sometimes mere seconds) before the Roboclaw ROS driver would crash with the following error:

File "", line 232, in run
    rospy.logdebug(" Encoders %d %d" % (enc1, enc2))
TypeError: %d format: a number is required, not NoneType

Once crashed, Phoebe no longer responds to movement commands, and that is bad. What’s going on here? The instance of TypeError that brought the whole thing crashing down gives us a place to start: one of its %d parameters (which could have been either enc1 or enc2) has a value of None instead of a number representing encoder count.

Looking through the source code file, I could see that the encoder values were initialized to None then replaced with an encoder count by a call to Roboclaw API ReadEncM1 / ReadEncM2. If this call should fail, there’s a try/except block to catch the error, but that leaves the encoder value at None, causing the crash I’ve experienced.

There exists an if statement whose intention might be to avoid this crash by checking for existence of the variable, but I don’t understand how it would help. Encoder count variables would always exist since they were declared and initialized to None.

There are two parts to this problem:

  1. Why is the Roboclaw API call failing?
  2. If it fails, how can we avoid crashing on TypeError?

Part 2 is easier so let’s address that first. TypeError complains when the variables are not numbers, so let’s explicitly check for numbers. Looking through trusty StackOverflow found this method of checking for numeric values in Python, so let’s use that to guard against TypeError.

This change would not address the root cause of API call failing, it’s just a band-aid to avoid fatally crashing and burning. When we fail to retrieve encoder count using Roboclaw API, our internal information become stale which causes more problems down the line. Problems like bad odometry calculations, which will cascade to more problems.

What we really need to do is to address the failing API call, which I’ll work on next. In the meantime this band-aid has been submitted via a pull request.

Roboclaw ROS Driver: Encoder Ticks Per Meter Of Travel

rosorg-logo1Now that Phoebe has taken physical form (imperfect as a first draft tends to be) it’s time to put together software necessary to run it. We’ve already previously established how to use the Neato LIDAR in ROS, so now focus turns to propulsion control via Roboclaw motor controllers. On their product information page, Roboclaw manufacturer linked to this Github repository with the label “Robot Operating System Driver”. So obviously that’s where I should start.

I had to look through this code earlier to learn “Motor 1” of the Roboclaw should be the right hand motor and “Motor 2” the left, but up to this point I’ve only ran this code enough to verify the correct motors turn in the correct direction. Now that I have to configure the driver parameter details to match my physical robot.

A critical parameter is ticks_per_meter, the number of encoder counts that transpire when the robot travels a meter. This encapsulates multiple pieces of information about a particular mechanical implementation: things like motor gear ratio and wheel diameter. This count is required for accurate velocity control, since velocity is specified in meters per second. It is also obviously important to calculate robot odometry, because Roboclaw tracks distance in encoder counts and ROS expects position information in meters.

To test this, I send a command “travel forward at 0.1 meters per second” for one second of time. Configuration is successful when this command makes Phoebe travel 10 centimeters, with corresponding confirmation in output odometry calculations. First I ran the test with default parameters, which resulted in a little over 1 centimeter of travel implying my robot’s ticks_per_meter is almost ten times that of default.

I then re-ran the test with a much higher value for ticks_per_meter parameter and the travel distance didn’t change. Then I tried even larger values, and smaller values, and no matter what the robot kept going forward the same amount. After I double checked that I was spelling my parameter correctly, I went looking into the code.

Thankfully the author had included a line to output parameter to ROS debug log system. (rospy.logdebug("ticks_per_meter %f", self.TICKS_PER_METER)) Once I found this debug message in the log, I was able to confirm that the default value was always used no matter what value I passed in.

Backtracking through the code, I found the cause: The call to retrieve parameter value has a typo, so it is looking for parameter tick_per_meter (singular tick) instead of the intended ticks_per_meter (plural ticks.) Once I added the missing ‘s‘, Phoebe started moving the correct distance. Once verified I submitted this fix via a pull request.

Establish Motor Directions For Phoebe TurtleBot

The first revision of Phoebe’s body frame has mounting points for the two drive wheels and the caster wheel. There are two larger holes to accommodate drive motor wiring bundle, and four smaller holes to mount a battery tray beneath the frame. Since this is the first rough draft, I didn’t bother spending too much time over thinking further details. We’ll wing it and take notes along the way for the next revision.

Phoebe Frame First Draft.PNG

After the wheels were installed, there was much happiness because the top surface of the frame sat level with the ground, indicating the height compensation (for height difference between motorized wheels and caster in the back) was correct or at least close enough.

Next, two holes were drilled to mechanically mount the Roboclaw motor control module. Once secured, a small battery was connected plus both motor drive power wires. Encoder data wires were not connected, just taped out of the way, as they were not yet needed for the first test: direction of motor rotation.

Phoebe TurtleBot Stage 1 PWM

The Roboclaw ROS node expects the robot’s right side motor to be connected as Motor #1, and the left as Motor #2. It also expects positive direction on both motors to correspond to forward motion.

I verified robot wiring using Ion Studio, the Windows-based utility published by the makers of Roboclaw. I used Ion Studio to command the motors via USB cable to verify the right motor rotates clockwise for positive motion, and the left motor counter-clockwise for positive motion. I got it right on the first try purely by accident, but it wouldn’t have been a big deal if one or both motors spun the wrong way. All I would have had to do is to swap the motor drive power wires to reverse their polarity.

(Cross-posted to

New Project: Phoebe TurtleBot

While my understanding of the open source Robot Operating System is still far from complete, I feel like I’m far enough along to build a robot to run ROS. Doing so would help cement the concepts covered so far and serve as an anchor to help explore new areas in the future.

Sawppy Rover is standing by, and my long-term plan has always been to make Sawppy smarter with ROS. Sawppy’s six-wheeled rocker-bogie suspension system is great for driving over rough terrain but fully exploiting that capability autonomously requires ROS software complexity far beyond what I could handle right now. Sawppy is still the goal for my ROS journey, but I need something simpler as an intermediate step.

A TurtleBot is the official ROS entry-level robot. It is far simpler than Sawppy with just two driven wheels and restricted to traveling over flat floors. I’ve been playing with a simulated TurtleBot 3 and it has been extremely helpful for learning ROS. Robotis will happily sell a complete TurtleBot 3 Burger for $550. This represents a discounted bundle price less than the sum of MSRP for all of its individual components, but $550 is still a nontrivial chunk of change. Looking over its capabilities, though, I’m optimistic I could implement critical TurtleBot functionality using parts already on hand.

Components for parts bin turtlebot phoebe

  • Onboard computer: This one is easy. I have several Raspberry Pi 3 sitting around I could draft into the task, with all necessary accessories like a microSD card, a case, and power supply.
  • Laser scanner: Instead of the Robotis LDS-01, I’ll use a scanning LIDAR salvaged from a Neato robot vacuum that I bought off eBay for experimentation. Somebody has already written a driver package to report its data as a ROS /scan topic and I’ve already verified it sends data that the ROS visualization tool RViz can understand.
  • Motor controller: Robotis OpenCR is a very capable board with lots of expansion capabilities, but I have a RoboClaw left from SGVHAK Rover (JPL Open Source Rover) project so I’ll use that instead. Somebody has already written a driver package to accept commands via ROS topic /cmd_vel and report motion via /odom, though I haven’t tried it yet.
  • Gearmotor + Encoder: TurtleBot 3 Burger’s wheels are driven by Robotis Dynamixel XL430-W250-T serial bus servos via their OpenCR board. Encoders are critical for executing motor commands sent to /cmd_vel correctly and for reporting odometry information via /odom. Fortunately some gearmotor+encoder from Pololu are available. These were formerly SGVHAK Rover’s steering motors, but after one of them broke they were all replaced with beefier motors. I’m going to borrow two of the remaining functioning units for this project.
  • Wheel: Robotis has a wheel designed to bolt onto their servos, and they have mounting hardware for their servos. I’m going to borrow the wheel and mounting hardware from a self-balancing robot toy gathering dust on a shelf. (Similar but not identical to this one.) That toy didn’t have encoders on its motors, but they have the same mounting points and output shaft as the Pololu gearmotor so it was an easy swap. The third wheel, a free wheeling caster, was salvaged from a retired office chair.
  • Chassis hardware: Robotis has designed a modular system (like an Erector Set) for building robot chassis like their TurtleBot variants. As for me… I have a 3D printer and I’m not afraid to use it.
  • Battery: I’ll be borrowing Sawppy’s 5200 mAh battery pack.

This forms the roster for my TurtleBot variant, with an incremental component cost of $0 thanks to the parts bin. “Parts Bin TurtleBot” is a mouthful to say and not a very friendly name. I looked at its acronym “PB-TB” and remembered R2-D2’s nickname “Artoo”. So I’m going to turn “PB” into Phoebe.

I hereby announce the birth of my TurtleBot variant, Phoebe!

(Cross-posted to

Examining Basic Requirements For Mapping in ROS

When I first looked at Gazebo robot simulation environment, I didn’t understand how to find the interface layer difference between a simulated robot and a physical robot. Now I’ve learned enough ROS to know: Look at the ROS Node graph to find a node named /gazebo. Anything provided by or consumed by that node is a virtual substitute provided by Gazebo. When the same ROS code stack is on a physical robot, nodes that interface with physical hardware replaces everything provided in simulation by /gazebo.

When I tested a cheap little laptop’s performance in ROS, I put this knowledge to use. Gazebo ran on a high-end computer and every other node ran on the laptop. This simulates the workload of running Gmapping algorithm as if the low-end laptop was mounted on a physical robot. But what, specifically, is required? Let’s look at the ROS node graph once again with rqt_graph. Here’s a graph generated while TurtleBot 3’s mapping demo is running in Gazebo:

TurtleBot 3 GMapping With UI

Here’s a slightly different graph, generated by running the same mapping task but with Gazebo’s GUI and ROS RViz visualization tool turned off. They are useful for the human developer but are not strictly necessary for the robot to run. We see the /gazebo_gui node has dropped out as expected, and the /map topic was also dropped because it was no longer being consumed by RViz for presentation.

TurtleBot 3 GMapping No UI

We can see the Gazebo-specific parts are quite modest in this particular exercise. A physical robot running Gmapping in ROS will need to subscribe to /cmd_vel so it can be told where to go, and provide laser distance scanning data via /scan so Gmapping can tell where it is. Gazebo also publishes the simulated robot’s state via /tf and /joint_states.

And now that I have a spinning LIDAR working in ROS to provide /scan, the next project is to build a robot chassis that can be driven via /cmd_vel. After that is complete, we can use it to learn about /tf and /joint_states.

ROS Is Not News But Shows Up On Hacker News Anyway

ROS is now over a decade old and quite established in its niche. Every day, there are newbies like myself who start their journey to learn the ropes. Motivated by ambition to build software stacks for their own robot projects. Personally, I’ve known about ROS for a long time, though only now am I putting in the effort to work with it firsthand.

When it comes to big software frameworks I definitely count myself as “Grumpy Old Man”. I’ve seen them before, I’ll get proficient at it, and then I’ll move on to the next thing. I don’t get excited… it’s just a tool. But some ROS newcomers get really excited about their discovery! It may not be news in the historical sense, but it’s certainly news to them and they might choose to submit to venues like Reddit or, in today’s example, YCombinator’s Hacker News. And apparently it’s only been about 10 months since the last time someone submitted ROS to Hacker News.

ROS on Hacker News

And since the internet is the internet, the comment thread is naturally filled with positive people espousing the benefits of ROS, thankful of an open source robot infrastructure available so no one has to reinvent the wheel.

Just kidding.

It’s the internet.

The comment thread is actually full of gripes from people critical of the cruft built up during the course of a 10+ year software project. Complaints about how the generalized infrastructure isn’t good enough for their specific needs. Complaints about how it doesn’t use [insert favorite pet technology thing here] without considering its history as of ten years ago. Was it mature? Dependable? Did it even exist back then?

Having worked on big software projects full of legacy code, I look at these “ROS people are idiots” complaints and I shake my head. I know that a software project as big as ROS would have made major decisions based on state of software not ten years ago (when ROS was released) but more like twelve or thirteen years ago when they started putting it together. Plus if they made early decisions based on proven track record, that track record would have stretched even further back.

Yeah, ROS is old. Which is why the Open Source Robotics Foundation, the organization supporting ROS, has focused most of its resources into ROS 2. A ground-up rewrite incorporating the lessons learned during all this time. I’m cautiously optimistic ROS2 will be all it’s promised to be, but we’ll just have to stay tuned to see how reality lines up.

In the meantime, ROS is here, it is mature, and while ROS 2 will be changing a lot of technical fundamentals, it will not change any of the underlying philosophies. So I’ll stay focused learning classic ROS. It has its values, no matter what internet comments say.

Symptoms Of A Computer Struggling To Perform ROS Mapping

Once my Dell Inspiron 11 3000 (3180) laptop had its factory installation of Windows safely saved away in a Windows system image backup, its meager 32GB eMMC storage was wiped clean for an installation of Ubuntu 16.04 and ROS Kinetic Kame. Installation was mostly uneventful, except its touchpad stopped working a few minutes after setup began, forcing me to complete setup using keyboard only. This issue seemed to be resolved after updating Ubuntu to latest packages, so it was only a minor annoyance on the way to answering my $130 question: is this meager AMD E2-9000e processor powerful enough to serve as a robot brain? The answer: Yes, but barely.

Dell 3180 SLAM

My test was to run a standard ROS package that performs SLAM (simultaneous location and mapping) using Robotis’ virtual TurtleBot 3 in Gazebo simulation. To reflect the workload of a robot brain running OpenSlam’s GMapping algorithm, I ran only mapping code on the laptop. My desktop computer handled the complex physics simulation and rendering of Gazebo in order to keep the two workloads separate. To give some context for this little laptop’s capabilities, the same mapping workload was run on two other systems for comparison. One faster, and one slower, than this little laptop.

Representing the high end is my desktop computer with an Intel Core i5-7600. It kept up with incoming sensor data effortlessly and matched them up to existing records. Here’s an output log excerpt on the way to generating a high quality map:

Average Scan Matching Score=313.501
neff= 93.949
Registering Scans:Done
update frame 243
update ld=0.0096975 ad=0.30291
Laser Pose= -1.44014 1.54989 -0.977552
m_count 177
Average Scan Matching Score=312.594
neff= 93.949
Registering Scans:Done
update frame 244
update ld=0.0278723 ad=0.896994
Laser Pose= -1.46775 1.55373 -1.87455
m_count 178
Average Scan Matching Score=309.104
neff= 92.9918
Registering Scans:Done
update frame 245
update ld=0.116176 ad=0.441108
Laser Pose= -1.40149 1.64916 -2.31565
m_count 179
Average Scan Matching Score=311.613
neff= 92.9897
Registering Scans:Done
update frame 246
update ld=0.23972 ad=2.51909e-05
Laser Pose= -1.23899 1.8254 -2.31568
m_count 180

There is one interesting observation, though: according to CPU utilization metrics, the ROS Node executing Gmapping consumed 100% of a single CPU core. I don’t know if this means the algorithm has more room for improvement if given a faster core, or if this just means the algorithm takes up as much CPU as it can grab regardless of workload.

On the other end of the performance spectrum is the low power ARM processor of a Raspberry Pi 3, and this SLAM code was too much for the little chip to handle. CPU utilization metrics also show 100% utilization of a single core, but it looks like sensor data avalanches in too quickly for a Pi to process and match up to existing map data. There were only rare successful matches in the sea of errors, as seen in this excerpt of output:

Scan Matching Failed, using odometry. Likelihood=-1430.25
lp:0.367574 2.18056 -1.47424
op:1.16607 1.6985 -3.09185
Scan Matching Failed, using odometry. Likelihood=-1183.3
lp:0.367574 2.18056 -1.47424
op:1.16607 1.6985 -3.09185
Scan Matching Failed, using odometry. Likelihood=-0.874547
lp:0.367574 2.18056 -1.47424
op:1.16607 1.6985 -3.09185
Scan Matching Failed, using odometry. Likelihood=-0.900994
lp:0.367574 2.18056 -1.47424
op:1.16607 1.6985 -3.09185
Average Scan Matching Score=208.949
neff= 55.0344
Registering Scans:Done
update frame 28
update ld=1.28116 ad=2.45138
Laser Pose= 2.08673 0.807569 0.739956
m_count 28
Scan Matching Failed, using odometry. Likelihood=-1376.72
lp:1.16607 1.6985 -3.09185
op:2.08673 0.807569 0.739956
Scan Matching Failed, using odometry. Likelihood=-707.376
lp:1.16607 1.6985 -3.09185
op:2.08673 0.807569 0.739956
Scan Matching Failed, using odometry. Likelihood=-653.378
lp:1.16607 1.6985 -3.09185
op:2.08673 0.807569 0.739956
Scan Matching Failed, using odometry. Likelihood=-116.341
lp:1.16607 1.6985 -3.09185
op:2.08673 0.807569 0.739956

So how did the budget laptop perform in comparison, on its AMD E2-9000e processor?

It was far better than the Raspberry Pi, delivering mostly successful scan matches, but I could see occasional failure messages indicating it was struggling to keep pace with a fire hose of data. Curiously, CPU utilization did not stay pegged to 100%. It sometimes dip as low as 80%, implying there’s another bottleneck in the system keeping the CPU from being fully fed with work. But it’s the results that matter most. A visual examination of the map it generated looks rougher than one generated by my desktop, but usable. Meaning the resulting map might be of “good enough” quality for a robot to use despite its occasional errors.

So the little machine didn’t ace the test, but it managed to squeak by with a passing grade of C+, maybe a B-. This is very encouraging news for performance delivered by a low-end chip. It means we can start experimenting with this inexpensive laptop for now, and we have lots of upgrade headroom in the future.

Windows 10 WSL Can Run ROS, With Firewall Caveat

To win developer acceptance, Microsoft added WSL (Windows Subsystem for Linux) to 64-bit editions of Windows 10. The original iteration was only advertised to support common command-line utilities like ‘git‘ that perform relatively simple operations. However, the product has been evolving since its initial release and has become increasingly more functional to run more complex Linux software.

Could Windows 10 WSL run ROS? According to this thread on ROS Answers, it didn’t start out that way. But blocking bugs were found and fixed over past months, and now it’s possible to run ROS inside WSL. I tried this and found this to mostly work, with a minor caveat on networking.

When bringing a ROS software stack online, there is the concept of a “ROS Master”. This process listens on TCP port 11311 and serves to orchestrate communication with other ROS Nodes. Every ROS Node needs to talk to ROS Master at least once on startup. Which meant port 11311 is the one probed by researchers looking for unsecured ROS robots that were inadvertently connected to public internet.

The default network firewall on a Windows 10 computer is Microsoft’s own Windows Defender Firewall. It has a good default of ignoring all incoming traffic, unless an application explicitly asks to open up ports. At the moment this integration does not exist, so software inside WSL opening ports wouldn’t open up those same ports on Windows firewall. When running ROS in WSL, this means incoming traffic on port 11311 are blocked which results in the following:

  • ROS Master running in WSL is accessible to ROS Nodes running on the same computer, because traffic on the same computer is unaffected by firewall.
  • ROS Master running on another computer is accessible to ROS Nodes running in WSL, because outbound traffic is not blocked by firewall.
  • ROS Master running in WSL is NOT accessible to ROS Nodes running on another computer, because inbound traffic is blocked by firewall.

ERROR: Unable to communicate with master!

If a developer wishes to run networking-aware software inside WSL, we’d have to go into Windows Defender Firewall and manually add a permission for network access. Ideally we can set up a rule to allow port 11311 only when we’re running a ROS Master within WSL. But such fine-grained control is not available. For now, the only option is to open a port with no limitation. It sounds like some improvements are on the way, but even then it will still require explicit developer action.

To open port 11311, we need to first get to Windows Defender section within control panel and select “Advanced Settings”

Windows Defender security center

Then we can create a new “Inbound Rule” to allow traffic on 11311.

Windows Defender Firewall control panel

Since this is not a fine-grained control over port 11311 access, it’s not a good idea to leave this rule active at all times. For best practice, enable this rule only when running a ROS Master in WSL and only when that master needs to work with ROS Nodes running on other computers.




Dell Inspiron 11 3000 (3180) As Robot Brain Candidate

Well, I should have seen this coming. Right after I wrote I wanted to be disciplined about buying hardware, that I wanted to wait until I know what I actually needed, a temptation rises to call for a change in plans. Now I have a Dell Inspiron 11 3000 (3180) on its way even though I don’t yet know if it’ll be a good ROS brain for Sawppy the Rover.

Dell Notebook Inspiron 11 3000 3180

The temptation was Dell’s Labor Day sale. This machine in its bare-bones configuration has a MSRP of $200 and can frequently be found on sale for $170-$180. To kick off their sale event, Dell made a number of them available for $130 and that was too much to resist.

This particular hardware chassis is also sold as a Dell Chromebook, so the hardware specs are roughly in line with the Chromebook comments in my previous post. We’ll start with the least exciting item: the heart is a low-end dual-core x86 CPU, an AMD E2-9000e that’s basically the bottom of the totem pole for Intel-compatible processors. But it is a relatively modern 64-bit chip enabling options (like WSL) not available on the 32-bit-only CPUs inside my Acer Aspire or Latitude X1.

The CPU is connected to 4GB of RAM, far more than the 1GB of a Raspberry Pi and hopefully a comfortable amount for sensor data processing. Main storage is listed as 32GB of eMMC flash memory which is better than a microSD card of a Pi, if only by a little. The more promising aspect of this chassis is the fact that it is also sometimes sold with a cheap spinning platter hard drive so the chassis can accommodate either type of storage as confirmed by the service manual. If I’m lucky (again), I might be able to swap it out with a standard solid state hard drive and put Ubuntu on it.

It has most of the peripherals expected of a modern laptop. Screen, keyboard, trackpad, and a webcam that might be repurposed for machine vision. The accessory that’s the most interesting for Sawppy is a USB 3 port necessary for a potential depth camera. As a 11″ laptop, it should easily fit within Sawppy’s equipment bay with its lid closed. The most annoying hardware tradeoff for its small size? This machine does not have a hard-wired Ethernet port, something even a Raspberry Pi has. I hope its on-board wireless networking is solid!

And lastly – while this computer has Chromebook-level hardware, this particular unit is sold with Windows 10 Home. Having the 64-bit edition installed from the factory should in theory allow Windows Subsystem for Linux. This way I have a backup option to run ROS even if I can’t replace the eMMC storage module with a SSD. (And not bold enough to outright destroy the Windows 10 installation on eMMC.)

Looking at the components in this package, this is a great deal: 4GB of DDR4 laptop memory is around $40 all on its own. A standalone license of Windows 10 Home has MSRP of $100. That puts us past the $130 price tag even before considering the rest of the laptop. If worse comes to worst, I could transfer the RAM module out to my Inspiron 15 for a memory boost.

But it shouldn’t come to that, I’m confident even if this machine proves to be insufficient as Sawppy’s ROS brain, the journey to that enlightenment will be instructive enough to be worth the cost.

The Spectrum of ROS Robot Brain Candidates

rosorg-logo1In the immediate future, my ROS experimentation will follow the TurtleBot 3 model: An on-board Raspberry Pi 3 will read sensor input and send motor output. It will communicate over wireless network to a PC (either desktop or laptop) who will process sensor data, evaluate potential actions, and plan actions to be sent back to RPi3 for execution.

This should scale well to pretty much any output mechanisms we have on the horizon, especially since we’re likely to offload real time control to dedicated co-processor modules like a Roboclaw or serial bus servos. They take care of direct motor control, leaving the RPi3 with very little to do beyond sending a short command.

Input will be a different story. We know simple bump sensors will be easy. And while the Neato scanning LIDAR isn’t taxing to a Pi, its limited sensing capability means we will eventually need better sensors that give more data. A depth-sensing camera sensor like an old Kinect or an Intel RealSense looks interesting, but they require USB 3 which is beyond a Raspberry Pi. And while a Pi can handle simple vision processing of a Duckiebot, doing something more complex will quickly outstrip its capabilities.

We won’t know for sure what the bottleneck would be until we start building some robots and run into limitations firsthand. Until we know, we risk wasting money on unnecessary capability. Still, it’s good to have information on a spectrum of candidates.

raspberry-pi-logoWe already have a starting point on the low end of the spectrum: Raspberry Pi. There are several other competitors in the single-board computer market, almost all of which claim to be more powerful than a Pi. But very few could match the mass volume pricing of a Pi or its software ecosystem. The last part is important because ROS runs best on something that has a port of Ubuntu, which is absent from many Pi competitors.

The first step beyond a Raspberry Pi 3, then, is probably going to be something cheap with a low-end Intel processor that can run Ubuntu. I had thought my collection of old PC hardware could step into this space, but an Acer Aspire 10 really doesn’t want to run Linux and the Dell Latitude X1 is just too old. Its CPU consumes far more power than modern chips while doing far less. And even worse than that, its spinning platter hard drive consumes more power than SSDs while being slower.

chromebook logoThis points to a modern Chromebook with x86 CPU, most of which could be convinced to run Ubuntu. The CPU won’t be anything exciting. They might even be slower than the RPi’s ARM chip for some tasks, but they’ll have a larger cache and connect to more memory. Chromebooks also tend to have more robust main storage (instead of a microSD card) plus keyboard, screen, and battery. Some of these will even have USB 3 ports for a complete package starting at around $200.

NVIDIA logoIf a Chromebook proves insufficient, it’ll likely be due to the low-end CPU. Where we go beyond that will depend on the nature of the work overloading the chip. If it’s in the arena of dedicated vision or other AI-related processing capabilities, we might move to something like NVIDIA’s Jetson which has dedicated processing hardware for specific tasks and could run Ubuntu. They’re not cheap, so we have to be sure we need the specialized capabilities before shelling out the dollars.

Intel logoFor less task-specific needs, the Intel NUC product line is a good candidate: compact little boxes that holds RAM, SSD, USB3 ports and available with a wide range of processors. From unexciting Chromebook-equivalent chips all the way up to Core i7 paired with a respectable AMD GPU. And unlike dedicated hardware like Jetson, an Intel NUC can be repurposed to a wide spectrum of other projects if not serving as robot brain. They’re quite capable little Swiss Army knives of computing.

But there’s one remaining scenario where a NUC might prove insufficient. And that’s when we need a powerful Intel CPU in conjunction with a powerful NVIDIA GPU. This means a high-end ‘gamer laptop’ at which point my Inspiron 15 7000 (7577) might end up sitting in Sawppy‘s equipment bay. This marks the high end of the spectrum and I hope I don’t end up there, because that’s going to be a very expensive trip.

Duckietown Is Full Of Autonomous Duckiebots

Duckietown duckiebotThe previous blog post outlined some points of concern against using Raspberry Pi 3 as the brains of an autonomous robot. But it’s only an inventory of concerns and not condemning the platform against robotics use. A Raspberry Pi is quite a capable little computer in its own right and that’s even before considering its performance in light of low cost. There are certainly many autonomous robot projects where a Raspberry Pi provides sufficient computing power for their respective needs. As one example, we can look at the robots ferrying little rubber duckies around the city of Duckietown.

According to its website, the Duckietown started as a platform to teach a 2016 MIT class on autonomous vehicles. Browsing through their public Github repository it appears all logic is expressed as a ROS stack and executed on board its Raspberry Pi, no sending work to a desktop computer over network like the TurtleBot 3.  A basic Duckiebot has minimal input and output to contend with – just a camera for input and two motors for output. No wheel encoders, no distance scanners, no fancy odometry calculations. And while machine vision can be computationally intensive, it’s the type of task that can be dialed back and shoehorned into a small computer like the Pi.

Making this task easier is assisted by Duckietown, an environment designed to help Duckiebots function by leveraging its strengths and mitigating its weaknesses. Roads have clear contrast to make vision processing easier. Objects have machine-friendly markers to aid object identification. And while such measures imply a Duckiecar won’t function very well away from a Duckietown, it’s still a capable little robotics platform for exploring basic concepts.

At first glance the “Duckiebooks” documentation area has a lot of information, but I was quickly disappointed by finding many pages filled with TODO and links to “404 Not Found”. I suppose it’ll be filled out in coming months, but for today it appears I must look elsewhere for guidelines on building complete robots running ROS on Raspberry Pi.

Duckietown TODO

Anticipating Limitations of a Raspberry Pi 3 Robot Brain

rosorg-logo1As investigation into ROS continues, it’s raising concern that a self-contained autonomous robot will likely need a brain more powerful than a Raspberry Pi. It’s a very capable little computing platform and worked well serving as Sawppy’s brain when operated as a remote-control vehicle. But when the rover needs to start thinking on its own, would a little single-board computer prove to be limiting?


raspberry-pi-logoThe most obvious point of concern is the low power ARM CPU. The raw processing capability of the chip is actually fairly respectable, and probably won’t be the biggest problem. But there are two downsides with the chip:

  1. It has a very small memory cache, so the chip will have problems working with large data sets. (Say, a detailed map of the robot’s surroundings.) Without a large cache or high bandwidth memory, the CPU will sit idle as it starves for data.
  2. It has limited heat dissipation. Under sustained load, the CPU will heat up and reach the point where it will have to slow itself down to avoid overheating.

Both of these are consistent with design objective of the chip. It is very good at quickly completing a task using its high-speed CPU, then wait for its next task. This type of workload is common to devices like cell phones. In contrast, a robot has to process sensor inputs, evaluate its current condition, and decide what to do about it in a constantly running loop. There’s no “wait for next task” downtime where the computer can cool down and clean up its memory cache.


The main memory is also a point of concern. There’s only 1GB of RAM on board a Raspberry Pi 3 with no option for expansion. This is already pretty cramped to run a modern operating system, never mind the robotic software we’d like to run on top. To mitigate limitations of small RAM, modern operating systems can page memory out to storage. But that just makes the next problem worse…


A Raspberry Pi uses commodity microSD flash memory as main storage. These devices are designed for usage scenarios like holding photos in a digital camera. Each bit of capacity is only expected to be written to a handful of times in its lifetime. But when serving as main storage of a Raspberry Pi actively running complex applications (or serving as paged memory) high traffic sections of the microSD may receive new write data several times a second, leading to premature failure.

Raspberry Pi in the TurtleBot 3

A Raspberry Pi 3 serves as the on-board brains of a TurtleBot 3 ‘Burger’ and ‘Waffle Pi’ variants. I had been curious how they got around the problems above and the answer is they’ve divided up workload of a robot brain across multiple computers. The Raspberry Pi 3 reads sensor data and outputs motor data, but performs little computation itself. Sensor data is sent over the network to a desktop computer who does the computation, evaluation, and decision making. Once an action is decided, the desktop computer sends motor commands over the network back to the Raspberry Pi.

This is a cost-effective approach because anyone doing robotics research will already have a powerful desktop computer where development is taking place. By offloading computation to said computer and keeping the robot’s on-board processing simple, it makes the robot a lot cheaper.

This is fine for development, but the fact TurtleBot 3 makers chose this approach reinforces the suspicion that an actual self-contained autonomous robot will need something more than a Raspberry Pi.

ROS Is Not Secure, This Is Not News

rosorg-logo1When I started reading about ROS, as soon as I learnt ROS modules communicate over standard networking protocols, I immediately thought “What about security?” This should rightly be the first question of any software developer dealing with any networking code in this day and age. But… ROS was not created in this day and age of constant relentless network attacks. It was created ten years ago, when the internet was a far less hostile environment, and security was declared not a concern.

I can cast no stones here, my own SGVHAK Rover project did the same thing, declaring network security Someone Else’s Problem. But it’s one thing to declare as such in a little hobbyist project, it’s quite another to do so on a widely used framework. Given that the framework was already established by the time the internet turned into the environment it is today, the best ROS can do is to clearly document this fact and they have done so on a document explicitly titled Security. In it, they declare the lack of built-in security measures explicitly. A bad actor getting on a ROS network has access to everything, so in ROS installations where security is a concern it must be walled off from the internet at large via tools of the network security trade: firewalls, VPN, etc.

Given this established fact, and clear documentation stating so, it was disappointing to see Wired making a fuss about how insecure ROS is. This really isn’t news, anyone who bothered to read ROS documentation would already know this. It’s difficult to see the Wired article as anything other than to sensationalize a piece of information for people not familiar with ROS, and stroke paranoia about robots in general. This is… not a hallmark of great journalism.

But that’s out there, and getting picked up by a few other tech news sources, and we’ll just have to see where this goes. The best available defense is the fact researchers are not blind to the situation and there already exists work to beef up ROS security. Though to be clear, it’s just a research project and does not claim to be tough enough for the nasty world out there.

And as briefly mentioned in the Wired article: ROS2, the future of ROS currently in development, has a baseline option for security. Communication in ROS is built on top of DDS and the intent is to enable secure ROS networks by letting people use an implementation that features DDS-Security.

So it’s not news, and it’ll be even less news in the future.

Notes on “ROS Robot Programming” Book by Creators of TurtleBot 3

ROS Robot Programming cover 800The people at Robotis who created TurtleBot 3 created a pretty good online manual for their robot that also served as a decent guide for a beginner like myself to start experimenting with ROS. But that’s not the only resource they’re released to the public – they’ve also applied themselves to writing a book. It has a straightforward title “ROS Robot Programming” and is described to be a compilation of what they’ve learned on their journey to create the TurtleBot 3. Pointers to the book are sprinkled throughout the TurtleBot 3 manual, most explicitly under the “Learn” section with additional resources.

The book is available in four languages: English, Chinese, Japanese, and Korean. The English and Chinese editions are also available freely as PDF. I thought I’d invest some time into reading the book, here are my comments:

Chapters 1-7: ROS Fundmentals

The reader is assumed to be a computer user who is familiar with general programming concepts, but no robotics knowledge is assumed. The book starts with the basic ideas behind ROS, then working from there. These chapters have a great deal of overlap with existing “Beginner Level” tutorials on ROS Wiki. Given this, I believe the bigger value of this book is in its non-English editions. Chinese/Japanese/Korean readers would probably benefit more from these sections written in their respective languages, making this information accessible for readers who can’t just go to the English ROS Wiki tutorials like I did.

Content-wise, the biggest deviation I found in this book was that it treated the action library as peers of ROS topics and services. As a user I agree it made sense to cover them together and I’m glad this book did it. And as someone who has worked on programming platforms, I understand why the official documentation treated them differently.

Chapter 8: Sensors and Motors

Given that chapters 1-7 overlapped a lot with the tutorials I’ve already covered, it was not terribly informative. That changed when I got into chapter 8, where we started talking about a few different classes of sensors people have used in ROS. When it came to motors, though, the only one covered was Robotis’ own Dynamixel product. This was a little disappointing – they could have at least put some minor lip service to motors other than their own product. But they chose not to.

This chapter ended with a useful tutorial and some words of wisdom about how to navigate the big library of ROS modules openly available for us to play with. This is a good place for the topic, because sensor and motor driver libraries are going to be the first things beginners have to deal with beyond core ROS modules. And skills dealing with these libraries will be useful for other things beyond sensors and motors.

Chapter 9-13: All Robotis All The Time

The rest of the book is effectively an extension of the TurtleBot 3 manual with side trips to other Robotis projects. They go over many of the same ideas, using their robots as example. But while the manual focused on a specific robot, the book did try to go a little deeper. They said their goal is to cover enough so the reader can adapt the same general ideas to other robots, but I don’t feel I’ve received quite enough information. This is only a gut feeling – I won’t know for sure until I start rolling up my sleeves and get to work on a real robot.

The final few chapters felt rushed. Especially the abrupt ending of the final manipulator chapter. Perhaps they will work to fill in some of the gaps in a future edition.

Final Verdict: B+

I felt like my time spent reading the PDF was well spent. If nothing else, I have a much better understanding of how TurtleBot 3 (and friends) work. The most valuable aspect was seeing ROS described from a different perspective. I plan to check out a few other ROS books from the library in the future, and after I get a few books under my belt I’ll have a better idea how “ROS Robot Programming” ranks among them. It’s clear the first edition has  room for improvement, but it is still useful.

Observations From A Neato LIDAR On The Move

Now that the laser distance scanner has been built into a little standalone unit, it’s easy to take it to different situations and learn how it reacts by watching RViz plot its output data. First I just picked it up and walked around the house with it, which led to the following observations:

  • The sensor dome sweeps in a full circle roughly four times per second. (240 RPM) This sounded pretty good at first, but once I started moving the sensor it doesn’t look nearly as good. Laser distance plot is distorted because it’s moving while it’s sweeping, visibly so even at normal human walking speeds. Clearly a robot using this unit will have to post-process distance data generated by this sensor to compensate for speed. Either that, or just move really slowly like the Neato XV-11 robot vacuum this LIDAR was salvaged from.
  • The distance data is generated from a single narrowly focused beam. This generates detailed sweep data at roughly one reading per vertical degree of separation. However, it also means we’re reading just a very narrow one degree horizontal slice of the environment. It’s no surprise this is limiting, but just how limited wasn’t apparent until we started trying to correlate various distance readings with things we can see with our eyes.

Autonomous vehicles use laser scanners that spin far faster than this one, and they use arrays of lasers to scan multiple angles instead of just a single horizontal beam. First hand experimentation with this inexpensive unit really hammered home why those expensive sensors are necessary.

Neato LIDAR on SGVHAK Rover

After the few handheld tests, the portable test unit was placed on top of SGVHAK Rover and driven around a SGVHAK workshop. There’s no integration at all…. not power, not structure, and certainly not data. This was just a quick smoke test that was very productive because it lead to more observations:

  • Normal household wall paint, especially matte or eggshell, works best. This is not a surprise given that it was designed to work on a home vacuum robot.
  • Thin structural pieces of shelving units are difficult to pick up.
  • Shiny surfaces like glass become invisible – presumably the emitted beam is reflected elsewhere and not back into the detector. Surprisingly, a laptop screen with anti-reflective matte finish behaved identically to shiny glass.
  • There’s a minimum distance of roughly 15-20cm. Any closer and laser beam emitted is reflected too early for detector to pick up.
  • Maximum range is over 4-5 meters (with caveat below). More than far enough for a vacuum robot’s needs.

The final observation was unexpected but obvious in hindsight: The detection capability is affected by the strongest returns. When we put a shiny antistatic bag in view of the sensor, there was a huge distortion in data output. The bag reflected laser back to the scanner so brightly that the control electronics reduced receiver sensitivity, similar to how our pupils contract in bright daylight. When this happens, the sensor could no longer see less reflective surfaces even if they were relatively close.

That was fun and very interesting set of experiments! But now it’s time to stick my head back into my ROS education so I can make use of this laser distance sensor.

Making My Neato LIDAR Mobile Again

The laser distance sensor I bought off eBay successfully managed to send data to my desktop computer, and the data looks vaguely reasonable. However, I’m not interested in a static scanner – I’m interested in using this on a robot that moves. Since I don’t have the rest of the robot vacuum, what’s the quickest way I can hack up something to see how this LIDAR unit from a Neato XV-11 works in motion?

Obviously something on the move needs to run off battery, and there’s already a motor voltage regulator working to keep motor speed correct. So that part’s easy, and attention turns to the data connection. I needed something that can talk to a serial device and send that data wirelessly to my computer. There are many ways to do this in the ROS ecosystem, but in the interest of time I thought I’d just do it in the way I already know how. A Raspberry Pi is a ROS-capable battery-powered computer and everything I just did on my computer would work on a Pi. (The one in the picture here has the Adafruit servo control PWM HAT on board, though the HAT is unused in this test.)

Mobile Scanning Module

The Raspberry Pi is powered by its own battery voltage regulator I created for Sawppy, supplying 5 volts and running in parallel with an identical unit tuned for 3 volts supplying power to spin the motor. As always, the tedious part is getting a Pi on the wireless network. But once I could SSH into the Pi wirelessly, I could run all the ROS commands I used on my desktop to turn this into a mobile distance data station. Reading in data via FTDI serial port adapter, sends data out as ROS topic /scan over WiFi.

Using a Raspberry Pi 3 in this capacity is complete overkill – the Pi 3 can easily shuttle 115200 bps serial data over the network. But it was quick to get up and running. Also – the FTDI is technically unnecessary because a Pi has 3.3V serial capability on board that we could use. It’s not worth the time to fuss with right now but something to keep in mind for later.

Now that the laser is mobile, it’s time to explore its behavior on the move…

Telling USB Serial Ports Apart with udev Rules

Old school serial bus is great for robot hacking. It’s easy and widespread in the world of simple microcontrollers, and it’s easy for a computer to join in the fun with a USB to serial adapter. In my robot adventures so far, it’s been used to talk to Roboclaw motor controllers, to serial bus servos, and now to laser distance scanners. But so far I’ve only dealt with one of them at any given time. If I want to build a sophisticated robot with more than one of these devices attached, how do I tell them apart?

When dealing with one device at a time, there is no ambiguity. We just point our code to  /dev/ttyUSB0 and get on with our experiments. But when we have multiple devices, we’ll start picking up /dev/ttyUSB1, /dev/ttyUSB2, etc. And even worse, there is no guarantee on their relative order. We might have the laser scanner on /dev/ttyUSB2, and upon computer reboot, the serial port associated with laser scanner may become /dev/ttyUSB0.

I had vague idea that a Linux mechanism called ‘udev rules‘ can help with this problem, but most of the documentation I found were written for USB device manufacturers. They can create their own rules corresponding to their particular vendor and product identification, and create nice-sounding device names. But I’m not a device manufacturer – I’m just a user of USB to serial adapters, most of which use chips from a company called FTDI and will all have the same vendor and product ID.

The key insight came in as a footnote of the XV-11 ROS node instructions page: it is possible to create udev rules that create a new name incorporating a FTDI chip’s unique serial number.

echo 'SUBSYSTEMS=="usb", KERNEL=="ttyUSB[0-9]*", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="sensors/ftdi_%s{serial}"' > /etc/udev/rules.d/52-ftdi.rules

Such a rule results in a symbolic link that differentiates individual serial devices not by an arbitrary and changing order, but a distinct and unchanging serial number.

FTDI udev

Here my serial port is visible as /dev/ttyUSB0… but it is also accessible as /dev/sensors/ftdi_AO002W1A. By targeting my code to the latter, I can be sure they’ll be talking on the correct port. No matter which USB port it was plugged into, or what order the operating system enumerated those devices. I just need to put in the one-time upfront work to write down which serial number corresponds to which devices, code that into my robot configuration, and all should be well from there.

This mechanism solves the problem if I use exclusively USB-to-serial adapters built from FTDI chips with unique serial numbers. Unfortunately, sometimes I have to use something else… like the LewanSoul serial bus servo interface board. It uses the CH341 chip for communication, and this chip does not have a unique serial number.

This isn’t a problem in the immediate future. One LewanSoul serial servo control board on can talk to all LewanSoul serial servos on the network. So as long as we don’t need anything else using the same CH341 chip (basically use FTDI adapters for everything else) we should be fine… or at least not worry about it until we have to cross that bridge.