Simple Radio Controlled Car


There’s nothing much to say about this, no micro-controller or programming or any fancy stuff involved… it’s just that I’ve always wanted to build a simple RC car for my kids…

And here’s a closer look:

This is using off the shelf RC servo, ESC and transmitter, the only slightly challenging bits being the steering mechanism and coupling the wheels to the motors, for which my new Fabrikator II mini 3D printer was just perfect ! (highly recommend this super cheap, yet very good printer, which allowed me to move from PLA to PETG which is far more resistant and better to work with, at least when used for robots / RC cars where mechanical strength and a little bit of elasticity are important…)

dHub = 20;
hHub = 14;
$fn=180;

difference() {
    union() {
        hub(hHub, dHub, 6.8, 6.3, 1, [6, 3], 3.2, -1.5, 0, 0, 2);
        translate([0, 0, -12]) {
            cylinder(h=5, r=7, $fn=6);
        }
    }
    cylinder(h=100, r=1.4, $fn=36, center=true);
}




// The hub (the part that holds the wheel onto the motor
module hub( height, 
            diameter, 
            boreDiameter, // The diameter of the motor shaft
            shaftFlatDiameter, // The diameter of the motor shaft at the flat, or shaftDiameter for no flat.
            nuts, // The number of set screws/nuts to render, spaced evenly around the shaft 
            nutSize, // Size [indiameter, thickness] of set screw nut. The depth is set automatically.
            setScrewDiameter, // The diameter of the set screw. 3 is the default for an M3 screw.
            setScrewNutOffset=0,	// The distance to offset the nut from the center of the material. -/+ = in/out
            hubZOffset=0, 
            baseFilletRadius=0, // The radius of the fillet (rounded part) between the hub and wheel.
            topFilletRadius=0, // The radius of the fillet (rounded part) at the top of the hub.
            chamferOnly=false, // Set to true to use chamfers (straight 45-degree angles) instead of fillets.
            hexbore=false // Make the bore a hex shaft vs a circle
) 
{

	hubWidth=(diameter-boreDiameter)/2;

	union() {	
		difference() {

			// Main hub shape
			union() {
				difference() {
					union() {
						cylinder( h=height, r=diameter/2, center=true );
			
						// First chamfer the base...
						rotate_extrude() 
							translate([diameter/2,-(height/2)-hubZOffset,0])
								polygon(points=[[0,0],[0,baseFilletRadius],[baseFilletRadius,0]]);
					}
			
					// Chamfer the top...
					rotate_extrude() 
						translate([diameter/2,height/2,0])				
							polygon(points=[[0.5,0.5],[-topFilletRadius-0.5,0.5],[0.5, -topFilletRadius-0.5]]);
			
					// Carve the bottom fillet from the chamfer
					if ( !chamferOnly ) { 
						rotate_extrude() {
							translate([(diameter/2)+baseFilletRadius,
								-(height-(2*baseFilletRadius))/2-hubZOffset,0]) {
								circle(r=baseFilletRadius);
							}
						}
					}
				}

				// Add the fillet back on top of the top chamfer 
				if (!chamferOnly) {
					rotate_extrude() {
						translate([
							(diameter/2)-topFilletRadius,
							(height-(2*topFilletRadius))/2,
							0])				
							circle(r=topFilletRadius);
					}
				}
			}
	
			// Remove the bore
			difference() {
				if (hexbore) {
					cylinder(r=boreDiameter/2/ cos(180/6),h=height+1,$fn=6, center=true);
            } else {
					difference(){
				   	cylinder( h=height+1, r=boreDiameter/2, center=true );
        				translate([(boreDiameter-shaftFlatDiameter+1)/2 + (boreDiameter/2) 
							- (boreDiameter - shaftFlatDiameter),0,0]) 
							cube( [boreDiameter-shaftFlatDiameter+1,boreDiameter,height+2], center=true );
					}
            } 
			}
			
	
			// Remove the captive nut
			for( i=[0:nuts-1] ) {
				if (hexbore) {
					rotate([ 0,0, (360/nuts)*i+30 ])
						translate([boreDiameter/2+(diameter-boreDiameter)/4 +setScrewNutOffset, 0, height/2 - (height+hubZOffset)/2]) {
							rotate([0,-90,0]) {
								captiveNut( nutSize, setScrewDiameter, depth=height/2+1, holeLengthTop=hubWidth/2+setScrewNutOffset
									+(boreDiameter-shaftFlatDiameter), holeLengthBottom=hubWidth+baseFilletRadius-setScrewNutOffset);
							}
						} 
				} else {
					rotate([ 0,0, (360/nuts)*i ])
						translate([boreDiameter/2+(diameter-boreDiameter)/4 +setScrewNutOffset,	0, height/2 - (height+hubZOffset)/2]) {
							rotate([0,-90,0]) { 
								captiveNut( nutSize, setScrewDiameter, depth=height/2+1, holeLengthTop=hubWidth/2+setScrewNutOffset
									+(boreDiameter-shaftFlatDiameter), holeLengthBottom=hubWidth+baseFilletRadius-setScrewNutOffset);
							}
						} 
				}
			}
		}
	}
}


