Yearly Archives: 2021

FlashForge Creator Pro 2 Dual Independent Extruder (IDEX) 3D Printer

Posted 30 August 2021,

For the last year and a half or so I have been struggling to get reliable multi-material 3D prints from my MakerGear M3-ID Independent Dual Extruder (IDEX) machine, and failing. I could get it dialed in for a few prints, but then prints would simply refuse to stay attached to the build plate no matter what I did. I tried everything I could think of, short of ‘hair-spray’ and ‘glue-stick’ options, which I refuse to do. In addition, the web-browser-only operator interface to the M3-ID was more than a little clunky. The printer was set up about 2 meters away from my PC, so I kept having to run back and forth to get simple things done, like load/unload filaments or do first-layer calibrations, or cancelling a print when it, once again, jumped off the build plate.

Eventually I realized I was never going to get it to work reliably in my small home lab, so I started looking for alternatives. In addition to the M3-ID, I have a Prusa MK3S single extruder printer that is completely reliable, almost completely silent when printing, and about twice as fast as the M3-ID. I seriously considered getting the MMU2 multi-material add-on to the Prusa, but there are some serious drawbacks to that option; for one, there have been many reports where running dissolvable and structural filaments through the same extruder causes the printed part to basically fall apart, due to filament cross-contamination. For another, printing a dual material part with the Prusa takes MUCH longer than printing the same dual material part with dual extruders, because the Prusa setup has to go through a complete filament retraction, change, and purge cycle for each material change. For a complex part this adds up to hundreds or even thousands of change cycles.

So, back to the web, where amazingly enough I found that FlashForge had just recently come out with their Creator Pro 2 IDEX model, at a very reasonable price-point. Moreover, I was able to find several YouTube reviews by well-respected 3D printer enthusiasts, and they had very good things to say about the printer. One of the most consistent comments was how easy it was to assemble the printer out of the box and get very high quality prints, and – important to me – get high quality prints using PVA water-soluble support material paired with PLA. In addition, at least one of the reviewers was successful in printing with PETG, even with the 240C extruder temperature limit on the stock extruders. And, even better, there is removable build plate option that works flawlessly (with the addition of a 2mm shim – available on Thingiverse). The build volume of the Creator Pro 2 is about half the volume of the M3-ID, but since I rarely build large items that wasn’t a deal breaker for me.

So, I put my MakerGear M3-ID up for sale on eBay for about a third of what I paid for it, and it sold in about an hour. Then I used the proceeds to buy the FlashForge Creator Pro 2 with a little bit left over – sweet!

When I received the unit, the shipping box looked a little bit beat up, and some of the foam packing material was damaged, so I was worried about printer damage. However, the printer itself seemed in perfect condition, other than a seriously mis-aligned X axis carriage (more on that later). Here are some photos I took during ‘unboxing’ and assembly.

After getting everything unboxed, I started going through the ‘quick start guide’, and rapidly got the extruder assemblies attached to the X-axis carriage, but when I checked the X-axis alignment with the very well thought-out alignment tools (the grey 3D printed parts in the above photo), I discovered the carriage was way out of true, as shown in the following photos:

So, I decided it was time to see how well FlashForge support worked, and I was pleasantly surprised that I got a very quick response to my email. I was connected to ‘Cheery’, who said he would be responsible for getting me going, and he did an excellent job of doing just that. After a couple of back-and-forths, he sent me a link to a video demonstrating how to re-align the extruder carriage. The video showed a FlashForge technician simply reaching into the printer, and brute-forcing the carriage into alignment – wow! Never in a million years would I have tried this without seeing the video – just not something you contemplate doing with your brand-new printer. While watching the video I heard some quite loud clicking sounds which at first I attributed to background noise in the obviously industrial setting, but then realized it was actually the printer itself making the clicking noises as the tech forced the carriage into alignment. I verified with ‘Cheery’ that I too should hear ‘clicks’, and that gave me the courage to apply enough force to overcome whatever ratcheting mechanism is associated with the carriage. I’m a big guy and in pretty good shape for an old fart, and it took some real force to make anything happen. In any case, I eventually got the right number of ‘clicks’ and the carriage was then aligned perfectly – yay!!

Once I got everything put together, I did a couple of the test prints provided by FlashForge, including one they called a ‘Hilbert Square’ that required a support material. I decided to try it with PVA water-soluble material support, and it turned out very nicely.

Not museum quality, but plenty good enough for me!

Although not perfect, this print was SO much better than I had ever been able to achieve with the M3-ID that I almost cried from happiness; out of the box with no real tuning, using FlashForge’s so-so proprietary FlashPrint5 slicer, and bingo – great prints with dissolvable supports – yay!

The next thing I tried was my pill-dispenser design, which features a sliding drawer arrangement to dispense one pill or caplet at a time from a bottle. The sliding drawer requires a dissolvable support to make it work, and again the print worked like a champ on the very first try.

