
I am currently working on building a custom active mountain bike shock.
What started as a simple 3D-printed shock body to test-fit on my mountain bike has sparked into an active, electronically-controlled damping system controlled by a micro neural network. The basic shock design (August 2024) is shown below, along with the 3D-printed part test-fitted to my 2018 Rocky Mountain Altitude A50.


The idea for the active shock is to have an ESP-32 module tied to a NEMA8 stepper motor, which adjusts a damping valve (NEMA8 actuated needle) based on inputs from an IMU along with Bluetooth signals from a pressure sensor (which effectively measures shock compression from the damping oil pressure) – same idea as a lockout valve on a shock, but instead of manual controls, it has active controls based on both hard-coded cases and predictive scenarios derived from shock compression (BLE pressure sensor / module) and position/velocity/acceleration (IMU). Below is a rough sketch of the main components.
Looking forward, while I have some demanding tasks ahead of me before this project is finished (Finalize PCB schematic and design in Altium, CFD for sizing damping oil flow rates, Vibrational Control and Enclosures), I hope to have a working and fully-functional prototype by January 2027.
Working with a team of 6 throughout the span of my MECH 223 Design Course, our team went through various stages of prototyping, simulation, and design to build a successful Rail Hauler capable of carrying 5+ kg with a BOM of <$25.
While speed, carrying capacity, size, weight, # of motors, and # of batteries were all considered in determination of a final score, one of my main contributions was through a script in Python which calculates the expected score of the vehicle based on its predicted parameters.
See the script below for the full python script, later implemented to a MATLAB script that clearly compared expected weighted scores to determine how many motors our team would use in the competition (write-up and graphs shown in the document attached above).
def Round1(Speed_Regulation, Motors):
# Initializing Performance Criteria Multipliers
Score = 0.0
Speed = 0.0 # Avg Speed around track excluding up the hill (1 Best, 3 Worst)
B = 100.0
PC = 0.0
PCT1 = 0.75
PCT2 = 0.75
PCH = 1.0
PCD = 1.0 # No delay assumption
'''if(Speed_Regulation == True):
if(Motors >= 2):
B = 50 # Does not make it up the hill
Speed = 1
PCH = 0.5
else:
B = 50 # Brakes, able to go faster but 1 motor - does not make it up the hill
Speed = 2
PCH = 0.3
if(Motors >= 2):
B = 100 # Fastest possible design, assumed that it makes it up the hill
Speed = 1
PCH = 1.0
PCT2 = 0.9
if(Speed_Regulation == False):
if(Motors >= 2):
False # Non-optimal design, screened out
else:
B = 50 # No Brakes, slower + doesn't make it up the hill
Speed = 3
PCH = 0.1
if(Speed == 1):
PCT1 = 0.9
elif(Speed == 2):
PCT1 = 0.8
else:
PCT1 = 0.7'''
PC = PCT1*PCT2*PCH*PCD # Calculating Total Performance Criteria
Score = B*PC # Calculating Round 1 Score
return Score
def Round2(Speed_Regulation, Motors):
# Initializing Performance Criteria Multipliers
Score = 0.0
B = 100.0
PC = 0.0
PCT1 = 0.75
PCT2 = 0.75
PCD = 1.0 # No delay assumption
'''if(Speed_Regulation == True):
B = 100
if(Motors >= 2):
B = 20 #Derails
PCT1 = PCT2 = 0.5
PCT1 = PCT2 = 0.9 # Able to clear track in fastest possible time
else:
PCT1 = PCT2 = 0.8 # Able to clear track slightly slower due to lower torque/acceleration
B = 20 # Derails
PCT1 = PCT2 = 0.5 # Derails
if(Speed_Regulation == False):
if(Motors >= 2):
False # Non-optimal design, screened out
else:
B = 20 # Assumes no brakes results in derailing
B = 100 # Assumes can clear round 2
PCT1 = PCT2 = 0.7
PCT1 = PCT2 = 0.6 # If can clear round 2, much slower due to high friction / low speed'''
PC = PCT1*PCT2*PCD # Calculating Total Performance Criteria
Score = B*PC # Calculating Round 2 Score
return Score
# Input Variables (Categories used for subjective design objectives i.e. from categories 1-3)
# Refer to spreadsheet to calculate costs for projected design
Cost = 135 # Cost in $CAD
WeightCat = 2 # Weight Category (1 Best, 3 Worst)
SizeCat = 2 # Size Category (1 Best, 3 Worst)
SusCat = 2 # Sustainability Category (1 Best, 3 Worst)
Speed_Regulation = True # MCU Regulated Speed (True/False)
Motors = 1 #Number of motors
# Check other configs
Bypass1 = False
Bypass2 = False
# Initializing Vehicle Criteria Multipliers
VCtotal = 0.0
VCw = 0.0
VCc = 0.0
VCv = 0.0
VCs = 0.0
VCa = 1.1 # Assuming average aesthetics, does not change with design
# Initializing Score Variables
R1Score = 0.0
R2Score = 0.0
Score = 0.0
# Cost Category Multiplier
if(Cost <= 50):
VCc = 0.5
elif(Cost >= 400):
VCc = 0.0
else:
VCc = 0.25 + 0.25*cos(pi*(Cost-50)/350)
# Predicted Weight Multiplier
if(WeightCat == 1):
VCw = 0.1
elif(WeightCat == 2):
VCw = 0.06
else:
VCw = 0.02
# Predicted Size Multiplier
if(SizeCat == 1):
VCv = 0.1
elif(SizeCat == 2):
VCv = 0.06
else:
VCv = 0.02
# Predicted Sustainability Multiplier
if(SusCat == 1):
VCs = 1.2
elif(SusCat == 2):
VCs = 1.0
else:
VCs = 0.8
VCtotal = (VCv+VCw+VCc)*VCs*VCa
R1Score = Round1(Speed_Regulation, Motors)
R2Score = Round2(Speed_Regulation, Motors)
if(R1Score > R2Score):
Score = (0.6*R1Score + 0.4*R2Score)*VCtotal
else:
Score = (0.4*R1Score + 0.6*R2Score)*VCtotal
print(Score)
print(R1Score)
print(R2Score)
print(VCtotal)