// This is the captive nut module  
module captiveNut( nutSize, setScrewHoleDiameter=3, depth=10, holeLengthTop=5, holeLengthBottom=5 )
{
	render()
	union() {
		nut( nutSize ); 
	
		if ( depth > 0 ) 
			translate([depth/2,0,0]) 
				cube( [depth, nutSize[0], nutSize[1]], center=true );
	
		translate([0,0,-(nutSize[1]/2)-holeLengthBottom]) 
			cylinder(r=setScrewHoleDiameter/2, h=nutSize[1]+holeLengthTop+holeLengthBottom, $fn=15);
	}
}

// nutSize = [inDiameter,thickness]
module nut( nutSize ) { 
	side = nutSize[0] * tan( 180/6 );
	if ( nutSize[0] * nutSize[1] != 0 ) {
		for ( i = [0 : 2] ) {
			rotate( i*120, [0, 0, 1]) 
				cube( [side, nutSize[0], nutSize[1]], center=true );
		}
	}
}
$fn=180;

hub(10, 8, 4, 2.5, 1, [5.5, 2.5], 3.2, 1);




// The hub (the part that holds the wheel onto the motor
module hub( height, 
            diameter, 
            boreDiameter, // The diameter of the motor shaft
            shaftFlatDiameter, // The diameter of the motor shaft at the flat, or shaftDiameter for no flat.
            nuts, // The number of set screws/nuts to render, spaced evenly around the shaft 
            nutSize, // Size [indiameter, thickness] of set screw nut. The depth is set automatically.
            setScrewDiameter, // The diameter of the set screw. 3 is the default for an M3 screw.
            setScrewNutOffset=0,	// The distance to offset the nut from the center of the material. -/+ = in/out
            hubZOffset=0, 
            baseFilletRadius=0, // The radius of the fillet (rounded part) between the hub and wheel.
            topFilletRadius=0, // The radius of the fillet (rounded part) at the top of the hub.
            chamferOnly=false, // Set to true to use chamfers (straight 45-degree angles) instead of fillets.
            hexbore=false // Make the bore a hex shaft vs a circle
) 
{

	hubWidth=(diameter-boreDiameter)/2;

	union() {	
		difference() {

			// Main hub shape
			union() {
				difference() {
					union() {
						cylinder( h=height, r=diameter/2, center=true );
                        // this is custom
                        cube([30, diameter, height], center=true);
			
						// First chamfer the base...
						rotate_extrude() 
							translate([diameter/2,-(height/2)-hubZOffset,0])
								polygon(points=[[0,0],[0,baseFilletRadius],[baseFilletRadius,0]]);
					}
			
					// Chamfer the top...
					rotate_extrude() 
						translate([diameter/2,height/2,0])				
							polygon(points=[[0.5,0.5],[-topFilletRadius-0.5,0.5],[0.5, -topFilletRadius-0.5]]);
			
					// Carve the bottom fillet from the chamfer
					if ( !chamferOnly ) { 
						rotate_extrude() {
							translate([(diameter/2)+baseFilletRadius,
								-(height-(2*baseFilletRadius))/2-hubZOffset,0]) {
								circle(r=baseFilletRadius);
							}
						}
					}
				}

				// Add the fillet back on top of the top chamfer 
				if (!chamferOnly) {
					rotate_extrude() {
						translate([
							(diameter/2)-topFilletRadius,
							(height-(2*topFilletRadius))/2,
							0])				
							circle(r=topFilletRadius);
					}
				}
			}
	
			// Remove the bore
			difference() {
				if (hexbore) {
					cylinder(r=boreDiameter/2/ cos(180/6),h=height+1,$fn=6, center=true);
            } else {
					difference(){
				   	cylinder( h=height+1, r=boreDiameter/2, center=true );
        				translate([(boreDiameter-shaftFlatDiameter+1)/2 + (boreDiameter/2) 
							- (boreDiameter - shaftFlatDiameter),0,0]) 
							cube( [boreDiameter-shaftFlatDiameter+1,boreDiameter,height+2], center=true );
					}
            } 
			}
			
	
			// Remove the captive nut
			for( i=[0:nuts-1] ) {
				if (hexbore) {
					rotate([ 0,0, (360/nuts)*i+30 ])
						translate([boreDiameter/2+(diameter-boreDiameter)/4 +setScrewNutOffset, 0, height/2 - (height+hubZOffset)/2]) {
							rotate([0,-90,0]) {
								captiveNut( nutSize, setScrewDiameter, depth=height/2+1, holeLengthTop=hubWidth/2+setScrewNutOffset
									+(boreDiameter-shaftFlatDiameter), holeLengthBottom=hubWidth+baseFilletRadius-setScrewNutOffset);
							}
						} 
				} else {
					rotate([ 0,0, (360/nuts)*i ])
						translate([boreDiameter/2+(diameter-boreDiameter)/4 +setScrewNutOffset,	0, height/2 - (height+hubZOffset)/2]) {
							rotate([0,-90,0]) { 
								captiveNut( nutSize, setScrewDiameter, depth=height/2+1, holeLengthTop=hubWidth/2+setScrewNutOffset
									+(boreDiameter-shaftFlatDiameter), holeLengthBottom=100);
							}
						} 
				}
			}
		}
	}
}