Needless to say I’m extremely happy with my new FlashForge Creator Pro 2 IDEX printer. There are lots of things to quibble about (FlashPrint doesn’t seem to recognize the USB connection, and it’s kind of clunky compared to Simplify3D or Prusa Slicer), but it really, really does a nice job of printing, at least with PLA and PVA support material. I’ve already printed a set of ‘universal filament spool holders’ to replace the FlashForge proprietary ones, and I’ll be doing some more prints in the near future. I have quite a backlog of dual-color, dual-material prints I have been unable to finish while fighting the M3-ID, and I’m looking forward to actually being able to set up a print and walk away, knowing the FFCP2 is reliable enough to leave alone.

28 March 2022 Update:

Since this post was first published back in August of last year, I have been pretty happy with the Flashforge Creator Pro 2 IDEX machine, especially since Jaco Theron and I collaborated (well, Jaco did all the work and I helped with the testing) on a Prusa Slicer config file and post-processing script for this printer, I’ve been even happier.

Unfortunately that all came to a screeching halt about a week ago as I was doing some dual-color test prints to prove out Jaco’s latest changes. First I suffered a filament jam in my right extruder, which I could not seem to clear using my tried-and-true methods.

After a lot of cursing and gnashing of teeth I finally did get it cleared, only to start getting errors about the right extruder not holding temperature. I’ve never heard of this error before, so after scratching my head and searching vainly through the inet, I wound up completely disassembling the right extruder assembly, whereupon I found that the thermocouple cable to the right extruder had broken off where it enters the heat block, as shown in the following photo:

broken thermocouple cable (blue wire) where it enters the heat block.

I had been working with ‘Cheery’ from FlashForge’s After-sales support site through this process, and after verifying the printer was still under warranty, he arranged to have a completely new extruder assembly shipped to my house, from China via DHL – talk about customer support – Wow!

So, the DHL box arrived today, and sure enough, it contained a brand-new extruder assembly, including a brand new stepper motor already attached – wow and wow!

New extruder assy still in its protective foam wrapper, old parts on build plate
Old (left) and new extruder assy – note damaged thermocouple cable on old part

30 March 2022 Update:

I had some time last night, so I installed the replacement extruder assembly. This took a couple of hours and the requisite number of mistakes, including the required “Oh shit – I dropped a screw into the printer and it (of course) went right into one of the gaps in the interior floor and into the electronics space in the bottom – crap!” At first I thought – “no problem, I have spares”, only to realize later that I didn’t have spares for that particular set of two screws – double crap!

And, one last little ‘gotcha’; when the new extruder module was assembled at the factory, the cooling fan was mounted upside down, with the open port for cooling air to the extruder pointing up, and the closed ‘top’ pointing down, as can be seen in the following photo:

red circles show upside down fan mounting. Airflow direction arrow circled at upper right

Anyhoo, after removing the bottom cover of the printer (this turned out to be pleasantly easy, with only four screws vs the 54,225 for normal printers) to find and recover the (literal) loose screw and re-orienting the cooling fan to mate properly with the cooling duct, I finally got everything back together. That was enough drama for last night, so I waited until today to see if my repair worked. After loading filaments and calibrating, I printed a two-color calibration cube using Prusa Slicer for slicing; It turned out pretty nice, as shown below:

two color calibration cube, with ‘draft shield’ enabled

Stay tuned,

Frank

Temperature Display for 3D Printer Enclosure

Posted 29 July 2021,

In my ongoing quest to convert my MakerGear M3-ID printer from a nice bench decorative piece into a real functioning printer.

Recently I have been having real problems with using dissolvable filaments with my MakerGear M3-ID dual-extruder 3D printer. I couldn’t get either the PVA (water soluble) or HIPS (Limonene soluble) filaments to stick worth a damn to the BuildTak surface. In the process of troubleshooting the problems, I discovered that the M3-ID has real trouble getting the print bed temperature above 100C – at least in my nicely air-conditioned lab spaces. So, I went on the hunt for a decent enclosure, and found this 3D Upfitters model.

3D Upfitters Enclosure for the MakerGear M3 series (M3 shown, but the same enclosure works for the M3-ID)

The dimensions shown for the enclosure look like they would work for my setup, so I ordered one – we’ll see. In the meantime I started thinking that I might like to control (or at least monitor) the internal temperature, especially the ambient temperature at the location of the Octoprint module and control electronics. My worry is that at high bed temperatures, the ambient temps might get worryingly close to the max temps for the control electronics. 3D Upfitters does offer a temperature readout, but I was pretty sure it wouldn’t accurately represent the ambient temps around the electronics, so I decided I would modify an earlier project to create a custom temperature probe, using the venerable Nokia 5110 LCD display, a DHT11 temperature/humidity sensor, and a Teensy 3.2.

