Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Autonomous R2D2


  • Please log in to reply
21 replies to this topic

#1 sjmill01

sjmill01

    Advanced Member

  • Members
  • PipPipPip
  • 45 posts
  • LocationSouth Carolina

Posted 23 November 2013 - 02:26 AM

This is a very early showcase of the code I wrote for the Netduino to control an R2D2.  To build the R2 this far, it took over 2 years of research, wood, plastic, and aluminum work.  I'm now back in my wheelhouse with programming and am loving the Netduino Plus 2.

 

http://www.youtube.c...h?v=vLJb6dg859Y



#2 Gutworks

Gutworks

    Advanced Member

  • Members
  • PipPipPip
  • 363 posts
  • LocationOttawa, Ontario

Posted 23 November 2013 - 06:57 AM

That's brilliant! What an amazing job. Where can I more information on your Netdunio R2D2? You mentioned a builders log in the video but I didn't see any links to a website. Fantastic job sjmill01. Very impressive. 



#3 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 23 November 2013 - 07:29 AM

That is super cool, sjmill01. We just liked the video and tweeted about it. Is this something you'd even bring to an event like MakerFaire? What are your next steps with the design? Chris

#4 sjmill01

sjmill01

    Advanced Member

  • Members
  • PipPipPip
  • 45 posts
  • LocationSouth Carolina

Posted 23 November 2013 - 02:15 PM

The site astromech.net houses a forum for R2 Builders.  It was started around 1999.  There are many, many approaches for building one using many different types of materials. 

 

Most active posters use RC control only for their R2, but I wanted to make choreographed routines to add a little magic.  With the autonomy, you feel like you have a toddler in the house - any moment he is going to bump his head or hurt a pet. 

 

I started on him 3 years ago, but its research that slows it down mostly.  It only took about a month from purchasing the Netduino to make him run on his own.

 

For events, my intent is to go to schools to promote science and visit kids at hospitals.  What is neat about R2 is that he is a happy machine.  He could roll through a prison and all the inmates would be smiling.  I don't have any experience with MakerFaire or know much about it.

 

One thing to note, a lot of folks in various forums said the Netduino Plus 2 couldn't handle this scope, but the specs of the card indicated differently.  So, I gave it a try.  I think the nay saying is probably related to people not being comfortable in C#.  I couldn't have pulled this off without 4 threads running simultaneously.

 

The next phases in design are to add two more proximity sensors, add a voice recognition card, and then add servos to his panels.  The panels in the dome will flip up comically on queue.  I'll offload the servo control to another board and communicate to it with serial from the Netduino Plus 2.

 

Thanks for the encouragement and help in prior posts.

 

See ya,

Sean



#5 Giuliano

Giuliano

    Advanced Member

  • Members
  • PipPipPip
  • 361 posts
  • LocationSimi Valley, CA

Posted 23 November 2013 - 07:49 PM

This is super awesome, nice job!!!



#6 vader7071

vader7071

    Advanced Member

  • Members
  • PipPipPip
  • 132 posts
  • LocationDothan, AL

Posted 05 December 2013 - 11:06 PM

@SJMILL01, OH do I ever need to keep an eye on this!

 

Do you mind a few questions?  I promise not to try and blast too many.  :D



#7 sjmill01

sjmill01

    Advanced Member

  • Members
  • PipPipPip
  • 45 posts
  • LocationSouth Carolina

Posted 08 December 2013 - 01:18 PM

Vader7071, ask away.



#8 vader7071

vader7071

    Advanced Member

  • Members
  • PipPipPip
  • 132 posts
  • LocationDothan, AL

Posted 08 December 2013 - 09:28 PM

Oh there are so many...lol

 

As promised, here are just a few.

 

1)  Are you using the Ping ultra-sonic sensor for the proximity sensor?

2)  Are you using a Netduino to control your speed controllers (sending RC controller signal to Netduino and then Netduino to speed controllers) and that is how you are able to swap between RC and autonomous control?



#9 sjmill01

sjmill01

    Advanced Member

  • Members
  • PipPipPip
  • 45 posts
  • LocationSouth Carolina

Posted 09 December 2013 - 10:09 PM

1)

 

SEN-00639[color=rgb(51,51,51);font-family:Arial, 'MS Sans Serif', Geneva, sans-serif;] [/color]
[color=rgb(51,51,51);font-family:Arial, 'MS Sans Serif', Geneva, sans-serif;]Ultrasonic Range Finder - Maxbotix LV-EZ1[/color]

 

 

 

[color=rgb(51,51,51);font-family:Arial, 'MS Sans Serif', Geneva, sans-serif;]2)  Yep.  It takes 4 threads to bring it all together. I attached the code below..[/color]

 

[color=rgb(63,63,63);font-family:verdana, geneva, lucida, 'lucida grande', arial, helvetica, sans-serif;font-size:13.63636302947998px;background-color:rgb(199,218,235);]These are the current features:[/color]