// This is the captive nut module  
module captiveNut( nutSize, setScrewHoleDiameter=3, depth=10, holeLengthTop=5, holeLengthBottom=5 )
{
	render()
	union() {
		nut( nutSize ); 
	
		if ( depth > 0 ) 
			translate([depth/2,0,0]) 
				cube( [depth, nutSize[0], nutSize[1]], center=true );
	
		translate([0,0,-(nutSize[1]/2)-holeLengthBottom]) 
			cylinder(r=setScrewHoleDiameter/2, h=nutSize[1]+holeLengthTop+holeLengthBottom, $fn=36);
	}
}

// nutSize = [inDiameter,thickness]
module nut( nutSize ) { 
	side = nutSize[0] * tan( 180/6 );
	if ( nutSize[0] * nutSize[1] != 0 ) {
		for ( i = [0 : 2] ) {
			rotate( i*120, [0, 0, 1]) 
				cube( [side, nutSize[0], nutSize[1]], center=true );
		}
	}
}
$fn=180;

difference() {
    hub(10, 20, 4.2, 3, 1, [5.5, 2.5], 3.2, -2, 0, 0, 2);
    for (i = [15:5:45]) {
        translate([0, i, 0]) {
            cylinder(h=100, r=1.5, center=true, $fn=18);
        }
        translate([0, -i, 0]) {
            cylinder(h=100, r=1.5, center=true, $fn=18);
        }        
    }
}



module rounded_cube(d, r) {
    hull() for(p=[[r,r,r], [r,r,d[2]-r], [r,d[1]-r,r], [r,d[1]-r,d[2]-r],
                  [d[0]-r,r,r], [d[0]-r,r,d[2]-r], [d[0]-r,d[1]-r,r], [d[0]-r,d[1]-r,d[2]-r]])
        translate(p) sphere(r, $fn=36);
}




