# Improving the moteus update rate, part 2

Back in part 1, I looked at the driving factors that limited the update rate of the full quadruped.  Now in part 2, I’ll cover the first half of the solution.

## Background

To begin with, there were two major paths that I could take based around the network topology.  In one path, I would remove the active bridging capability from the junction board, and rely on the Raspberry Pi to drive all the servos directly, and in the other the active bridge would stick around.  There were a number of key disadvantages to both approaches:

Passive bridge: In this model, the raspberry pi has no choice but to rapidly turn around 12 separate queries and responses.  There is no hope for parallelization.

Active bridge: Here, the junction board’s STM32 can offload the multiple queries.  However, there are two big downsides.  The first are that the data must flow across 2 separate 485 busses.  The second, and possibly more problematic, is that it only works out better if the junction board can stream a large amount of data consecutively to the raspberry pi.  In my previous experiments, I had run into what I believe was a kernel bug that killed the serial port until a power cycle upon receive overruns.  Debugging that could easily be a large project.  Implementing the active bridge would also be a lot of work, as I don’t currently have a protocol client that runs on the STM32.

My initial back of the envelope calculations, surprisingly, indicated that both approaches could potentially get up to 250 or 300Hz with sufficient margin to still do other things.  I had expected that parallelization would win the day, but it turned out that duplicating the data across both busses had the potential to completely negate that advantage.

## Solution steps (first half)

### 1. RT_PREEMPT

The first thing I did was to switch to using a RT_PREEMPT enabled kernel with the governors set to performance.  This by itself reduced the Raspberry Pi’s reply to query turnaround from 200us to 100us.  I wanted to at this time also upgrade to the 4.19 kernel, as I hoped that it would have some fixes for high serial rates.  However, it doesn’t look like anyone has a RT enabled 4.19 kernel that supports USB and ethernet, both of which are moderately useful on a board with not many other interfaces.

### 2. “Passive” bridging

In lieu of spinning a new board right away, I instead modified the firmware of the junction board to remove almost all functionality and instead just busy loop shoving individual bytes around between the interfaces.  It is fast enough to just barely be able to achieve this at 3Mbit if interrupts are off.  The downside is that the IMU won’t be usable.  To fix that, I’ll have to spin a new board that just passively wires all the busses together and dangles the STM32 off the bus like any other node and hope that the star topology won’t matter over such short distances.

Doing this removed the double transmission penalty, as well as the additional 90us latency in the junction board on all transfers.  This, combined with RT_PREEMPT, got the overall cycle time down to about 6727us, or ~150Hz.

### 3. Controller turnaround time

Next, I started in on the moteus firmware, in order to improve the turnaround time between when a query is sent, and when the corresponding reply is generated.  This resulted in many optimizations throughout the code.  The first set were made to the primary control ISR, which runs at 30kHz currently.  Tiny improvements here make everything else run much faster.

• Constructors: The ARM gcc toolsuite, regardless of the optimization level, seems to implement a constructor of an all zero structure as a call to memset.  This is true even if the structure is a small number of bytes.  There were several places in the firmware where a structure was zeroed out by calling its default constructor.  Switching that to a “clear” method which manually assigned all the fields drastically reduced the time spent there.
• Appropriate types: When calculating a smoothed velocity, the filter was using an int64_t as an intermediate variable, which is not very efficient on an ARM.  Nothing more than an int32_t was actually needed.
• fmod: I was wrapping between zero and two pi by using fmod.  The cases where wrapping occurs were never more than one or two phases off, so just dividing and truncating was much faster with no appreciable loss of precision.
• Pre-computing some variables: Some of the calculations done in the ISR were repeated unnecessarily, so now they are on re-computed upon a configuration change.
• Make debug output optional: The moteus board has a high rate RS422 debug output, which is only rarely used.  I added a configuration knob to turn it off when not in use.
• SPI overhead: I was using the ST HAL API to access the position sensor over SPI.  I still do that for setup, but now just twiddle the raw registers to do the actual transfer, which saves a little bit of overhead in the HAL.
• SPI frequency: The AS5047P has a nominal SPI frequency limit of 10MHz.  I asked the HAL for 10MHz, but the closest it could actually do was 6MHz.  I decided to brave some flakiness and upped it to 12MHz to shave another microsecond off.

After these changes, the ISR uses around 7-8us when stopped, and around 13us per iteration when in position control mode, or around 40% of the CPU budget.

Next I made more optimizations to the software which runs in the main loop:

• StaticFunction: I was using a home-grown solution to a bounded size type-erased function callback that wasn’t particularly fast.  I switched it out for the SG14 inplace_function from github, modified to allow shrinking to a smaller type.  That sped up everything in the main loop by a fair amount.
• Protocol parsing: A number of steps in the protocol parsing and emitting eventually delegated to a call to “memcpy” into a particular type.  I broke some abstractions so that all that parsing and emitting eventually compiles down to just loads and stores.
• Reply encoding: The RS485 register protocol allows clients to query multiple consecutive registers.  As a shortcut, the server was responding to those with a series of single register replies.  I fixed that by implementing sending multiple replies at once to shave off a few bytes and some processing.

I experimented with using the STM32F446’s built in CRC unit, however it only implements a fixed CRC-32 variant.  So updating that would have required reflashing all my bootloaders.  Using that is probably best taken by updating to an STM32F7 or STM32G4 which have more configurable CRC units.  I also tried hand assembling an optimized version of the CCITT16 checksum I was using, but was only able to achieve the same 8 cycles per byte that boost already had.

With all of these changes, the average turnaround time for a single servo was down from 140us to in the 70-80us range for a full control update.

## Next steps

Coming up, in part 3, I’ll cover the remainder of the steps I took to improve the overall update rate.

# Improving the moteus update rate, part 1

The moteus brushless controller I’ve developed for the force controlled quadruped uses an RS485 based command-response communication protocol.  To complete a full control cycle, the controlling computer needs to send new commands to each servo and read the current state back from each of them.  While I designed the system to be capable of high rate all-system updates, my initial implementation took a lot of shortcuts.  The result being that for all my testing so far, the outgoing update rate has been 100Hz, but state read back from the servos has been more at like 10Hz.  Here I’ll cover my work to get that rate both symmetric, and higher.

In this first post, I’ll cover the existing design and how that drives the update rate limitations.

# Individual contributors

There are many pieces that chain together to determine the overall cycle time.  Here is my best estimate of each.

## RS485 bitrate

The RS485 protocol that I’m using right now runs at 3,000,000 baud half duplex.  That means it can push about 300k bytes per second in one direction or the other.  While the STM32 in the moteus has UARTs capable of going faster than that, control computers that can manage much faster than 3Mbit are rare, so without switching to another transport like ethernet, this is about as good as it will get.

This means that at a minimum, there is a latency associated with all transmissions associated with the amount of data, which is roughly $bytes * 10 / 3000000$.

## Servo turnaround

The RS485 protocol moteus uses allows for unidirectional or bidirectional commands.  In past experiments, all the control commands were sent in a group as unidirectional commands, then the state was queried in a series of separate command-reply sequences.  The firmware of the moteus servo currently takes around 140us from when a command finishes transmission and the corresponding reply is started.  The ideal turnaround for a bare servo is then $(txbytes * 10 / 3000000) + 140us + (rxbytes * 10 / 3000000)$.

## IMU junction board

The current quadruped has a network topology that looks like:

The junction board is an STM32F4 processor that performs active bridging across the RS485 networks and also contains an IMU.  This topology was chosen so that the junction board could query both halves of the quadruped simultaneously, then send a single result back to the host computer.  However, that has not been implemented yet, thus all the junction board does is further increase the latency of a single command.  As implemented now, it adds about 90us of latency, plus the time required to transmit the command and reply packets a second time.  That makes the latency for a single command and reply now: $2 * (txbytes * 10 / 3000000) + 110us + 90us + 2 * (rxbytes * 10 / 3000000)$

## Raspberry pi command transmission

As mentioned, the current system first sends new commands to the servos, then updates their state.  When sending the new commands, the existing implementation makes a separate system call to initiate each servos output packet.  Sometimes the linux kernel groups those together into a single outgoing frame on the wire, but more often than not those commands ended up being separated by 120us of white space.  That adds $12 * 120us$ of additional latency to an overall update frame.  So, $12 * 120us = 1440us$

## Raspberry pi reply to query turnaround

During the phase when all 12 of the servos are being queried, after each query, the raspberry pi needs to receive the response then formulate and send another query.  This currently takes around 200us from when the reply finishes transmission until when the next query hits the wire.  This is some combination of hardware latency, kernel driver latency, and application latency.  It sums up to $200us * 12 = 2400us$

## Packet framing

The RS485 protocol used for moteus has some header and framing bytes, that are an overhead on every single command or response.  This is currently:

• Source ID: 1 byte
• Destination ID: 1 byte
• Payload Size: 1 byte for small things
• Checksum: 2 bytes

That works out to a 7 byte overhead, which in the current formulation applies 12x for the command phase, and 48 times for the query phase.  12x for the raspberry pi sending, 12x for the junction board sending, and 24x for the combined receive side.  That makes a total of $(12 + 48) * 7 = 420 bytes * 10 / 3000000 = 1400us$

## Data encoding

In the current control mode of the servo, a number of different parameters are typically updated every control cycle:

• Target angle
• Target velocity
• Maximum torque
• Feedforward torque
• Proportional control constant
• Not to exceed angle (only used during open loop startup)

The servo protocol allows each of these values to be encoded on the wire as either a 4 byte floating point value, or as a fixed point signed integer of either 4, 2, or 1 bytes.  The current implementation sends all 6 of these values every time as 4 byte floats.  Additionally two bytes are required to denote which parameters are being sent.  That works out to: $((6 parameters * 4 byte float + 2) * 12 servos * 2 for junction board * 10) / 3000000 = 2080us$

The receive side returns the following:

• Current angle
• Current velocity
• Current torque
• Voltage
• Temperature
• Fault code

And in the current implementation all of those are either sent as a 4 byte float, or a 4 byte integer.  That makes $((6 parameters * 4 bytes + 2) * 12 servos * 2 for junction board * 10) / 3000000 = 2080us$

# Overall result

I put together a spreadsheet that let me tweak each of the individual parameters and see how that affected the overall update rate of the system.

I made a dedicated test program and used the oscilloscope to monitor a cycle and roughly verified these results:

Thus, with a full command and query cycle, an update rate of about 80Hz can be achieved with the current system.

Next up, working to make this much better.

# Revisiting machining the sun gear holder

My very first sun gear holder I machined myself was something of an artistic feat.  Each operation was re-run many times, and as a result the part was largely a one-off.  The final part properties were not really indicative of the final program.  My next step in my learning adventure was to up my Pocket NC game and get to a single reproducible program that would emit a part that I could use, then be able to run it over and over again.

The biggest problem I had in making this happen was pull out problems that manifested intermittently, but persistently.  When machining the part in a single operation, the mill needed to be able to reach all the way to, and slightly past the center of rotation of the B axis of the machine.  With the Z travel of the Pocket NC, that means that you need a stickout of almost 27mm or sometimes even more.  Standard length tools can kind of do that, but they don’t result in much of the shank being in the collet.  All my testing with them resulted in occasional pull out at some point during the hour or two of machining.

I tried getting a Kyocera 2 inch endmill 2 flute end mill, which has a minimum stickout of about 34mm.  To run it without mind numbing chatter required dialing the feeds and speeds so far back that the part would take twice as long to complete.  That hardly seemed worth it.

Next, I stepped back and decided to try a 2 operation approach using the Sherline 4 jaw chuck.  This has the advantage that the part is always kept well within the Z travel of the mill, so that standard length end mills can be used, but the disadvantage that you have to manually flip the part over and try to keep things registered.  I don’t have great, or really any, metrology that would allow me to measure the resulting concentricity errors so I was really trying to avoid using this approach, however, I was kind of at a loss at this point so decided to give it a go.

For this configuration, I used a 1.5″ round stock cut to approximately 1″ long.  The first operation used a Datron 3mm end mill to do nearly everything on the back side, with a final chamfer mill pass to break the edges.  The second operation used a Datron 2mm end mill to do everything there, with the chamfer mill once again to do the countersinks and break edges.

The parts that come off here are usable, and about what I expected to achieve without heroics in terms of final accuracy from a Pocket NC.  All the diameters and dimensions are around at most a thousandth off from what I intended.  The walls aren’t as vertical as I would like, but they are serviceable.

Granted, I probably won’t be using these parts for much of anything going forward, but it was a great learning experience in making the Pocket NC do what I wanted.  Next in this adventure is probably machining a planet input, which I forsee continuing to use in future iterations of the gearbox.

To demonstrate the dynamic capability of the full rotation quadruped, I figured I would start by doing some full machine jump tests to a relatively low height, just to show that it was capable.

Thus, I rigged up an open loop script which squatted a small fraction of the available distance, and then powered up at a relatively small fraction of the available maximum speed.  I don’t have the telemetry yet to extrapolate how high this will be able to go at maximum, but I think it should be a fair amount higher.  For now, I want to do some more instrumentation and walking testing (and have more spares) before I manage to break things by jumping really high.

# First walking on the full rotation quad

Last time, I had finished physically assembling all the motors for the updated quadruped with legs that can rotate freely 360 degrees.  After the long summer break, I powered up and configured all the servos.  Then, after setting up the gait engine for the new configuration (for which there are still a TODOs when the lateral shoulder offset is non-trivial as in this configuration), I was able to achieve some amount of walking.  Here is one of the first videos I took, without much in the way of tuning or work.  The control is a little wobbly still, but so far there are no signs of any mechanical failures as with the older design.

# Full rotation quadruped build continued

The next step in (re-)building the quadruped with a full rotation leg was getting all the motors ready.  I had to first install reinforcing rings on 6 of the motors:

Then, I needed to lengthen the power leads on 3 of the motors to serve as the lower leg joint.

Then I had to assemble all the new legs:

I mounted them all to the chassis:

And then re-installed the battery stud and “resting” feet:

Next up, will be actually powering them and getting it to walk!

# Fusion 360 3D adaptive and thin walls

I have been trying to improve the tool paths for the BE8108 gearbox sun gear holder.  The first time, I ended up slowing things down a lot and actually took some of the initial adaptive passes in several iterations as I fixed problems, so it wasn’t clear that any one iteration would be functional from start to finish.

So,  I tried it on a fresh piece of stock, with settings that I thought would resolve the pullout issues I had seen earlier.  Lo and behold, what did I find but more pullout!  It appeared to happen in exactly the same situations as before.  The adaptive clearance would leave a thin sliver of material, then “round it off” very rapidly, resulting in a large chunk of sliver hitting the mill at once.  Increasing the minimum cutting radius and tolerance helped reduce the problems some, but didn’t get rid of them entirely.

Eventually, I managed to find this thread on the Fusion 360 forum where others were having exactly the same issue.  From there, I discovered there is an undocumented parameter, only available in the “Compare and Edit” screen called “Curve in Radius”.  By default, it is calculated based on the tool diameter, but I found that I could add a random fudge factor of approximately 1mm to whatever the calculated version was and boom, the thin slivers were handled in a much more appropriate way and the tool never rolled over them until the sharp point was gone.

I still have more bugs to work out of this toolpath, but at least that’s one down.

# Machining a sun gear holder on the Pocket NC v2-50

After doing my first cuts in aluminum, I wanted to actually try and make a real part, to prove that the Pocket NC v2-50 was capable of making things that I can actually use.  My first attempt, was the same part I did my first aluminum cuts in, the sun gear holder from the BE8108 planetary gearbox.

This part attaches to the rotor, the sun gear, the position sense magnet, and has bearing interfaces to the planet input and the back housing.  While not terribly big, the number of features and mating surfaces is relatively large.

First, before we start, here’s a video montage of all the machining, apologies for the bad lighting and focus:

Second, be warned that this is a *long* post!

## Programming

As mentioned earlier, I decided to try an approach that would allow the entire part to be machined from a single piece of stock and a single setup.  That is one of the advantages of the 5 axis pocket NC.  Even for parts like this, the 3+2 indexing means that I can access all the faces in one go.  Then to finish it off, at the end I left a thin tab which would be broken by hand and filed away.

Before I get into the details, let me be clear, that this is the first real part I have ever programmed for a CNC and there is a lot I am likely doing in a sub-optimal, if not terrible way.  I won’t document all future parts in this much depth, but writing out my thoughts for this one will, if nothing else, give me a reference to come back to as I find my footing.

I marked all the individual tool paths with an initial bolded number (N:) so that you can correlate them to the video if you like.

## Setup

I used 1 x 1.5″ rectangular 6061 stock cut to 2″ in length (so it came approximately 2.125″).  This was placed into the Pocket NC vise longwise, so that the 1″ side just about exactly matches the vise’s set screws.

This part may have *just barely* fit into a 0.75″ x 1.5″ stock, but I didn’t want to risk having so little margin on my first attempt.  I originally chose this orientation because I thought that it would be easier to center by hand, although upon reflection putting the long side against the set screws would allow me to use the narrower 0.75″ stock.

I don’t yet have a reasonable means of cutting stock here, so just had Speedy Metals ship it cut to the correct length.

## Back side roughing

1: To start with, I used the 3mm Datron single flute endmill that Pocket NC sells for the bulk roughing of the back side.  The majority of the roughing for that side was accomplished using a single adaptive clear.  The final path looked like:

I started out with the speeds and feeds I had used last time, derived from Ed Kramer’s posts.  Since I was using a 3mm instead of a 2mm end mill, I just upped the stepover to 0.3mm initially and left everything else the same.

This was definitely the tool path that I had the most trouble getting to work — I had a couple of different problems.

### First problem – tool pullout

To start with, this tool path worked just fine, if a bit slow.  However, once the path got into more complicated geometry, I had what I eventually diagnosed as a tool pull out event that manifested as the spindle stalling out.  It looked like the tool pulled out about 0.2 – 0.4mm, and then started just hogging through uncut material.  This both caused a defect in the surface and then the spindle to stall out.

My debugging of this problem was complicated by a different issue.  I had not really done any math to select a proper stickout for the tool for this path.  Slightly past the point of pull-out, with my initial 20mm stickout, the machine faulted because on the next pass the Z axis travel limit would have been reached.  However, the fault was so far removed from the program point where the limit would have been reached, I initially assumed it had just pulled out again.  Thus, I made a lot of tweaks, none of which fixed the stickout problem, and some of which may have fixed the pullout problem.

Ultimately, I now believe that the pullout problem was likely caused by the “Tolerance” parameter of the 3D adaptive clearing path in Fusion 360.  My initial settings for this were 0.1mm or 0.15mm of tolerance and 0.3mm optimal load, which I figured would result in a usable amount of error.  However, I hadn’t expected geometries like this:

When the tolerance was set too large, the tool “cut off” that long piece of material, going from an optimal level of engagement to nearly 100% engagement in a heartbeat.  The v2-50’s NSK spindle doesn’t have a lot of clamping force on the tool, so that drastic change in load must have pulled it out.

Despite that as my ultimate conclusion, I still ran all the rest of my paths a bit slower at 30k rpm 600mm/min and a stepover slightly under 10%, so 0.25mm for the 3mm endmill because that was what I did my final testing for this problem with.  I wanted to get a part in this round, not necessarily optimize speeds and feeds.

### Second problem – Z axis travel

As I mentioned above, the second problem I had here was that the Pocket NC Z axis travel was insufficient to reach past approximately the midpoint of my part with a 20mm stickout.  In fact, I had to increase the stickout all the way up to 28mm before I could complete this toolpath.  I didn’t experience a whole lot more chatter after that change, and my surface finish was equally mediocre before and after, so it was probably reasonable at least for cuts that were this gentle.

## The rest of the backside

The remaining pieces of the backside were:

• The central cavity
• The planet input bearing interface
• The back most visible surface
• The “bearing lip” for the planet input bearing
• The back surface that mates against the rotor

2: First, let’s tackle the central cavity.  I roughed out the cavity with a bore toolpath.  I used a relatively small initial bore diameter, then did multiple stepovers to reach the desired diameter.  I later realized this was moderately inefficient, and I should just do a bore of a single diameter and let adaptive clearing handle the rest, but I didn’t bother updating this path with that strategy.

I was pretty apprehensive running this boring operation, as clearing out a cavity is what broke my first endmill.  I ran this at a very conservative 0.2mm depth per turn, and at 30k rpm with 400mm/min cutter speed.  Also, I discovered that I needed to manually set the top height a bit above the start of the cut to get the cutter to actually start its helix before engaging.  With the default 0mm offset, it ended up plunging straight in and sounded terrible.