[color=rgb(63,63,63);font-family:verdana, geneva, lucida, 'lucida grande', arial, helvetica, sans-serif;font-size:13.63636302947998px;background-color:rgb(199,218,235);]General:[/color]

  • Selectable Modes: Stick Control, Autonomous Movement
  • Sound: triggers on distance in either mode
  • 4 Threads running at all times: Determining/playing a sound based on distance from an object or time, determining distance from objects in front, setting preferred speed and direction based on the current mode and distance from an object, sending motor drivers pulses based on the preferred speed and direction

[color=rgb(63,63,63);font-family:verdana, geneva, lucida, 'lucida grande', arial, helvetica, sans-serif;font-size:13.63636302947998px;background-color:rgb(199,218,235);]Stick Control:[/color]

  • Turning speed scaled down so one doesn't have to be an expert to drive him.
  • Forward movement interlocks if an object is ~2 feet in front

[color=rgb(63,63,63);font-family:verdana, geneva, lucida, 'lucida grande', arial, helvetica, sans-serif;font-size:13.63636302947998px;background-color:rgb(199,218,235);]Autonomous Mode:[/color]

  • Object in zone 3, R2 goes forward and turns in the same direction as his last turn. If he doesn't find a clear path in a few seconds, he stops and yells.
  • Object in zone 2, R2 stops and spins in a circle in a random direction. If he doesn't find a clear path in a few seconds, he stops and yells.
  • Objects in zone 1, R2 stops and yells. He then backs up and turns 90 degrees.
  • If object is in zone 14 and the pitch knob is turned up, he'll do a dance to the 8-bit sounding Cantina song.
  • All other zones, R2 drives forward...the further from an object, the faster he goes.

[color=rgb(63,63,63);font-family:verdana, geneva, lucida, 'lucida grande', arial, helvetica, sans-serif;font-size:13.63636302947998px;background-color:rgb(199,218,235);]Future:[/color]
[color=rgb(63,63,63);font-family:verdana, geneva, lucida, 'lucida grande', arial, helvetica, sans-serif;font-size:13.63636302947998px;background-color:rgb(199,218,235);]Add dome movement[/color]
[color=rgb(63,63,63);font-family:verdana, geneva, lucida, 'lucida grande', arial, helvetica, sans-serif;font-size:13.63636302947998px;background-color:rgb(199,218,235);]Add light show control[/color]

