Wall-E2 Motor Controller Study

Posted 19 May 2019

Over the last few weeks I have noticed that Wall-E2, my wall-following robot, seems to be suffering from a general lack of energy.  I’ve been doing some testing involving a series of 45 degree S-turns, and Wall-E2 is having trouble moving at all, and when it does move, it does so very slowly.  At first I thought this might be due to a low battery condition, but it exhibits the same behavior even with a fully charged battery pack.  Then I thought it might be the battery pack itself dying, but now that I can monitor Wall-E2’s operating current and voltage ‘on the fly’ it is apparent that the battery pack is healthy and delivering rated power – it’s just that the power doesn’t seem to be getting to the motors.  About this same time I began noticing that the cheap L298N motor drivers I have been using were getting pretty hot; enough to burn my finger, and enough to cause a ‘burning insulation’ smell.

So, I decided to go back to the drawing board and see what else is out there in terms of ‘better’ (whatever that means) motor and motor driver technology. As usual I started with a broad internet search and then started narrowing down to specific technologies and modules as I learned more.  What I learned right away is that the L298n technology is notoriously inefficient, as it uses a bipolar transistor H-bridge which pretty much guarantees 1-2V voltage drop between the motor power supply and the motors themselves.  This technology has been superceded by MOSFET-based H-bridge modules with much lower voltage drops and commensurately higher efficiencies.  In fact, most of the modules I found no longer require heat sinks due to the much lower power dissipation.

VNH5019 Motor Driver Carrier

The Pololu VNH5019 Motor Driver Carrier is a single-channel motor driver based on the STMicroelectronics VNH5019 chip, with the following major features:

  • Relatively high cost compared to other products – about $25 ea.
  • 5.5 – 24V operating range. This matches well with Wall-E2’s battery output range of 7-8.4V.
  • Very low Rds(ON) – less than 100mΩ.  This means almost no voltage drop at the typical motor operating current of 100-200mA, and only about 0.2V at 2A stall current, or 0.4W power dissipation, worst case.
  • Peak operating current of 12A – way more than I’ll need.
  • There is also a current sensing output, but it’s only accurate during the active drive portion of the PWM waveform. Although I could probably deal with this by timing measurements to coincide with the drive cycle, I probably won’t bother, as I already have independent current measurement capability.
  • Very simple operation – essentially identical to the L298n scheme.

There are, however, two major drawbacks to this option; the first is that the modules are single-channel only, so I either need to use four (one for each motor) or run two motors in parallel. The second is that they are much more expensive (like an order of magnitude) than the L298n driver modules.

TB67H420FTG Dual/Single Motor Driver Carrier

Pololu’s TB67H420FTG Dual/Single Motor Driver Carrier uses the Toshiba TB67H420FTG part, with the following features:

  • Single or dual channel operation.  In dual motor mode, each channel is limited to 1.7A, which should be OK for Wall-E2’s motors.
  • Minimum motor drive voltage is specified as 10V.  This is too high for my 2-cell LiPo setup that tops out at 8.4V.  It’s still possible it will work OK down to 7V, but only experimentation will tell

Well, this is a much cheaper part ($10 vs $25) and each part can potentially handle twice the number of motors. Nominally $20 for four motors vs $100.  However, the minimum motor voltage of 10V is probably a deal breaker.  Besides, if I parallel two motors on each VNH5019 module, the price differential drops to 2.5:1 vs 5:1 and I don’t have to worry about the minimum motor supply voltage.

TB9051FTG Single Brushed DC Motor Driver Carrier

The Pololu TB9051FTG brushed DC motor driver is based on Toshiba’s TB9051FTG part, and has the following features:

  • Low price – $8.49 (single channel)
  • Compatible motor supply voltage range (4.5 – 28V).
  • Can deliver 2.6A continuously, which should be much more than I’ll ever need
  • 1″ x 1″ form factor, so fitting four modules onto Wall-E2’s chassis shouldn’t be too much of a problem

According to the TB9051FTG’s datasheet, the Rds(ON) has a max of 0.45Ω, so very little IR drop & power loss.  This could make a very nice setup, even if I have to use four modules.  This will still cost much more than my current dual L298n setup, but well worth it for the much lower voltage drop.

Dual TB9051FTG Motor Driver Shield for Arduino

This board is a dual-channel version of the above single-channel part, sized and laid out as an Arduino compatible ‘shield’ module. Pertinent features:

  • $19.95 ea – essentially the same per-channel price as the single channel version
  • Same voltage (4.5 – 28V) & current (2.6A continuous) range per channel
  • Motor driver control pins broken out on one side so can be used without an Arduino
  • Size is 1.9″ x 2.02″ or about 4 times the area of the single-channel boards. This is undoubtedly due to the requirement to physically match the Arduino Uno/Mega pin layout.

This isn’t really a good option for my Wall-E2 project, as I can fit four of the single channel modules in the same footprint as one of these boards – effectively making a 4-channel version in the same footprint.

Dual MAX14870 Motor Driver for Raspberry Pi (Partial Kit)

This module uses the Maxim MAX14870 part on a board physically compatible with the later versions of the Raspberry Pi microcomputer.  Pertinent parameters

  • Reasonable cost – $12.75 means the per-channel cost is around $6.50/channel.
  • Small size:  0.8″ x 1.7″, so all four channels would fit in a 1.6″ x 1.7″ space. This is significantly smaller than the 2 x 2″ space requirement for 4 ea TB9051FTG modules
  • Same 4.5 – 28V motor supply range, and same 1.7A continuous/channel current rating.
  • Low Rds(ON) – less than 0.3Ω

This unit looks very promising; it’s small size and form factor, combined with dual motor control could make it the winner in the ‘replace the L298n’ project.

Adafruit DRV8833 DC/Stepper Motor Driver Board

This is a dual H-bridge for higher current applications than the non-current-limiting Featherwing can handle.  It can handle one stepper or two DC brushed motors and can provide about 1.2A per motor, and expects a motor drive input of 2.7-10.8VDC.  From the documentation:

  • Low MOSFET ON resistance (approx 360 mΩ)
  • 1.5A RMS output current, 2A peak per bridge
  • Power supply range 2.7 – 10.8 V
  • PWM winding current regulation and Current Limiting
  • Overcurrent protection, short-circuit protection, undervoltage lockout, overtemp protection
  • Reasonable per-channel cost; $5 per module, but each module will handle two motors – nice!

Adafruit DRV8871 Single Channel Motor Driver:

This is a single H-bridge for even higher current applications.  From the documentation:

  • 6.5V to 45V motor power voltage
  • Up to 5.5V logic level on IN pins
  • 565mΩ Typical RDS(on) (high + low)
  • 3.6A peak current
  • PWM control
  • Current limiting/regulation without an inline sense resistor
  • Overcurrent protection, short-circuit protection, undervoltage lockout, overtemp protection
  • Higher per-channel cost; $8 per module, so $32 for all 4 motors

I was thinking that I might be able to use just two of these modules and parallel the left motors on one and the right motors on the other; this would give me 4 motor drives for $16, so comparable to the DRV8833.  However, even if I use one per channel, the 4 units would still occupy a smaller footprint than the current setup with two L298N driver modules (and even less if I stack them vertically)

Adafruit Featherwing Motor Driver:

The Adafruit Featherwing motor driver kit is intended to plug into Adafruit’s ‘Feather’ microcontroller product, so to integrate it into my Mega 2560 project I’ll need to make sure I interface to the proper pins, etc.  The module uses I2C for communications and motor control, so the low-level drivers for this module will be quite different than the ones currently in use for my Wall-E2 robot.  Looking over the Adafruit documentation, I get the following:

  • Motor Power and Motor outputs:  these are all on 2-pin terminal blocks, so no changes
  • Logic Power Pins:  The Featherwing requires 3.3V & GND on the 2nd & 4th pins on the ‘long’ header side, counting from the end with three terminal blocks.
  • I2C Data Pins: Last two pins on the ‘short’ header side
  • I2C Addressing:  The default feather address is 0x60, so it should be OK. The Wall-E2 project currently has four devices connected to the I2C bus;
    • IR Homing Teensy uC slave at addr = 0x08
    • FRAM module at addr = 0x50
    • DS3231 RTC at addr = 0x68 (fixed)
    • MPU6050 IMU module at addr = 0x69
  • Smaller voltage range (4.5 – 13.5V), but OK for my 2-cell LiPo application with max V <= 8.5V
  • Lower max current rating – 1.2A/channel; this could be a problem, but I don’t think so; the motors draw less than 0.5A, and won’t ever need 1.2A except in stalled rotor configuration, where it doesn’t matter. However, there is no current limiting on the Featherwing, so it is entirely possible to burn out a channel if the limit is exceeded!
  • Reasonable cost on a per-channel basis. The board costs $20, but it will drive all four motors, for a per-motor cost of only $5 – nice!

So, I ordered several of each of the above parts, with the idea of running some simple tests of each and picking the one that best suits my application

Adafruit Featherwing Motor Driver Testing:

The first unit I tested was the Adafruit FeatherWing.  I set up a spare Mega 2560 as the controller. This turned out to be quite simple, as there are only four connections; 3.3V, GND, SCL and SDA.  I used my DC lab power supply to provide 8.0V, and used two spare JGA25-370 geared DC motors connected to M1 and M3 for the test. As the following short video shows, this worked great.

As a side-effect of this test, I noticed that one of the JGA25-370 210RPM gear motors seemed to be behaving a little differently than the other.  After some experimentation, I discovered that one motor required a speed control input of almost half the full-speed value to even start turning, while the other one seemed to respond correctly to even low level speed drive values.  Some more investigation revealed that the ‘problem’ motor was definitely stiffer than the ‘good’ one, and when the problem motor was being driven from my lab power supply, the power supply output current went up to almost 1A until the motor started moving and then dropped down to under 100mA while the motor was actually running. The ‘good’ motor current stayed under 100mA for the entire range of speeds.

I continued by connecting all four JGA25 gear motors and noticed that only one was really functioning properly, and the other three all had difficulty at low commanded speeds.  To investigate further, I added an Adafruit INA219 high-side current sense module to the circuit so I could record both the current and power for each step on each channel.  I ran all 4 motors in sequence, and then plotted the results in Excel as shown below

Testing all 4 JGA25-370 motors

Current and Power vs time for 4 JGA25-370 motors.  Note the current/power spikes when the ‘sticky’ motors turn ON

After some posts on the Adafruit forum, I was able to troubleshoot the motor problems to some extent. I was able to free up one of the troublesome motors by exercising the gear train, but on two other motors it became obvious that it was the motors themselves, not the gear train that was sticking.  A 50% (or 75% depending on one’s threshold) failure rate isn’t very good.  One of the posters on the Adafruit forum suggested I take a look at the metal gear motors offered  by Pololu, and I ordered some of their ’20D’ motors for evaluation.

After testing the metal gear motors, I tried some of my stock of plastic motors, as shown in the following photo and Excel plot.

two ‘red cap’ and two ‘black cap’ motors

Current and Power vs time for two ‘red cap’ and two ‘black cap’ motors

All four motors behaved reasonably well, but as shown in the above plot, the ‘red cap’ motors drew a LOT more current than the ‘black cap’ ones. This was a mystery to me until I measured the coil resistance of the two types.  The ‘red cap’ motors exhibited coil resistances of about 1Ω, whereas the ‘black cap’ motors were more like 5.7Ω.  I surmise that the ‘red cap’ motors are for 3V applications and the ‘black cap’ ones are for 6-12V.

After this, I decided to test the motors currently installed in my Wall-E2 robot, so I simply placed the prototype board containing the Featherwing and the Mega 2560 controller on top of the robot and rerouted the motor wires from the LN298 controllers to the Featherwing. The following photo and plot show the setup and the results.