Even with the helix moving before engaging, it still sounded pretty bad, but did make it through.  I guess using an actual drill is probably the only way to achieve this depth of cavity on the Pocket NC without having significant chatter.  Fortunately, I didn’t really care about the surface finish, and the chatter didn’t seem bad enough to break anything so I just ran with it.

3: Then I used a 2D face to finish the back most surface:

4: Then I used a 3d adaptive to finish clearing out the bulk material from the central cavity.  This is what I should have just let remove all the material after the initial bore:

5: Next I used a 3D parallel to finish off the rotor mounting surface.  This was a little bit tricky, as at this point in the program I have the beginnings of a “tab” beneath the part that I don’t want to dig into.  Thus I defined a boundary as a sketch in the model for all the things that I wanted to keep away from that tab.  Here I used used it as the “Machining Boundary” with the “Tool inside boundary” option and added a random additional offset set to make the simulation not get into the tab.

6/7: Finally, for the back side, I used two 2D circular paths to get the planet input bearing interface, and the “lip” behind it, to their proper diameter.  I used a 3mm stepdown for both, although for the best finish the 3mm endmill is long enough I should have just done it in a single cut.  I also used the “Wear” tool offset compensation, so that I could enter small offsets into the Pocket NC tool diameter to dial in the exact dimensions I wanted.

## The front side

8: For the front side, I initially did as much as possible as I could do with the 3mm end mill.  So, that started with a roughing of the non-cavity pieces.  This used identical parameters to what I settled on for the back side roughing.

9: Then I used a 2D face to finish the front most surface.  This is both ways and has a 0.5mm stepover.

10: I used a bore path once again to carve out an initial hole in the front cavity up until the narrower part of the hole.  For this one, I also used a few stepovers, here spaced at 0.25mm and also manually set the top height 0.5mm above the hole.

11: After that, I used another bore to get the narrower hole in the middle.

12: A 2D parallel finished up the front surface.  Nothing actually mounts to this, but I figured I might as well make it flat.  As with the facing, a 0.5mm stepover was used.  I ended up just selecting everything but the back flat face as a “Avoid/Touch Surface” set to avoid to keep the path from trying to get there.  I also didn’t do anything special to keep this away from the tab, which resulting in it digging a bit into that region.  I was mostly worried about engaging the full width of the cutter, but it didn’t cause too big of a problem, and the 2d parallel path did that a bit anyways as it worked its way around the central post.

13/14: The last two things that I did with the 3mm flute were finishing the interface to the rotor bearing, and the bearing lip using 2D circular paths.

## Front side – 2mm end mill

The other features I needed to do on the front side were all too small to complete with the 3mm end mill, so I switched to a 2mm single flute Datron with 5mm long flutes for the subsequent operations.

15: First, bores opened up the four bolt mounting holes that connect the part to the rotor.  For these, I used just a single boring pass, but manually set the height to be a bit above and below the part.  Above so that the helix would start before it engaged, and below so that I’d be sure to actually get all the way through the part.

16: A 3D adaptive removed most of the remaining material in the front central cavity:

17: And a 2D contour removed the final bit of material:

18: The last thing I did in this tool setup was to clear away the remaining outside material that was above the tab, as all the “heavy” cutting is done.  This was done with a single 3D adaptive clearing, with the depth of cut set to 3.5mm so it finished in a single stepdown.

## Chamfer milling

Now that almost all the material is removed, I switched to a 90 degree chamfer mill.  This I just got from Shars, part 416-3509.  I wanted to break all the sharp edges I could and also needed to cut the countersinks for the mounting bolts.

19: First, the front chamfers.  I used a 2D contour with 0.25mm of “Chamfer Width” and 0.25mm of “Chamfer Tip Offset”:

20: Then, while still on the front side, I did the countersinks.  For these I also used the 2D Contour toolpath.  I used as many stepovers at 0.2mm as I could fit in, which worked out to 7 in this case.  Since these chamfers were modeled, I left the “Chamfer Width” and “Chamfer Tip Offset” at 0mm.  In hindsight, I probably wanted some tip offset still, as it left a tiny flat at the start of the chamfer.

21: The last thing I did with the chamfer mill was the back chamfers, which were done identically to the front ones, just with a different “Tool Orientation” selected to come from the back side.

