Here’s a little project that my 6 year old son and I have been working on using some ServoCity’s Actobotics parts. Once we have some code written, we’ll do a codeshare and a bill of materials used.
@ munderhill - I will as soon as I have everything bolted in place… I currently have a bill of material of all the parts that I have purchased but not all of them are being used on this project.
Here is a list of all the ServoCity parts used on this project:
2 - 9.00" Aluminum Channel (585450)
16 - Aluminum Beams (585412)
8 - 90° Quad Hub Mount A (545420)
4 - Standard Hitec ServoBlocks
2 - Hub Spacers (545384)
8 - Hub Spacers (545376)
4 - 90° Hub to Hub Mount (545400)
2 - Aluminum Robot Shocks (585032)(pairs)
2 - 17mm Hex Wheel Adaptors
4 - 0.770" Set Screw Hubs (545576)
4 - Aluminum Clamping Motor Mount
16+ - 6-32 Aluminum Standoffs (Round) (534-3489)
1 - Battery Mount A (585564)
4 - 90° Dual Side Mount (585470)(pairs)
100+ - Locking Washers (91102A007)
6+ - #6 Standard Washers Pack (632144)
2+ - 6-32 Socket Head Machine Screw (Zinc-Plated)( 632106)
2+ - 6-32 Socket Head Machine Screw (Zinc-Plated)( 632110)
4+ - 6-32 Socket Head Machine Screw (Zinc-Plated)( 632114)
2 - 1.50" Aluminum Channel (585440)
4 - 45° Dual Angle Channel Bracket (585426)
2 - Channel Connector Plate A (545532)(2 pack)
2 - Beam Attachment Blocks (585403)(4 pack)
I’ll update as more parts are added
Warning to self - Ensure that you know what you are building first, as you can become overwhelmed with purchasing parts some of which you may never use even though the intent is good.
"Yeah, I could probably use that. I should have one. Maybe two."
Corollary : "Don’t throw that away - it has a perfectly good switch on it"
The first step is admitting you have a problem. And man, do I have a problem.
I’ll take a bet that at least 99.99% of the people on this forum suffer from this very thing. I know I do.
I am due to make a house move overseas soon and the thought of clearing out some of my stuff is scary. My wife already told me I should only take what I need. I told her I need it all
Over the past few weeks we have done some changes to the Rover and we finally received a LidarLite sensor yesterday to integrate into it. So where does this project stand? So far in terms of electronics, here’s what we have
[ol]FezCerb40 II
4x Servos
4x HobbyWing QuicRun 860 dual motor esc
2x IR sensors - Sharp GP2Y0A41SK0F Analog Distance Sensor 4-30cm
LidarLite sensor
1x ONYX 5000mah LIPO battery
[/ol]
We have made a few modifications to the frame as show in these pictures. It is hard to give you a design for the frame as it was design on the go from our heads and not from any plans.
Here is our preliminary code (I will expand as time permits):
using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHI.Pins;
using System.Threading;
using System.Diagnostics;
namespace Rover
{
public enum RunModeOption { Forward, Reverse, Brake }
public class Program
{
const double SYSTEM_VOLTAGE = 3.3;
const double TYPICAL_SERVO_DURATION_RANGE = 1200; //This is based on a typical servo range
const uint ESC_NEUTRAL_POSITION = 1500;
const double IR_THRESHOLD_CUT_OFF_VOLTAGE = 1.20; //Roughly 10cm from object
readonly AnalogInput _frontMountedIRSensor;
readonly AnalogInput _rearMountedIRSensor;
readonly PWM _electronicSpeedControllerA;
readonly PWM _electronicSpeedControllerB;
private RunModeOption _lastRunModeOption;
private bool _isReady;
uint _current;
uint _targetPWMDuration;
private InputPort _escBECReadyMode;
private I2CDevice _lidarSensor;
public Program()
{
_current = ESC_NEUTRAL_POSITION;
_targetPWMDuration = 2100;
_lidarSensor = new I2CDevice(new I2CDevice.Configuration(0x62, 100)); //0x62 is the I2C address of the LIDARLite sensor
_isReady = false;
_lastRunModeOption = RunModeOption.Brake;
_frontMountedIRSensor = new AnalogInput(FEZCerb40II.AnalogInput.PC0);
_rearMountedIRSensor = new AnalogInput(FEZCerb40II.AnalogInput.PC1);
_escBECReadyMode = new InputPort(FEZCerb40II.Gpio.PC9, false, Port.ResistorMode.PullDown);
_electronicSpeedControllerA = new PWM(FEZCerb40II.PwmOutput.PC7, 20000, 1500, PWM.ScaleFactor.Microseconds, false);
_electronicSpeedControllerB = new PWM(FEZCerb40II.PwmOutput.PC6, 20000, 1500, PWM.ScaleFactor.Microseconds, false);
}
public static void Main()
{
Program p = new Program();
p.Run();
Thread.Sleep(Timeout.Infinite);
}
private void Run()
{
//Start PWM outputs
PWM.Start(new PWM[] { _electronicSpeedControllerA, _electronicSpeedControllerB });
//Fire up some child threads
new Thread(CalculateDistanceAndSetDesireSpeedOfRover).Start();
new Thread(SetControllerSpeed).Start();
new Thread(ContinuouslyReadLidarSensor).Start();
_isReady = true;
}
private void ContinuouslyReadLidarSensor()
{
while(true) {
}
}
private void SetControllerSpeed()
{
bool isReversing = false;
RunModeOption lastRunMode = RunModeOption.Brake;
while(true)
{
var escOnState = _escBECReadyMode.Read();
if(_isReady && escOnState)
{
switch(_lastRunModeOption)
{
case RunModeOption.Forward:
if(lastRunMode == RunModeOption.Reverse && isReversing)
{
isReversing = false;
ApplyBrakes();
}
//Gradually increases the speed
if(_current < _targetPWMDuration)
_current += 50;
//Gradually decreases the speed
if(_current > _targetPWMDuration)
_current -= 50;
break;
case RunModeOption.Reverse:
if(lastRunMode == RunModeOption.Forward && !isReversing)
{
isReversing = true;
ApplyBrakes();
}
if(_current > (uint)(ESC_NEUTRAL_POSITION - (TYPICAL_SERVO_DURATION_RANGE / 2)))
_current -= 50;
break;
case RunModeOption.Brake:
default:
ApplyBrakes();
break;
}
}
else
{
ApplyBrakes();
}
_electronicSpeedControllerA.Duration = _electronicSpeedControllerB.Duration = _current;
lastRunMode = _lastRunModeOption;
Thread.Sleep(10);
}
}
private void ApplyBrakes()
{
_current = _electronicSpeedControllerA.Duration = _electronicSpeedControllerB.Duration = ESC_NEUTRAL_POSITION;
if(Debugger.IsAttached)
{
Debug.Print("Applying the brakes");
}
Thread.Sleep(2000);
}
private void CalculateDistanceAndSetDesireSpeedOfRover()
{
while(true)
{
if(!_isReady)
continue;
var frontIRSensor = System.Math.Round(_frontMountedIRSensor.Read() * 100 * SYSTEM_VOLTAGE) / 100;
var rearIRSensor = System.Math.Round(_rearMountedIRSensor.Read() * 100 * SYSTEM_VOLTAGE) / 100;
if((frontIRSensor >= IR_THRESHOLD_CUT_OFF_VOLTAGE && _lastRunModeOption == RunModeOption.Forward) ||
(rearIRSensor >= IR_THRESHOLD_CUT_OFF_VOLTAGE && frontIRSensor >= IR_THRESHOLD_CUT_OFF_VOLTAGE) ||
(rearIRSensor >= IR_THRESHOLD_CUT_OFF_VOLTAGE && _lastRunModeOption == RunModeOption.Reverse))
{
_lastRunModeOption = RunModeOption.Brake;
CalculateAlternateRoute();
}
else if(frontIRSensor >= IR_THRESHOLD_CUT_OFF_VOLTAGE && rearIRSensor < IR_THRESHOLD_CUT_OFF_VOLTAGE)
{
_lastRunModeOption = RunModeOption.Reverse;
}
else
{
//Calculate route before moving forward
if(_lastRunModeOption == RunModeOption.Reverse)
CalculateAlternateRoute();
_lastRunModeOption = RunModeOption.Forward;
if(frontIRSensor > 0.40)
_targetPWMDuration = (uint)(ESC_NEUTRAL_POSITION + (TYPICAL_SERVO_DURATION_RANGE / 8)); //Move forward at 50%
else
_targetPWMDuration = (uint)(ESC_NEUTRAL_POSITION + (TYPICAL_SERVO_DURATION_RANGE / 2)); //Move forward at 100%
}
Thread.Sleep(10);
}
}
private void CalculateAlternateRoute()
{
Debug.Print("Calculating Alternate Route");
//TODO: Lidar sensor
}
}
}