I decided to use the Teensy 3.2 micro-controller rather than an Arduino UNO to avoid the issue with 5/3.3V level conversion and because I’ve used this item several times before in other projects. I’m sure there are cheaper alternatives, but this is what I had available, and they are rock-solid products. Here’s the schematic:

And here are some photos:

And here is the code that interfaces to the sensor and drives the display:

Stay Tuned,

Frank

Caplet Dispenser

Being an old fart, I have unfortunately accreted a number of meds that I must take on a daily basis; the current count is four different meds – two in caplet style, and to in pill style. Each night I have to remove the cap from four different bottles, extract just one pill/caplet, and close the cap, all without losing any. After the umpteenth time that I either lost a pill on the floor, or had some very expensive caplets literally go down the drain, I said “there must be a better way…”.

So, I started designing a mechanism that would dispense just one pill/caplet on each cycle. I started out by creating TinkerCad models of the pills and caplets, as shown below:

Meds to be dispensed. Large caplet is about 7 x 19 mm

Then I started working on a dispensing mechanism, and wound up with the following design.

Caplet is loaded in upper image, dispensed in lower one

The mechanism consists of a sliding drawer and a collar with a caplet-sized slot. In the ‘load’ position (upper image above), the caplet falls through the slot into a caplet-sized bay in the drawer. In the ‘dispense’ position, the caplet falls through the open bottom of the drawer bay. Each time the drawer is moved back to the ‘load’ position, another caplet falls into the dispense bay, and is dispensed when the drawer is moved back to the ‘dispense’ position.

The above design worked very well, but there were two significant problems; the drawer wouldn’t stay in the ‘load’ position, so care had to be taken to avoid dispensing several caplets at a time, and it was possible for two caplets to fit vertically into the slot, jamming the mechanism, as shown below:

Two caplets oriented vertically, jamming the drawer mechanism

To keep the drawer in the ‘load’ position, a small rubber band was attached to the collar (the red part above) and around the right end of the drawer, using a hot-glue gun. This keeps the drawer in the ‘load’ position until actively pushed against the rubber band tension to the ‘dispense’ position. The issue with two caplets jamming the drawer was solved by placing a ‘flap’ over the slot in the collar, reducing the hole size such that only one caplet at a time can fit. A caplet goes through the hole vertically, and then slides down into the drawer bay, winding up horizontally in the bay, as shown below:

final design showing reduced-size collar slot allowing only one caplet at a time into bay

The next step in the project was to design and fabricate an adaptor piece to connect the dispensing mechanism to the pill bottle. TinkerCad doesn’t really support morphing from one shape to another, so I had to find a different way. I tried Blender, and while it did work, I had no experience with the product and so stumbled around a lot. Next I tried Open SCAD and discovered the ‘hull()’ feature, which does pretty much exactly what I want. After playing around with this a while, I came up with the following OSCAD script to do what I wanted:

The above code and parameter set produced the following model:

The cylindrical shape at the top just accepts a 56mm diameter bottle cap. The above model was converted to an STL file and then imported into TinkerCad, where it was mated with the dispensing drawer. Then the entire thing was printed in one go using my Prusa MK3S 3D printer, as shown below:

Prusa Slicer 2.3.0 showing ‘sliced’ model with supports, ready to print. This print takes about 3.5 hours.

After the print finishes, the support material between the drawer collar and the drawer itself has to be removed manually with an Exacto knife. This is a bit of a PITA, but worth it to have the entire thing printed as a single piece. The photo below shows the finished product.

30 August 2021 Update:

I just recently acquired a Flashforge Creator Pro 2 dual-independent extruder printer to replace the MakerGear M3-ID I sold on eBay for about a third of what I paid for it. Even after a year and a half of diligent work, I could NOT get the M3-ID to print with dissolvable filament worth a damn. I tried everything, including a brand-new roll of PVA filament and adding a BuilTak removable build plate system (see this post). I even ordered the 3D upFitters enclosure for the M3-ID, but came to my senses before I assembled and installed it. After some more web research, I came across the FlashForge Creator Pro 2 IDEX (FFCP2) system, available for about 1/4 the price of the M3-ID, and this printer came with glowing YouTube recommendations from many reputable 3D printer enthusiasts. After receiving my FFCP2 and setting it up, I was able to print the above pill/caplet dispenser design using PLA for the structure and PVA dissolvable filament for the support material. The result was a very high-quality build and the support material dissolved out after just a few hours in warm water – YAY!

Stay tuned,

Frank

Another Try at Wall Offset Tracking, Part II

Posted 22 June 2021

In my previous post on this subject, I described my effort to improve Wall-E2’s wall tracking performance by leveraging controlled-rate turns and the dual VL53L0X ToF LIDAR arrays. This post describes an enhancement to that effort, aimed at allowing the robot to start from a non-parallel orientation and still capture and track a desired wall offset.