// The hub (the part that holds the wheel onto the motor
module hub( height, 
            diameter, 
            boreDiameter, // The diameter of the motor shaft
            shaftFlatDiameter, // The diameter of the motor shaft at the flat, or shaftDiameter for no flat.
            nuts, // The number of set screws/nuts to render, spaced evenly around the shaft 
            nutSize, // Size [indiameter, thickness] of set screw nut. The depth is set automatically.
            setScrewDiameter, // The diameter of the set screw. 3 is the default for an M3 screw.
            setScrewNutOffset=0,	// The distance to offset the nut from the center of the material. -/+ = in/out
            hubZOffset=0, 
            baseFilletRadius=0, // The radius of the fillet (rounded part) between the hub and wheel.
            topFilletRadius=0, // The radius of the fillet (rounded part) at the top of the hub.
            chamferOnly=false, // Set to true to use chamfers (straight 45-degree angles) instead of fillets.
            hexbore=false // Make the bore a hex shaft vs a circle
) 
{

	hubWidth=(diameter-boreDiameter)/2;

	union() {	
		difference() {

			// Main hub shape
			union() {
				difference() {
					union() {
						cylinder( h=height, r=diameter/2, center=true );
                        // this is custom
                        translate([-diameter/4, -50, -height/2]) {
                            rounded_cube([diameter/2, 100, height/2], 2);
                        }
			
						// First chamfer the base...
						rotate_extrude() 
							translate([diameter/2,-(height/2)-hubZOffset,0])
								polygon(points=[[0,0],[0,baseFilletRadius],[baseFilletRadius,0]]);
					}
			
					// Chamfer the top...
					rotate_extrude() 
						translate([diameter/2,height/2,0])				
							polygon(points=[[0.5,0.5],[-topFilletRadius-0.5,0.5],[0.5, -topFilletRadius-0.5]]);
			
					// Carve the bottom fillet from the chamfer
					if ( !chamferOnly ) { 
						rotate_extrude() {
							translate([(diameter/2)+baseFilletRadius,
								-(height-(2*baseFilletRadius))/2-hubZOffset,0]) {
								circle(r=baseFilletRadius);
							}
						}
					}
				}

				// Add the fillet back on top of the top chamfer 
				if (!chamferOnly) {
					rotate_extrude() {
						translate([
							(diameter/2)-topFilletRadius,
							(height-(2*topFilletRadius))/2,
							0])				
							circle(r=topFilletRadius);
					}
				}
			}
	
			// Remove the bore
			difference() {
				if (hexbore) {
					cylinder(r=boreDiameter/2/ cos(180/6),h=height+1,$fn=6, center=true);
            } else {
					difference(){
				   	cylinder( h=height+1, r=boreDiameter/2, center=true );
        				translate([(boreDiameter-shaftFlatDiameter+1)/2 + (boreDiameter/2) 
							- (boreDiameter - shaftFlatDiameter),0,0]) 
							cube( [boreDiameter-shaftFlatDiameter+1,boreDiameter,height+2], center=true );
					}
            } 
			}
			
	
			// Remove the captive nut
			for( i=[0:nuts-1] ) {
				if (hexbore) {
					rotate([ 0,0, (360/nuts)*i+30 ])
						translate([boreDiameter/2+(diameter-boreDiameter)/4 +setScrewNutOffset, 0, height/2 - (height+hubZOffset)/2]) {
							rotate([0,-90,0]) {
								captiveNut( nutSize, setScrewDiameter, depth=height/2+1, holeLengthTop=hubWidth/2+setScrewNutOffset
									+(boreDiameter-shaftFlatDiameter), holeLengthBottom=hubWidth+baseFilletRadius-setScrewNutOffset);
							}
						} 
				} else {
					rotate([ 0,0, (360/nuts)*i ])
						translate([boreDiameter/2+(diameter-boreDiameter)/4 +setScrewNutOffset,	0, height/2 - (height+hubZOffset)/2]) {
							rotate([0,-90,0]) { 
								captiveNut( nutSize, setScrewDiameter, depth=height/2+1, holeLengthTop=hubWidth/2+setScrewNutOffset
									+(boreDiameter-shaftFlatDiameter), holeLengthBottom=100);
							}
						} 
				}
			}
		}
	}
}


// This is the captive nut module  
module captiveNut( nutSize, setScrewHoleDiameter=3, depth=10, holeLengthTop=5, holeLengthBottom=5 )
{
	render()
	union() {
		nut( nutSize ); 
	
		if ( depth > 0 ) 
			translate([depth/2,0,0]) 
				cube( [depth, nutSize[0], nutSize[1]], center=true );
	
		translate([0,0,-(nutSize[1]/2)-holeLengthBottom]) 
			cylinder(r=setScrewHoleDiameter/2, h=nutSize[1]+holeLengthTop+holeLengthBottom, $fn=36);
	}
}

// nutSize = [inDiameter,thickness]
module nut( nutSize ) { 
	side = nutSize[0] * tan( 180/6 );
	if ( nutSize[0] * nutSize[1] != 0 ) {
		for ( i = [0 : 2] ) {
			rotate( i*120, [0, 0, 1]) 
				cube( [side, nutSize[0], nutSize[1]], center=true );
		}
	}
}

Some generous amount of hot glue to keep together the rear wheel on its axle and that’s about it:

And finally, some testing in the park…

Testing in the park

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: