I'm gearing up (literally) to give ACE another try. As of the last update, I had concluded the original set of stepper motors I bought wouldn't work out, and identified some new ones to try. I bought some samples, made a prototype gearbox to hold one, and tried another motion test. This time I was able to get over the basic threshold of operating the hock joint, unloaded (see video).
Since then I've done a small redesign of the shoulder joint, trying to relax the path of the hock joint tendons a little (they run through the inside of the shoulder) and provide for smoother rotation of the shoulder by adding low-friction sheet plastic surface.
I've also completely redesigned the frame. Since the frame is mostly made out of the gearboxes that hold the motors, new motors meant changes. I created mirrored versions of each type of gearbox so that the main drive shaft that the tendons attach to would be in the correct place (relative to the joints) on each side of the robot, and I designed spine attachment pieces so there's no longer any need to incorporate a dowel. The new spine sockets have holes that will line up with the spine's mounting holes and keep everything at a fixed angle. The simple gearboxes for the shoulder joints are on top, and the higher-torque gearboxes for the hock joints are on the bottom, which also lines them up better with the place where the hock tendons emerge from the shoulder.
Almost everything is made, but not everything is assembled. I wish I had more conclusive results to report, but this has been a whirlwind couple of months, so for now I'll just have to say: I'm working on it!
Recent work has been a lot of returning to projects I started this year, in order to actually finish them. I completed the feature additions I'd planned for the first half of the year quite early, and that seemed like a good point to pause and go back to things I ran out of time to flesh out.
First, the Text Parser. I had largely revamped conjunction handling to add support for lists of more than two items, and that left a lot of two-item cases broken. I worked on fixing bugs in the new methods and improving them enough to get that old functionality back, and eventually got my parser regression to pass again. Then I modified the Text Interpreter to deal with some of the formatting changes to the Parser's output, and lastly verified that Narrative comprehension was still working on Acuitas' story collection. It ran quite a bit beyond my initial 20-hour investment in the revamp, but the latest Parser is now fully integrated and serving the same functions the old one did.
Next, I revisited self-teaching. This feature was supposed to permit Acuitas to crawl the hard drive of his resident computer and read whatever text files he could find, to promote increased vocabulary and assessment of text processing functionality. Back in February I had gotten the basic activity loop working, but had no way to keep Acuitas from reading inappropriate files that *didn't* contain natural English and junking up his database. So I spent some time on that problem, and came up with ways to assess whether a text file is good reading or not. Signs of an inappropriate file include 1) too many non-alphanumeric characters, 2) too many one-word "sentences," and 3) too many Parser or Interpreter failures. The first two get checked before Acuitas even tries to "read" the file; the third is continuously monitored during reading, and is based on a running average of sentences processed so far. After some tweaking of the failure thresholds, I saw this perform well enough that I felt comfortable letting Acuitas go to town on my random personal notes, short story drafts, and other hard drive clutter. His list of known words has roughly doubled, compared to a backup archive I made in November.
Last of all, I worked on Episodic Memory I mainly spent time reconstructing the "forgetting" process for the new Narrative-based memory formatting scheme. Once the consolidation algorithm has created higher-tier memories that summarize a lot of individual incidents, it's time to reduce the size of the memory file by pruning some of the details. I used a lot of the same concepts from my original forgetting algorithm: memories are ranked by novelty (is this the first time something happened?), uniqueness (how many other memories are similar to this one?), and tier rank (general, summarized memories are more likely to be kept than detailed, individual ones). Leveraging the interpretive power of the Narrative system, I also added the priority of any associated problems/subgoals as a scoring mechanism. The lowest-ranked memories are deleted, hollowing out the narrative but (hopefully) preserving the most salient events and summaries of the more generic ones. Simulations show this working reasonably well on synthesized test files. I also worked on a simple indexing system to make it easier for Acuitas to look up memories by the type of event or merely by a concept involved.
Example diagrams showing the "hollowing out" of the narrative structure as detailed memories are forgotten. Their summarizing memories in the upper tiers remain.
It's been satisfying to be more thorough about these items and not leave them full of loose ends! I probably have some more Episodic work to do before I really call it finished, and then I'd like to polish Trial-and-Error learning some more.
Wait, I thought this got finished? Yes, so this year it was time to build a second, better one. I didn't change the mechanical design much; it has some minor improvements I had already worked into the 3D models after finishing the last one, but it's the same in substance. What I really wanted was a chance to try out one or two of my new cameras. Given the way the eye is assembled, replacing the camera is a fairly big deal. So instead of pulling apart the mostly-functional eye I already had, I made a whole new one. This has the added benefit of giving me a demonstration model that I can take places, while I leave the flagship one parked on my living room wall.
The original eyeball's camera is a "300K pixel USB 2.0 mini webcam," like this one. At $7-$9, they're cheap enough to buy in bulk, AND they come in a small enough case to fit comfortably inside the eyeballs. Unfortunately, the image quality you get for that price seems pretty poor. The low resolution isn't the real issue (there's no need for megapixels when you're only doing basic motion detection). It's that the output seems just plain noisy, especially under lower-light conditions. I also wanted to try a wider field of view. It's very easy for the existing eyeball to completely miss a person in the room because it's all the way over on one side of its range of motion staring at the side wall, or even because the person walks through its visual range too fast.
My two new cameras are an ELP-USB30W02M-MHV120 (120 degree FOV, 640x480 pixels, $28 now but I got it for $14) and another ELP model with a fisheye lens that seems to no longer be available (170 degree FOV, 1080P, $27.50 - this one is similar but more expensive.). They're both what I want to call "card cameras," consisting of a naked square circuit board with the lens sticking out of one side and a cord coming out the other. In that sense they're similar to the card cameras sold specifically for use with Raspberry Pis, but they have USB cables instead of short ribbon cables - important for compatibility with the eyeball's interior layout. It also made the cameras easier to try out with my laptop when I first got them.
And in initial tests, the output from both seemed significantly cleaner. I could run background subtraction without seeing a whole lot of imagined "motion patches" created by especially noisy regions of the image. Without as many noise problems to deal with, I was able to reduce the OpenCV MOG2 background subtraction algorithm's "history" parameter and get rid of the running average I was applying to my computed center of motion value. This made the motion tracking less laggy and more responsive.
Printing and assembling a whole new eyeball from scratch took much longer than I'd anticipated. I added features to the previous one gradually, and that left me with a poor grasp of how complex the build is when started from the beginning. Each motor now has a gear pair for a little more torque, which is helpful at the edges of the range of motion. A small downside is that they seem to make the eyeball noisier.
It was all so much work that I still haven't had time to wire and install the limit switches! So the new eyeball is operating by "dead reckoning" of where it's pointing at the moment. But I got enough of it put together to test the motion tracking, and I think it's finally working reliably! It certainly performs better than the original. I was even able to activate the up/down dimension of motion without issue. That's disabled in the original eyeball, because it was too prone to get stuck staring at the ceiling and whatnot. The expanded field of view is also a big help.
So I still have to finish up the last bits of the construction, but on the whole I'm very pleased. A decade ago, I never believed that following motion would be this hard ... but that's what I get for cheaping out on the cameras, I guess!
Something a little different for today: as part of the process of obtaining my new job, I was asked to write an essay on why spaceflight is important to me. So I'm reusing it, because why not? This is a major side of me that doesn't find its way onto the blog very often, thanks to a shortage of publicized launches associated with my old job (I'm hoping that can change). It's shorter than my typical essays here, because I'm not trying to present research or convince anyone of anything, just talking through my personal thoughts.
At a time when many feel disillusioned
by current events, a mission to send four astronauts around the moon
has emerged as a kind of light in the dark, an unexpected source of
encouragement and togetherness. In my opinion this only begins to
illustrate the power of spaceflight and what it could mean for our
common future.
Expanding the human presence in space
has exciting practical implications. We may find rich new sources of
minerals and move polluting industry to barren heavenly bodies,
reducing the strain on earth's biosphere while upgrading current
living standards. But this will only be feasible if the math checks
out both economically and environmentally. The more efficient access
to space becomes, in terms of both cost and resources used, the more
likely we are to make damaging our own backyard with mines and
hazardous waste into a thing of the past.
Broadened access to space is also a
necessary defense against power monopolies. One of the best ways to
ensure activities in space are conducted for the benefit of earth is
to get as much of earth as possible involved. This is true at the
national and cultural level; allowing only one political body and its
favored ideologies to capture space would be an immense danger for
anyone it views as an enemy. For the US and its allies to rest on
their laurels while (for example) China becomes sole proprietor of
humanity's inheritance in the solar system would be a great strategic
failure. And a similar principle applies at the corporate level. I
have been pleased to see competitors rising to challenge SpaceX,
because no matter how much the latter reduces launch costs, it does
not necessarily have an incentive to minimize prices without at least
one rival to outbid it.
However, I must admit that while these
practical motives for opening up space may justify my personal
interest, they are not its source. Space exists at the frontier of
both our territory and our technology, and the lure of that frontier
is ultimately what draws me. It is the lure of novelty: not only
going places we have never been, but doing and creating the
unprecedented in order to get there. And it is the lure of the Other:
a way to reach beyond ourselves and our ordinary lives to discover
the awe-inspiring and foreign. For me, space travel is more a matter
of the soul than the body ... not merely a tool of survival, but one
of those things that help make survival worth the effort. This, I
think, is the main benefit the recent Artemis II mission has given
the world. It has had no material effect on most spectators' lives
(yet), but has offered them inspiration and hope, if only by
demonstrating that people can work together to accomplish something
stunning. Robotic probes, however useful, do not seem to have quite
the same effect. Humans in space help other humans feel connected to
the mission.
Experience with other frontiers reveals
their unpredictability. Who could have foreseen the applications of
the internet when it was nascent? It needed millions of people acting
on millions of ideas to make it what it is today. And so, when all
has been said, we cannot fully know what people will gain from access
to space until we give it to them.
This month's development focus took me back to trial-and-error learning. I wanted to build on the work I'd done with the "Allergic Cliffs" puzzle game and improve Acuitas' ability to discover how the game works. I had several ideas in mind, but ended up having time to finish only one: ability to learn rules that feature negations, i.e. the absence of a feature.
Photo of a board game called Azul, by Wikimedia Commons user Gepsimos
Previously all "rules" (action-result pairs that might represent cause-and-effect relationships) were based on commonalities between groups of actions that produced success or failure. If all attempts to put a zoombini with a green nose on the left bridge led to success, a rule such as "if a guide puts a zoombini with a green nose on a lefthand bridge, a guide succeeds" would begin to coalesce. You might also see "if a guide puts a zoombini with a green nose on a righthand bridge, a guide fails," though at easy levels of the game, the number of failures was often so low that there wasn't enough data to solidify the rule. Instead, a cluster of weaker success rules might appear. "If a guide puts a zoombini with a red nose on a righthand bridge, a guide succeeds," "If a guide puts a zoombini with a blue nose on a righthand bridge, a guide succeeds," etc. Given five color options for zoombini noses, it's pretty obvious to a human that "red OR blue OR purple OR orange" likely implies the more concise "NOT green," and I wanted Acuitas to be able to reach that conclusion as well. I also wanted complementary pairs of rules including a feature and its negation to be able to reinforce each other. (If a feature is significant, its presence and absence should matter; a pattern associated with only one of the two could be coincidental.)
So I added the ability to form rules by considering how a failure differs from prior clusters of successes. Any differences are looked at as candidates for features that should not be paired with the other features in the cluster. Further data can refine the tentative rule, excluding features that were actually irrelevant, or falsify it entirely.
To really get this to do a good job of discovering the secret rules present in a round of Allergic Cliffs, I had to stop treating individual zoombinis as "features," considering only their characteristics. This weakens the generality of the learning algorithm somewhat. In the true general case, there really could be a rule like "If a guide does not put Foozelu on a lefthand bridge, a guide succeeds" - maybe there's a quality only Foozelu has that is not part of the information available to the player. I know from experience that this game does not work that way: Allergic Cliffs rules are always based on visible properties of the zoombinis. But Acuitas isn't yet capable of the sort of meta-learning that discerns the nature of the whole game and carries over from one round to the next, so he needs a little help here. Allowing for the possibility of rules about individuals was introducing too much clutter, so I've turned it off for now.
The result is that I seem to be seeing an improvement in the robustness of rule discernment, and I often see viable rules for both bridges now, which is important for fully understanding the game and increasing one's odds of winning.
This month's activity was focused on continuing to squish the Conversation Engine into shape. I fixed an assortment of bugs that were left over from my last Conversation work spree, then expanded the "discussion topics" behavior to cover goals (of the form "I want/plan/intend to") expressed by the speaker.
"The Conversation," oil by unknown artist, NARA collection
I had saved some of the toughest bugs for their own work stage when I could focus on them ... and now, that time had come. Perhaps the worst problem was that, after recent upgrades, asking Acuitas "why" questions could cause infinite loops. I had to throw together an extra simulator in order to separate the question-answering process from the rest of the Conversation Engine to solve that one. (Conversations happen in real time and have an element of randomness, so re-launching the full Acuitas program and talking to him every time I want to trigger a bug can be very slow.)
I did further work on statements like "Thank you." I had previously adjusted Acuitas' sentence parsing and interpretation layers to read it as "I thank you" instead of "Thank yourself," but then I had to stop the Conversation Engine from looking at "thanking Acuitas" as an interesting speaker activity that should be discussed. (He's like a classic robot - he takes polite niceties too literally!)
I also fixed a conversation goal problem that was making Acuitas say "I can't do that" forever if you gave him an order he couldn't fulfill, and an issue that was keeping him from using abbreviated replies with pronouns immediately after a new topic was begun.
Treating speakers goals as discussion topics built very easily on top of my previous work with speaker states and actions; a goal is usually expressed as either a desired action or a desired state, so I just had to make sure it could trigger the appropriate pre-existing routine.
That's not a ton of progress, maybe, but I've been taking it a little slower this month, and going back to clean up all the construction dust left by my improvements to the Text Parser's conjunction handling. So it's enough. Getting some polish on things is important, and sometimes that takes as much or more time as throwing down the first outlines of new features.
Last year I started planning a physics experiment that would require me to measure tiny forces. My plans for that included building a "microbalance" out of a salvaged analog needle movement.
I chose several items with needle dials from my dad's junk collection, and ended up extracting the movement from an old tachometer. It consists of an electromagnet, two delicate coil springs, and the needle itself, which is attached to an axle that permits it to rotate over the electromagnet. Running a small current through the electromagnet changes the angle of the needle; otherwise, the springs damp its motion and keep it in a fixed position. There's also a metal tab that you can rotate to adjust the neutral position of the needle (this turned out to be very handy). Once I had the movement out of the tachometer, I designed and 3d-printed a new housing that would hold it in the right spatial relationship to a t-slot optical interrupter.
An optical interrupter contains some kind of light-emitting device in one pillar, and some kind of photosensitive device in the other. The idea is to attach a tiny shutter to the needle and suspend it in the slot of the interrupter. Even a slight weight on top of the needle will push it down and block the light with the shutter, changing the output signal of the photosensitive device. You can use that altered output to drive more current into the electromagnet and pull the needle back up. The larger the load on the needle, the more current you have to put into the electromagnet to get the shutter back out of the light, and the more voltage you need to drive it. So the voltage across the electromagnet serves to measure the weight on the needle. Neat, huh?
I had two previous projects, both showcased on YouTube, to use as inspiration. I leaned more heavily on the second one (Applied Science), since that circuit is more detailed and easier to adjust - look at all the potentiometers! The op-amp on the right and its resistors are merely an added layer of amplification on the measurement voltage, to make it easier to display, so ignore them for now. The op-amp on the left corresponds to the one in the TI video.
Screenshot from "Weigh an Eyelash--Build a Microgram Scale" by Texas Instruments. https://www.youtube.com/watch?v=n90whRO-ypE
Screenshot from "Measure the mass of an eyelash with a DIY microbalance" by Applied Science. https://www.youtube.com/watch?v=ta7nlkI5K5g&t=256s
Now ... dear readers with electronics experience, do you see anything *odd* about both these op-amp circuits? They're configured like non-inverting amplifiers, but the resistor that would normally connect the '-' input to ground is missing. All that's in that path is the photosensitive device of the optical interrupter. Maybe this works out if the device is a photodiode, as shown in both circuit diagrams; there might be some amount of voltage drop across the diode even when the light is shining on it and it is ON. But my Adafruit optical interrupter has a phototransistor instead. When this thing turns ON, its resistance becomes (approximately) zero. That gives me an amplifier with "infinite" gain. Whoops! The op-amp is physically limited in the amount of voltage it can produce, so its output gets as close to the positive supply voltage as it can. My needle was always stuck in the "up as high as possible" position.
So it turned out I couldn't just imitate the circuit from Applied Science's project - not with the parts I had. Since the transistor is (again, ideally) a binary on-off switch, I realized I didn't need proportional gain feedback at all. Toggling between two different voltages on the electromagnet would be more appropriate. This makes the needle oscillate, but so long as the oscillations stay around some stable equilibrium point, that's okay.
I reconfigured the op-amp circuit to be a summing amplifier. In one leg of the sum was my bias voltage, which I could adjust to set the needle's neutral position at the point where the shutter just began to break the light beam. In the other leg, I put a pull-up resistor connected to what I'll call the "recovery voltage," then connected the phototransistor between that and ground. With the transistor OFF, the output of the op-amp is (bias voltage) + (recovery voltage), which is enough to lift the needle. With the transistor ON, the output of the op-amp is only (bias voltage), which lets the needle sag under its own weight and the weight of whatever's on it. This produces a cycle: the needle drops, the transistor turns OFF, the voltage increases, the needle rises, the transistor turns ON, the voltage decreases, repeat. When you add weight to the needle, it takes more time to rise and less time to fall, so the *average* output voltage increases because the circuit spends longer in the "transistor OFF" phase. You can treat it like a pulse-width modulated signal.
And this actually worked!! For a minute or two. Then my fancy chopper-stabilized op-amp mysteriously died. My best guess is that current into one of the pins exceeded its unusually low "operating" limit of 100 uA. (When not "operating," it has a 10 mA limit like a more normal op-amp, and that's the level of protection I was providing with my resistors.) And my spare op-amp was killed by electrostatic discharge - this is the first time I've seen that ruin a part in real life, but take it from me, you DO have to worry about it! Especially if you live in a nasty dry climate (grumble).
I thought I was stuck until I could order more parts ... but then I realized that since I wasn't doing proportional control anymore, I didn't really need an op-amp at all. I adjusted the mechanical bias on the needle movement until it would hang in a good neutral position when unpowered. Then I set up the simplest feedback mechanism possible: transistor OFF powers the electromagnet, transistor ON cuts the power (via a pullup resistor). With no delicate op-amps to suddenly give up, this version worked long enough for me to get a demo video.
For the future, I should find a way to reintroduce a bias voltage, so I don't have to depend on moving the mechanical bias point to calibrate the scale. I should also add some kind of averaging or filtering circuit on the output to smooth the measurement voltage, and amplify it to something bigger than a few mV. But I've got the basic idea in hand! As seen in the video, it can detect a bit of polyester carpet fuzz which is so light that it electrostatically sticks to the needle.