@@ -89,6 +89,19 @@ FRAME_Z_OFFSET = GROUND_CLEARANCE;
8989// Calculate wheel X offset - wheels positioned outside the outer panels
9090WHEEL_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
22662470lifetrac_v25_assembly();
2471+ calculate_cog();
22672472
22682473// Ground plane reference at Z=0 (shifted down slightly so wheels visibly touch it)
22692474color ("Green" , 0.2 )
0 commit comments