proto board on top of Wall-E2

Current and Power vs time for the four ‘red cap’ motors currently installed in Wall-E2

Of the four installed ‘red cap’ motors currently installed in Wall-E2, only two (M1 & M4 above) exhibited reasonable performance – the other two were basically inoperative. Some closer inspection revealed that M3 exhibited essentially infinite coil resistance, i.e. an open circuit.  Apparently I had managed to burn out this coil entirely, probably because the ‘red cap’ motors were intended for lower voltage applications.

Adafruit DRV8871 Single Channel Motor Driver Testing:

I removed the Featherwing module and replaced it with Adafruit’s DRV8871 Driver module. This module uses two PWM outputs to drive and direction control (vs I2C for the Featherwing) and is very simple to hook up and use.  After getting it hooked up I ran a Pololu ’20D’ metal gear motor and a ‘good’ JGA25-370 over one complete up/down speed cycle, with no mechanical load. This produced the plots shown below:

Pololu 20D metal gear motor with DRV8871 Motor Driver

JGA25-370 metal gear motor with DRV8871 Motor Driver

Since I had already tested the same JGA25-370 motor with the Featherwing driver, I thought I would get similar results.  However, the earlier Featherwing test produced a significantly different plot, as shown below

JGA25-370 metal gear motor with Featherwing driver

Why the differences?  The coarseness of the DRV8871 plot versus Featherwing may well be because the DRV8871 uses the Arduino PWM outputs at the default frequency of around 980Hz, and I suspect the Featherwing PWM frequency is much higher.  However, this doesn’t explain the dip in both power & current at the peak of the speed curve.

 

Frank

 

Back to the future with Wall-E2. Wall-following Part III

Posted 24 March 2019,

In my previous post on this subject, I described some initial wall-following tests using a PID controller, and concluded that I would have to use some sort of two-stage approach/capture arrangement to make things work properly.  In addition, I needed some more ‘field’ (i.e., my hallway) testing to determine the best PID values for wall-following.

I started by causing the distance input to vary in a square wave fashion with an amplitude of about 20cm around the 50cm target value.  The resulting speed variations are shown in the following Excel plot.  This gave me some confidence that the basic PID mechanism was working properly.

Square wave response of PID = 1,0,0

After verifying basic operation, I started field testing with the most basic values possible – PID = 1,0,0:  This resulted in a very slow oscillation, as shown in the following video and Excel plot:

PID = 1,0,0, initial offset = target distance = 50cm

Then I moved on to the PID value I had obtained from previous field testing, i.e. PID = 10,0,0. This resulted in the behavior shown in the following video and Excel plot

PID = 10,0,0, initial offset = target distance = 50cm

For completeness, I also tested the PID = 10,2,0 case, but as can be seen in the following video and Excel plot, this did not appreciably change wall-tracking performance, at least not for the offset = target case.

PID = 10,2,0, initial offset = target distance = 50cm

A comparison of all three PID values is shown in the following Excel plot

Next I tried the PID = 1,0,0 case with an initial offset of 25cm and a target of 50cm, to gauge how the PID algorithm performs with an initially large error term.

PID = 1,0,0, initial offset = 25cm, target distance = 50cm

Upgraded Variance Calculation for Wall-E2 ‘Stuck’ Detection

Posted 23 April 2019

A little over three years ago I developed an effective technique for detecting when Wall-E2, my autonomous wall-following robot, had gotten itself stuck and needed to back up and try again.  The technique, described in that post, was to continuously calculate the mathematical variance of the last N forward distance measurements from the onboard LIDAR module.  When Wall-E2 is moving, the forward distances increase or decrease, and the calculated variance is quite high (generally in the thousands).  However, when it gets stuck the distances remain steady, and then the variance decreases rapidly to near zero. Setting a reasonable threshold allows Wall-E2 to figure out it is stuck on something and take appropriate action to recover.

The above arrangement has worked very well for the last three years, but recent developments and some nagging concerns prompted me to re-examine this feature.

Computational Load:

When I first added the variance calculation routine, there was very little else going on in Wall-E2’s brain; ping left, ping right, LIDAR forward, calc variance, adjust motor speeds, repeat.  Since then, however, I have added a number of different sensor packages, all of which add to the computational load in one way or another. I began to be concerned that I might be about to exceed the current 100 mSec time budget.

Performance Problems:

Although the LIDAR/Variance technique has performed very well, there were still occasions where Wall-E2’s behavior indicated something was wrong; either he would think he was stuck when he obviously wasn’t, or it would take too long to detect a stuck condition, or other random glitches.  This didn’t happen very often, but just enough to  put me on notice that something wasn’t quite right.

Brute Force vs Incremental Variance Calculation:

I could only think of two ways to address the computational load; reduce the size of the array of LIDAR distances used, or reduce the time required for each computation.  The original size selection of 50 measurements was sort of based on the idea that it would take about 5 seconds for a 50-entry array to be completely refreshed at the current 10Hz loop frequency.  This means that Wall-E2 should be able to detect a stuck condition about 5 seconds after it happens.  Decreasing the array size would decrease the detection time more or less linearly, but would probably also increase the chances of a false positive.  The other alternative would be to find a way to speed up the variance calculation itself.

Variance is a measure of the variability of a dataset, and is defined as:

The current algorithm for computing the variance is a ‘Brute Force’ method that loops through the entire array of LIDAR distance values twice – once for computing the mean, and then again to compute the squared-difference term, before subtracting the squared mean from the squared difference term to get the final variance value, as shown below:

This works fine, but is computationally clunky.  With a 50-element array, this computation takes around 1.5 mSec.  This is only about 1.5% of the 100 mSec time window for a 10Hz cycle time, but still…

Starting from a slightly different definition of the variance calculation

and then expanding, manipulating algebraically and then recombining, we arrive at an incremental version of the variance formula, as shown below:

Incremental version of the variance expression

And this is implemented in the code section shown below:

The incremental calculation doesn’t use any loops and therefore is quite a bit faster, for no loss in accuracy.  For a 50-element array as above, the incremental calculation takes only about 220 uSec, or about 7-8 times faster than the ‘brute force’ technique.

As a side-benefit of changing from the ‘brute force’ to the incremental technique, I also discovered the reason Wall-E2 was displaying occasional odd behavior.  The sonar ping measurements have a maximum value of 200 cm, which fits nicely into a 8-bit ‘byte’ data type.  However, the LIDAR front distance sensor goes out to 400 cm, which doesn’t.  Unfortunately, I had defined the forward distance array as an array of ‘byte’ values, which meant that everything worked fine as long as the reported distance was less than 255 cm, but not so much for distances over that value.  Fixing this problem turned out not to be as simple as changing the array definition from ‘byte’ to uint16_t, as then I ran into numerical overflow problems in the incremental variance calculation because of the squaring operations involved.  Due to the way the compiler operates, it isn’t sufficient to cast the result of uint16_t * uint16_t, to uint32_t, as the overflow happens before the cast.  To avoid this problem, the individual terms need to be uint32_t before the multiplication occurs.  Once this was done, all the results settled down to what they should be, and now correct results are obtained for the full range of possible distance values.  Shown below is an Excel plot of a test run performed using simulated input values from 100 to 400 (anything above 255 used to cause overrun problems)

As can be seen in the above plot, the ‘brute force’ and incremental methods both produce identical outputs, but the incremental method is 7-8 times faster.

Shown below is the same setup, but this time the input data is actual LIDAR data from the robot

 

03 May 2022 Update:

As part of my ongoing work to integrate charging station homing into my new Wall-E3 autonomous wall-following robot, I discovered that the ‘stuck detection’ routine wasn’t working anymore. Upon investigation I found that the result returned by the above incremental variance calculation wasn’t decreasing to near zero as expected when the robot was stuck; in fact, the calculated variance stayed pretty constant at about 10,000 – how could that be? After beating on this for a while, I realized that if the four terms to the right of the previous incremental variance calculation add to near zero, which they will if the array contents are constant, then the new calculated variance will be the same as the old calculated variance. So, if the ‘previous variance’ number starts out wrong, it will stay wrong, and the ‘stuck’ flag will never be raised – oops!

I started thinking again about why I changed from the ‘brute force’ method to the incremental method in the first place. The reason at the time was my concern that the brute force method might eat up too much of the available loop cycle time, but now that I have changed the processor from an Arduino MEGA2560 to a Teensy 3.5, that might not be applicable anymore. So I replaced the incremental algorithm with the brute force one, and instrumented the block with a hardware pin to actually measure the time required to compute the variance. As shown below, this is approximately 8μSec, insignificant compared to the 100 mSec tracking loop duration.

‘Brute Force’ variance calculation duration

Stay tuned,

Frank

Better Battery Charging for Wall-E2

Posted 08 February 2019,

After recovering from my bout with #include file hell, I’m back to working on Wall-E2, my autonomous wall-following robot.  In a previous post I described the integration of the TP5100 charger module into Wall-E2’s system, but I have lately discovered that the TP5100 end-of-charge (EOC) detection scheme isn’t very reliable in my application.  The TP5100 uses a current threshold to determine EOC, which works fine in a normal application where the battery pack isn’t simultaneously supplying current to the load, but in my application, Wall-E2 stays active and alert while it’s docked at it’s feeding station; it has to, in order to be able to respond to the EOC signal and detach itself.  So, the current going through the TP5100 never goes below the idling current for Wall-E2, which is on the order of 300mA or so.  This is enough to keep the charging current above the TP5100 EOC threshold, and so Wall-E2 hangs on to the charging station forever – not what I had in mind!

Life would be good if I somehow measure Wall-E2’s idling current while on charge and the total charging current.  Then I could subtract the two values to get the excess current, i.e. the current going into the battery but not coming out – the current actually going into increasing the battery charge level. When this current falls below an appropriate threshold, then charging could be terminated. This scenario is complicated by the need to measure the current on the high side of the charging circuit and of the +Vbatt supply to the rest of the system.

Well, as it turns out, Adafruit (and I’m sure others) makes a high-side current sensor just for this purpose, based on the 1NA219 and INA169 chips. The INA219 module reports current via an I2C connection, while the 1NA169 module provides a open-emitter current source proportional to the current through an onboard 0.1Ω resistor (see this data sheet for details).  My plan is to use two of these modules; one at the charging circuit input, and a second one at the 8.4V +VBatt supply from the battery to the rest of the system. Since Wall-E2 stays awake during charging, it should be simple to monitor both currents and decide when charging is complete (or complete enough, anyway).  As a bonus, I should be able to extend the life of Wall-E2’s battery pack by terminating the charge at less than 100% capacity. See this very informative post by François Boucher for the details.

03 March 2019 Update:

After the usual number of mistakes and setbacks, I think I have the dual current sensor feature working, and now WallE-2 charges by monitoring both the battery voltage and the actual charging current (total current measured at the charging connector minus the run current measured on WallE-2’s main power line).  As a final test, I discharged the main battery pack at about 1A for about 1 hour, and then charged it again using the two-current method. As shown in the Excel plots below, Charging terminated when the actual battery charging current fell below 50mA.

Complete charge cycle, after discharging at approx 1A for approx 1 Hr

Last 20 minutes or so of charge operation, showing detail of end-of-charge behavior

two 1NA169 high-side current sensors mounted in the battery/motor compartment.  Note the 3D-printed mounting plates.

Here is a showing the installation of the two 1NA169 sensor modules in WallE-2’s battery compartment.  The one on the left measures running current, and the one on the right measures total current (charging + running).

The following figure shows the system schematic for WallE-2, with the two new 1NA169 sensors highlighted

System schematic with locations of new current sensors highlighted

Now that I have the current sensors and the new charge algorithm working, it’s time to go back an take another look at the charge/discharge characteristics of the Panasonic 18650B cells I’m using to see if I can extend their life with a more intelligent charge/discharge scheme.  The following plot shows the charge characteristics for this cell.

Charge plot for the Panasonic 18650B LiPo cell

As noted by François Boucher, the red line above is the total energy returned to the battery during charge.  As he notes, the battery acquires about 90% of its total capacity in the first 105 minutes of the charge period, when charged at 0.5C at 25C.  My battery pack is a 2-cell parallel x 2-cell series stack, and currently I’m charging to a 50mA cutoff.  According to Boucher, this is way too low – I’m charging to almost 100% capacity and thereby limiting the cycle life of the battery pack.  Looking at the end-of-charge detail plot above (repeated below), I should probably use a charge current threshold of around 500mA charging current (250mA per cell in the parallel stack) for about 90% capacity charge.

End-of-charge detail with approximate 90% charge current highlighted

On the discharge side, Panasonic’s Discharge Characteristics plot below shows a discharge down to 2.50V/cell.  WallE-2’s typical current drain is about 1A or about 0.3 – 0.5C, and the cutoff I’m using is 3.0V cell.  From the plot, this gives about 3150mAH of the approximately 3300mAH available at 0.5C, or about 95%.  So, it looks like I should raise the discharge cutoff voltage to about 3.2V or about 3000mAH of the 3300mAH available, or about 90%.

Conclusion:

So revisiting WallE-2’s battery management seems to have paid off; I now have much better visibility into and control over charge/discharge of the 18650B battery pack, and at least some expectation that I can use WallE-2’s new found battery super powers for good rather than evil ;-).

Stay tuned!

Frank

 

Back to the future with Wall-E2. Wall-following Part II

Posted 09 February 2019

A long time ago in a galaxy far, far away, I set up a control algorithm for my autonomous wall-following robot Wall-E2.  After a lot of tuning, I wound up with basically a bang-bang system using a motor speed step function of about 50, where the full range of motor speeds is 0-255.  This works, but as you can see in the following chart & Excel diagram, it’s pretty clunky.  The algorithm is shown below, along with an Excel chart of motor speeds taken during a hallway run, and a video of the run.

for left wall tracking

for right wall tracking

 

Run 1, using homebrew algorithm

Note the row of LEDs on the rear. They display (very roughly) the turn direction and rate.

Since the time I set this up, I started using a PID algorithm for the code that homes the robot in on its charging station using a modulated IR beam, and it seems to work pretty well with a PID value of (Kp,Ki,Kd) = (200,0,0).  I’d like to use the knowledge gained for the IR homing subsystem to make Wall-E2 a bit more sophisticated and smooth during wall-following operations (which, after all, will be what Wall-E2 is doing most of the time).

In past work, I have not bothered to set a fixed distance from the wall being followed; I was just happy that Wall-E2 was following the wall at all, much less at a precise distance. Besides, I really didn’t know if having a preferred distance was a good idea.  However, with the experience gained so far, I now believe a 20-30 cm offset would probably work very well in our home.

So, my plan is to re-purpose the PID object used for IR homing whenever it isn’t actually in the IR homing mode, but with the PID values appropriate for wall-following rather than beam-riding.

PID Parameters:

For the beam-riding application I used a setpoint of zero, meaning the algorithm adjusts the control value (motor speed adjustment value) to drive the input value (offset from IR beam center) to zero.  This works very nicely as can be seen in the videos.  However, for the wall-following application I am going to use a setpoint of about 20-30cm, so that the algorithm will (hopefully) drive the motors to achieve this offset.  The Kp, Ki, & Kd values will be determined by experimentation.

 

 

13 February 2019 Update:

I got the PID controller working with a target offset of 25cm and Kp,Ki,Kd = 2,0,0 and ran some tests in my hallway.  In the first test I started with Wall-E2 approximately 25cm away from the wall. As can be seen in the following video, this worked quite well, and I thought “I’m a genius!”  Then I ran another test with Wall-E2 starting about 50cm away from the wall, and as can be seen in the second video, Wall-E2 promptly dived nose-first right into the wall, and I thought “I’m an idiot!”

 

 

The problem, of course, is the PID algorithm correctly turns Wall-E2 toward the wall to reduce the offset to the target value, but in doing so it changes the orientation of the ping sensor with respect to the wall, and the measured distance goes up instead of down.  The PID response is to turn Wall-E2 more, making the problem even worse, ending with Wall-E3 colliding nose-first with the wall it’s supposed to be following – oops!

So, it appears that I’ll need some sort of two stage approach the the constant-offset wall following problem, with an ‘approach’ stage and a ‘capture’ stage.  If the measured distance is outside a predefined capture window (say +/- 2cm or so), then either the PID algorithm needs to be disabled entirely in favor of a constant-angle approach, or the PID parameters need to change to something more like Kp,Ki,Kd = 0,1,1 or something.  More experimentation required.

Stay tuned,

Frank

 

 

State memory for Wall-E2 – writing telemetry packets to FRAM

posted 28 September 2018

In previous posts, I have described my effort to give time, memory and relative heading super-powers to Wall-E2, my autonomous wall-following robot.   This posts describes a helper class I created to allow Wall-E2 to periodically write its current operating state to FRAM memory, for later readout by his human master(s), and a small test program to verify proper operation of the helper class.

My current conception of Wall-E2’s operational state consists of the current time/date, its tracking mode and submode, and the current left, right, and forward distances, and the current battery voltage. These parameters have been encapsulated in a CFramStatePacket class with methods for writing state packets to FRAM and reading them back out again.   The complete code for this class is shown below. Note that all the class code is contained in just one file – FramPacket.h.   There is no associated .cpp file, as I didn’t think that was necessary.

To test my new CFRAMStatePacket class, I created a small test program that periodically writes simulated state packets to FRAM using the helper class methods, and optionally (if the user creates an interrupt by grounding the appropriate pin) reads them back out again.   This program is designed to run on an Arduino Mega 2560.   If a Uno is used, the interrupt pin number would have to be changed.

The test code also looks for a low on the  CLEAR_FRAM_PIN (Pin 3) on startup.   If it finds one, it will clear  NUM_FRAM_BYTES_TO_CLEAR (currently 2000) FRAM bytes and then read them back out again, byte-by-byte.   Otherwise, the program will continue storing state packets where it left off the last time it was powered up.   Here’s the test code:

And here’s some output from a typical run:

And here is an Excel plot showing the simulated values

Plot of the simulated values generated by the test program

 

So now I have a way for Wall-E2 to write a minute-by-minute diary of its operating state to non-volatile storage, but I don’t yet have a good way to read it all back out again.   That’s the next step – stay tuned!

01 October Update:

I created a small program to read back telemetry packets from FRAM.   When I want to see what  Wall-E2 has been up to, I will replace his normal operating firmware with this sketch, which will allow me to read out all or parts of FRAM contents.   The sketch is included below:

and a typical output run is shown below.   Note that this program decodes the stored 4-byte unix time value into human-readable date/time format.   And yes, I know it’s in that funny ‘American’ mm/dd/yyyy format, but I’m an American, so … ;-).

 

 

Frank

 

Integrating Time, Memory, and Heading Capability, Part VIII

Posted 13 September 2018

Now that I have worked out most of the problems associated with the MPU6050 6DOF IMU module, it was time to integrate the new heading-based turn algorithm into the main Wall-E2 operating system.   As I have done in many past projects over the last half-century or so, I started this process by documenting the entire OS, with particular emphasis on how Wall-E2 currently navigates around his world.   When I started doing this in the 1970’s, the medium I used was an MIT Engineering notebook, hand-written in ink.   Over the ensuing decades the medium has changed, but not the basic idea – the process of putting coherent sentences and paragraphs onto paper (or screen) forces me to think through what is – and is not – important/true.   I have solved many a seemingly intractable problem not with an oscilloscope or debugging tool, but by simply writing things down.   In the current iteration of this process, I use Microsoft Word (not for any particular reason, except that it is available and familiar)   initially, and then dump the results into a post like this one – see below ;-).

 

Description of FourWD_WallE2_V1 Navigation Algorithm

09/04/18

At the start of each pass through loop(), the software determines the current OPMODE given the current environment and the immediately previous OPMODE.   The existing OPMODEs are NONE, CHARGING, IRHOMING, WALLFOLLOW, and DEADBATTERY

  • NONE: Default OPMODE when no other mode can be found to apply to the situation.   As of this writing, the only use for this OPMODE is to initialize the PrevOpMode and CurrentOpMode loop variables in Setings()
  • CHARGING: set in GetOpMode() if the charger is physically connected (CHG_CONNECT_PIN goes HIGH) and the CHG_SIG_PIN is active (LOW). In the CurrentOpMode Switch the PrevOpMode is also set to CHARGING (so that both prev and current op modes are CHARGING), the motors are stopped, and MonitorChargeUntilDone() is called.
    • MonitorChargeUntilDone() blocks until charging is complete, or the BATT_CHG_TIMEOUT value is reached or the charger is physically disconnected (manually pulled out for some reason).
  • IRHOMING: Set in GetOpMode() when the call to IRBeamAvail() (checks IR beacon signal strength) returns TRUE.   In the CurrentOpMode Switch the PrevOpMode is also set to IRHOMING (so that both prev and current op modes are IRHOMING).   A blocking call is made to IRHomeToChgStn() with an Avoidance Distance’ of 0 for hungry’ or 30cm (for full- no need to charge’. The idea here is that in the full’ case, the robot will continue to home until near the charging station, and then break off.
    • IRHomeToChgStn(): sets up a PID and enters a loop, exited only when either the charger connects, or the robot gets stuck or it gets too close to the charging station (this can only happen in the full’ case).   Is Stuck’ is determined in IsStuck() if the front distance variance gets too small (i.e. the front distance isn’t changing).
  • WALLFOLLOW: This is the OpMode that is assigned by GetOpMode() when none of the other mode conditions apply. IOW, this is what the robot does when it isn’t doing anything else.   In the WALLFOLLOW Case section of the CurrentOpMode Switch, the wall-following operation is further broken down into a TrackingCase Switch, with   TRACKING_LEFT, TRACKING_RIGHT, TRACKING_NEITHER sub-modes, with state mode variables maintained for both the current and previous tracking modes.   Each time through the loop(), the various tracking cases make one adjustment to the left/right motor speeds. there are no blocking calls at all in the entire WALLFOLLOW section, with the exception of the BackupAndTurn()’ calls in the TRACKING_LEFT and TRACKING_RIGHT cases when an obstruction or the stuck’ condition is detected.
    • BackupAndTurn( bool bIsLeft, int motor_speed): The idea here is for the robot to back up and do a course change to extract itself from some situation. Up until now, this has been accomplished by making a timed turn one way or the other, but this hasn’t worked well because the correct time for turning on carpet is wildly different than the correct time on hard flooring.   The new heading sensor is intended to solve this problem.
    • Now that Wall-E2 can make accurate turns, the question becomes “what’s the best way to do obstruction-avoidance or stuck-recovery turns?”. If Wall-E2 is following a wall when it gets stuck, maybe it should back up slightly and try to go around, or maybe it should just turn around and go back the way it came.   Maybe a simple obstruction should be treated one way, but a stuck’ condition treated another?   The go back the way I came’ model is simple enough but might result in an uninteresting ping-pong’ shuttle track where it stays until it runs out of battery.   A more complex response might allow the robot to go around obstacles and continue its journey?   Maybe it backs up slightly (wall-following in reverse, maybe?), and then makes an X degree turn away from the wall, runs straight for a second, and then starts wall following again.

09/05/18

The current BackupAndTurn()’ routine takes bIsLeft, a Boolean representing the current tracking direction (left or right) and a motor speed.   All it does is call RollingTurnRev(bIsLeft, 1500), where 1500 is the time in millisecond to run the motors.

RollingTurnRev() just calls RunBothMotorsMsec() with the motor speed on one side set to MAX and on the other to OFF (we know this won’t work on Wall-E2, because the wheelbase is too wide – he just locks up.

RollingTurnRev() is called in two places; ExecDiscManeuver() and   BackupAndTurn(). BackupAndTurn() is called from 4 places;   TRACKING_NEITHER/RIGHT/LEFT, and IRHomeToChgStn().   In all these cases, the robot knows which (if any) wall is closer, so it can execute the proper rolling turn

From what I see so far, it appears all these cases can be handled by a turn routine that does the following:

  1. Moves straight backward for just a second or so (or maybe even less)
  2. Makes a 45 ° forward rolling turn away from the nearest wall. If there is no nearest wall, go opposite the way it went last time (requires a global Boolean to save this value)
  3. Makes another 45 turn in the opposite direction to the first one.   This will have the effect of a side-step maneuver, as shown in this post.

After this review, it was clear that all I had to do to integrate the new heading-based turn capability into Wall-E2’s OS was to replace the RollingTurnRev() function with a new ‘RollingTurn()’ function that takes flags for FWD/REV and for CCW/CW, and a parameter for the number of degrees to turn.   Since I had already demonstrated all the code blocks in stand-alone test programs, all I had to do was copy the appropriate code pieces into the appropriate spots in Wall-E2’s OS, and then spiff things up a bit here and there.   When I was done, I had a single function that could facilitate a range of maneuvers.

To test the newly integrated capability, I added some code to Wall-E2’s setup() function to perform a series of S-turns, each of which demonstrates a typical avoidance maneuver. For convenience, I told Wall-E2 to execute a ‘K-turn’ reversal and then S-turn his way back to me.   As can be seen in the following short video, this worked fairly well!

Now that I have the basic heading-based turn capability integrated into Wall-E2, the next step will be to demonstrate that Wall-E2 can use its new superpowers to avoid obstacles in ‘the real world’ (as real as it gets for Wall-E2, anyway).

Stay tuned!

Frank

 

Integrating Time, Memory, and Heading Capability, Part VII

Posted 29 August 2018

In my last post on this subject, I demonstrated Wall-E2’s new found ability to make heading-based turns instead of timing-based ones, making the turns much more terrain-independent.   Unfortunately, as I continued to test this capability, it became clear that Wall-E2’s heading superpower wasn’t quite ready for prime time.   Sometimes he turned 45 deg or even 180 deg when asked to do 90 – oops!

So, I went back to my small test setup – a spare Mega and a small solderless breadboard, as shown below, and started going through the problem slowly and methodically.   Eventually I figured out that most of the problem was caused by the way I was retrieving yaw data from the Inversense MPU6050 (I have the DFRobots version).   I had the module set up to produce interrupts at 20Hz, and the code was trying to keep up with that (unsuccessfully, as it turned out).   Once I figured that out, I backed the code off to where it only checks for heading changes at a 10Hz rate, and things started working much better.

Arduino Mega and small plugboard test setup for robot turn management

I also figured out that the algorithm I was using for detecting the desired heading was fatally flawed.   I was trying to watch for the case where the current heading passed the target heading, but with all the special cases (both directions, the -180 – to – +180 cut, etc) I kept getting it wrong.   I finally found this post that describes a very simple formula for comparing two compass headings.   The formula assumes both values are in degrees in the range 0-360.   Mine are in degrees but in the range -180 to +180, but I took care of that by adding 360 to negative headings.   After some experimentation I settled on a match ratio of about 0.90 for the ‘slow down’ threshold, and 0.98 for ‘match’.   The 0.98 threshold provides about a 6 degree error margin, which with 10 measurements/sec means that the robot would have to rotate more than 60 deg/sec to get through 6 deg between measurements.   Experiments show that a 90 deg turn takes about 3 sec, meaning 30 deg/sec.   So there should always be at least 2 measurements at 0.1sec/measurement in the 0.2 sec it takes the robot to rotate 6 deg – a 2/1 safety margin.

The short video clip below shows the robot doing a series of 180 deg K-turns, simulating an avoidance maneuver.

 

So, at this point I think I’m pretty much done with adding turn management capability to Wall-E2’s superpower repertoire; however, I still have to update Wall-E2’s operating system software to replace the current timed-turn routines with the new heading-based turn routines.

08 September 2018 Update:

In my current Wall-E2 OS, when the robot gets stuck or runs into an obstacle, it backs straight up, and makes a timed turn away from the nearest wall if there is one, otherwise it turns the opposite way it did from the last time it was in a similar situation.   This is all fine and good, but  now that Wall-E2 has heading-aware turn capability, he should be able to respond a little more intelligently.   As indicated in the diagram below, the idea is that Wall-E2 could back straight up from an obstacle, and then go around it by making two linked 45-90 º turns one way or the other (away from the nearest wall if there is one.

New avoidance maneuver made possible by Wall-E2’s new heading superpowers

As an experiment, I programmed this maneuver into Wall-E2 using linked 45 º degree turns, just to see how it would work out.   As the following short video shows, it seems to work very well.

Stay tuned!

Frank

 

Mid-2018 Wall-E2 Project Status

Posted 26 August 2018

It’s been a year and a half since I last described the status and challenges in my ongoing campaign to create Wall-E2, an autonomous wall-following robot.   The name ‘Wall-E’ was taken from the 2006 movie of the same name.   In the movie, Wall-E was an autonomous trash-compactor robot that had all sorts of adventures, and my Wall-E2 autonomous wall-following robot certainly fills that bill!

From the previous system status report in early 2017, I described the following tasks:

Its been a year and a half since I updated the status of my ongoing campaign to create an autonomous wall-following robot.   The robot system consists of the following main subsystems:

  • Battery and charging subsystem
  • Drive subsystem (wheels, motors and motor drivers)
  • IR homing subsystem for charging station
  • LIDAR for front ranging and ultrasonic SONAR for left/right ranging
  • I2C Sensor subsystem (MPU6050 6DOF IMU, FRAM, RTC)
  • Operating system

Battery and charging subsystem:

Since the last update, the battery and charging system has been updated from dual 1-Amp single-cell Adafruit PB1000C chargers utilizing a 5V source to a TP-5100 2-amp dual-cell charger utilizing a 12V source.   This significantly simplified the entire system, as now the battery pack doesn’t have to be switched between series and parallel operation. Also, now the charging and supply leads are independent so the supply leads to the rest of the robot were upgraded to lower gauge wire to reduce the IR drops when supplying motor drive currents.   See this post for details.

Drive subsystem (wheels, motors and motor drivers):

The motors were upgraded to provide a better gear ratio, although this was done before I realized that most of the traction issues were caused by IR drops in the battery wiring.   The motor driver modules are unchanged, but I may later swap them out for more modern 3V-capable drivers so that I can swap in an Arduino Due microcontroller for the Mega (the Due has the same footprint/IO as the Mega, but has a much faster CPU and more memory)

 IR homing subsystem for charging station:

The IR homing subsystem utilizes a pulsed IR beacon on the charging station coupled with dual IR sensors in a flared sunshade housing, backed by a Teensy 3.5 CPU configured as a null pattern matched-filter.   The Teensy reports left/right homing error as a value between -1 and 1 over an I2C bus to the main microcontroller, which drives the motors to null out the signal.   As the system stands today, the operating system can successfully home in on the charging station and connect to the charger. The robot knows its current battery voltage (charge condition) and therefore can decide to connect to the charger or to avoid it.

LIDAR for front ranging and ultrasonic SONAR for left/right ranging:

The front/left/right ranging subsystem is one of the most mature subsystems on the robot.   The subsystem can successfully follow walls, and detect/recover from stuck’ conditions.   The only thing this subsystem lacks is the ability to make consistent turns on different terrain, due to the lack of heading information (this will be supplied by the new tri-sensor module)

I2C Sensor subsystem (MPU6050 6DOF IMU, FRAM, RTC):

The I2C sensor subsystem is a new addition since the last update, and has yet to be fully integrated into he system.   The subsystem consists of a Inversense MPU6050 6DOF solid-state accelerometer, and Adafruit FRAM (Ferromagnetic RAM) and RTC (Real-Time Clock) modules.   The MPU6050 gives the robot the ability to sense relative heading changes, which makes it capable of executing consistent N-degree turns on both hard flooring like the kitchen and atrium areas and the carpet in the rest of the house. The FRAM and RTC units should allow the robot to remember its charge/discharge history, even through power ON/OFF cycles.

The relative heading capability has been tested off-line from the main operating system, but has not yet been integrated into the OS. Same for the FRAM/RTC modules.   Integration of this subsystem was stalled for quite a while due to problems with the Arduino I2C (Wire) library, but these problem were just recently resolved by switching to a more robust I2C library (SBWire).   See this post for details.

 

Operating system:

The operating system has evolved quite a bit over the course of this adventure, but its current state seems pretty stable.   The OS is implemented as a set of modes, as follows:

  • MODE_CHARGING: Occurs when the robot is physically connected to a charging station
  • MODE_IRHOMING: Occurs when a charging station beacon signal is detected
  • MODE_WALLFOLLOW: Occurs when the robot isn’t in any other mode.
  • MODE_DEADBATTERY: Occurs when the sensed battery voltage falls below DEAD_BATT_THRESH_VOLTS volts

 

 

Future Work Plans:

  • Complete the integration of the tri-sensor module: This entails adding the hardware and software required to sense loss of power so that the current date/time stamp can be written to the FRAM, along with the complementary ability to read out the last power cycle date/time stamp from the FRAM on power-up.   In addition, the current timed turn routines need to be replaced by the new heading-sensitive turn algorithms.
  • Investigate the idea of multiple charging stations with different IR beacon frequencies. The current matched filter algorithm forms a very narrow-band filter, to discriminate the desired IR beacon signal from unwanted flooding’ from overhead lighting sources and sunlight.   The center frequency of the filter is set in software on the Teensy microcontroller, so it should be possible to have the Teensy routinely check for beacon signals at other signals, as long as the frequencies are far enough apart to prevent overlap.   The current filter center freq was more or less arbitrarily set to 520Hz. high enough to be well away from, and not a multiple of, 60Hz, but low enough for the Teensy processing rate.   Something like 435Hz (60*7.25) would probably work just as well, and is far enough away from 520Hz to be well outside the filter bandwidth (about +/- 10Hz IIRC).

Complete the implementation of the fixed charging station.

This task has been completed, and along the way the charging voltage was changed from 5V to 12V, to accommodate the new 12V on-board battery charging system.   See this post for details

Integrate the IR homing software from the 3-wheel robot into Wall-E2’s code base:

This task has also been accomplished.   See this post for details.

Integrating Time, Memory, and Heading Capability, Part VI

Posted 25 August 2018

In my previous posts, I have been describing my efforts to give Wall-E2, my autonomous wall-following robot, relative heading sensing ability using the DFRobots MPU6050 6DOF module.   As I went through this process, I discovered that the ‘standard’ Arduino Wire library was seriously defective, and the problem had been known, but not fixed for almost a decade!   Once I figured this out, I was able to fix my local copies of Wire.c/h and twi_c/h and all my hangup problems went away.   Subsequently I found another Wire library (SBWire by Shuning (Steve) Bian that also incorporates the necessary fixes, so I started using his library instead of my own local fixes.

Anyway, after all the I2C drama, I finally got the damned thing working, and ran some tests to demonstrate Wall-E2’s new-found ability to make reasonably precise and consistent turns.   In the first test I had Wall-E2 make a series of 90-deg (ish) turns, and in the second one I had him make some 180-deg (ish) K-turns to simulate what he might want to do after disconnecting from (or avoiding) a charging station.