Skip to content

Commit 9bebca5

Browse files
committed
Add engine model and stability analysis to loader
Introduced a parametric engine model and its placement in the assembly. Added a comprehensive weight distribution and stability analysis module, including center of gravity and tipping load calculations. Updated wheel assembly logic for improved rear wheel placement and included new engine and analysis parameters in the configuration file.
1 parent 19e3847 commit 9bebca5

File tree

2 files changed

+222
-4
lines changed

2 files changed

+222
-4
lines changed

LifeTrac-v25/mechanical_design/openscad/lifetrac_v25.scad

Lines changed: 209 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ FRAME_Z_OFFSET = GROUND_CLEARANCE;
8989
// Calculate wheel X offset - wheels positioned outside the outer panels
9090
WHEEL_X_OFFSET = TRACK_WIDTH/2 + SANDWICH_SPACING/2 + PANEL_THICKNESS + WHEEL_WIDTH/2 + WHEEL_CLEARANCE;
9191

92+
// =============================================================================
93+
// ENGINE CONFIGURATION
94+
// =============================================================================
95+
96+
ENGINE_HP = 25; // Desired horsepower (e.g., 25HP V-Twin)
97+
// Estimate engine weight (kg) based on HP (approximate for small engines)
98+
// Base 30kg + 1.2kg per HP
99+
ENGINE_WEIGHT_KG = 30 + (ENGINE_HP * 1.2);
100+
101+
// Engine Position (Middle near back)
102+
ENGINE_POS_Y = 400; // Forward from rear of frame
103+
ENGINE_POS_Z = FRAME_Z_OFFSET + 200; // Height above frame bottom
104+
92105
// =============================================================================
93106
// LOADER ARM DIMENSIONS - PARAMETRIC CALCULATION
94107
// =============================================================================
@@ -1777,6 +1790,173 @@ module base_frame() {
17771790
mid_stiffener_plate(800, FRAME_Z_OFFSET + 100, FRAME_Z_OFFSET + 825 - 25.4);
17781791
}
17791792

1793+
// =============================================================================
1794+
// ENGINE
1795+
// =============================================================================
1796+
1797+
module engine() {
1798+
// V-Twin Engine Representation
1799+
// Dimensions approx for 25HP engine
1800+
eng_width = 450;
1801+
eng_depth = 400;
1802+
eng_height = 450;
1803+
1804+
translate([0, ENGINE_POS_Y, ENGINE_POS_Z])
1805+
{
1806+
color("DimGray")
1807+
union() {
1808+
// Crankcase base
1809+
translate([0, 0, 100])
1810+
cube([eng_width, eng_depth, 200], center=true);
1811+
1812+
// Cylinders (V-Twin)
1813+
for (rot = [-45, 45]) {
1814+
rotate([0, rot, 0])
1815+
translate([0, 0, 200])
1816+
cylinder(d=120, h=150, center=true);
1817+
}
1818+
1819+
// Flywheel/Fan cover
1820+
translate([0, -eng_depth/2 - 20, 150])
1821+
rotate([90, 0, 0])
1822+
cylinder(d=250, h=50, center=true);
1823+
1824+
// Muffler
1825+
translate([0, eng_depth/2 + 50, 150])
1826+
cube([300, 100, 100], center=true);
1827+
}
1828+
}
1829+
}
1830+
1831+
// =============================================================================
1832+
// WEIGHT DISTRIBUTION & STABILITY ANALYSIS
1833+
// =============================================================================
1834+
1835+
module calculate_cog() {
1836+
// --- WEIGHT ESTIMATES (kg) ---
1837+
w_frame = 350; // Chassis, panels, crossmembers
1838+
w_wheel = 40; // Tire + Rim + Motor (x4)
1839+
w_arm = 180; // Arms + Crossbeams + Cylinders
1840+
w_bucket = 120; // Bucket + QA Plate
1841+
w_engine = ENGINE_WEIGHT_KG;
1842+
w_operator = 90; // Operator on rear deck
1843+
w_fluids = 40; // Hydraulic oil + fuel
1844+
1845+
// Max Load Calculation (Tipping Load)
1846+
// We calculate moments about the FRONT AXLE
1847+
1848+
// --- POSITIONS (Y, Z) relative to World Origin ---
1849+
// Frame: Centroid approx middle of machine
1850+
pos_frame = [MACHINE_LENGTH/2, FRAME_Z_OFFSET + 300];
1851+
1852+
// Wheels:
1853+
// Front axle Y is calculated in wheel_assemblies
1854+
front_axle_y = WHEEL_BASE - WHEEL_RADIUS;
1855+
// Rear axle Y is calculated in wheel_assemblies (re-calculating here for logic)
1856+
rear_axle_y_target = LIFT_CYL_BASE_Y + WHEEL_RADIUS + 100;
1857+
1858+
// Re-calculate wheel positions to match wheel_assemblies logic
1859+
_front_y = WHEEL_BASE - WHEEL_RADIUS;
1860+
_rear_y_target = LIFT_CYL_BASE_Y + WHEEL_RADIUS + 100; // Moved forward of cylinder
1861+
_min_wb = WHEEL_DIAMETER + 50;
1862+
_max_rear_y = _front_y - _min_wb;
1863+
_rear_y = min(_rear_y_target, _max_rear_y);
1864+
1865+
pos_wheels_front = [_front_y, WHEEL_RADIUS];
1866+
pos_wheels_rear = [_rear_y, WHEEL_RADIUS];
1867+
1868+
// Engine
1869+
pos_engine = [ENGINE_POS_Y, ENGINE_POS_Z + 200];
1870+
1871+
// Operator (Standing on deck at rear)
1872+
pos_operator = [-200, 300]; // Behind machine
1873+
1874+
// Arms (Dynamic based on angle)
1875+
// Centroid approx 40% along arm
1876+
arm_cg_dist = ARM_LENGTH * 0.4;
1877+
pos_arm = [
1878+
ARM_PIVOT_Y + arm_cg_dist * cos(ARM_LIFT_ANGLE),
1879+
ARM_PIVOT_Z + arm_cg_dist * sin(ARM_LIFT_ANGLE)
1880+
];
1881+
1882+
// Bucket (Dynamic)
1883+
// At tip of arm
1884+
pos_bucket = [
1885+
ARM_PIVOT_Y + ARM_LENGTH * cos(ARM_LIFT_ANGLE),
1886+
ARM_PIVOT_Z + ARM_LENGTH * sin(ARM_LIFT_ANGLE)
1887+
];
1888+
1889+
// --- MOMENT CALCULATION (About Rear Axle for CoG Y) ---
1890+
// We use World Y=0 (Rear of frame) as reference datum.
1891+
1892+
moment_frame = w_frame * pos_frame[0];
1893+
moment_wheels = 2 * w_wheel * pos_wheels_front[0] + 2 * w_wheel * pos_wheels_rear[0];
1894+
moment_engine = w_engine * pos_engine[0];
1895+
moment_operator = w_operator * pos_operator[0];
1896+
moment_arm = w_arm * pos_arm[0];
1897+
moment_bucket = w_bucket * pos_bucket[0];
1898+
moment_fluids = w_fluids * (MACHINE_LENGTH * 0.3); // Tank usually near back
1899+
1900+
total_weight_empty = w_frame + 4*w_wheel + w_engine + w_arm + w_bucket + w_fluids;
1901+
total_moment_empty = moment_frame + moment_wheels + moment_engine + moment_arm + moment_bucket + moment_fluids;
1902+
1903+
cog_y_empty = total_moment_empty / total_weight_empty;
1904+
1905+
// --- TIPPING LOAD CALCULATION ---
1906+
// Tipping happens when moment about Front Axle is zero.
1907+
// Counter-balance moments (behind front axle) vs Load moments (in front)
1908+
// Distances relative to Front Axle
1909+
1910+
// Note: Positive distance = Behind axle (Counter-weight)
1911+
// Negative distance = In front of axle (Load)
1912+
1913+
// Sum of counter-moments (Weight * Distance from Front Axle)
1914+
// We want the machine to stay flat, so Sum(Weight_i * (Front_Y - Y_i)) > 0
1915+
1916+
tipping_moment_capacity =
1917+
w_frame * (_front_y - pos_frame[0]) +
1918+
2 * w_wheel * 0 + // Front wheels have 0 moment arm
1919+
2 * w_wheel * (_front_y - pos_wheels_rear[0]) +
1920+
w_engine * (_front_y - pos_engine[0]) +
1921+
w_operator * (_front_y - pos_operator[0]) +
1922+
w_fluids * (_front_y - (MACHINE_LENGTH * 0.3)) +
1923+
w_arm * (_front_y - pos_arm[0]) +
1924+
w_bucket * (_front_y - pos_bucket[0]);
1925+
1926+
// Load is at bucket position.
1927+
// Load Moment = Load_Weight * (pos_bucket[0] - _front_y) <-- Distance in front
1928+
// Tipping occurs when Load_Moment = Tipping_Moment_Capacity
1929+
1930+
load_dist_from_axle = pos_bucket[0] - _front_y;
1931+
1932+
// If load is behind axle (e.g. fully raised and back), it won't tip forward
1933+
tipping_load_kg = (load_dist_from_axle > 0) ? (tipping_moment_capacity / load_dist_from_axle) : 9999;
1934+
1935+
rated_load_kg = tipping_load_kg * 0.5; // 50% of tipping load is safe rating (ISO standard)
1936+
1937+
// --- VISUALIZATION ---
1938+
// Draw CoG Sphere (Empty)
1939+
color("Magenta")
1940+
translate([0, cog_y_empty, FRAME_Z_OFFSET + 400])
1941+
sphere(d=100);
1942+
1943+
// Console Output
1944+
echo("=========================================");
1945+
echo("STABILITY ANALYSIS");
1946+
echo("=========================================");
1947+
echo("Engine HP:", ENGINE_HP);
1948+
echo("Engine Weight:", w_engine, "kg");
1949+
echo("Total Machine Weight (Empty):", total_weight_empty, "kg");
1950+
echo("CoG Y Position (Empty):", cog_y_empty, "mm from rear");
1951+
echo("Front Axle Position:", _front_y, "mm");
1952+
echo("Rear Axle Position:", _rear_y, "mm");
1953+
echo("Wheelbase:", _front_y - _rear_y, "mm");
1954+
echo("Load Distance from Front Axle:", load_dist_from_axle, "mm");
1955+
echo("TIPPING LOAD (at current arm reach):", tipping_load_kg, "kg");
1956+
echo("RATED OPERATING CAPACITY (50%):", rated_load_kg, "kg");
1957+
echo("=========================================");
1958+
}
1959+
17801960
// =============================================================================
17811961
// WHEEL ASSEMBLIES
17821962
// =============================================================================
@@ -1785,13 +1965,36 @@ module wheel_assemblies() {
17851965
if (show_wheels) {
17861966
// Wheel positions - outside the outer panels with clearance
17871967
// Wheel centers at Z=WHEEL_RADIUS so bottom of wheel touches Z=0 (ground)
1788-
// Front wheels moved back so front of tire aligns with front of machine body
1789-
front_wheel_y = WHEEL_BASE - WHEEL_RADIUS; // Front of tire at Y=WHEEL_BASE
1968+
1969+
// Front wheels: Standard position relative to frame front
1970+
front_wheel_y = WHEEL_BASE - WHEEL_RADIUS;
1971+
1972+
// Rear wheels: Moved forward of cylinder mount
1973+
// Cylinder mount is at LIFT_CYL_BASE_Y
1974+
// We want rear axle > LIFT_CYL_BASE_Y
1975+
// Add clearance for cylinder body and wheel rotation
1976+
rear_wheel_target_y = LIFT_CYL_BASE_Y + WHEEL_RADIUS + 100;
1977+
1978+
// Check for collision with front wheels
1979+
// Ensure minimum wheelbase
1980+
min_wheelbase = WHEEL_DIAMETER + 50; // 50mm gap between tires
1981+
1982+
// Let's calculate the max Y the rear wheel can be at without hitting front wheel
1983+
max_rear_y = front_wheel_y - min_wheelbase;
1984+
1985+
// Use the target, but clamp to max
1986+
final_rear_y = min(rear_wheel_target_y, max_rear_y);
1987+
1988+
// Warn if we couldn't meet the "in front of cylinder" constraint due to space
1989+
if (final_rear_y < LIFT_CYL_BASE_Y) {
1990+
echo("WARNING: Rear wheels could not be placed in front of cylinder mount due to lack of space!");
1991+
}
1992+
17901993
positions = [
17911994
[-WHEEL_X_OFFSET, front_wheel_y, WHEEL_RADIUS], // Front left
17921995
[ WHEEL_X_OFFSET, front_wheel_y, WHEEL_RADIUS], // Front right
1793-
[-WHEEL_X_OFFSET, 0, WHEEL_RADIUS], // Rear left
1794-
[ WHEEL_X_OFFSET, 0, WHEEL_RADIUS] // Rear right
1996+
[-WHEEL_X_OFFSET, final_rear_y, WHEEL_RADIUS], // Rear left
1997+
[ WHEEL_X_OFFSET, final_rear_y, WHEEL_RADIUS] // Rear right
17951998
];
17961999

17972000
for (i = [0:3]) {
@@ -2249,6 +2452,7 @@ module lifetrac_v25_assembly() {
22492452
if (show_frame) {
22502453
base_frame();
22512454
control_housing();
2455+
engine();
22522456
}
22532457

22542458
wheel_assemblies();
@@ -2264,6 +2468,7 @@ module lifetrac_v25_assembly() {
22642468
// =============================================================================
22652469

22662470
lifetrac_v25_assembly();
2471+
calculate_cog();
22672472

22682473
// Ground plane reference at Z=0 (shifted down slightly so wheels visibly touch it)
22692474
color("Green", 0.2)

LifeTrac-v25/mechanical_design/openscad/lifetrac_v25_params.scad

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,19 @@ _calc_base_y = _attach_y_world - (_attach_z_world - LIFT_CYL_BASE_Z) / tan(_targ
125125
// 5. Clamp Y to valid frame range (0 to WHEEL_BASE/2)
126126
LIFT_CYL_BASE_Y = max(50, min(WHEEL_BASE/2, _calc_base_y));
127127

128+
// =============================================================================
129+
// ENGINE CONFIGURATION
130+
// =============================================================================
131+
132+
ENGINE_HP = 25; // Desired horsepower (e.g., 25HP V-Twin)
133+
// Estimate engine weight (kg) based on HP (approximate for small engines)
134+
// Base 30kg + 1.2kg per HP
135+
ENGINE_WEIGHT_KG = 30 + (ENGINE_HP * 1.2);
136+
137+
// Engine Position (Middle near back)
138+
ENGINE_POS_Y = 400; // Forward from rear of frame
139+
ENGINE_POS_Z = FRAME_Z_OFFSET + 200; // Height above frame bottom
140+
128141
// =============================================================================
129142
// BUCKET DIMENSIONS
130143
// =============================================================================

0 commit comments

Comments
 (0)