## Finishing up and parting off

22: At this point, I switched back to the 2mm end mill from before with the same stickout to get ready for parting.  First, I used a careful selection of boundary to remove all the material laterally, except for the final parting tab with a 3D adaptive.  Because this is reaching in far, and the shank of the tool would otherwise rub, it spends some time carving away at the base of the stock to give room for the shank to reach in.

23: Now, as much of the outer surface is exposed as is going to be exposed, so this is where I stuck my finish pass for that surface.  I used a 3D contour, which let me restrict its operation using the same machining boundary I used before so that it didn’t run itself into the tab.

24: And finally, I used a 3D adaptive to clear away all but about 0.2mm of the remaining material in the tab.  The selection of tab width was one I was concerned about, never having done this before.  Too thick, and breaking it off would be annoying.  Too thin, and the part might fall off while the mill is still engaged and spinning.  In the end, the 0.2mm was totally reasonable to bend off by hand, but still seemed relatively well fixed as the mill worked under it.

## The final result!

Some pictures of the final part:

Clearly, there are plenty of areas for improvement.

Surface finish: As seen in the close ups, the surface finish, is at best, “mediocre”.  I didn’t really try to optimize this, but did try to avoid obviously loud chatter.  Perhaps I can run some of the paths with the same tools but at smaller stickouts, or perhaps tweak the RPM to find areas of stability to improve things?  Also, my choice of finishing paths seems to have left a lot of circular swirls on the front side.  I should probably switch to something circular there instead of linear so that the center stud doesn’t cause so much heartache.

Back cavity finish: In this iteration, I didn’t actually do a finish pass on the interior surface of the back cavity.  This is a precision surface that the sun gear fits into, so I definitely need to do that and tune it in.

Accuracy: The bearing surfaces’ accuracy is probably best described as “meh”.  I tweaked the tool diameter offset until I could fit the bearings on, but the difference between “won’t fit on at all because it binds near the base” and “fits pretty loosely around the whole thing” was not really controllable.  There was a moderate taper between the near end of each surface and the far end, despite doing several “spring” passes.  I suspect this is just an inherent limitation of the Pocket NC.  I believe it only has steps for 0.0002″ in position and isn’t that rigid either, so the tool deflection is pretty measurable.  Probably the best I could do would be to run those paths with a smaller stickout tool in the hopes of minimizing deflection based error.

Single run: I did this first part executing each tool path singly with my hand on the estop the whole time.  After I get the back cavity bore path working, I’d like to try one with the only intervention being at tool changes.

## Summary

Wow.  This was significantly more involved than I expected, although most of that was due to debugging the tool pull out and the Z axis limit problems.  Even still, I spent a good week getting the tool paths programmed and tested.  I hope that with practice, I’ll get to where I can turn these around more quickly.  Especially since this was the “easy” part I had!

# Stripping the coaxial quadruped for parts

To switch to the full rotation gear design, I needed to get all my gearbox motors, some bearings, and a lot of other bits and pieces disassembled and ready for re-use.

Taking everything apart took a surprising amount of time, nearly a full day.  Each leg resulted in quite a collection of fasteners.  Seeing them all in one place made me realize how complex this has become!

I’ve also got the full set of parts printed for the full rotation legs:

Now I just need to get to assembling and reworking to get them all installed!

# First assembled full rotation leg

As I described earlier, the first draft brushless quadruped leg design was insufficiently robust for the gearbox driven motors and I am updating it to a geometry that allows full rotation.  I’ve made at least some progress on that front, so here is an intermediate report.

First, after doing some analysis, it appeared that the 3mm pitch 6mm wide belt was unlikely to be able to carry the full torque from the motors.  So I’ve switched to a 5mm pitch 15mm wide belt, which while still unable to carry the full torque indefinitely is only a factor of 2 or 3 off instead of a factor of 20 off.  Secondly, I added a bearing opposite the upper pulley so that it is supported from both sides.  The recommended belt tension for this belt works out to something like 120lb, which is a fair amount of cantilevering, even over the 16mm wide pulley.  The updated CAD looks like:

And the newly added bearing can be seen in this section view:

I did a first test print of all these parts and put them together.  While there were a few tweaks necessary for the second revision, it looks like this leg is probably usable.

Next up is building 3 more of them!