using System;using System.Net;using System.Net.Sockets;using System.Threading;using Microsoft.SPOT;using Microsoft.SPOT.Hardware;using SecretLabs.NETMF.Hardware;using SecretLabs.NETMF.Hardware.Netduino;using System.IO.Ports;namespace R2D2Netduino{    public class Program    {   //v1.0 by Sean J. Miller        static AnalogInput pin_ProximitySensorSignal_Front=new AnalogInput(AnalogChannels.ANALOG_PIN_A0); //a short name for a critical variable I'll be using.        static bool sound_playing = false;  //this is used to prevent clipping a sound that is playing if the person moves from zone to zone.        const bool INTERRUPT_TRACK=true;        const bool DONT_INTERRUPT_TRACK = false;        static SerialPort serial_MP3TriggerBoard = new SerialPort(SerialPorts.COM1, 38400, Parity.None, 8, StopBits.One);        static double temp_front_pin_voltage = 0;        static bool thumbstick_controlled = true;//The default mode where he beeps based on the proximity sensor        static bool pitch_knob_set_on = false;//If autonomous mode is off, pitch_knob_set_on can be flipped on to beep randomly        static bool choreography_routine = false;//This is used to check to see if a pre-programmed movement & sound routine is currently playing to prevent interrupt by another thread.        static int preferred_dome = 0;//The prefferred direction and speed of the dome as determined by the thumbstick or method        static int current_dome = 0;//The last set speed and direction of the dome for comparison logic.        static int preferred_straight_speed = 0;//The preferred direction and speed of the robot in a straight line.  0 is stop -100 is full backwards. 100 is full forwards.        static int current_straight_speed = 0;//The last set speed and direction of the robot in a straight line.        static int preferred_direction = 0;//The preferred direction and speed of the robot turn.  0 is stop -100 is full clockwise. 100 is full counterclockwise.        static int current_direction = 0;//The last set speed and direction of the robot turn.        static double K = 1; //for ramping of motor control        static System.Threading.Timer timer_sound_reset;//this is to auto reset sound_playing to false after 20 seconds just in case I miss the serial RX event.        static TimerCallback myTimerCallbackMethod;        static double distance_increment = .06; //distance increment is an analog voltage increment.  Used Debug.Print to assess values for the zones.        static int front_distance_zone = 10;//this is to track which increment zone the object is in per the proximity sensor.  It starts out at 10 arbitrarily.        static Random random_number = new Random();        static InterruptPort inputPort5_Gear, inputPort6_PitTrim, inputPort7_LeftThumbHorizontalChannel3, inputPort12_RightThumbChannel1, inputPort13_RightThumbChannel2;        static long leadingEdge5 = 0;//Used to time the PWM from Channel 5 on pin 5        static long leadingEdge6 = 0;//Used to time the PWM from Channel 6 on pin 6        static long leadingEdge7 = 0;//Used to time the PWM from Channel 3 on pin 7        static long leadingEdge12 = 0;//Used to time the PWM from Channel 1 on pin 12        static long leadingEdge13 = 0;//Used to time the PWM from Channel 2 on pin 13        static long currentPulseWidth7, currentPulseWidth12, currentPulseWidth13; //used to pulse out PWM on pins 7, 12, and 13 (dome, speed, direction)        static SecretLabs.NETMF.Hardware.PWM PWM_DomeMovementToPin9, PWM_StraightMovementToPin10, PWM_TurnMovementToPin11;  //Dome, Straight Line, and Turn pulses to motor drivers        static Thread movementThread, soundThread, distanceThread, pulseThread;//One thread to handle dome and feet motors and the other is to handle playing sounds.        public static void Main()        {   //This initiates the program and starts the forever looping routine named update.            //R2D2Netduino.Program my_program = new R2D2Netduino.Program();            //my_program.init();            myTimerCallbackMethod = new TimerCallback(clearTime);            timer_sound_reset = new System.Threading.Timer(myTimerCallbackMethod, null, 20000, 60000);//used to reset audio the Netduino misses the X sent back by the MP3Trigger Board            serial_MP3TriggerBoard.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler_TriggerBoard);            serial_MP3TriggerBoard.Open(); serial_MP3TriggerBoard.Flush();            inputPort5_Gear = new InterruptPort(Pins.GPIO_PIN_D5, true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);            inputPort5_Gear.OnInterrupt += new NativeEventHandler(inputPort_OnInterrupt5);            inputPort6_PitTrim = new InterruptPort(Pins.GPIO_PIN_D6, true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);            inputPort6_PitTrim.OnInterrupt += new NativeEventHandler(inputPort_OnInterrupt6);            inputPort7_LeftThumbHorizontalChannel3 = new InterruptPort(Pins.GPIO_PIN_D7, true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);            inputPort7_LeftThumbHorizontalChannel3.OnInterrupt += new NativeEventHandler(inputPort_OnInterrupt7);            inputPort12_RightThumbChannel1 = new InterruptPort(Pins.GPIO_PIN_D12, true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);            inputPort12_RightThumbChannel1.OnInterrupt += new NativeEventHandler(inputPort_OnInterrupt12);            inputPort13_RightThumbChannel2 = new InterruptPort(Pins.GPIO_PIN_D13, true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);            inputPort13_RightThumbChannel2.OnInterrupt += new NativeEventHandler(inputPort_OnInterrupt13);            PWM_DomeMovementToPin9 = new SecretLabs.NETMF.Hardware.PWM(Pins.GPIO_PIN_D9);            PWM_StraightMovementToPin10 = new SecretLabs.NETMF.Hardware.PWM(Pins.GPIO_PIN_D10);            PWM_TurnMovementToPin11 = new SecretLabs.NETMF.Hardware.PWM(Pins.GPIO_PIN_D11);            soundThread = new Thread(performSound);            soundThread.Start();//start the thread that beeps and bloops            movementThread = new Thread(performMovements);            movementThread.Start();//start the thread that handles movement            distanceThread = new Thread(checkAndSetDistance);            distanceThread.Start();//start the thread that categorizes distance from objects into zones which is used to rome autonomously or fire interlocks on thumbstick control            pulseThread = new Thread(pulseToMotorDrivers);            pulseThread.Start();            Thread.Sleep(Timeout.Infinite);//sleep and let the threads do their magic        }        static void checkAndSetDistance()        {   //Identify what zone the object is that is infront of the robot.  It runs in the distanceThread, so            //we will always be aware of the distance from the sensors.            while (true)            {                temp_front_pin_voltage = pin_ProximitySensorSignal_Front.Read();                                if (temp_front_pin_voltage < distance_increment) front_distance_zone = 1;                else if (temp_front_pin_voltage < 2 * distance_increment) front_distance_zone = 2;                else if (temp_front_pin_voltage < 3 * distance_increment) front_distance_zone = 3;                else if (temp_front_pin_voltage < 4 * distance_increment) front_distance_zone = 4;                else if (temp_front_pin_voltage < 5 * distance_increment) front_distance_zone = 5;                else if (temp_front_pin_voltage < 6 * distance_increment) front_distance_zone = 6;                else if (temp_front_pin_voltage < 7 * distance_increment) front_distance_zone = 7;                else if (temp_front_pin_voltage < 8 * distance_increment) front_distance_zone = 8;                else if (temp_front_pin_voltage < 9 * distance_increment) front_distance_zone = 9;                else if (temp_front_pin_voltage < 10 * distance_increment) front_distance_zone = 10;                else if (temp_front_pin_voltage < 11 * distance_increment) front_distance_zone = 11;                else if (temp_front_pin_voltage < 12 * distance_increment) front_distance_zone = 12;                else if (temp_front_pin_voltage < 13 * distance_increment) front_distance_zone = 13;                else if (temp_front_pin_voltage < 14 * distance_increment) front_distance_zone = 14;                Thread.Sleep(250);            }        }        static void performSound()        {   //This is the main program that monitors sensors and loops infinitely.            int last_front_distance_zone = -1;//set initially to something that will ensure sound is triggered initially.            int ticker = 0;//used to trigger on time.            while (true)            {                                if (last_front_distance_zone!=front_distance_zone)                {   //trigger on distance                    PlayTrack(randomInRange(front_distance_zone),DONT_INTERRUPT_TRACK);                    last_front_distance_zone=front_distance_zone;//this keeps him from chattering too much.                }                                if (ticker++>30) {                    //trigger on time                    PlayTrack(random_number.Next(182),DONT_INTERRUPT_TRACK);                    ticker = 0;                }                Thread.Sleep(500);            }        }        static void setDomeStickPreferredSpeedAndDirection()        {            if (currentPulseWidth7 > 14810)            {                preferred_dome = (int)(100 - 100 * (18960 - currentPulseWidth7) / (18960 - 14810));            }            else if (currentPulseWidth7 > 14800 && currentPulseWidth7 < 14820)            {                preferred_dome = 0;            }            else            {                preferred_dome = (int)(100 * (currentPulseWidth7 - 14820) / (14820 - 10640));            }        }        static void setRobotStickPreferredSpeed()        {            if (currentPulseWidth12 > 14810)            {//Forward                if (front_distance_zone==1)                {                   preferred_straight_speed = 0;//this is to interlock forward movement if something is super close.                   PlayTrack(132, DONT_INTERRUPT_TRACK);                }  else preferred_straight_speed = (int)(100 - 100 * (18960 - currentPulseWidth12) / (18960 - 14810));            }            else if (currentPulseWidth12 > 14800 && currentPulseWidth12 < 14820)            {//stopped                preferred_straight_speed = 0;            }            else            {   //reverse                  preferred_straight_speed = (int)(100 * (currentPulseWidth12 - 14820) / (14820 - 10640));            }        }        static void setRobotStickPreferredDirection()        {            if (currentPulseWidth13 > 14810)            {                preferred_direction = (int)(100 - 100 * (18960 - currentPulseWidth13) / (18960 - 14810));            }            else if (currentPulseWidth13 > 14800 && currentPulseWidth12 < 14820)            {                preferred_direction = 0;            }            else            {                preferred_direction = (int)(100 * (currentPulseWidth13 - 14820) / (14820 - 10640));            }        }        static void pulseToMotorDrivers()        {            while(true)            {                //Pulse Straight Speed                if (preferred_straight_speed == 0)                {                    PWM_StraightMovementToPin10.SetPulse(20000, (uint)(1500));                    current_straight_speed = 0;                }                else if (System.Math.Abs(preferred_straight_speed - current_straight_speed) >5)                {                    current_straight_speed = current_straight_speed + (int)(K * (preferred_straight_speed - current_straight_speed));                    PWM_StraightMovementToPin10.SetPulse(20000, (uint)(1500 + (5 * current_straight_speed)));                }                        //Pulse Direction                if (preferred_direction == 0)                {                    PWM_TurnMovementToPin11.SetPulse(20000, (uint)(1500));                    current_direction = 0;                 }                else if (System.Math.Abs(preferred_direction - current_direction) >5)                {                    current_direction = current_direction + (int)(K * (preferred_direction - current_direction));                    PWM_TurnMovementToPin11.SetPulse(20000, (uint)(1500 + (3 * current_direction)));                }                //Pulse dome                if (System.Math.Abs(preferred_dome - current_dome) > 5)                {                    PWM_DomeMovementToPin9.SetPulse(20000, (uint)(1500 + (5 * preferred_dome)));                    current_dome = preferred_dome;                }                     Thread.Sleep(100);            }        }        static void performMovements()        {            while (true)            {                if (thumbstick_controlled)                {   //If the gear switch is down, then it will turn off the choreography routine and                     //use the sticks on the controller for input.                    if (!choreography_routine)//this lets the choreography routine finish up before taking over with stick controll.                    {                        setDomeStickPreferredSpeedAndDirection();                        setRobotStickPreferredDirection();                        setRobotStickPreferredSpeed();                    }                }                else if (!choreography_routine)                {   //if the gear switch is flipped up and the pitch is turned up, it will trigger                    //the choreography if not already triggered.                                            choreography_routine = true;                        //performCantinaDance();  someday, I need to add another remote trigger                        romeAroundAutonomously();                        choreography_routine = false;                        sound_playing = false;                        PlayTrack(2, DONT_INTERRUPT_TRACK);                        Thread.Sleep(1000);//give some time to remember to flip to thumbstick control if desired.                                    }                else stopRobot();                                 Thread.Sleep(100);            }        }        static private void stopRobot()        {   //this is written with a Dimension Engineering Sabertooth 2x.. Motor Driver in Mind            preferred_straight_speed=0;            preferred_direction=0;        }        static private void romeAroundAutonomously()        {            PlayTrack(177, INTERRUPT_TRACK);            int ii = 0; int last_direction = 50;            stopRobot();//if already moving...stop it.            Thread.Sleep(500);//give his bones a rest before taking off again.            while (true)            {                   if (thumbstick_controlled) { stopRobot(); return; }                if (front_distance_zone == 1)                {                    stopRobot();//just about hit something, so stop and yell!                    PlayTrack(132, INTERRUPT_TRACK);                    Thread.Sleep(1000);                    preferred_straight_speed = -55;                    preferred_direction = last_direction;                    Thread.Sleep(1500);                    stopRobot();                    Thread.Sleep(3000);//give the owner time to flip to thumbstick control                    return;                }                else if (front_distance_zone == 2)                {   //if we are in Zone 2, we need to just stop and turn until we find a hole to drive through.                    preferred_straight_speed=0;                    preferred_direction=80-160*(random_number.Next(2));                    last_direction = preferred_direction;                                        ii = 0;                    while (front_distance_zone < 3 && ii < 55)                    {   //if we don't find a hole to drive through in a few seconds of turning, just yell for help.                         ii++;                        Thread.Sleep(250);                        if (thumbstick_controlled) { stopRobot(); return; }                    }                    stopRobot();                    if (ii >= 55)                    {//couldn't find a hole, so yell it up to have the owner take control.                        PlayTrack(132, INTERRUPT_TRACK);                        Thread.Sleep(4000);//give the owner time to respond with flipping back to control.                        return;//get out of this movement.                    }                    preferred_straight_speed=30;//if we made it here, then we found a hole...move slowly ahead.                                    }                else if (front_distance_zone == 3)                {   //if we are in zone 3, we need to slow down and turn sharp until we find a hole to drive through.                    preferred_straight_speed=30;                    preferred_direction = (int)((double)last_direction*1.1);                                        if (thumbstick_controlled) { stopRobot(); return; }                    ii = 0;                    while (front_distance_zone < 4 && ii < 55)                    {   //if we don't find a hole to drive through in a few seconds of turning, just yell for help.                         ii++;                        if (front_distance_zone < 3)                        {                            stopRobot();                            break;                        }                        Thread.Sleep(100);                        if (thumbstick_controlled) { stopRobot(); return; }                    }                    stopRobot();                    if (ii >= 55)                    {//couldn't find a hole, so yell it up to have the owner take control.                        PlayTrack(132, INTERRUPT_TRACK);                        Thread.Sleep(2000);//give the owner time to respond with flipping back to control.                        return;//get out of this movement.                    }                    preferred_straight_speed=35;                }                else if (front_distance_zone == 4)                {                    preferred_direction=0;                    preferred_straight_speed=35;                }                else if (front_distance_zone == 5)                {                    preferred_direction=0;                    preferred_straight_speed=38;                }                else if (front_distance_zone == 6)                {                    preferred_direction = 0;                    preferred_straight_speed = 40;                }                else if (front_distance_zone == 7)                {                    preferred_direction = 0;                    preferred_straight_speed = 42;                }                else if (front_distance_zone == 8)                {                    preferred_direction = 0;                    preferred_straight_speed = 45;                }                else if (front_distance_zone == 9)                {                    preferred_direction = 0;                    preferred_straight_speed = 48;                }                else if (front_distance_zone == 10)                {                    preferred_direction = 0;                    preferred_straight_speed = 50;                }                else if (front_distance_zone == 11 || front_distance_zone == 12 || front_distance_zone == 13)                {                    preferred_direction = 0;                    preferred_straight_speed = 52;                }                else if (pitch_knob_set_on)                {                    stopRobot();                    Thread.Sleep(1000);                    performCantinaDance();                    Thread.Sleep(2000);                }                Thread.Sleep(100);            }        }        static private void performCantinaDance()        {            stopRobot();            PlayTrack(13,INTERRUPT_TRACK);            preferred_direction=(80+random_number.Next(20));            Thread.Sleep(1000 + random_number.Next(2000));            preferred_direction=(0);            if (thumbstick_controlled) return;                        Thread.Sleep(500 + random_number.Next(250));            if (thumbstick_controlled) return;            preferred_direction=(-(80 + random_number.Next(20)));            Thread.Sleep(1000 + random_number.Next(2000));            preferred_direction=(0);            if (thumbstick_controlled) return;            Thread.Sleep(500+random_number.Next(250));            preferred_direction=(80 + random_number.Next(20));            Thread.Sleep(1000 + random_number.Next(2000));            preferred_direction=(0);            if (thumbstick_controlled) return;            Thread.Sleep(500 + random_number.Next(250));            preferred_direction=(-(80 + random_number.Next(20)));            Thread.Sleep(1000 + random_number.Next(2000));            stopRobot();            if (thumbstick_controlled) return;            Thread.Sleep(500 + random_number.Next(250));            preferred_direction = (-(80 + random_number.Next(20)));            Thread.Sleep(1000 + random_number.Next(2000));            stopRobot();            if (thumbstick_controlled) return;            Thread.Sleep(500 + random_number.Next(250));            preferred_direction = (-(80 + random_number.Next(20)));            Thread.Sleep(1000 + random_number.Next(2000));            stopRobot();            if (thumbstick_controlled) return;                        Thread.Sleep(2000);        }        static void inputPort_OnInterrupt5(uint data1, uint data2, DateTime time)        {   //RC Channel 5 Reading in (Gear)            if (data2 == 1) leadingEdge5 = time.Ticks; else if ((long)((time.Ticks - leadingEdge5)) >= 17000) thumbstick_controlled = false; else thumbstick_controlled = true;        }        static void inputPort_OnInterrupt6(uint data1, uint data2, DateTime time)        {//RC Channel 6 Reading in (Pit Trim)            if (data2 == 1) leadingEdge6 = time.Ticks; else if ((long)((time.Ticks - leadingEdge6)) > 16000) pitch_knob_set_on = false; else pitch_knob_set_on = true;        }        static void inputPort_OnInterrupt12(uint data1, uint data2, DateTime time)        {   //RC Channel 12            if (data2 == 1) leadingEdge12 = time.Ticks; else currentPulseWidth12 = (time.Ticks - leadingEdge12);        }        static void inputPort_OnInterrupt13(uint data1, uint data2, DateTime time)        {   //RC Channel 13            if (data2 == 1) leadingEdge13 = time.Ticks; else currentPulseWidth13 = (time.Ticks - leadingEdge13);        }        static void inputPort_OnInterrupt7(uint data1, uint data2, DateTime time)        {//RC Channel 3            if (data2 == 1) leadingEdge7 = time.Ticks; else currentPulseWidth7 = (time.Ticks - leadingEdge7);        }        static private void clearTime(object state)        {   //This is called by timer_sound_reset thread every 20 seconds as a safety net for the serial event handler not catching the "track ended" response from the trigger board.            sound_playing = false;            timer_sound_reset.Change(20000, 60000);//resets the timer.        }        static private void DataReceivedHandler_TriggerBoard(object sender, SerialDataReceivedEventArgs e)        {   //This is triggered whenever some TTL is piped into Digital pin 0.            if (((SerialPort)sender).ReadByte() == 88) sound_playing = false;//check for the Letter X from MP3Triggerboard.  This signals the track is finsihed playing.        }        static private int randomInRange(int the_distance)        {            return (the_distance * 10 + random_number.Next(10));//This gives a random number to select random tracks within a zone.        }        static private void PlayTrack(int ii, bool interrupt)        {   //Sparkfun MP3TriggerBoard Only!            //first, we ensure we don't interrupt a playing track if so desired.            if (!sound_playing||interrupt==INTERRUPT_TRACK)            {                sound_playing = true;                timer_sound_reset.Change(20000, 60000);//resets the timer.                byte the_track = (byte)(ii);                serial_MP3TriggerBoard.Write(new byte[] { 0x74, the_track }, 0, 2);//write out the serial to the MP3Trigger board which is the letter t followed by a byte value of 1-255.            }        }                static private void setVolume(int ii)        {//Sparkfun MP3TriggerBoard Only!            byte the_setting = (byte)(ii);//according to the MP3Trigger Board, 0 is the loadest and 64 can't be heard.            serial_MP3TriggerBoard.Write(new byte[] { 0x76, the_setting }, 0, 2);        }    }}


#10 vader7071

vader7071

    Advanced Member

  • Members
  • PipPipPip
  • 132 posts
  • LocationDothan, AL

Posted 09 December 2013 - 10:40 PM

Awesome!  Thank You!

 

I am not as experienced as you, but I am working on an automated Holo-emitter movement for all 3 holo's.  A servo for each x and a servo for each y and I am doing a random number generator to randomly adjust servo position and sleep duration, so the holo-emitters will move randomly.  I'll post here once I get it tested and give more details on servo mechanics.

 

I see you are using a Netduino Plus 2.  I am assuming I will need to upgrade from my Netduino std V1?



#11 sjmill01

sjmill01

    Advanced Member

  • Members
  • PipPipPip
  • 45 posts
  • LocationSouth Carolina

Posted 09 December 2013 - 11:10 PM

Not sure if that is the case or not.  I make use of interrupts and PWM pins on the Plus 2.



#12 vader7071

vader7071

    Advanced Member

  • Members
  • PipPipPip
  • 132 posts
  • LocationDothan, AL

Posted 10 December 2013 - 12:23 AM

Ah, looking at the pin outs between the Std V1 and the Plus V2, there is a difference.

 

V2 has 6 PWM pins while the Std only has 4.

V2 has 4 Serial UART while Std has 2

V2 has 22 D I/O while the std only has 20.

 

So there are some differences.  This tells me that my holo-emitter idea won't work on a std (I need 6 PWM outs), to have it all on one board, I'll have to have a plus, and then no other PWM at all.  But I can at least make 2 emitters work.

 

But looking at the V2 std, it looks like it may work.  It seems the major difference is the ethernet connections, SD card, and extra storage and processor speed.  



#13 Darduino

Darduino

    New Member

  • Members
  • Pip
  • 8 posts

Posted 10 December 2013 - 11:06 AM

Nice project

 

@Vader, if all you need the PWM for is to control your servo, then I suggest you get a breakout broad servo controller, there some that can control up to 8 servo. you conect it to you Netduino Std via SPI or I2C 

 

@sjmill01, Am encouraged by your success cause am working on similar project ( ...well still sourcing component ), Mine is mostly for out door but will work in two modes

 

[color=rgb(40,40,40);font-family:helvetica, arial, sans-serif;]Autonomous [/color]Mode:

in this mode if you send a GPS coordinate ( Lat, Long ) to the Robot ( not as fancy as your, just a flat bed on wheels) through a WiFi Connection, the robot will try to navigate to the location avoiding any ultrasonic detectable object in it path

 

Remote Mode:

In remote mode, the robot will be controlled remotely over a WiFi connection using a Joystick attached to a Windows 8 Laptop ( I hope to be able to control it with a Windows 8 Phone using the in-built accelerometer ) 

 

In both mode the robot will be sending back video from an on-broad  camera, most off the components are currently on order, but I still have problem sourcing an electric motor that will be give me enough power to mover a 100 kg payload, I noticed you used an NPC Wheel chair motor that why i got interested in your project

 

so, how much power where able to get from the Motor, and which ESC did you use, am thnking  of using ESC from RoboteQ SDC2130 



#14 sjmill01

sjmill01

    Advanced Member

  • Members
  • PipPipPip
  • 45 posts
  • LocationSouth Carolina

Posted 12 December 2013 - 12:31 AM

My NPC motors are driven by a Dimension Engineering Sabertooth 2x25.  They have dip switches that allow one to take microcontroller input or a failsafe autocalibrated RC mode (although I don't use that mode).  My robot weighs probably around 100 pounds, but I hear the motors are fine for 160 lb droids in my club.  They are 12V.



#15 vader7071

vader7071

    Advanced Member

  • Members
  • PipPipPip
  • 132 posts
  • LocationDothan, AL

Posted 13 December 2013 - 01:09 AM

Here is what I have gotten for the servo control for the holo-emitters:

using System;using System.Threading;using Microsoft.SPOT;using Microsoft.SPOT.Hardware;using SecretLabs.NETMF.Hardware;using SecretLabs.NETMF.Hardware.Netduino; namespace holo_emitter_control{    public class Program    {        static PWM servo1 = new PWM(Pins.GPIO_PIN_D5);        static PWM servo2 = new PWM(Pins.GPIO_PIN_D6);        static PWM servo3 = new PWM(Pins.GPIO_PIN_D9);        static PWM servo4 = new PWM(Pins.GPIO_PIN_D10);         public static void Main()        {            // write your code here            Thread s1 = new Thread (Servo1);            s1.Start();             Thread s2 = new Thread (Servo2);            s2.Start();             Thread s3 = new Thread (Servo3);            s3.Start();             Thread s4 = new Thread (Servo4);            s4.Start();             Thread.Sleep(Timeout.Infinite);        }         static void Servo1()        {            Random randomGenerator = new Random();             while(true)                {                    uint pos1 = (uint) ((randomGenerator.NextDouble()*1000)+1000);                    int sleep1 = (int) ((randomGenerator.NextDouble()*120000)+1000);                     servo1.SetPulse(20000, pos1);                    Thread.Sleep(sleep1);                }        }         static void Servo2()        {            Random randomGenerator = new Random();             while(true)                {                    uint pos2 = (uint) ((randomGenerator.NextDouble()*1000)+1000);                    int sleep2 = (int) ((randomGenerator.NextDouble()*120000)+1000);                     servo2.SetPulse(20000, pos2);                    Thread.Sleep(sleep2);                 }        }         static void Servo3()        {            Random randomGenerator = new Random();             while(true)                {                    uint pos3 = (uint) ((randomGenerator.NextDouble()*1000)+1000);                    int sleep3 = (int) ((randomGenerator.NextDouble()*120000)+1000);                     servo3.SetPulse(20000, pos2);                    Thread.Sleep(sleep3);                }            }         static void Servo4()        {            Random randomGenerator = new Random();             while(true)                {                    uint pos4 = (uint) ((randomGenerator.NextDouble()*1000)+1000);                    int sleep4 = (int) ((randomGenerator.NextDouble()*120000)+1000);                     servo4.SetPulse(20000, pos4);                    Thread.Sleep(sleep4);                }            }    }}

The sleep commands will have the servo sleep for anywhere from 1 second to 2 minutes.  The position  will be randomly from 0 to 90 degrees.  These servo positions were based out of the book "Getting Started with Netduino"  This only does 2 emitters (one for each x axis and one for each y axis).  Adding a 3rd wouldn't be hard at all, as long as the board has the extra 2 PWM outputs.

 

I'm not as adept as you are Sean yet, but hopefully this can help you.



#16 sjmill01

sjmill01

    Advanced Member

  • Members
  • PipPipPip
  • 45 posts
  • LocationSouth Carolina

Posted 14 December 2013 - 04:13 PM

Great stuff.  Thanks!



#17 vader7071

vader7071

    Advanced Member

  • Members
  • PipPipPip
  • 132 posts
  • LocationDothan, AL

Posted 24 December 2013 - 04:40 PM

Here is some of the dome work I have done.  Whatcha think?

 

http://youtu.be/SB-kVblAhTI



#18 vader7071

vader7071

    Advanced Member

  • Members
  • PipPipPip
  • 132 posts
  • LocationDothan, AL

Posted 30 December 2013 - 10:00 PM

Sean, here is my code for an entire dome controller.  Tell me what you think.

 

It is built on a Netduino 1, so I don't have all the PWM pins I need.  But what I do have is setup for the logic function display and the logic diagnostic display on the back.  These use (6) MAX7219 daisy chained with an 8x8 led matrix per chip (like what i showed in my video above) via SPI.  It has (2) led's on a random PWM to adjust intensity (for use for the 1" color spot next to the holo emitter) and (2) servo controls (for random movement of one of the holo emitters).

 

Notes are built into the code, so hopefully it makes sense.  I have been testing on my desk, just have to get all the pretty parts to assemble in my dome.  One word of warning, the Netduino board can only power 4 of the MAX7219.  After 4, it drags the power supply down and the processor just continually reboots.  I would have the MAX chips and the servos powered from a separate 5VDC source other than the Netduino.

 

The project does have (3) *.cs files to make everything work.  I am still working on shaving it down as much as I can.  My goal is to get rid of 2 of the 3 files.  I have someone working with me on that now.

 

Any suggestions would be greatly appreciated.  Thanks.

Attached Files



#19 sjmill01

sjmill01

    Advanced Member

  • Members
  • PipPipPip
  • 45 posts
  • LocationSouth Carolina

Posted 31 December 2013 - 02:56 PM

Thanks for sharing.  I'm awaiting LEDs to finish my Max7219 drivers.  I'm using PCBs a guy from Astromech designed. (Teeces)  They are designed for the Arduino pro mini/micro, but actually just need 3 pins to drive them (D, L, C of the Max).

 

Did you design the PCBs for your Max7219's?

 

I tried opening your solution with VB C# 2010 Express, but it failed.  Did you use VB2008?  I'm a decent coder of 30 years using notepad, but clumsy with IDE's and which to use.  If I can open the code, I might be able to help refine it.  It would be awesome to make a fully autonomous R2 with just one Netduino including driving lights.

 

I'm new to PWM, too.  Can you explain how PWM pulses an LED?  I don't see how this physically works.  I've seen demos show a slow increase in light.  I don't see how pulsing thousands of times a second does this.

 

I have taken my R2 apart to set up his dome rotation. Rotation is already covered in my code, but I don't have the hardware in place to do it, yet.  He is in pieces and its kind of depressing seeing him like he looked a year of effort ago.  :wacko:

 

Happy New Year

Sean



#20 vader7071

vader7071

    Advanced Member

  • Members
  • PipPipPip
  • 132 posts
  • LocationDothan, AL

Posted 31 December 2013 - 03:36 PM

I use Visual Studio Pro 2012.  I have converted the cs files to txt files and uploaded them here.  Now you can open them in notepad and just copy and paste into a new project.  Keep in mind, all 3 files are needed to make it work.  You have to have them all in the one project.  And keep the name of the files the same.

 

My boards, I cheated.  Bought a bunch of MAX7219 and 8x8 led matrix off eBay.  Got 5 units for $15.  I need 6 total to make my dome work, so I ordered 2 batches.  Comes with all the parts and pieces, all you have to do is assemble.  Here is the link.

 

PWM to an LED works on percentages.  It is fairly simple, yet a little tricky.  Here is the best way I can explain it.  If you set your PWM duty cycle to 50% (that means for every cycle, the signal is on 50% and off 50%) the led will be 50% bright.  If you set it to 75% duty cycle (75% on, 25% off) the led will be 75% bright.  100% = 100% bright (on at all times).

 

To try and explain why it works, I'll use 10 VDC (easy numbers).  We want to dim an LED but all we have is 10 VDC on or off.  No adjustment.  So what we do is send 10 VDC in a PWM output.  This makes the output pulse.  With a 50% duty cycle, we will turn on the 10 VDC for half the time, and the other half it will be off.  On an oscilloscope, you will see the square wave on and off.  But to the LED, it sees "effectively" 5VDC (50% of 10 VDC is 5 VDC).  So now the LED is working on only 5 VDC instead of the full 10.  At a 25% duty cycle, the signal is on only 25% of the time, so the "effective" voltage is 2.5 VDC.  The trick is to do it so fast the LED doesn't have time to fully react to the full 10 VDC signal.  If we did 1 cycle every second, we would see full on, full off.  No dimming.  But at a cycle rate in the KHz range, we "fool" the led into dimming.  If it would help, I think I have an image that may help give a visual of what is going on. To explain the time, let's look at a 100 ms cycle(1 Kilohertz).  In a 25% duty cycle, from t=0 to t=24 ms, we will be outputting 10 VDC.  from t=25 to t=99 ms, we will output 0 VDC.  Then at 100 ms, it starts all over.  That is a definition of a cycle.  And the PWM output is done in the multiple Kilohertz range.  RC servos run on a cycle of 20 KHz.  Using 20 Khz, that is a cycle ever 50 microseconds.  Pretty darn fast.

 

I hope I didn't go too basic on my explanation.  I can assume that with your programming background you know about cycles, but I put it in there for anyone else who is unfamiliar.

Attached Files






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.