The previous post showed that when starting from a parallel orientation either inside or outside the desired wall offset, the robot would make a turn toward the offset line, move straight ahead until achieving the desired offset, then turn down-track and start tracking the desired offset. The parallel starting condition was chosen to make things easier, but of course that isn’t realistic – the starting orientation may or may not be known. In previous work I created an entire function ‘RotateToParallelOrientation()’ to handle this situation, but I would rather not have to do that. It occurred to me that I might be able to eliminate this function entirely by utilizing the known characteristics of the triple-VL53L0X array. The linear array exhibits a definite relationship between ‘steering value’, (the difference between the front and rear sensor values, divided by 100) and the off-perpendicular orientation of the array. At perpendicular (parallel orientation of the robot), this value is nominally zero, and exhibits a reasonably linear relationship out to about +/- 40ยบ from perpendicular, as shown in the following plot from this post from a year ago.

Array distances and steering value for 30 cm offset. Note steering value zero is very close to parallel orientation

As can be seen above, the ‘Steering’ value is reasonably linear, with a slope of about -0.0625/deg. So, for instance, the calculated value for an offset of 30cm would be -0.0625*30 = -0.1875, which is very close to the actual plotted value above for 30ยบ

So, it should be possible to calculate the off-perpendicular angle of the robot, just from the measured steering value, and from that knowledge calculate the amount of rotation needed to achieve the desired offset line approach angle.

The desired approach angle was set more or less arbitrarily by

and this assumes an initial parallel orientation (i.e. 0ยบ offset in the above plot). If, for instance, the initial steering value was -0.1875, indicating that the robot was pointing 30ยบ away from parallel, then the code to compute the total cut (assuming we are tracking the wall on the left side of the robot) would look something like this:

So, for example, if the robot’s starting orientation was offset 30ยบ away from the wall, and 20cm inside the desired offset line of 30cm, we would have:

cutAngleDeg = WALL_OFFSET_TGTDIST_CM – (int)(Lidar_LeftCenter / 10.f);

cutAngleDeg = 30 – 10 = 20

adjCutAngleDeg = cutAngleDeg – 30 = -10ยบ, so the robot would actually turn 10ยบ CCW (back toward the wall) before starting to move to capture the desired offset line.

If, on the other hand, the robot was starting from outside the desired offset (say at 60cm), but with the same (away from wall) pointing angle, then the result would be

cutAngleDeg = WALL_OFFSET_TGTDIST_CM – (int)(Lidar_LeftCenter / 10.f);

cutAngleDeg = 30 – 60 = -30

adjCutAngleDeg = cutAngleDeg – 30 = -30 – 30 = -60ยบ, so the robot would actually turn 60ยบ CCW (back toward the wall) before starting to move to capture the desired offset line.

26 June 2021 Update:

I modified my test program to just report the rear, center, and front VL53L0X distances, plus the steering value and the computed off-parallel angle, using the -0.0625 slope value obtained from the above plots. Unfortunately, the results were wildly unrealistic, leading me to believe something was badly wrong somewhere along the line. So, I redid the plots, thinking maybe the left side VL53L0X sensors were different enough to make that much of a difference. When I plotted the steering value vs off-parallel angle for 10, 20, 30 & 40cm wall offsets, I got a significantly different plot, as shown below:

As can be seen in the above plot, the slope of steering values to off-parallel angles is nice and linear, and also quite constant over the range of wall offset distances from 10 to 40 cm; this is quite a bit different than the behavior of the right-side sensor array, but it is what it is. In any case, it appears that the slope is close to 1.4/80 = 0.0175, or almost twice the right-side slope derived from the previous right-side plots.

Using the value of 0.0175 in my GetSteeringAngle() function results, and comparing the calculated off-parallel angle with the actual measured angle, I get the following Excel plot.

As can be seen in the above plot, the agreement between measured and calculated off-parallel angles is quite good, using the average slope value of 0.0175.

The above plot shows the raw values that produced the first plot.

So, back to my ‘FourWD_WallTrackTest’ program to develop Wall-E2’s ability to capture and then track a particular desired offset. In my first iteration, I always started with Wall-E2 parallel to the wall, and calculated an intercept angle based on the difference between the robot’s actual and desired offsets from the near wall. This worked very nicely, but didn’t address what happens if the robot doesn’t start in a parallel orientation. However, when I tried to use the data from a previous post, the results were wildly off. Now it is time to try this trick again, using the above data instead. Because the measured steering value/off angle slopes for all selected wall offsets were essentially identical, I can eliminate the intermediate step of calculating the appropriate slope value based on the current wall offset distance.

So, I modified my ‘getSteeringAngle()’ function to drop the ‘ctr_dist_mm’ parameter and to use a constant 0.0175 slope value, as shown below:

