Skelebot Kinematic Analysis & Synthesis

The goal of our design was to fit three mechanisms (head/jaw/candy movement) into the skull which provided a design challenge in terms of space constraints and movement timing. The main driver of constraints and timing is the skull movement which we focused our analysis on. The mechanism chosen for skull rotation was the crank rocker. 

Mobility Analysis

The degree of freedom of the crank rocker was 1 with the crank (L1) rotating a full 360 degrees and the rocker (L3) rotating under restricted motion to simulate scary skull movement. Joints J1 and J4 represent rotating shafts, J1 is the main shaft driver while J4 is the structural shaft support which acts as the point of rotation for the skull. J3 is a pin joint bolted onto the side of the skull to move the rocker. L1 and L2 are physical links whereas L4 is an imaginary link representing the distance between the rotating shafts J1 and J4. 

L3 is also an imaginary link representing the distance between rotating shaft J4 and bolted pin joint J3. Theoretically as the crank L1 rotates around, L3 doesn’t follow the typical circular arc of a crank rocker as L3 will shorten as the skull rotates counterclockwise (pulls J3 towards centerline of shafts since L3 is an imaginary link) and lengthen when skull rotates clockwise (pulls J3 away from centerline of shaft). However, by placing joint 3 as close to the back of the skull as possible and by minimizing the angle of rotation of the skull, we assume that L3 will follow close to a circular path and therefore remain a constant length as the imaginary link in our analysis. 

Our mechanism capability is restricted by several spatial constraints which we will outline below:

Constraint 1: Rotating and Structural Shaft Locations

The structural shaft and rotating driver shaft locations were determined based on the locations of the jaw movement and candy movement mechanisms. The distance between J1 (driver shaft) and J4 (structural shaft) was determined to be 60 mm. This creates our first link, the ground link, of 60 mm length. 

Constraint 2: Candy Dispenser Location

The candy dispenser mechanism rotates along the driver shaft J1 but its mechanism radius extends to around 25 mm. To prevent the clash of the crank (L1), we opted for a radius <= 25 mm. Given that the Grashoff requirement for the crank must be met, we chose a shorter crank length of 10 mm. 

Constraint 3: Crank is Grashoff

In order to have a successful crank rocker mechanism, the Grashoff condition must be satisfied to ensure that the crank (L1) has a full 360 degree freedom to rotate. To satisfy Grashoff, the following must be true of link lengths: (shortest link + longest link) <= (other link 1 + other link 2)

Constraint 4: Avoid Candy Dispenser Bucket Clash with Skull Side Wall

As the skull rotates counterclockwise, the candy dispenser bucket may clash with the sidewall of the skull if the rotation of L3 is too large. Therefore, the goal is to restrict the rotation angle to avoid hitting the side of the skull while having sufficient rotation to mimic skull rotation realism. In the three figures above, the skull is rotated at 0, 20, and 25 degrees counterclockwise. At 20 degrees CCW, the clearance between the sidewall of the skull and the candy dispenser pipe is 20 mm which is sufficient margin of safety. Therefore, we determined the maximum rotation of L3 (theta 3) to be 20 degrees in either the clockwise or counterclockwise directions. 

Position Analysis

Given the constraints outlined above and established link lengths (L1 = 10, L4 = 60), the goal is to optimize L2 and L3 to meet the Grashoff requirement and maximum rotation of link 3 of 20 degrees in either direction. 

Hand Calculations

To establish the range of rotation of link 3, we must find a relationship between theta 3 and theta 1 as theta 1 goes through a full 360 degree rotation. 

Code File: Final Simulation.ipynb or Final Simulation.py:

#Constants
import math
import matplotlib.pyplot as plt
import pandas as pd

L1, L4 = 10, 60
print(f'Fixed Variables: L1 = {L1}, L4 = {L4}')

#L2/L3 Pairs and Theta 3 @ Neutral Position

L2 = [95.7,96,96.5,97,98,98.5,99,99.5,100,101.2,103.5,105,106.8,108.5,1110,110.8,111.1]  #Millimeters
L3 = [87.2,84.4,81.5,78.6,75,72.9,70.4,67,63.2,60.4,59.2,58,56.9,55.3,54.1,52.9,51.9] #Millimeters
O3 = [101.2,98.5,95.4,92.2,87.7,84.9,81.6,77,71.6,65.6,59.5,54.1,48.1,39.7,30.8,22.2,13.4] #Degrees

L2L3 = []

for i in range(len(L2)):
    L2L3.append([L2[i],L3[i],O3[i]])

print(f'L2/L3/Theta3 Combinations.... \n')
combinations = pd.DataFrame(L2L3, columns = ['L2','L3','Theta 3 Starting'])
print(combinations)

#Grashoff Check

L2L3_grash = []
L2L3_nongrash = []

for i in range(len(L2L3)):
    lengths = [L1, L2L3[i][0], L2L3[i][1], L4]
    ascending_lengths = sorted(lengths)

    if ascending_lengths[0] + ascending_lengths[3] <= ascending_lengths[1] + ascending_lengths[2]: #Shortest link fully rotates
        L2L3_grash.append(L2L3[i])
    else:
        L2L3_nongrash.append(L2L3[i])

print(f'Grashoff Combinations: {len(L2L3_grash)}, Non-Grashoff Combinations: {len(L2L3_nongrash)}\n')
print(f'Grashoff Combinations.... \n')
grashoff_combinations = pd.DataFrame(L2L3_grash, columns = ['L2','L3','Theta 3 Starting'])
print(grashoff_combinations)

#Generate Theta 3 Given 360 rotation of Theta 1, L2/L3 Pairs, L1 = 10, and L4 = 60

def theta3_range(combinations, L1 = 10, L4 = 60):
    L2 = combinations[0]
    L3 = combinations[1]
    k1 = L4/L1
    k2 = L4/L3
    k3 = (L1**2 - L2**2 + L3**2 + L4**2)/(2*L1*L3)

    thetas = []
    theta3_starting = combinations[2]
    theta3_max = -1000
    theta3_min = 1000

    for theta1 in range(0, 361, 5):
        A = math.cos(math.radians(theta1)) - k1 - k2*math.cos(math.radians(theta1)) + k3
        B = -2*math.sin(math.radians(theta1))
        C = k1 - (k2 + 1)*math.cos(math.radians(theta1)) + k3
        discrim = (-B-math.sqrt(B**2-4*A*C))/(2*A)
        
        theta3 = 2*(math.atan(discrim)*(180/math.pi))

        if theta3 < theta3_min:
            theta3_min = theta3

        if theta3 > theta3_max:
            theta3_max = theta3

        thetas.append([theta1, theta3])

    plt.plot([theta[0] for theta in thetas], [(theta[1] - combinations[2]) for theta in thetas])
    plt.ylabel('Theta 3: Skull (Degrees)')
    plt.xlabel('Theta 1: Crank (Degrees)')
    plt.title(f'Rotation of Skull as a Function of Crank Rotation (L2 = {L2} and L3 = {L3})')
    plt.show()

    theta3_range = theta3_max - theta3_min
    max_cw = theta3_max - theta3_starting
    max_ccw = theta3_min - theta3_starting

    result = [L1, L2, L3, L4, theta3_starting, max_cw, max_ccw]
    return result, thetas

#Simulate Range of Skull Rotation (theta 3) for all Grashoff L2/L3 Pairs

L2L3_final = []
max_rotation = 20

for pair in L2L3_grash:
    result, thetas = theta3_range(pair) #L1, L2, L3, L4, theta3_starting, max_cw, max_ccw
    max_cw, max_ccw = result[5], result[6]
    if (max_cw >= max_rotation) or (max_ccw <= -max_rotation):
        continue
    else:
        L2L3_final.append(result)

options = pd.DataFrame(L2L3_final, columns = ['L1', 'L2', 'L3', 'L4','theta3_starting', 'max_cw', 'max_ccw'])
print(options)

#Chose Option 8


Code Part 1: Generate L2/L3 Combinations

Once the relationship between theta 3 and theta 1 were derived, the remaining unknowns were L2 and L3 lengths. The L3 length is dependent on the L2 length and its associated pin joint location (J3). To find L2/L3 pairs, J3 joint locations were chosen and L2 and L3 distances were measured from this point as shown in the figure below. A total of 17 pin joint locations were chosen thus generating 17 L2/L3 pairs.

The theta 3 positions for each L2/L3 pair were then determined. 

Each L2/L3 pair and it’s corresponding theta 3 at static skull forward position was recorded into an array generating the following combinations: 

Code Part 2: Grashof Condition

The L2/L3 list generated in part 1 was further filtered to meet the Grashof condition thereby reducing the combination list from 17 pairs to 13 pairs.

Coding Part 3: Calculate range of O3

With the Grashoff pairs remaining, the range of theta 3 for each pair was calculated as theta 1 goes from 0 to 360 degrees. Any pairs that exceeded the maximum clockwise or counterclockwise range of 20 degrees were eliminated from the combinations. This resulted in the following leftover combinations: 

Any of these combinations can be chosen to meet our requirements, however, in the beginning we made the assumption that joint 3 would be placed as far back to the head as possible to ensure that the rocker link 3 followed as circular of a path as possible. Therefore we chose option 8 because it has the longest link 2 that will extend the furthest to the back of the skull. The physical configuration of option 8 is shown below along with the skull rotation through one 360 degree rotation of the crank. 



The motion of the crank rocker can also be visualized through the animation shown below: