Fez Cerb40 II Rover

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.

9 Likes

Looks like a cool project and I really like that frame! :clap:

That is some impressive framework. I’m guessing your 6 year old will be able to ride it.

I am going to show this to the students we are working with, can’t wait to show them a demo video.

@ kirklynk - Awesome project! Could you provide a list of Actobotics parts that are being 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.

@ Gary - Feel free to and as soon as I have something coded, I will make some videos of it when it is operational.

Fantastic, I can’t wait.

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.

@ kirklynk - Thanks!

Ain’t that the truth!

1 Like

I have that exact problem myself!

Oh! Shiny!

I’ll have two of those. :smiley:

1 Like

"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 :slight_smile:

1 Like

And some more, but just a few bits more, not much really… :whistle:

1 Like

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
		}
	}
}

7 Likes

@ kirklynk - Looking good! I’m looking forward to some video!

I saw it a few ago at a local event, it’s pretty cool.