With this modification, I was able to get pretty decent results; Wall-E2 successfully captured and tracked the desired offsets from three different ‘inside’ (robot closer to wall than the desired offset) orientations, as shown in the following short videos:

‘Inside’ capture, with initial orientation angle > desired approach angle
‘Inside’ capture with initial orientation angle < desired approach angle
‘Inside’ capture with negative initial orientation angle

04 July 2021 Update:

After succeeding with the ‘inside’ cases, I started working on the ‘outside’ ones. This turned out to be considerably more difficult, as the larger distances from the wall caused considerable variation in the VL53L0X measurements (lower SNR?), which in turn produced more variation in the starting and ‘cut’ angles. However, the result does seem to be reasonably reliable, as shown in the following videos.

‘outside’ capture with initial outward angle
‘outside’ capture with initial inward angle
‘outside’ capture with small outward angle.

05 July Update:

After getting the left-side tracking algorithm working reasonably well, I ported the ‘TrackLeftWallOffset()’ functionality to ‘TrackRightWallOffset()’. After making (and mostly correcting) the usual number of mistakes, I got it going reasonably well, as shown in the following short videos:

Right wall tracking, starting inside desired offset, oriented toward wall
Right wall tracking, starting inside desired offset, oriented away from wall
Right wall tracking, starting outside desired offset, oriented toward wall
Right wall tracking, starting outside desired offset, oriented away from wall

Here is the complete code for my wall capture/track test program:

Here is a link to the above file, plus all required library & ancillary files.

Stay tuned,

Frank

Another Try at Wall Offset Tracking

Posted 22 June 2021

About nine months ago (October 2020) I made a run at getting offset tracking to work (see here and here). This post describes yet another attempt at getting this right, taking advantage of recent work on controlled-rate turns. I constructed a short single-task program to do just the wall tracking task, hopefully simplifying things to the point where I can understand what is happening.

One of the big issues that arose in previous work was the inability to synch my TIMER5 ISR with the PID library’s ‘Compute()’ function. The PID library insists on managing the update timing internally, which meant there was no way to ensure that Compute() would be called every time the ISR ran. I eventually came to the conclusion that I simply could not use the PID library version, and instead wrote my own small function that did the Compute() function, but with the timing value passed in as an argument rather than being managed internally. This forces the PID calculations to actually update in synch with the TIMER5 interrupt. Here’s the new PIDCalcs() function:

As can be seen from the above, this is a very simple routine that just does one thing, and doesn’t incorporate any of the improvements (windup suppression, sample time changes, etc) available in the PID library. The calling function has to manage the persistent parameters, but that’s a small price to pay for clarity and the assurance that the output value will indeed be updated every time the function is called.

With this function in hand, I worked on getting the robot to reliably track a specified offset, but was initially stymied because while I could get it to control the motors so that the robot stayed parallel to the nearest wall, I still couldn’t get it to track a specific offset. I solved this problem by leveraging my new-found ability to make accurate, rate-controlled turns; the robot first turns by an amount proportional to it’s distance from the desired offset, moves straight ahead until the offset is met, and then turns the same number of degrees in the other direction. Assuming the robot started parallel to the wall, this results in it facing the direction of travel, at the desired offset and parallel to the nearest wall. Here is the code for this algorithm.

Here are a couple of videos showing the ability to capture and track a desired offset from either side.

In both of the above videos, the desired wall offset is 30 cm.

Stay Tuned,

Frank

Turn Rate PID Tuning, Part IV

Posted 10 June 2021,

In my last post on this issue, I described using a small test program to explore an in-line version of the PID (Proportional-Integral-Differential) algorithm for turn rate control with Wall-E2, my autonomous wall-following robot. This post describes some follow-on work on this same subject.

The fundamental problem with all the available Arduino PID libraries is they all require the user to wait in a loop for the PID::Compute() function to decide when to actually produce a new output value, and since this computation is inside the function, it is difficult or impossible to synchronize any other related timed element with the PID function. In my case where I want to control the turn rate of a robot, the input to the PID engine is, obviously, the turn rate, in degrees/sec. But, calculation of the turn rate is necessarily a timed function, i.e. (curent_heading – last_heading) / elapsed_time, where the ‘elapsed_time’ parameter is usually a constant. But, all the Arduino PID libraries use an internal private class member that defines the measurement period (in milliseconds), and this value isn’t available externally (well, it is, but only because the user can set the sample time – it can’t be read). So, the best one can do with the current libraries is to use the same constant for PID::SetSampleTime() and for any external time-based calculations, and hope there aren’t any synchronization issues. With this setup, it would be quite possible (and inevitable IMHO) for the PID::Compute() function to skip a step, or to be ‘phase-locked’ to producing an output that is one time constant off from the input.

