North (formerly Thalmic Labs), the creator of the Myo armband, was acquired by Google in June 2020. Myo sales ended in October 2018 and Myo software, hardware and SDKs are no longer available or supported. Learn more.
Part Two: Wait for it...
Welcome back. Yesterday we talked about how to detect and control specific apps with Myo Scripts. Today, we’re going to sink our teeth into the meat of this tutorial with details on how to periodically get rotation data from your Myo armband.
But first, here’s what I did for
function onForegroundWindowChange(app, title) myo.debug("onForegroundWindowChange: " .. app .. ", " .. title) local titleMatch = string.match(title, "Race The Sun") ~= nil or string.match(title, "RaceTheSun") ~= nil; myo.debug("Race the Sun: " .. tostring(titleMatch)) if (titleMatch) then myo.setLockingPolicy("none") end return titleMatch; end
I chose match, but find would also have worked. In more complicated scripts (or scripts targeting apps with odd titles) you may need to supply a fancier pattern that matches things more dynamically (the Writing Myo Scripts tutorial has an example), but what we have is sufficient for this game. You could also match based on app, but you would need separate code for Mac and Windows, and you’d still need string matching for the browser demo.
Anyway, with our script now only being active when Race The Sun is in the foreground, we’re ready to start driving it.
We’re going to control the glider in Race The Sun by making the player’s arm a rudder. The idea is that they hold their arm out parallel to the floor and rotate left or right to change the direction of the glider.
Built into the Myo armband is an IMU, which gives you all sorts of interesting data like the current acceleration, pitch, roll, or yaw of the user’s arm. Yaw is the left/right rotation that we are interested in for now, and you retrieve it with myo.getYaw(). This returns the current yaw in radians. If you’re not exactly down with radians you can convert them into degrees by multiplying by 180/pi, which is about 57.286. Degrees are for quitters though, so I’m going to stick with radians.
So, we’ve got a way to get the current left/right rotation of the player’s arm. That leaves us with two questions: “When do we get the rotation?” and “Wait, left or right of what?” Let’s answer that second question first.
This is a common problem you will run into when building Myo Scripts that use the IMU. Fortunately it’s pretty easy to solve, and we’re going to do it with a centreing gesture. (Yeah I spelled it “centre”. I’m Canadian, deal with it.)
Add some code to detect the fist pose and trigger a function that saves the current yaw in a global variable. Also set that value back to 0 and hit escape when the user does fingers spread.
Mine looks like this:
centreYaw = 0 function onPoseEdge(pose, edge) myo.debug("onPoseEdge: " .. pose .. ", " .. edge) if (edge == "on") then if (pose == "fist") then centre() elseif (pose == "fingersSpread") then escape() end end end function centre() myo.debug("Centred") centreYaw = myo.getYaw() myo.vibrate("short") end function escape() myo.debug("Escape!") centreYaw = 0 myo.keyboard("escape","press") end
The final callback (at least, at the time of writing) is onPeriodic(), and it fires automatically every 10 milliseconds. This is your opportunity to get the current sensor data and act on it. What we want to do for Race The Sun is press the left arrow key when the player’s arm is a bit to the left of where they centered, and the right arrow key when they are a bit to the right. From my experimentation, “a bit” is about 0.1 radians, which is like 5 or 6 degrees. You can adjust this to what feels right for you, but when you’re finished you’ll have a region of space in the centre — we’ll call it the “yaw deadzone” — where you won’t send left or right commands. If you move out of that deadzone on either side the craft will also move in that direction.
For tomorrow, implement
onPeriodic such that it presses down the left or right arrow if the player is out of the yaw deadzone on the left or right side, and releases the key if they go back into the deadzone (or skip the deadzone and go directly into the opposite side). Let’s also prevent any of that code from executing if
centreYaw is 0 (meaning it’s never been set, since it’s unlikely to ever actually be 0), so we aren’t accidentally firing inputs.
Hint: The yaw value increases when you move your arm clockwise.
Once you think you’ve got it working, try it out in the game! If you get stuck, I’ll be back with the solution tomorrow.