What the Fork: Own Your Fork
by canadasushi in Circuits > Arduino
145 Views, 4 Favorites, 0 Comments
What the Fork: Own Your Fork
This project was conducted as part of the Computational Design and Digital Fabrication seminar in the ITECH masters program.
Authors : Chris & Zahra
Do you have one of those things that you want to use as yours ONLY?
We played with the core element that gives an object functionality. In such cases, it could be a plastic membrane of a water bottle or a padding area of a ping-pong racket.
What about everyday object such as pencils, chairs, or forks?
The common element of these objects is that their functions are only fulfilled when elements are rigid. We furthered that idea of manipulating the core functional element, then subverting it in order to remove its usefulness. By actuating and sensing, the object actively chooses whose hand to be used.
Our fork comes with a manipulatable joint that is limp under an anonymous user, then straightens back to rigid state to be useful when the main user gets their hands on it.
Supplies
3D printing machine (Prusa i3 MK3S+ is used)
PLA filament
Nylon string 0.4mm
SG90 Micro servo x 2
9V battery
Arduino Nano 33 BLE
Voltage regulator power module
10k resistor
TACT Switch Push Button
Jumper wires
Super glue
Soldering iron
Heat shrink wrap
CAD
The goal of this fork is to maintain its compact and relatively in-scale final product. Therefore, a complete 3D model is carefully designed with millimeter-precise models of all parts.
To achieve both limping and straightening, you need a mechanism that can freely bend and easily lock back to straight. In this model, print-in-place ball joint chains are iteratively designed.
In comparison to the universal joints that are widely used in those 3D printed dragons, ball joints achieve larger degree of freedom, and larger radius of curvature.
First, try out the print-in-place ball joint test with a tolerance of 0.2mm. The settings that I have experimented with were print speed and flow rate, support or without support. At the end, 100% with 95 flow rate(which varies between printers and filament type) and no support with brim setting gave the most promising results. I recommend furthermore tweaks in your printer such as the layer height and print speed, as well as printing chain model to get consistent working parts.
In the chain model, there are more features added to achieve our goal.
- linear joint to also have sliding mechanism, such that when relaxed, it elongates even more, and contraction pulls them tighter
- Coupled spherical profile between joints such that at contraction, they match to achieve the original linear state
These settings allowed to actuate the mechanism with relatively low torque.
Then, the body design takes advantage of precisely measured parts to fit everything compact. The full assembly instruction is found below.
The full library used in this project can be found and downloaded under these links
Printing
3D printing is always a constant fight with digital model and physical matter. Try lowering speed for crisp first layer adhesion, bed leveling calibration. Increasing flow rate has been demonstrated to work against the constant tolerance. Remove all overhang by filleting.
Necessary prints consist of 4 parts: the fork, ball joint chain, front and back casing.
Assembly Part 1
Assembly sequence follows below.
- Thread the nylon string through the 4 holes of ball joint chain.
- Tie a knot on the top area of the joint - towards the fork.
- Add a dap of super glue to fix in place.
- Leave about 8 inches of strings and repeat for all holes.
- Join fork part to ball joint chain. These two parts come with press fit detail. Fix with a dap of superglue.
- Join joint part to back casing part with super glue. These two parts come with snug fit coupled detail.
- Check if servo fits into the detail without any friction.
- Wind each end of nylon to two opposite side of servo wings. Don't tighten yet
- Place the servo in place, pull the pair of wires equally to straighten the mechanism, then tie a knot.
At the end you will have top and bottom strings attached to a one servo, left and right string attached to another.
Wiring + Assembly Part 2
The circuit is pretty simple as all parts make discrete connections.
For all connections, I recommend stripping, soldering and shrink wrapping to preventing any shorts happening. Also, I connected all the 5V together and ground together to make it simple.
Servo connections:
brown/black : GND
yellow : PWM
red : 5V
Button: //adding resistor makes button to signal LOW when not pressed.
Top left: signal
Bottom left: 5V
Bottom right: GND + 10K resistor
Once the wiring is done, you are ready for final assembly! Take a break and take a haribo :)
Final assembly sequence:
- Insert two servo motor each on bottom and top case
- Press fit arduino nano into bottom case detail
- Glue the user button on the side
- Add power supply board
- Add 9V battery
- Pack everything nice and close the lid!
Trying Out
It is important to check each step in hardware to not have last minute panick! Check if everything is working with a simple sweep code.
Coding
The logic is simple, once it detects motion, the fork will go limp. If the button is pressed, stay stiff.
The code below syncs inbuilt IMU on arduino Nano with servo movement. The machine remembers its initial position and rotation when it initiates, then if the movement is significant, the servo makes the joint bend towards to gravity.
#include <Servo.h>
#include <Arduino_LSM9DS1.h>
const int upDownServoPin = 8;
const int leftRightServoPin = 7;
const int buttonPin = 2;
Servo upDownServo;
Servo leftRightServo;
float x, y, z;
int threshold = 0.04;
int buttonState = 0;
int middleUD = 90;
int middleLR = 90;
float xInitPos, yInitPos, zInitPos = 0;
int offset = 30;
int currentPosUD;
int currentPosLR;
int d = 3;
void setup() {
// put your setup code here, to run once:
SerialBegin();
IMUSetup();
ServoSetup();
pinMode(buttonPin, INPUT);
getInitialPos(400);
}
void loop() {
readAccel();
}
bool GetButtonState(){
buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) {
return true;
} else {
return false;
}
}
void readAccel(){
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(x, y, z);
delay(100);
if (checkIfNotMoving()){
GoStraight();
//Serial.println("not moving");
}
else{
if (!GetButtonState()){
getDominantAxis(x,y,z);
}
}
}
}
bool checkIfNotMoving(){
if (abs(zInitPos - z) < 0.08 && abs(yInitPos - y) < 0.08){
return true;
}else{
return false;
}
}
void getDominantAxis(float x, float y, float z){
if (abs(z) > abs(y)){
// fork is up or backwards
if (z > 0){
//Serial.println("Up");
GoLimp(0,1);
}
else if (z < 0){
//Serial.println("Down");
GoLimp(0,-1);
}
}
else if (abs(y) > abs(z)){
// fork is tilted right or left
if (y < 0){
//Serial.println("Right");
GoLimp(1,1);
}
else if (y > 0){
//Serial.println("Left");
GoLimp(1,-1);
}
}
}
void GoLimp(int servo, int dir){
if (servo == 0 && dir == 1){
Rotate(0, middleUD + offset);
Rotate(1, middleLR);
}
else if(servo == 0 && dir == -1){
Rotate(0, middleUD - offset);
Rotate(1, middleLR);
}
else if(servo == 1 && dir == 1){
Rotate(1, middleLR - offset);
Rotate(0, middleUD);
}
else if(servo == 1 && dir == -1){
Rotate(1, middleLR + offset);
Rotate(0, middleUD);
}
}
void Rotate(int index, int angle){
if (index == 0){ //UD moving
if (currentPosUD < angle){ //pos 45, targetAngle 90
for (int i = currentPosUD; i < angle; i++){
upDownServo.write(i);
delay(d);
}
currentPosUD = angle;
}
else{ //pos 90, targetAngle 45
for (int i = currentPosUD; i > angle; i--){
upDownServo.write(i);
delay(d);
}
currentPosUD = angle;
}
}else{
if (currentPosLR < angle){ //pos 45, targetAngle 90
for (int i = currentPosLR; i < angle; i++){
leftRightServo.write(i);
delay(d);
}
currentPosLR = angle;
}
else{ //pos 90, targetAngle 45
for (int i = currentPosLR; i > angle; i--){
leftRightServo.write(i);
delay(d);
}
currentPosLR = angle;
}
}
}
void GoStraight(){
upDownServo.write(middleUD);
leftRightServo.write(middleLR);
currentPosUD = middleUD;
currentPosLR = middleLR;
}
//------------setup------------
void SerialBegin(){
Serial.begin(9600);
while (!Serial);
//Serial.println("Serial Port Initiated");
}
void ServoSetup(){
upDownServo.attach(upDownServoPin);
leftRightServo.attach(leftRightServoPin);
GoStraight();
}
void IMUSetup(){
if (!IMU.begin()) {
//Serial.println("Failed to initialize IMU!");
while (1);
}
//Serial.print("Accelerometer sample rate = ");
//Serial.print(IMU.accelerationSampleRate());
//Serial.println("Hz");
}
void getInitialPos(int sampleSize){
float x1,y1,z1;
for(int i = 0; i < sampleSize; i++){
IMU.readAcceleration(x1, y1, z1);
xInitPos += x1;
yInitPos += y1;
zInitPos += z1;
}
xInitPos = xInitPos / sampleSize;
yInitPos = yInitPos / sampleSize;
zInitPos = zInitPos / sampleSize;
}
Downloads
Final Remarks
You finally have your OWN fork that only YOU can use. In other's hands, it is totally useless ㋡
Have fun with it!