The solution to this problem is to not use a PID library at all, and instead place the PID algorithm in-line with the rest of the code. This ensures that the PID calculation any related time-based calculations are operating on the same schedule. The downside of this arrangement is loss of generality; all the cool enhancements described by Brett Beauregard in his wonderful PID tutorial go away or have to be implemented in-line as well. From Brett’s tutorial, here’s ‘the beginner’s PID algorithm’:

The difficulty with all the current PID libraries is the ‘dt’ parameter in the above expression; the implementation becomes much easier if ‘dt’ is a constant – i.e. the time between calculations is a constant. However, this constraint also requires that the library, not the user program, controls the timing. This just doesn’t work when the ‘Input’ parameter above also requires a constant time interval for calculation. In my case of turn rate control, the turn rate calculation requires knowledge of the time interval between calculations, and the calculation itself should be done immediately after the turn rate is determined, using the same time interval. So, the turn rate is calculated and then PID::Compute() is called, but Compute() may or may not generate a new output value, because it can return without action if it’s internal time duration criteria isn’t met; see the problem? It may, or even might, generate a new output value each time, but there is no way to ensure that it will!

After figuring this out the hard way (by trying and failing to make the library work), I finally decided to forget the library – at least for my turn rate problem, and in-line all the needed code. Once I had it all running, I abstracted just the PID algorithm to its own function so I could use it elsewhere. This function is shown below:

As you can see, the explanatory comments are much bigger than the function itself, which is really just eight lines long. Also, it has a huge number of arguments, five of which are references that are updated by the function. This function wouldn’t win any awards for good design, as it has too many arguments (wide coupling), but it does have high cohesion (does just one thing), and the coupling is at least ‘data’ coupling only.

