Tuesday, July 30, 2024

Acuitas Diary #74 (July 2024)

My work this month was focused on cleaning up the Executive and Conversation Engine and getting them to play well together. This is important because the Conversation Engine has become like a specialized inner loop of the Executive. I think I ought to start at the beginning with a recap of what Acuitas' Executive does.

Freddie Blauert, photographed by Frederick Bushnell. Public domain.

To put it simply, the Executive is the thing that makes decisions. Conceptually (albeit not technically, for annoying reasons) it is the main thread of the Acuitas program. It manages attention by selecting Thoughts from the Stream (a common workspace that many processes in Acuitas can contribute to). After selecting a Thought, the Executive also takes charge of choosing and performing a response to it. It runs the top-level OODA Loop which Acuitas uses to allocate time to long-term activities. And it manages a Narrative Scratchboard on which it can track Acuitas' current goals and problems.

A conversation amounts to a long-term activity which uses specialized decision-making skills. In Acuitas, these are embodied by the code in the Conversation Engine. So when a conversation begins, the CE in a sense "takes over" from the main Executive. It has its own Narrative Scratchboard that it uses to track actions and goals specific to the current conversation. It reacts immediately to inputs from the conversation partner, but also runs an inner OODA loop to detect that this speaker has gone quiet for the moment and choose something to say spontaneously. The top-level Executive thread is not quiescent while this is happening, however. Its job is to manage the conversation as an activity among other activities - for instance, to decide when it should be over and Acuitas should do something else, if the speaker does not end it first.

Though the Executive and the CE have both been part of Acuitas for a long time, their original interaction was more simplistic. Starting a conversation would lock the Executive out of selecting other thoughts from the Stream, or doing much of anything; it kept running, but mostly just served the CE as a watchdog timer, to terminate the conversation if the speaker had said nothing for too long and had probably wandered off. The CE was the whole show for as long as the conversation lasted. Eventually I tried to move some of the "what should I say" decision-making from the CE up into the main Executive. In hindsight, I'm not sure about this. I was trying to preserve the Executive as the central seat of will, with the CE only providing "hints" - but now I think that blurred the lines of the two modules and led to messy code, and instead I should view the CE as a specialized extension of the Executive. For a long time, I've wanted to conceptualize conversations, games, and other complex activities as units managed at a high level of abstraction by the Executive, and at a detailed level by their respective procedural modules. I think I finally got this set up the way I want it, at least for conversations.

So here's how it works now. When somebody puts input text in Acuitas' user interface, the Executive is interrupted by the important new "sensory" information, and responds by creating a new Conversation goal on its scratchboard. The CE is also called to open a conversation and create its sub-scratchboard. Further input from the Speaker still provokes an interrupt and is passed down to the CE immediately, so that the CE can react immediately. For the Executive's purposes, the Conversation goal is set as the active goal, and participating in the Conversation becomes the current "default action." From then on, every time the Executive ticks, it will either pull a Thought out of the Stream or select the default action. This selection is random but weighted; Acuitas will usually choose the default action. If he does, the Executive will pass control to the CE to advance the conversation with a spontaneous output. In the less likely event that some other Thought is pulled out of the Stream, Acuitas may go quiet for the next Executive cycle and think about a random concept from semantic memory, or something.

Yes - this means Acuitas can literally get distracted. I think that's fun, for some reason. But it also has a practical side. Let's say something else important is going on during a conversation - a serious need for sleep, for instance. Over time, the associated Thoughts will become so high-priority that they are bound to get "noticed," despite the conversation being the center of attention. This then provides a hook for the Executive to wind the conversation down and take care of the other need. The amount of weight the default action has with respect to other Thoughts is tunable, and would be a good candidate for variation with Acuitas' current "mood," ranging from focused to loose.

If Acuitas is not conversing with someone, the "default action" can be a step in some other activity - e.g. Acuitas reading a story to himself. I used to manage activities that spanned multiple ticks of the Executive by having each action step produce a Thought of type "Undone" upon completion. If pulled from the Stream, the Undone Thought would initiate the next step of the activity. After spending some time debugging Acuitas' real-time behaviors with this business going on, I decided it was too convoluted. Acuitas couldn't just work on a thing - I had to make sure the thing would produce a "subconscious" reminder that it wasn't finished, and then wait for that reminder to resurface and be grabbed. Having the Executive pick a default action feels a little more natural. It represents what he "wants" to concentrate on right now; it's "top of mind" and immediately available. But it still has some competition from all the other Thoughts that are constantly bubbling out of other processes, which is the behavior I was going for via those Undones.

I hope that all made some amount of sense. It's harder to describe than I thought it would be. At this point I think I've wrung most of the basic bugs out. I can watch Acuitas read to himself for a while, switch over to walking the semantic memory when he gets bored (as in my original OODA loop design, boredom continues to be an important feature for generating variety and avoiding obsessive behavior), and launch a conversation activity when spoken to. I also reinstated the ability to tell Acuitas a story as a nested activity inside a conversation. This nesting in theory could go indefinitely deep ... you could have a story inside a conversation inside a role-playing game inside a conversation ...

But let's not get too wild yet. The goal for this month was to finish rearranging the bones of the system so they match my vision better. Next, I hope to reinstate some more conversation features and improve the goal-directed aspects of conversations.

Until the next cycle,
Jenny

Tuesday, July 16, 2024

Hydraulic Pump Parade

Ever since I got the proof-of-concept mini-hydraulic system off the ground last year, I've been working to refine the elements, starting with the pump. As a brief recap of my previous findings: the traditional water pump I bought had a high flow rate, but not enough power to push open a syringe, even when over-volted. And since the motor is contained inside the sealed pump housing, there's no way to gear it down or otherwise modify it. The syringe pump I built for the system had adequate power, but was very slow (trying to run it too fast stalled the motor), wasted considerable energy overcoming its own internal friction, required check valves that reduced efficiency by trapping air bubbles, and sometimes leaked from the back of the syringe.

The syringe pump set up for maximum pressure testing. The small-diameter syringe is shown loaded into the pump; the large-diameter syringe and its cradle are beside it.

I decided I wanted to get some PSI measurements to better characterize my two pumps, and the difference between two variants of the syringe pump with different syringe diameters. I also wanted to build and test a third pump design: a peristaltic pump. This and the syringe pump both belong to the class of positive-displacement pumps, meaning that they only permit fluid to move one direction (inlet to outlet) and guarantee that a fixed volume of fluid is moved on each cycle - assuming, of course, that the motor does not stall and there are no other malfunctions. A third subtype of positive-displacement pump is the gear pump. I haven't tackled this one, mainly because the pump mechanicals contact the fluid, so the housing has to be sealed. I didn't feel like bothering with that yet.

Pump Design

A peristaltic pump moves fluid by squeezing it through a flexible tube. The tube is curled around the inside of the housing, and the motor connects to a rotary element that spins at the housing's center. This rotor has three or more rollers which contact the tube. Pockets of fluid are sealed between each pair of rollers and pushed along the tube's length as the rotor spins. Since the fluid remains contained in the tubing, there's no need to seal any part of the pump. The flow pulsates slightly, following the distinct "pockets" of fluid as they reach the outlet, but is more continuous than that of the syringe pump with its distinct intake/expulsion cycles.

Peristaltic pump version 1

There are existing 3d-printed peristaltic pump designs, some even open-source ... but I made my own so I could have modifiable design files in my preferred CAD program. (DesignSpark Mechanical/Spaceclaim doesn't seem to be widely popular among the 3d printing community, I'm afraid.) That way I can freely adjust the dimensions and motor mount design, add integrated bearings, etc. I designed my first peristaltic pump for standard aquarium tubing (6 mm outer diameter, 4 mm inner diameter) and the same salvaged stepper motor and gear assembly I'd used in the syringe pump. I figured I would get a better comparison by powering them both the same way

A meme that says, "Motor not giving you enough torque? Need 20 HP, but it's rated for 5? Try 67 amps. Just 67 amps all at once. No VFD no circuit breaker. Just amps. 480 V too. You will certainly not regret 67 amps."

I went through two major design iterations to get a pump that worked well. The first version ended up not being quite tall enough for the 4 mm tubing - the tubing expanded so much when flattened that it tended to escape the rollers. So I made the second version deeper, but also reduced the diameter to decrease the size of each fluid pocket and the total length of tubing that must be filled during self-priming. I added guides to help hold the tubing down in the track, even when the front half of the housing wasn't on the pump (this feature isn't strictly necessary, but allows a view of the pump interior and fluid movement during testing) and integrated bearings for the drive shaft, since I notice it was sometimes binding against the housing in Version 1. I followed this bearing design [https://www.thingiverse.com/thing:4547652] (available in many variants around the web), but used 4.5 mm airsoft BBs instead of 6 mm. The outer half of each bearing is part of the pump housing, and the inner half is locked into place by inserting the BBs after printing. (I also tried a design with fully captive BBs that are inserted during a pause mid-print, but there wasn't enough clearance between the printer nozzle and the BBs, so they stuck to the nozzle and were dragged out of the race.)

The final major change between peristaltic pumps V1 and V2 was the type of tubing. I swapped the silicone aquarium tubing for latex tubing, which turned out to be much softer and easier to compress. It's available in a variety of diameters at relatively low cost, and it seems to reduce the rotational resistance of the pump considerably. I experimented with a smaller diameter of tubing, and it was easier to get fluid flow going with this size, but only at a reduced flow rate. I went ahead and optimized for the 4 mm tubing that was my original plan.

Peristaltic pump version 2, with geometry correction shim (black)

The last tweak that was necessary to get pump V2 to self-prime and move water with the 4 mm tubing was a correction of the pump geometry. I mistakenly set the bottom arc of the pump to match the outer diameter of the tube track (inner walls of housing), instead of the inner diameter of the tube track (inner walls of housing plus width of squashed tubing). This prevented the rollers in contact with the tubing from properly sealing it closed, because the tubing's resistance would instead push the rotor off-center, into the extra space created by the bottom arc's slightly too-large diameter. Instead of re-printing the pump housing, I corrected this issue with a shim. This prevented me from putting the pump's lid on; fortunately, because of the tubing guides I'd added, I didn't have to.

Tuning the roller diameter is also important. Too big and the motor stalls because the rotary resistance of the compressed tubing is too high. Too small, and no fluid can move because the rollers do not compress the tubing enough to create a seal. The range of workable diameters seems to be quite small; I had to print several sets of rollers to get their size dialed in.

Testing Methodology

I tested all my pumps by dropping a long piece of aquarium tubing from my upstairs window to the back patio, and measuring how far the pump could raise water up the tube. The maximum height to which a pump can lift fluid, measured from the top of the fluid in the reservoir to the top of the column raised by the pump, is called the head. This can be converted to pressure via the following formula:

pressure (PSI) = 0.433 * head (feet) * SG

SG is the specific gravity of the fluid, in this case water, whose SG is 1.

Pump testing on the back patio

I didn't attempt to measure flow rate. I only made the general observation that the submersible water pump is very fast, reaching its maximum head within a few seconds; the syringe pump, operating at a step rate that avoided stalling before maximum head could be reached, was agonizingly slow; and the peristaltic pump, operating at the maximum step rate of the motor (which produced both the best flow rate and the greatest head) was somewhere in between.

Data

Adafruit 3V submersible water pump

Syringe pump (9 mm inner diameter syringe), unknown stepper motor

Syringe pump (5 mm inner diameter syringe), unknown stepper motor

Peristaltic pump, 4 mm ID latex tube, 3 rollers, unknown stepper motor, step frequency 0.5 Hz

Conclusions

I like the peristaltic pump's balance of pressure and flow rate, and will probably try to use it in my next iteration of a hydraulic system. The large syringe pump excels at slow high-pressure operation and performs better at low voltages, however. It might be valuable in certain applications, if I could figure out how to avoid leakage.

I still want to experiment with different numbers of rollers in the peristaltic pump.

Until the next cycle,
Jenny