Abandoning ros_control Analogy for Micro Sawppy

Every time I revisit the task of writing Sawppy rover control code, I’m optimistic that I’m one step closer to not having to rewrite from scratch again. This time around, I’m following conventions set by decade-long development of ROS (Robot Operating System) but writing in low-level C code that should run on anything from embedded microcontrollers on up. This approach adds a few challenges but I hope it’ll be more adaptable to other platforms (both software and hardware) in the future.

On the hardware front, many robot chassis that advertise ROS compatibility uses the velocity command (cmd_vel) topic as their interface layer to ROS autonomy logic nodes. Hardware-specific code listens to messages sent to that topic, and handle translating desired velocity into physical movement. I will follow that precedent but I also had the ambition to go one step further with some inspiration from ros_control. The key word is “had”, past tense.

As I understand it, ros_control original motivation was to interface with components that one would find on a robot arm, creating a generic interface for motors and actuators. This allows for generalized operation with ROS nodes like the MoveIt framework for motion planning, and it is also the interface layer for switching between real and virtual robots for the Ignition Gazebo simulator. And since it’s generic, it’s perfectly valid to try to apply those concept to motors and actuators for chassis locomotion.

I like the concept, but I got lost when wading into documentation. When I dug into one of the actual interfaces like JointPositionController, I saw only a setCommand() that sends a single double-precision floating point value. This is clearly only part of the picture. What is the context that number? For actuators like RC servo it would be a rotation, but for a linear actuator it would be a distance. How would I communicate that information, along with other context like whether that rotation/distance is along X, Y, or Z axis?

Furthermore, the interface assumes closed-loop control on every actuator, since the interface has getPosition() to query status and setGains() to adjust PID coefficients. I wouldn’t be able to offer any of that on a micro Sawppy rover. RC servos do perform closed-loop control, but the loop is closed within the servo and the robot has no visibility to actual position or adjust PID coefficients. And without an encoder wheel, TT gear motors have no closed loop control at all. Some ROS tutorials (like this one) asserted there’s no point to ros_control on low-end robots and I now understand their point.

Since I don’t understand concepts of ros_control yet, I’ve chosen to postpone that idea for ESP32 Micro Sawppy software. The worst downside is that I might go down a path that will require another rewrite in the future. But hey, I’ve done it before and I can do it again. To mitigate this risk, I’ll still communicate my rover actuator commands using unit conventions as described by REP103. So I’ll communicate steering angles using radians instead of RC servo commands, which should be friendlier to conversion to different actuators in the future. Similarly, wheel speed will be communicated in meters per second rather than motor power level, again in the hopes it’ll be generic enough for other actuators in the future. Today I’ll pack them into a single message for short term ease and deal with that problem later. I have a more immediate problem: I found the TT gearboxes have a narrower range of speed than I originally thought.

Manual Control Square Peg in a ROS Round Hole

I have converted my test code reading a joystick via ESP32 ADC peripheral to generate ROS-like data messages as output. There were some hiccups along the way but it’s good enough to proceed. The next step in the pipeline is to interpret those joystick commands in a little Sawppy rover context and generate ROS-like robot chassis velocity command (cmd_vel) as output. And as I start tackling the math I realized I’m going to face a recurring theme. I have some specific concepts around manual control of a little Sawppy rover, but those concepts aren’t a good fit for ROS conventions like REP103.

The first concept is velocity. ROS command for linear velocity is specified in meters per second. There’s no good way to say “go as fast as you can go”. Specifying meters per second will never be accurate on an open-loop DC motor system like micro Sawppy for multiple reasons. The first and most obvious one is battery power: full speed will be faster on a fully charged battery. The next problem is robot geometry. When traveling in an arc, Sawppy’s top speed will be constrained by the top speed of the outer-most wheel. As the arc tightens, that outer-most wheel running as fast as it could would still constrain the rover to a lower top speed. So Sawppy’s top speed will vary based on other conditions in the system.

The second concept is rotation. ROS command for angular velocity is specified in radians per second. There’s no good way to say “pivot about the rover’s inner wheel” because the center of the turn is dictated by the combination of linear and angular velocity. Thus the center of the turn is a result of (instead of a part of) the command. In my experience I’ve found that people have a hard time working with Sawppy turning radius when the center of turn is inside the rover’s footprint. People have an easy time with turns that resemble the cars we see on the roads everyday. People also had no problem with Sawppy pirouetting around the center axis. But in between those realms, people get confused pretty quickly. When I created the wired controller for Sawppy, I divided up the control space into two regimes: One mode where Sawppy pivots in place, and another mode where Sawppy’s turning radius is constrained to be no tighter than pivoting on one of the middle wheels. Deliberately constraining Sawppy’s maneuverability to be a subset of its capabilities was a worthwhile tradeoff to reduce user confusion .

But the whole reason of ROS is autonomy, and autonomous robots have no problem dealing with all degrees of freedom in robot movement, so there’s no reason to block off a subset of capability. However, that also meant if I’m structuring the manual joystick control system to follow ROS conventions, I have no easy way to block off that subset for human friendliness. It is certainly possible to do so with lots of trigonometry, but it always makes my head hurt. ROS works in radians and I have yet to develop a good intuitive sense for thinking about angles in radians. All of my mental geometry have been working in degrees.

These are but two of the problems on the road ahead for Sawppy, in its first draft as a manual remote-controlled vehicle. I hold out hope that this up front pain will make Sawppy software work easier in the future as I adapt ROS nodes to give Sawppy autonomy. But it does mean making today’s manual control Sawppy a square peg trying to fit in a round hole, with lots of imprecision that will fall to “best effort” basis instead of rigidly complying with expectations of ROS. But I’ll stay with it to the best of my abilities, which means revisiting rover geometry math in this new context.

Goals and Challenges for Sawppy ESP32 Software

With a cardboard rover test platform ready to go, I’ve run out of excuses to put off the software portion of micro Sawppy rover work. If my little rover is going to come alive, the software work must be done. This task shouldn’t be as intimidating as it feels, because this is my second round through writing rover brain code. I have learned some lessons from writing SGVHAK rover code running on a Raspberry Pi (code which I then adapted to Sawppy) and those lessons should help me this time on the ESP32.

One goal for my new ESP32 project is to be more ROS-friendly. I’m not ready to dive straight into micro-ROS right now. But when I (or someone else) tackles the challenge, the project shouldn’t be tripped up by something that needlessly complicates the task. I wrote SGVHAK rover code before I knew much about ROS, and in hindsight I made some decisions that made a ROS port annoying for no good reason. For example, the ROS convention for robot forward is along the +X axis. Ignorant of this when I wrote SGVHAK rover software, I chose forward to be +Y axis. The ROS convention for angular measurements are radians for units, and the right-hand rule for positive rotation. Meaning a positive rover steering rotation is counter-clockwise. For SGVHAK rover, I worked with angles in degrees and positive rotation was clockwise. There was no reason to go against these ROS conventions, it was done purely out of ignorance. If I were to adapt my SGVHAK rover code to ROS, a whole bunch of conversions need to occur risking additional surface area for bugs. I completely understand why Rhys Mainwaring wrote a rover ROS software stack from scratch instead, I would have done the same!

So for my upcoming ESP32 Sawppy rover brain project, I’ll keep ROS conventions in mind. Specifically REP103 for units and coordinate conventions. Another ROS-inspired similarity will be messages, which is a new luxury gained by the change in platform. For SGVHAK rover I avoided the complications of introducing multiprocessing concepts to Python code running on a Raspberry Pi. But now I’m targeting the customized FreeRTOS Espressif created to run an ESP32, I gain a lot of multiprocessing tools as a natural and fundamental part of the platform. I will be organizing my code components into FreeRTOS tasks, which has similarities to how ROS organizes code components into nodes. So when my tasks communicate with each other, I’ll look to see if there is a ROS equivalent. I won’t be able to make my FreeRTOS messages compatible with ROS message formats, though. Because I won’t have some of the luxuries like dynamically sized arrays. But the data organization should at least look similar.

I foresee the following items, and I’ll probably find more as I go:

  • I will rewrite my ESP32 ADC joystick code to communicate with a new data message. It should resemble sensor_msgs/Joy from ROS, normalized to the same value ranges.
  • When I write the task which translates raw input into desired rover chassis command, that result will be communicated with something that resembles geometry_msgs/Twist used for ROS topic cmd_vel (Commanded Velocity).
  • When that cmd_vel command is broken down to commands for individual motors and actuators, those individual commands should resemble joint commands of ros_control. I have not yet worked with ros_control myself so this one is the fuzziest with the most unknowns. But I look forward to learning!

And as expected of a learning project, I ran into problems immediately with the first item on the list. I eventually decided to devise a workaround for ESP32 ADC behavior I don’t understand yet.

Sawppy Rover Dances Like Real Rovers

Whenever I build something physical from my imagination, the reality always served a few surprises I failed to anticipate in my mind. Two recurring themes are seeing real joints flexing beyond their primary rotation axis, and realizing physical objects aren’t as perfectly rigid as they are in CAD. Nothing exemplifies this more than the rocker-bogie suspension of my Sawppy rovers. Their movement are well-defined when I articulate them in CAD, but once printed and assembled, I saw movement that weren’t in my CAD model. When rolling over obstacles, Sawppy would wobble and bounce in response to mechanical shocks that weren’t absorbed by its curved wheel spokes. A few other Sawppy rovers builders have asked me to check if this was supposed to happen on their rovers. I answered that it was not an intentional design feature, but they have done nothing wrong as I see it on my rover as well. I joked that our rovers are just dancing.

Part of this comes from Sawppy’s low-cost construction. The aluminum extrusion beams are not as rigid as the carbon composite tubes of real Mars rovers, and the 3D-printed connectors for those beams are not as rigid as CNC-machined metal components. The commodity 608 bearings I used for Sawppy’s joints give me smooth movement and load bearing capability, but they also add some mechanical slop to the system.

But part of this came from the suspension geometry itself, which is unlike suspension systems of cars we drive here on planet earth. Due to far higher speeds involved, our car suspensions are robust and bolted to car chassis at multiple points for added rigidity. In contrast, Mars rovers experience road impacts at a far slower rate, reducing the need for a heavily braced system. Such bracing are undesirably heavy on their strict weight management regimen. So rover suspension components are rather spindly and are attached at only one (or in rare cases, two) points. With so few attachment points, and a multi-segmented construction that puts large subassemblies at the end of several joints, any component movement and flex is compounded.

I had been curious how this tradeoff manifested on real Mars-bound rovers. They have sturdier components, but Sawppy copied their geometry and share the associated challenges. I kept my eyes open on footage of Perseverance rover while it was being tested to see if I see any wobble or bounce, but those tests are slow-moving affairs that imposed no major mechanical shocks to propagate through the system. Since I don’t expect anyone to swing a sledgehammer at the rover, I had resigned to never knowing.

But then I was happily surprised when I watched video footage of Perseverance landing sequence, specifically the part where the descent stage unspooled its tethers to lower the rover. (Thanks to Emily Velasco for turning it into an animated GIF I can embed here.)

Perseverance rover descends during skycrane maneuver

During this sequence, Perseverance suspension was released from its compact travel configuration and unfolded to its driving configuration in preparation for landing. This drop-and-lock action imposed mechanical shock on rover suspension elements, and we can see everything wobbling and bouncing just as I see frequently on my own little rover. It was both enlightening and entertaining to know that real Martian rovers can dance, too! They just choose not to, most of the time.

UPDATE: NASA JPL has released sounds recorded by Perseverance on-board microphone as it started driving across the Martian landscape. In that sound clip, we can hear rover suspension components flex and squeak in response to driving over surface features. Not too different from what I hear from my own Sawppy rover.


[Title image by NASA/JPL-Caltech]

NASA’s 3D Printable Curiosity Rover

When I take Sawppy out for some publicity, people frequently ask about the 3D printable Curiosity rover static model released by NASA. Some mistakenly thought Sawppy was the NASA-released design, others wanted to know how the rovers compared. I couldn’t answer the latter because I never printed the NASA rover, to the surprise of some, so I thought I should do it at least once.

NASA’s 3D printing resources page for a printable Curiosity points to a GitHub directory that actually has two printable models. I’ve seen the smaller one at a MatterHackers event, printed by another attendee who left her little rover on Sawppy’s table to keep my rover company.

The small model has limited articulation. All six wheels can roll, but cannot steer and it could only sit on a flat surface because its rocker-bogie suspension joints are fixed. I also noticed the robot arm joint articulation doesn’t match that of the real rover’s. Still, it is undeniably a representation of Curiosity and a cute little model.

Since I’ve seen the little one, I decided to skip it and try building the larger one. “Large” is relative, of course, it would still be much smaller than Sawppy. Another important difference is that it is an unmotorized static display model, which is actually the main reason I had not tried to build it. I wanted a rover that moved!

But I’m glad I’ve built it, because it was a good study into the different compromises this model made for the sake of being 3D printing friendly.

Inspiration From Droids of Star Wars

Today is the fourth day of the month of May, which has grown into “Star Wars day” due to “May the Fourth” sounding like that film’s popular parting line “may the Force be with you.” A quick search confirmed I’ve never explicitly said anything about Star Wars on this blog and that should be corrected.

By the time I saw Star Wars, I had already been exposed to popular science fiction concepts like space travel, interstellar commerce, and gigantic super-weapons. And the idea of a cult that promises to make their followers more special than regular people… we certainly didn’t need science fiction for that. So none of those aspects of Star Wars were especially notable. What left a lasting impression was R2-D2.

R2-D2 had its own expression of duty and loyalty. Companion to humans, and a Swiss Army knife on wheels. A character that managed to convey personality without words or a face. R2-D2 was the most novel and compelling character for me in the film. I wouldn’t go far as to say R2-D2 changed the path of my life, but there has definitely been an influence. More than once I’ve thought of “does this help me get closer to building my own R2” when deciding what to study or where to focus.

I was happy when I discovered there’s an entire community of people who also loved the astromech droid and banded together to build their own. But that turned to disappointment when I realized the dominant approach in that community was focused on the physical form. Almost all of these were remote-controlled robots under strict control of a nearby human puppeteer, and little effort was put into actually building a capable and autonomous loyal teammate.

I can’t criticize overly much, as my own robots have yet to gain any sort of autonomy, but that is still the ultimate long-term goal. I love the characters of R2-D2 and the follow-on BB-8 introduced in the newest trilogy. Not in their literal shape, but in the positive role they imagined for our lives. This distinction is sometimes confusing to others… but it’s crystal clear to me.

Oh, I thought you loved Star Wars.

Star Wars is fine, but what I actually love are the droids.

I still hope the idea becomes reality in my lifetime.