Once this function was implemented, the calling function (in this case ‘SpinTurn()’ looks like this:

In the above code, the lines dealing with ‘TIMSK5’ are there to disable and then re-enable the TIMER5 interrupt I have set up to update external sensor values every 100 mSec. I’m not really sure that this HAS to be done, but once I learned how to do it I figured it wouldn’t hurt, either โ˜บ

Now that I have this ‘PIDCalcs()’ function working properly, I plan to use it in several other places where I currently use the PID library; it’s just so much simpler now, and because all the relevant parameters are visible to the calling program, debugging is now a piece of cake where before it was just an opaque black box.

12 June 2021 Update:

After chasing down and eliminating a number of bugs and edge-case issues, I think I now have a pretty stable/working version of the ‘SpinTurn’ function, as shown below:

With this code in place, I made some 180ยบ turns at 45 & 90 deg/sec, both on my benchtop and on carpet, as shown in the plots and video below:

Average turn rate = 41.8 deg/sec
Average turn rate = 86 deg/sec
Average turn rate = 44.8 deg/sec
Average turn rate = 90.1 deg/sec

Stay Tuned!

Frank

Turn Rate PID Tuning, Part III

In my previous post on this subject, I described my efforts to control the turn rate (in deg/sec) of my two-wheel robot, in preparation for doing the same thing on Wall-E2, four wheel drive autonomous wall following robot.

As noted previously, I have a TIMER5 Interrupt Service Routine (ISR) set up on my four wheel robot to provide updates to the various sensor values every 100 mSec, but was unable to figure out a robust way of synchronizing the PID library’s Compute() timing with the ISR timing. So, I decided to bag the PID library entirely, at least for turn rate control, and insert the PID algorithm directly into the turn rate control, and removing the extraneous stuff that caused divide-by-zero errors when the setSampleTime() function was modified to accept a zero value.

To facilitate more rapid test cycles, I created a new program that contained just enough code to initialize and read the MP6050 IMU module, and a routine called ‘SpinTurnForever() that accepts PID parameters and causes the robot to ‘spin’ turn forever (or at least until I stop it with a keyboard command. Here’s the entire program.

This program includes a function called ‘CheckForUserInput()’ that, curiously enough, monitors the serial port for user input, and uses a ‘switch’ statement to execute different commands. One of these commands (‘q’ or ‘Q’) causes ‘SpinTurnForever()’ to execute, which in turn accepts a 4-paremeter input that specifies the three PID parameters plus the desired turn rage, in deg/sec. This routine then starts and manages a CCW turn ‘forever’, in the ‘while()’ block shown below:

This routine mimics the PID library computations without suffering from library’s synchronization problems, and also allows me to fully instrument the contribution of each PID term to the output. This program also allows me to vary the computational interval independently of the rest of the program, bounded only by the ability of the MPU6050 to produce reliable readings.

After a number of trials, I started getting some reasonable results on my benchtop (hard surface with a thin electrostatic mat), as shown below:

Average turn rate = 89.6 deg/sec

As can be seen in the above plot, the turn rate is controlled pretty well around the 90 deg/sec turn rate, with an average turn rate of 89.6 deg/sec.

The plot below shows the same parameter set, but run on carpet rather than my bench.

Average turn rate = 88.2 deg/sec

Comparing these two plots it is obvious that a lot more motor current is required to make the robot turn on carpet, due to the much higher sideways friction on the wheels.

The next step was to see if the PID parameters for 90 deg/sec would also handle different turn rates. Here are the plots for 45 deg/sec on my benchtop and on carpet:

Average turn rate = 44.7 deg/sec
Average turn rate = 43.8 deg/sec

And then 30 deg/sec on benchtop and carpet

Average turn rate = 29.8 deg/sec
Average turn rate 28.8 deg/sec

It is clear from the above plots that the PID values (5,0.8,0.1) do fairly well for the four wheel robot, both on hard surfaces and carpet.

Having this kind of control over turn rate is pretty nice. I might even be able to do turns by setting the turn rate appropriately and just timing the turn, or even vary the turn rate during the turn. For a long turn (say 180 deg) I could do the first 90-120 at 90 deg/sec, and then do the last 90-60 at 30 deg/sec; might make for a much more precise turn.

All of the above tests were done with a 20 mSec time interval, which is 5x smaller than the current 100mSec time interval used for the master timer in Wall-E2. So, my next set of tests will keep the turn rate constant and slowly increase the time interval to see if I can get back to 100 mSec without any major sacrifice in performance.

28 May 2021 Update:

I went back through the tests using a 100 mSec interval instead of 20 mSec, and was gratified to see that there was very little degradation in performance. The turn performance was a bit more ‘jerky’ than with a 20 mSec interval, but still quite acceptable, and very well controlled, both on the benchtop and carpet surfaces – Yay! Here are some plots to show the performance.

Average turn rate = 29.7 deg/sec
Average turn rate = 28.4 deg/sec
Average turn rate = 44.4 deg/sec
Average turn rate = 43.0 deg/sec
Average turn rate = 89.7 deg/sec
Average turn rate = 86.6 deg/sec

31 May 2021 Update:

I made some additional runs on benchtop and carpet, thinking I might be able to reduce the turn-rate oscillations a bit. I found that by reducing the time interval back to 20 mSec and increase the ‘D’ (differential) parameter. After some tweaking back and forth, I wound up with a PID set of (5, 0.8, 3). Using these parameters, I got the following performance plots.

Average turn rate = 87.3 deg/sec
PID = (5,0.8,3), 20mSec interval, 90 deg/sec

As can be seen in the Excel plot and the movie, the turn performance is much smoother – yay!

Stay tuned!

Frank

New Batteries for Wall-E2

I have been using a set of four Panasonic 18650 LiPo batteries in a 2-cell stack configuration in Wall-E2, my autonomous wall following robot. for a little over three years now, and they are starting to show their age. So, I decided to replace these

with these

Panasonic 18650G-A 3500 mAH 3.7V LiPo battery

Three years younger, and with another 100 mAH (rated, at least).

Here’s a photo record of the process of changing out the batteries

Old batteries still installed (can tell by the green showing through the ‘V5’ cutout)
Old batteries before replacement
New batteries installed in holder

Stay tuned,

Frank

Turn Rate PID Tuning, Part II

Posted 14 May 2021,

In my previous post on this topic, I described my efforts to use the Arduino PID library to manage turns with Wall-E2, my autonomous wall following robot. This post talks about a problem I encountered with the PID library when used in a system that uses an external timing source, like the TIMER5 ISR in my system and a PID input that depends on accurate timing, such as my turn-rate input.

In my autonomous wall-following robot project, I use TIMER5 on the Arduino Mega 2560 to generate an interrupt ever 100 mSec, and update all time-sensitive parameters in the ISR. These include results from all seven VL53L0X ToF distance sensors, the front-mounted LIDAR, and heading information from a MP6050 IMU. This simplifies the software immensely, as now the latest information is available throughout the code, and encapsulates all sensor-related calls to a single routine.

In my initial efforts at turn-rate tuning using the Arduino PID library, I computed the turn rate in the ISR by simply using

This actually worked because, the ISR frequency and the PID::Compute() frequency were more or less the same. However, since the two time intervals are independent of each other there could be a phase shift, which might drift slowly over time. Also, if either timer interval is changed sometime down the road, the system behavior could change dramatically. I thought I had figured out how to handle this issue by moving the turn-rate computation inside the PID::Compute() function block, as shown below

In a typical PID use case, you see code like the following:

After making the above change, I started getting really weird behavior, and all my efforts at PID tuning failed miserably. After a LOT of troubleshooting and head-scratching, I finally figured out what was happening. In the above code configuration, the PID generates a new output value BEFORE the new turn rate is computed, so the PID is always operating on information that is at least 100mSec old – not a good way to run a railroad!

Some of the PID documentation I researched said (or at least implied) that by setting the PID’s sample time to zero using PID::SetSampleTime(0), that Compute() would actually produce a new output value every time it was called. This meant that I could do something like the following:

Great idea, but it didn’t work! After some more troubleshooting and head-scratching, I finally realized that the PID::SetSampleTime() function specifically disallows a value of zero, as it would cause the ‘D’ term to go to infinity – oops! Here’s the relevant code

As can be seen from the above, an argument of zero is simply ignored, and the sample time remains unchanged. When I pointed this out to the developer, he said this was by design, as the ‘ratio’ calculation above would be undefined for an input argument of zero. This is certainly a valid point, but makes it impossible to synch the PID to an external master clock – bummer!

After some more thought, I modified my copy of PID.cpp as follows:

By moving the SampleTime = (unsigned long)NewSampleTime; line out of the ‘if’ block, I can now set the sample time to zero without causing problems with the value of ‘ratio’. Now PID::Compute() will generate a new output value every time it is called, which synchs the PID engine with the program’s master timing source – yay!

I tried out a slightly modified version of this technique on my small 2-wheel robot. The two-wheeler uses an Arduino Uno instead of a Mega, so I didn’t use a TIMER interrupt. Instead I used the ‘elapsedMillisecond’ library and set up an elapsed time of 100 mSec, and also modified the program to turn indefinitely at the desired turn rate in deg/sec.

I experimented with two different methods for controlling the turn rate – a ‘PWM’ method where the wheel motors are pulsed at full speed for a variable pulse width, and a ‘direct’ method where the wheel motor speeds are varied directly to achieve the desired turn rate. I thought the PWM method might work better on a heavier robot for smaller angle turns as there is quite a bit of inertia to overcome, but the ‘direct’ method might be more accurate.

Here’s the code for the ‘direct’ method, where the wheel speeds are varied with

Here’s the code for the PWM method: the only difference is that is the duration of the pulse that is varied, not the wheel speed.

Here’s a short video showing the two-wheel robot doing a spin turn using the PWM technique with a desired turn rate of 90 deg/sec, using PID = (1,0.5,0).

The average turn rate for the entire run was about 85 deg/sec.

Here’s another run, this time on carpet:

Average turn rate for the entire run was about 85 deg/sec

Here’s some data from the ‘direct’ method, on hard flooring

Average turn rate was ~ 85 deg/sec

And on carpet

Average turn rate ~83 deg/sec

So, it appears that either the PWM or ‘direct’ methods are effective in controlling the turn rate, and I don’t really see any huge difference between them. I guess the PWM method might be a little more effective with the 4-wheel robot caused by the wheels having to slide sideways while turning.

Stay Tuned!

Frank

Wall Parallel Find PID Tuning

Posted 10 April 2021

In addition to using PID for homing to its charging station and for turn rate control, Wall-E2 also uses PID for finding the parallel orientation to a nearby wall. After successfully tuning the turn rate and IR Homing PID controllers using the Ziegler-Nichols method for PID tuning, I decided to see what I could do with the PID controller for parallel orientation finding

Wall-E2 uses two 3-element VL53L0X Time-of-Flight distance sensors for parallel orientation finding. The idea is that when all three sensors report the same distance, then the robot must be oriented parallel to the wall. The Teensy 3.5 Array Controller MCU calculates a ‘steering value’ using the expression (shown for the left side array):

This value is fed to the PID engine, which drives the motors to zero it out – thus arriving at a parallel orientation. Originally I just basically ‘winged it’ in choosing PID Kp, Ki & Kd values, arriving emperically at Kp = 200, Ki = 50, Kd = 0. However, after going through the K-N process with Wall-E2’s other two PID control setups, I decided to try it with this one as well.

The first step is to determine Kc, the Kp value for which the system oscillates in a reasonably stable fashion. To accomplish this I started with Kp = 20 and worked my way up in stages, plotting the ‘steering value’ each time. The last three trials (as shown in the following plots) were for Kp = 400, Kp = 500 and Kp = 600:

Looking at the above plots, it looks like Kp = 600 will work for Kc. Using the K-N formula, we get

Using the above values for the Parallel Find PID, we get the following plot:

Which is not exactly what I thought it would be – it looks like my guess for Kc must be off. Trying again with a Kc = 400 –> PID = 200,180,240, we get:

which, to my eye at least, seems a bit better.

To test how this worked with ‘real’ parallel finding, I incorporated these parameter values into my ‘RotateToParallelOrientation()’ routine and ran a couple of tests. Here’s one where Wall-E2 starts in the ‘toed-out’ position:

And here’s the Excel plot from this same run

As can be seen, the robot takes less than two seconds to converge on a pretty decent parallel orientation, starting from a 30-40ยบ angle to the near wall.

Here’s another run where the robot starts in the ‘toed-in’ orientation.

And here’s the Excel plot for the run

Again, the robot gets to a pretty decent parallel orientation within 2 seconds of the start of the run. The only concern I have with this run is that it winds up pretty close to the wall.