Quantcast
Channel: Arduino-Pi Ramblings
Viewing all 47 articles
Browse latest View live

Mounting the OSLRF on Bottwo - Adding to my ranging arsenal

$
0
0
I've finally mounted the awesome Open Source Laser Range Finder (OSLRF01) onto Bottwo with a panning servo to provide mapping functionality.

I was previously attempting to do this with the Sonar, but was stymied by limited range and cone size.

The narrow beam and greater sensing distance of the LIDAR will give me the ability to accurately map out a room in near real time, and then use the other sensors for closer proximity measurements.

How much is too much? 

Yeah, I heard that!    Yes, I've got four  Sharp IR Sensors, two front and back for collision detection, two left and right for following walls at a specific distance.  
I've now got two more short range IR sensors  front and back facing the floor... so we don't fall down stairs again.


The purpose for keeping the front and rear panning MaxSonar is simply to fill the near field void that the LIDAR does not cover.  Because of the distance between the optics, the OSLRF cannot see closer than 1/2 a meter.  Also, like IR sensors, Laser is not fantastic at identifying thin objects like chair legs.  So I use the Sonar to sweep the near-field for collision avoidance as well.

I hope to have video up soon of this in action.


A* path-finding Examples in Python and Javascript (Not my own work)

$
0
0
A* Search Algorithms are used to find the shortest path from A to B while avoiding obstacles in the path.  

There are a number of such algorithms, and I'm not going to explain them here, but rather point you to some very good videos and some working Open Source Code that you can use in your own robot or game design.



Path Finding 0.1.1

A demo visualizing the execution of various path-finding algorithms

Curently five algorithms are included:
  1. A* (using Manhattan distance)
  2. A* (using Euclidean distance)
  3. A* (using Chebyshev distance)
  4. Dijkstra
  5. Bi-Directional Breadth-First-Search
This demo allows you to choose algorithms from above and visualize their execution.
Meanwhile, this is a Client/Server application. You should start the server first and then the client.
Also, you can start the server on one machine and run the client on a different machine, as long as the two machines are connected.
Screenshot:

Source:
Requires Python 2.6+ and Pygame
http://mycodeplayground.googlecode.com/files/pathfinding-0.1.1-src.tar.gz
Win32 Binary Distribution:
http://mycodeplayground.googlecode.com/files/pathfinding-0.1.1-win32-dist.zip
Note: There's a javascript version at http://qiao.github.com/PathFinding.js/visual/


Guiding a Robot with A*


Visual explanation of A* Algorithm 

For a tutorial on how A* Path finding works please check out --> https://www.youtube.com/watch?v=KNXfSOx4eEE



References:
Wikipedia: A* Search Algorithm
https://www.youtube.com/watch?v=KNXfSOx4eEE
https://www.youtube.com/watch?v=DINCL5cd_w0
http://theory.stanford.edu/~amitp/GameProgramming/AStarComparison.html



Controlling a Raspberry Pi / Arduino Bot from the Internet Part 1 of 3

$
0
0
This is a three part Series to explain how I control my BotTwo semi-Autonomous Robot from a webpage on the Internet. 

BotTwo has a few modes of operation.  

  • In one of his Autonomous Modes, he can wander about aimlessly, simply avoiding objects, all the while building or refining a map of his surroundings for future use.  
  • A second function in his Autonomous Modes allows you to select a location from his floorplan map, and he will find the shortest path to it, using A* algorithm. 
  • In Manual Mode, you can drive him like an RC Car from a webpage. 



This is the mode that I will describe here, with code examples for each programming platform.  As part of my learning process, I chose a very simple DIY approach, as opposed to a framework like node.js 




This example starts with a HTML page that uses javascript to pass a Command variable and associated Parameter variable to a php script.
The php script opens a simple Web Socket to the Raspberry Pi on the Robot, and passes the command to it.  

This is just one of many ways to do this, but it works well for me, and may be a starting place for you.
I chose to run my webserver (Apache/Php/MySQL)  on a dedicated computer, as my Raspberry Pi is up and down as I'm working on it, or as the battery dies.   You may chose to run the webserver on your Pi.  

Here will present the HTML page, along with it's associated javascript, and php scripts that enable sending a command in real-time from a Button Control panel on your laptop/tablet/phone to your robot.





This HTML document simply sets up an imagemap of a button console, and attaches javascript actions to the area shapes that comprise the buttons:   

<area shape="circle" 
    coords="43,43,32"     
    onClick="sendCommand(4,0);"
    onMouseOver="showButtons('Look Left');" 
    onMouseOut="showButtons('');"/>

A slider Control or  HTML5 <input type= "range"> is used to send speed control messages using the same methods described below.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en">
<head>
    <title>Control Testing for Raspberry Pi/Arduino Autonomous Platform</title>
    <meta http-equiv="Content-Type"
    content="text/html; charset=iso-8859-1" />
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" type="text/javascript"></script>
    <script src='autonomous.js'></script>

</head>

<?PHP include("insert_commands.php"); ?>

<body  onload='init();'>    
<center> 
     <H1> Control Panel for Web Guided Robot </H1>
     <BR> <HR> <BR>

   <center><b>Navigation Controls</b></center>
   <div> 
  <center>
<style>
.help {
   background-color: #FFFF73;
   border-radius: 10px;
   display: none;
   opacity: 0.9;
   padding: 10px;
   z-index: 100;
}

.help_link:hover + span {
   display: inline;
}
</style>


<form name="myform">
  <center><b>Action:</b> <input type="text" name="stage" size="10" />
      <b>Param:</b>   <input type="text" name="param" alt="Param" id="param" size="3" value="100"/> 
      <br><a href="#" class="help_link">Help</a> 
<span class="help">Distance you want to travel in mm.</span><br />
      <b>Speed:</b>  
<input type="range" id="speed" value="50" min="0" max="100" onchange="changeSpeed();"/>
      <div id="uno">&nbsp;</div>
  </center>
</form>

<!-- Create  Mappings -->
<img src="button_box_new.png" alt="Navigational Buttons" 
border="0" usemap="#buttons"/>

<map name="buttons">

  <area shape="circle" 
   coords="43,43,32"
    
   onClick="sendCommand(4,0);"
   onMouseOver="showButtons('Look Left');" 
   onMouseOut="showButtons('');"/>
 
  <area shape="circle" 
   coords="130,43,32"
   onClick="sendCommand(6,0);"
   onMouseOver="showButtons('Look Ahead');" 
   onMouseOut="showButtons('');"/>
 
  <area shape="circle" 
   coords="217,43,32"
   onClick="sendCommand(5,0);"
   onMouseOver="showButtons('Look Right');" 
   onMouseOut="showButtons('');"/>


  <area shape="circle" 
   coords="43,130,32"
   onClick="sendCommand(8,135);"
   onMouseOver="showButtons('Look Up');" 
   onMouseOut="showButtons('');"/>
 
  <area shape="circle" 
   coords="130,130,32"
   onClick="sendCommand('f',100);" 
   onMouseOver="showButtons('Move Forward');" 
   onMouseOut="showButtons('');"/>
 
  <area shape="circle" 
   coords="217,130,32"
   onClick="sendCommand(6,45);"
   onMouseOver="showButtons('Look Toward');" 
   onMouseOut="showButtons('');"/>

  <area shape="circle" 
   coords="43,217,32"
   onClick="sendCommand('l',100)"
   onMouseOver="showButtons('Turn Left');" 
   onMouseOut="showButtons('')"/>
 
  <area shape="circle" 
   coords="130,217,32"
   onClick="sendCommand('x',0)"
   onMouseOver="showButtons('Full Stop');" 
   onMouseOut="showButtons('')"/>
 
  <area shape="circle" 
   coords="217,217,32"
   onClick="sendCommand('r',100)"
   onMouseOver="showButtons('Turn Right');" 
   onMouseOut="showButtons('')"/>


  <area shape="circle" 
   coords="43,304,32"
   onClick="sendCommand(2,5)"
   onMouseOver="showButtons('Sonar Scan');" 
   onMouseOut="showButtons('')"/>
 
  <area shape="circle" 
   coords="130,304,32"
   onClick="sendCommand('b',100)"
   onMouseOver="showButtons('Move Backward');" 
   onMouseOut="showButtons('')"/>
 
  <area shape="circle" 
   coords="217,304,32"
   onClick="sendCommand(3,5)"
   onMouseOver="showButtons('Sonar Sweep');" 
   onMouseOut="showButtons('')"/>

  
  <area shape="circle" 
   coords="43,391,32"
   onClick="sendCommand(10,0)"
   onMouseOver="showButtons('Sleep/Wake');" 
   onMouseOut="showButtons('')"/>
 
  <area shape="circle" 
   coords="130,391,32"
   onClick="sendCommand(31,10)"
   onMouseOver="showButtons('Random Walk');" 
   onMouseOut="showButtons('')"/>
 
  <area shape="circle" 
   coords="217,391,32"
   onClick="sendCommand('t',5)"
   onMouseOver="showButtons('Turn Toward');" 
   onMouseOut="showButtons('')"/>

</map>

</center>
   </div>

      <center>
        <p>Copyright 2013 Michael Ball 
            <a href="http://arduino-pi.blogspot.com" target ="new">Arduino-Pi</a> 
            Email:  <a href="mailto:unix_guru@hotmail.com?Subject=Arduino-Pi" target="new">unix_guru@hotmail.com</a></p>
      </center>

</body>
</html>
On action, the Command and Parameter values are passed to php  via the sendCommand  function.

// #########################################################################

// autonomous.js


function showButtons(name){     / Show imagemap buttons values for controlling robot
  document.myform.stage.value = name;
}

function changeSpeed(){ // Slider control for managing robot speed 
    var speed=document.getElementById("speed");
    $.get("insert_commands.php", { command: 18, parameter: speed.value },                       // In my case '18' is the command
       function(command,parameter){  }, "json");      // to change motor speed
}
// This function passes both the Command and Parameter Variables from the Image map       // to a serverside PHP Script which will create a Websocket to the Robot. 
function sendCommand(command,parameter){                                         
    var parameter2=document.getElementById("param");

    if(parameter2 > 0) parameter = parameter2.value;    // Override default
    $.get("insert_commands.php", { command: command, parameter: parameter2.value },
       function(command,parameter){}, "json");
}

function init() {
    // Initalize the various page elements here...
    // in this case it is a dummy function...   
}
The php script accepts the Command and Parameter values, formats them into a string, and passes them to the Raspberry Pi, via a Web Socket. 
<?php
/*  
# BotTwo "insert_commands.php" Takes a Command/Paramater pair and sends 
# them to A listening robot via Web Socket
# 2014 Michael Ball  unix_guru at hotmail dot com

/*** robot address ***/
$robot_addr = '192.168.0.105';

/*** robot socket port ***/
$robot_sock = '5000';
if (isset($argv)) {// If running from the command line
   $command = $argv[1];
   $parameter = $argv[2];
}
else {// Else if called from a Web Page
   $command = $_GET['command'];
   $parameter = $_GET['parameter'];
}

// Initialize a Command string from the values passed in 'Command' and 'Parameter'
        $sock_cmd = $command . ", " . $parameter . "\n\r";  

if (isset($command)) {// If a command is available

// Create a TCP/IP socket to Robot
// http://www.php.net/manual/en/function.socket-create.php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

if ($socket === false) {
   return "Error: socket_create() failed: reason: " .
       socket_strerror(socket_last_error());
}
// Connect to the server running on the 'bot at  $robot_addr:$robot_sock
$result = socket_connect($socket, $robot_addr, $robot_sock);
if ($result === false) {
   return "Error: socket_connect() failed.\nReason: ($result) " .
       socket_strerror(socket_last_error($socket));
}

// Write the command string to the Robot
socket_write($socket, $sock_cmd, strlen($sock_cmd));
socket_close($socket);
}
?>

As I stated, this is an example... If you wish for me to go further into detailed explanation,   please comment below.


In the next article (2 of 3), I will describe how to receive and parse the command in python on the Raspberry Pi and forward it to an attached Arduino via the I2C bus.


And finally in article 3 of 3, I will describe the Arduino code that receives the I2C command, and turn it into motor actions to steer and run the robot. 

Edit (05/13/2014): here is the button_box image... it's missing from my github... 



Resources:

https://code.google.com/p/phpwebsocket/
http://www.w3schools.com/tags/att_area_shape.asp
http://letsmakerobots.com/node/39791
http://www.php.net/manual/en/function.socket-create.php
http://www.php.net/manual/en/sockets.examples.php





Controlling a Raspberry Pi / Arduino Bot from the Internet Part 2 of 3

$
0
0
This is part two of a three part Series to explain how I control my BotTwo semi-Autonomous Robot from a webpage on the Internet. 

In Part One, we talked about the Interaction and Communications between the end user device (laptop/tablet/phone), the Web Server presenting the control panel, and the socket service listening for commands on the Robot itself.

This series will allow you to operate your Robot like an RC Car via the Internet.



This is the mode described here, with code examples for each programming platform.  As part of my learning process, I chose a very simple DIY approach, as opposed to a framework like node.js 


Raspberry Pi Arduino I2C
Raspberry Pi / Arduino I2C



In this posting, we will discuss how to use python on the Raspberry Pi to initialise a tcp socket listener for incoming commands from the Web Server.

We will take the incoming message, and repeat it via I2C to the Arduino that is managing the DC motors and wheel encoders.

        Note: typically, there would be a validation step between receiving the command, and issuing it                      to the motor controllers.

You would want to ensure that the command made sense, but also ensure that it was not going to put the robot into danger.  This would include proximity / obstacle detection, as well as "cliff detection". In more advanced robots, you may also have environmental sensors that could ensure that the path chosen was safe to travel.

Ok... so... in my implementation, I rely heavily on the Adafruit Raspberry Pi python library for I2C communications both for existing I2C sensors, as well as for communicating with my Arduino's.  I fully admit to replicating an existing I2C sensor library, and then making it work with my motor controller Arduino.

Adafruit_I2C.py  provides a number of methods for sending and receiving data via I2C, in 8bit bytes, 16bit integers, or character array (python list) form.

I'm not going to explain how to prepare your Pi for I2C... 


 I had looked at Quick2Wire, and WiringPi, but their implementations is based on python3, whereas it appears that more I2C connectivity libraries exist for python 2.x via Adafruit.

Here is my stripped down "library" for sending a command string to the Arduino Motor Controller via I2C.

Motion.py 
#!/usr/bin/python

# Python library for Arduino as I2C Motor Controller.

# Copyright 2014 Michael Ball  unix_guru at hotmail dot com

from Adafruit_I2C import Adafruit_I2C

class Motion(Adafruit_I2C):

    # Minimal constants carried over from Arduino library

    MOTION_ADDRESS          = 0x33 # I2C Device Address
    MOTION_DATA             = 0x00 # Sensor data 


    def __init__(self, busnum=-1, debug=False):
        self.move = Adafruit_I2C(self.MOTION_ADDRESS, busnum, debug)


    def getMotionData(self):
        return self.move.readU8(self.MOTION_DATA) # & 0x0F


    # Read the Motion sensors
    def read_sensors(self):
        raw = self.move.readList(self.MOTION_DATA, 8)
        res = []
        for i in range(0, 8, 2):
            g = (raw[i] << 8) | raw[i+1] 
            res.append(g)
        return res

    # Send Commands to Motion Controller
    def write_command(self, command):
err = self.move.writeList(self.MOTION_ADDRESS, map(ord,command))
return err

# Simple example prints wheel encoder data and then sends a 
# "Forward 200mm" command every 2 seconds:
# Please note that sending a string via I2C bus is not the most 
# efficient use of this bus.  
# Data register method will be shown in an upcoming article

if __name__ == '__main__':

    from time import sleep

    motion = Motion()

    print '[Motion lpulse/rpulse]'
    while True:
        print motion.getMotionData()

        print motion.read_sensors()
sleep(.5)
print motion.write_command("f, 200 \n\r");
        sleep(2)  

With no further ado... here is my python "command repeater"  aka tcp socket listener-I2C Master.
#! /usr/bin/python
#
#  Process_Socket_Commands.py   Michael Ball Feb 2014
#  Manage command/response between Arduino Motor/Sensor controller, 
#  and Raspberry Pi intelligent controller.
#  Using tcp socket listener to receive commands from end user, 
#  and I2C to communicate with Arduino

import socket               # Import socket module
import time
import thread

from Motion import Motion



####################################################################
# Global Variables
cmd_received = 0
rcvdCommand = ""


####################################################################
# Declare functions

# Retrieve commands from tcp socket if available, and send to I2C
def get_next_command():       

  while True:
    # Inside processing loop we wait for a connection
    client_socket, address = server_socket.accept()
    print 'Got connection from', address
    client_socket.send('Thank you for connecting')
    rcvdCommand = client_socket.recv(1024)

    # rcvdCommand is a comma separated string from the Webserver
    # that consists of a single character Command, plus a Parameter
    # Move forward 200mm  would be represented as "f,200\n\r"
    print rcvdCommand

    # Normally, you would insert validation checking here, 
    # like proximty / obstacle avoidance, or cliff checking


    #send command to arduino
    motion.write_command(rcvdCommand)
    rcvdCommand = ""# Clear the command 
    client_socket.close()               # Close the connection


def get_motor_status():

   while True:
    # Read Wheel Encoder info from arduino 
    print "Motion, LCount, RCount, Speed"
    print motion.read_sensors()
    time.sleep(1)


            
##########################################################################
#
#This is where all the good stuff starts...
#
print 'Arduino Bot Command Processor - Feb 2014'

#  Create and open the socket will be listening on
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("192.168.0.105", 5000))
server_socket.listen(5)

print "Running Socket Server"

# initialize I2C Arduino motor controller - 
# Note there are no condition checks here
# a robust implementation would ensure that 
# the motor controller was actually online
motion = Motion()       

print "Arduino I2C Motor Controller Initialized"


try:
    running = True
    # Start up a Command tcp socket listener thread
    thread.start_new_thread(get_next_command, ()) 

    # Start up a thread to receive feedback from the Arduino
    thread.start_new_thread(get_motor_status, ())
    while 1:# Continuous loop
       pass

  


except (KeyboardInterrupt, SystemExit): #when you press ctrl+c
    print "\nKilling Thread..."
    # db.close()
print "Done.\nExiting."

Please read the comments.  This is a bare-bones implementation that takes a command string, and forwards it to The arduino via I2C for processing.   

You will need to add your proximity and safety testing to this.  As well, sending a string via I2C is not the most efficient use of the bus.   I simply did this so that I had both options available on the Arduino.
A proper Data Register method will be shown in an upcoming article.

As you will see in the next segment of this series,  I share the Command Interpreter / processor between the serial port and the I2C.  Commands can be received via either.




References:

http://dsscircuits.com/index.php/articles/78-arduino-i2c-slave-guide
http://gammon.com.au/i2c
Adafruit: Configuring the Pi for I2C
http://blog.oscarliang.net/raspberry-pi-arduino-connected-i2c/
https://wiki.python.org/moin/TcpCommunication
https://docs.python.org/3/howto/sockets.html
https://docs.python.org/2/library/socketserver.html
http://www.lucidtronix.com/tutorials/16
http://www.instructables.com/id/How-To-Make-an-Obstacle-Avoiding-Arduino-Robot/
https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code
http://en.wikipedia.org/wiki/I%C2%B2C
http://www.robot-electronics.co.uk/acatalog/I2C_Tutorial.html
http://www.penguintutor.com/linux/raspberrypi-webserver
http://www.instructables.com/id/Raspberry-Pi-I2C-Python/?ALLSTEPS
http://raspberrypi4dummies.wordpress.com/2013/07/18/raspberry-pi-master-controls-arduino-uno-slaves-via-i2c/

Also read:

http://quick2wire.com/category/python/
https://github.com/quick2wire/trackbot
http://blog.chris.tylers.info/index.php?/archives/274-New-Pidora-Package-quick2wire-python-api.html
http://think-bowl.com/raspberry-pi/installing-the-think-bowl-i2c-libraries-for-python/
https://projects.drogon.net/raspberry-pi/wiringpi/i2c-library/






Speeding up the I2C bus on Raspberry Pi and Arduino

$
0
0

From Wikipedia

The I²C reference design has a 7-bit or a 10-bit (depending on the device used) address space.[3] Common I²C bus speeds are the 100 kbit/s standard mode and the 10 kbit/s low-speed mode, but arbitrarily low clock frequencies are also allowed. Recent revisions of I²C can host more nodes and run at faster speeds (400 kbit/s Fast mode, 1 Mbit/sFast mode plus or Fm+, and 3.4 Mbit/s High Speed mode). These speeds are more widely used on embedded systems than on PCs.
So by default the I2C bus is running at 100kHz. Not bad, but no blazing speed demon either. 

Most I2C devices can readily run at 400khz, if you keep the lines short.

On the Raspberry Pi, the bcm2708 chip manages I2C (smbus) 







#define I2C_TIMEOUT_MS 150




#define DRV_NAME "bcm2708_i2c"




staticunsignedintbaudrate=CONFIG_I2C_BCM2708_BAUDRATE;


module_param(baudrate,uint,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);


MODULE_PARM_DESC(baudrate,"The I2C baudrate");



To increase the speed on the Raspberry Pi, you can pass an optional baudrate parameter via modprobe to the I2C module
                                                                         sudo modprobe -r i2c_bcm2708 && sudo modprobe i2c_bcm2708 baudrate=400000  
To make that change permanent, you have to create a new i2c.conf file:
1
2
3
4
5
sudo nano /etc/modprobe.d/i2c.conf
    
type    options i2c_bcm2708 baudrate=400000
                                                             ctrl-x to save (answer yes)
This will force your Raspberry Pi to initiate communications at 400k baud.





On the Arduino side, you have to locate the file twi.h 

sudo nano /usr/share/arduino/libraries/Wire/utility/twi.h
find the string “#define TWI_FREQ 100000L” and change it to:
1
#define TWI_FREQ 400000L (was 100000L)

This should speed up your I2C communications sufficiently, without breaking anything.



References:

http://en.wikipedia.org/wiki/I%C2%B2C
http://www.raspberrypi.org/phpBB3/viewtopic.php?t=18852
http://neophob.com/2013/04/i2c-communication-between-a-rpi-and-a-arduino/
http://raspberrypi.znix.com/hipidocs/topic_i2cdev.htm
https://github.com/raspberrypi/linux/blob/rpi-3.6.y/drivers/i2c/busses/i2c-bcm2708.c#L73

Sorry... part 3 coming shortly... webserver died..

$
0
0
I will be publishing part three of the Raspberry PI / Arduino communications shortly.  I just have to rebuild my webserver first.  

Local construction in my area has caused a series of power failures / surges, that managed to bypass my UPS surge protection.

Both UPS and webserver motherboard have failed.

 

Adding a 4dof arm to my Autonomous Rover - Now I have to learn Inverse Kinematics

$
0
0
http://www.animicausa.com/shop/Gift-Ideas/Corkers-Robots-Set.html
My wife has said for the past several months, that she would only be impressed with my "toy" when it was able to bring her a glass of wine... 
Sounds like a challenge to me...

So I set about acquiring, assembling, and getting  a  4dof (degree of freedom) arm  attached.  It is neither powered nor operational today, but I am in the process of understanding the art of Inverse Kinematics.

Note: I had initially called this a 5dof arm, thinking the open/close action of the grip was a degree of freedom. I stand corrected.


While at rest, the arm is tucked up and out of the way.  It rests mechanically upon itself so I can remove power.   It is not very clear in this image, but the arm at rest does not block either the front Sharp IR Sensor nor the OSLRF range finder.

 I am currently reaching out to my friends at LetsMakeRobots to understand what sensors I need to make this both a reliable and useful robotic arm.

Consensus is:
  1. Electrical currentsensor on the 5v rail feeding the servos to ensure that we do not damage them with weight.
  2. Tactile PressureSensor or Flex Sensor  on the fingers to grip the glass.
  3. Accelerometer so we don't spill the wine.
  4. And of course rubber grips.



The Raspberry Pi Camera is able to see and webcast the entire range of motion of the arm. I *assume* that at some point in time OpenCV can help guide the manipulator.




Please let me know if you know of any good resources I should be looking at, or any "must have" sensory feedback I must include.

I will be updating this post over the next few weeks as this gets hooked up and active.

















Resources:

Wikipedia:  Inverse Kinematics
Wikipedia: Degrees of Freedom
LearnAboutRobots: Robot Inverse Kinematics 
Society of Robots: Robot Arm Tutorial
Robotic Arm Inverse Kinematics on Arduino (with sample code)
CMU.EDU: Mathematics of Inverse Kinematics
http://letsmakerobots.com/node/31729
https://code.google.com/p/robotic-arm-control/
http://www.huffingtonpost.com/2012/09/28/wine-robot-wall-ye_n_1923808.html
https://www.synthetos.com/the-robot-arm-is-working/







Controlling a Raspberry Pi / Arduino Bot from the Internet Part 3 of 3

$
0
0

This is part three of a three part Series to explain how I control my BotTwo semi-Autonomous Robot from a webpage on the Internet. 

In Part One, we talked about the Interaction and Communications between the end user device (laptop/tablet/phone), the Web Server presenting the control panel, and the socket service listening for commands on the Robot itself.

In Part Two, we discussed how to use python on the Raspberry Pi to initialise a tcp socket listener for incoming commands from the Web ServerWe took the incoming message, and repeated it via I2C to the Arduino that is managing the DC motors and wheel encoders.


Raspberry Pi Arduino I2C
Raspberry Pi / Arduino I2C

In this, the third and final instalment, we will discuss the pieces of code used on the arduino to receive the commands via I2C, format and process them, and send a status back to the Raspberry Pi consisting of Motion status, Left Encoder count, Right Encoder count, and current Speed.

As mentioned in the previous article, I am treating the I2C as a fast serial interface, and as such send a formatted, comma separated string to the Arduino consisting of a command character, a comma, a parameter, and a line termination. 

For example:   'f',200 \n\r    would indicate  "Go Forward 200mm"

I'm going to have to assume that you know something about Arduino if you've made it this far in the conversation, so I'm not going to lay out the entire code here, simple the parts that fulfill the I2C and serial communications, command processing, and status reporting.

There is an excellent article from Oddbot on LetsMakeRobots describing how to control motor speed through PID with an Arduino here: http://letsmakerobots.com/node/38636
  
I personally am using the Arduino Motor Shield V3, there is great documentation in Arduino Playground, and there is a very good Instructable on it


So, without further ado... lets get into our code!



For the Arduino slave, you need to set up a few defines:
#define SLAVE_ADDRESS 0x33      // I2C slave address
#define REG_MAP_SIZE 32 // Max I2C Buffer size
#define MAX_SENT_BYTES 7 // Command, parameter, heading, batt
#define IDENTIFICATION 0x0D // An identifier for this slave




Then we include the Wire library.

#include <Wire.h>         // Include the Wire library so we can use I2C. 


Set up some variables for communications:
String Command = "";                  // The parsed Command from the RPi
String Parameter = ""; // The parsed Parameter from the RPi

char inData[REG_MAP_SIZE]; // Raw Buffer for the incoming serial data
char *inParse[REG_MAP_SIZE]; // Buffer for the parsed data chunks

char I2CinBuffer[REG_MAP_SIZE]; // Raw Buffer for the incoming I2C data
byte I2CoutBuffer[REG_MAP_SIZE]; // Buffer for the outgoing I2C data

String inString = ""; // Storage for data as string
int index = 0;

boolean stringComplete = false; // Tells the main loop that it has received
// a command via the Serial port

boolean I2CComplete = false; // Tells the main loop that it has received
// a command via I2C

In setup() we initialize Serial and Wire:
Serial.begin(115200);

Serial.println("RST, Running Motors_I2C_01. 140328");

Wire.begin(SLAVE_ADDRESS); Serial.println("I2C Initiated.");

Wire.onRequest(requestEvent); // Set up Request Interrupt Service Routine
Wire.onReceive(receiveEvent); // Set up Receive Interrupt Service Routine

I2CoutBuffer[0x08] = IDENTIFICATION; // ID register

Serial.println("########## LET US BEGIN #############");

In loop()  you need to watch for Serial or I2C commands available:
SerialEvent();                        // Grab characters from Serial

if (stringComplete) // if there's any serial available, read it:
{
ParseSerialData(); // Parse the recieved data
inString = ""; // Reset inString to empty
stringComplete = false; // Reset the system for further input of data
}


if (I2CComplete) // if I2C commands are available, read it:
{
ParseI2CData(); // Parse the recieved data
inString = ""; // Reset inString to empty
I2CComplete = false; // Reset the system for further input of data
}
Then there are Serial Receive and I2C Request and Receive handlers:

void SerialEvent()
{
while (Serial.available() && stringComplete == false) // Read while we have data
{
char inChar = Serial.read(); // Read a character
inData[index] = inChar; // Store it in char array
index++; // Increment where to write next
inString += inChar; // Also add it to string storage

if (inChar == '\n' || inChar == '\r') // Check for termination character
{
index = 0;
stringComplete = true;
}
}
}



void receiveEvent(int howMany) {
int readCount = Wire.readBytes(I2CinBuffer, howMany);
I2CinBuffer[readCount] = 0;
I2CComplete = true;
}



void requestEvent()
{
Wire.write(I2CoutBuffer, 8);
lcount = 0; rcount = 0; // reset left and right tick counters

}

// storeData is used to copy volatile variables into a register for I2C send
void storeData()
{
cli();
I2CoutBuffer[0x00] = highByte(motion); // Notice Highbyte/Lowbyte order MSB,LSB
I2CoutBuffer[0x01] = lowByte(motion);
I2CoutBuffer[0x02] = highByte(lcount);
I2CoutBuffer[0x03] = lowByte(lcount);
I2CoutBuffer[0x04] = highByte(rcount);
I2CoutBuffer[0x05] = lowByte(rcount);
I2CoutBuffer[0x06] = highByte(spd);
I2CoutBuffer[0x07] = lowByte(spd);
I2CoutBuffer[0x08] = IDENTIFICATION;
sei();

}
Once the Command and Parameters are received they have to be parsed into functions to control the Autonomous Rover:
// Take the Comma separated Serial string and format it to Command and Parametervoid ParseSerialData()
{
char *p = inData; // The data to be parsed
char *str; // Temp store for each data chunk
int count = 0; // Id ref for each chunk

while ((str = strtok_r(p, ",", &p)) != NULL)
// seperate at each "," delimiter
{
inParse[count] = str; // Add chunk to array
count++;
}

if(count == 2) // If the data has two values then..
{
Command = inParse[0]; // Define value 1 as a Command identifier
Serial.print("Command from Serial in is.... "); Serial.println(Command);
Parameter = inParse[1]; // Define value 2 as a Parameter value
Serial.print("Parameter from Serial in is.... "); Serial.println(Parameter);

processCommand();
}
}

// Take the Comma separated I2C string and format it to Command and Parameter
void ParseI2CData()
{

char *p = I2CinBuffer; // The data to be parsed
char *str; // Temp store for each data chunk
int count = 0; // Id ref for each chunk

while ((str = strtok_r(p, ",", &p)) != NULL) // seperate at each "," delimiter
{
inParse[count] = str; // Add chunk to array
count++;
}
// Serial.print(I2CinBuffer); Serial.print(""); Serial.println(count);

if(count == 2) // If the data has two values then..
{
Command = inParse[0]; // Define value 1 as a Command identifier
Serial.print("Command from I2C in is.... "); Serial.println(Command);
Parameter = inParse[1]; // Define value 2 as a Parameter value
Serial.print("Parameter from I2C in is.... "); Serial.println(Parameter);

processCommand();
}
}

// Determine actions from Command / Parameter -- This can be called from either
// Serial or I2C parser
void processCommand()
{
char buf[REG_MAP_SIZE]; // make this at least big enough for the whole string
Parameter.toCharArray(buf, sizeof(buf)); // Convert String to Character array

Serial.print("CMD,"); Serial.print(Command); Serial.print(",");
Serial.print(Parameter); Serial.print(","); Serial.println(now);

digitalWrite(rmbrkpin,LOW); digitalWrite(lmbrkpin,LOW); // remove brakes

// Call the relevant identified Command
if(Command[1]) Command[0]=Command[1];
switch(Command[0])
{
case 'f': // Move Forward "Parameter" ticks
lspeed = spd; rspeed = spd;
ldir = forward; rdir = forward;
MotionStop = atoi(buf);

motion = MOVE_FORWARD;
TargetHeading = CurrentHeading;
turning = 0;
break;

case 'b':
lspeed = spd; rspeed = spd;
ldir = backward; rdir = backward;
MotionStop = atoi(buf);
motion = MOVE_BACKWARD;
TargetHeading = CurrentHeading;
turning = 0;
break;

case 'r':
lspeed = spd; rspeed = spd;
ldir = forward; rdir = backward;
MotionStop = atoi(buf);
motion = TURN_RIGHT;
motion = IN_MOTION; turning = 1;
break;

case 'l':
lspeed = spd; rspeed = spd;
ldir = backward; rdir = forward;
MotionStop = atoi(buf);
motion = TURN_LEFT;
motion = IN_MOTION; turning = 1;
break;

case 's': // Set Desired Speed
spd = atoi(buf);
break;

case 'x': // STOP!!!
lspeed = 0; rspeed = 0;
MotionStop = 0;
motion = STOP;
digitalWrite(rmbrkpin,HIGH); digitalWrite(lmbrkpin,HIGH); // Apply brakes
lcount = 0; rcount = 0; // reset left and right tick counters
break;

}
digitalWrite(rmbrkpin,LOW); digitalWrite(lmbrkpin,LOW); // release brakes
}


I will post the complete code up on my github in the next day or so.

Please let me know if I need to clarify anything...
 

References:

http://dsscircuits.com/index.php/articles/78-arduino-i2c-slave-guide
http://gammon.com.au/i2c
Adafruit: Configuring the Pi for I2C
http://blog.oscarliang.net/raspberry-pi-arduino-connected-i2c/

PenguinBot - Fun weekend Arduino Project!

$
0
0








This video is right after placing the covering back on, and powering it up.  I have not fixed a bug in the object avoidance yet, so you will hear the motors running full steam backward to get away from the phantom obstacle.    I really wanted to post this just to show how obnoxious the sound from the preserved toy is!

********************************************************************************


After seeing the Awesome Hurby Bot, I thought I would have a bit of fun.

My wife is out for the weekend, lets see what I can conjure up!
I started with (very noisy) Penguin toy that has not worked in a year or so. We left the batteries in it, and they leaked all over the inside, corroding the leads to the motor... Hmmm... fix it as is? Or make it AWESOME!!!


So, of course I chose AWESOME!
I expect this build to take a day, with some code tinkering over the next week.
So what do we have to start with, and what can we add?




The penguin toy itself had an odd combination of two wheels at 90 degree angles to each other. These would alternately spin causing a very strange walking pattern. It also had an offset gear rocking a lever inside, with a plastic ball attached to the top of the penguin body with velcro.

While walking the penguin would rock back and forth, screeching, as penguins do. (I want to preserve this motion.)






So, first thing to do was find two SMALL DC gear motors to provide proportional steering. A trip to the dollar store, and I had what I wanted. A pair of cheap locomotive engines with 3v DC gear motors!



I will use an Arduino Pro Mini to run this Bot, the motors will be managed through a Polulu DRV883 H-bridge.



For obstacle avoidance, I will be using a MaxSonar EZ1. A *lot* of overkill, and way too expensive for this project, but... It's what I've got in the parts bin.
Two Light Sensitive resistors should allow it to waddle towards a light, or follow my children with a flashlight.
A small microphone will allow it to react to sound.
A micro servo to rock the body back and forth.
And a few strategically placed leds just for fun...

I guess I'll just call this board "Bird Brain"!


Wish me luck!

Update: 4:30pm Sunday afternoon



Electronics and most of the mechanical is done.  90% of the coding is complete as well....

My challenge to myself was to start from scratch at 6pm Friday night, and with a house full of kids, (my 7 year old son had two other boys over as well) and the wife gone for the weekend, complete this project by midnight tonight.

I actually believe I'm on track.

Update: 8:00pm  Sunday night.  4 hours to go, and I've got it reassembled and the "skin" back on.  Just some mild "glitches" in the code, and we should have another video up before midnight... (unless the wife gets home before then?)



Update: 10:00pm  Sunday night. 

Ok, I'm exhausted.  I've been at this hack for over 60hrs... Sleep calls... I can no longer see...

For what it's worth, the code to this point is here:
https://github.com/michaeljball/PenguinBot

I will finish it tomorrow...
Cheers.







References:
Pololu: DRV8835 Dual Motor Driver Carrier
Texas Instruments: DRV8835 Dual Low Voltage H-Bridge
Instructables: Arduino Motor Shield V3
MAXBOTIX: LV-MaxSonar®-EZ1
Arduino light seeker
Arduino powered Braitenberg vehicle
Light chaser
Servo Problems With Arduino - Part 1
Get on the BlinkM Bus with a BlinkM Cylon



Quick Update on PenguinBot

$
0
0

I've moved the MaxSonar EZ1 onto a panning servo, and attached it to the outside of the electronics enclosure.



I've also added a "BlinkM" intelligent LED to give PenguinBot some personality.  I haven't written the code for it yet, so it's currently just scanning through it's default pattern... but there's always tonight....

I've added Serial Control to drive it tethered via USB, my expectation is that I will get a BlueTooth-FTDI  adapter...

I've broken the Arduino code out into separate tabs according to their role, ie: motion control, serial interface, sound...



This project is growing legs.....













References:

BlinkM datasheet


My journey into building a 3d printer from old printer parts

$
0
0
Just because I've got nothing better to do with all my free time....

I got looking at all of the various DIY 3D printers on the market, and got wondering to myself... 

Why are there not many hobbyists re-using old All-In-One or Multifunction printers to scavenge parts from? 

My gut feel provides two answers.... 
  1. Many people have tried and failed...  but I see little evidence of this through Google searches.  
  2. The various RepRap kits have made it both affordable and technically possible through decent and thorough documentation to build from a kit, thus no need to do it completely from scratch.


Further research into true DIY 3D printers, finally led me to this incredible project out of Africa, building a 3D printer from recycled electronics.



Then I got looking at this DIY 3D printer below , and decided that I need to at least try this.



I visited Kijiji.ca and did a search on cheap Multifunction Printers in my area.

I immediately purchased a functional Brother MFC-7340   for $25

And a Canon Pixma MG-3520 All-In-One Scanner/Printer *NEW in Box* for $20


Between these two printers, I should be able to scavenge the X/Y bed from the scanner intact. Including linear bearings, rods, steppers, gears and belts.   I will leave the dimensions as is.  I know it will be a bit strange, but...  This entire project is a bit strange.

I should also be able to get two Z axis steppers, linear rods/bearings gears and belts as well.  I will have to fashion a sturdy support  from T-Slot extrusion...

To that end,  I purchased some 80/20 T-Slot aluminum extrusion for $40







Then there are the parts that I KNOW that I cannot (should not!) make myself. 

The Electronics will be purchased according to RepRap specifications
              Electronics: $140  or on   Ebay for $100?

I will purchase and install a prefab Extruder with Hot end:    $95

And will listen to the various recommendations for using a Heated bed:  $25

Total budget?

2x recycled printers     $  50
T-Slot framing             $  40
Electronics                   $140
Extruder                       $100
Heated Bed                  $  25

Total:                           $355

Yes, I know I can get a PrintrBot for $300, but mine will be significantly larger... 

...and I hope I'll learn more than just frustration along the way.   (Yes, I also know there will be frustration to share)


Whether I succeed or fail... I will document the journey here, in photos and possibly some video.

Wish me luck...

If you have tips, recommendations, whatever, please feel free to leave a comment below.  (Just don't tell me I'm an id10t ...  My wife has well informed me on that aspect...)





References:

http://reprap.org/wiki/RepRap
http://hackaday.com/2013/04/30/scratch-built-3d-printer-shows-rock-solid-performance/
http://hackaday.com/2013/04/05/h-bot-style-3d-printer-moves-bed-for-z-axis/
http://www.popsci.com/article/diy/check-out-3-d-printer-made-e-waste
http://diy3dprinting.blogspot.ca/2013/05/wafate-african-3d-printer-out-of.html
http://www.npr.org/blogs/alltechconsidered/2013/11/08/243951594/for-a-few-hundred-bucks-you-can-make-your-own-3-d-printer
http://www.instructables.com/id/Harvesting-parts-from-a-Laser-Printer/
http://www.grassrootsengineering.com/blog/mymachine/
https://www.kickstarter.com/projects/printrbot/printrbot-your-first-3d-printer
http://www.craftsmanspace.com/free-projects/3d-printers-diy-plans-and-build-instructions.html
http://3dprinters.ws/ord-bot-quantum/
http://inhabitat.com/portabee-launches-portable-robust-affordable-3d-printer-for-under-500/
http://www.techthefuture.com/featured/build-your-own-3d-printer-1-%E2%80%94-repraps-improved/
http://en.wikipedia.org/wiki/3D_printing
http://www.tridimake.com/2012/10/homemade-heated-bed.html
http://el34world.com/Misc/Cnc/CNC21.htm
http://www.instructables.com/id/DIY-Glow-3D-Printer/
http://makezine.com/projects/eventorbot-open-source-diy-3d-printer/
http://www.3ders.org/pricecompare/3dprinters/?a=DIY%20kit
http://www.npr.org/blogs/alltechconsidered/2013/11/08/243951594/for-a-few-hundred-bucks-you-can-make-your-own-3-d-printer


My local 3D printer supply company:  (local being an hour or so away...)

http://www.mixshop.com/index.php?main_page=contact_us&zenid=9jh0gbklv4to338i1fmb1abp13
http://www.mixshop.com/index.php?main_page=product_info&cPath=11&products_id=147


Contact Us:Address:
Unit 121, 2400 Midland Avenue, Toronto, ON Canada M1S 5C1

Workshop: 1 (416) 792-8388
Sales & Support: sales@mixshop.com / support@mixshop.com - Hang Zhuo
3D Printing Services: 3d-printing@mixshop.com - Tong He
Other Inquiry: info@mixshop.com - Jing Guo






Introducing an Arduino Finite State Machine library to PenguinBot

$
0
0
First off... PenguinBot was featured on Hackaday last Friday!  Yay!




I spent a little time yesterday reworking my code for PenguinBot to replace the homemade state machine of  If/then/else and switch statements, with a proper State Machine Library.

I specifically chose the SMLib  State Machine Library because of it's simplicity and lack of overhead.   I know there are other full featured State Machine Libraries out there, but this one serves the purpose well. 

After all..  it's just a penguin that avoids obstacles...  most of the time... more or less... 

This library allows you to have a Head and Body for each state.  
The "Head" is for initializing variables each time you transition into the state.  The "Body" contains the actions that are to take place each time you loop through the state,such as incrementing a counter, or checking a sensor.



Regardless of whether you are in "Manual Mode", "Object Avoidance Mode", or the yet to be implemented "Light Following Mode",  State Machine "m1" controls motion. 
The states are as follows:
1 = Stopped
2 = Forward
3 = Avoiding Obstacle
4 = Turn Left
5 = Turn Right
6 = Reverse
The Global Variable "MotionStop" dictates how much time is spend in any state.


Current (imperfect) code here:



Setup and Initialization of the State Machine :
/************************************************************************/
#include <SM.h>
SMm1(m1s1h,m1s1b);  //Initialize state machine m1 with head and body

setup(){
// There is nothing in setup related to SMLib
}

//  Typical events to manage an Autonomous Robot in the main loop

voidloop(){

  get_sensors(); // Read all sensors, like proximity, etc...

  EXEC(m1); // Execute the State Machine


  provide_feedback(); // Send status updates via Serial

  get_serial();// Grab commands from Serial

}

/**************************************************************************/



This is the FSM.ino module:
/**********************************************************************/
State machine m1 is for motion control
the states are as follows:
1 = Stopped
2 = Forward
3 = Avoiding Obstacle
4 = Turn Left
5 = Turn Right
6 = Reverse
MotionStop dictates how much time is spend in any state.
*/

Statem1s1h(){// m1s1 is --- Motion:Stopped
  Serial.println("Motion:Stopped (State 1)");
  halt();
}//m1s1h()

Statem1s1b(){
  if(m1.Timeout(5000)){// Maximum 5 seconds idle time
    m1.Set(m1s2h,m1s2b);
    Serial.println("changing to Motion:Forward (State 2)");
  };
}//m1s1b()

Statem1s2h(){// m1s1 is --- Moving Forward
  Serial.println("Motion: Forward (State 2)");
}//m1s2h()

Statem1s2b(){
  if(m1.Timeout(MotionStop)){// Maximum 5 seconds forward motion
    m1.Set(m1s1h,m1s1b);
    Serial.println("changing back to Motion:Stopped (State 1)");
  }
  
  if(Distance>MINDIST){
   BlinkM_fadeToRGB(blinkm_addr,0,255,0);// Display GREEN Status
   forward();
  }else{
    Serial.println("changing to Motion:Avoid Obstacle (State 3)");
    m1.Set(m1s3h,m1s3b);
  }
}//m1s2b()

Statem1s3h(){// m1s3 is --- Avoid Obstacle
  Serial.println("Motion: Avoid Obstacle (State 3)");
  MotionStop=200;// Set turn time for Obstacle Avoidance
  BlinkM_fadeToRGB(blinkm_addr,255,0,0);// Display Warning RED

}//m1s3h()

Statem1s3b(){
  if(DistLeft>DistRight){
    m1.Set(m1s4h,m1s4b);// Change State to Left Turn
  }elseif(DistRight>DistLeft){
    m1.Set(m1s5h,m1s5b);// Change State to Right Turn
  }else{
    Serial.println("changing back to Motion:Stopped (State 1)");
    m1.Set(m1s1h,m1s1b);
  }
}//m1s3b()


Statem1s4h(){// m1s1 is --- Turning Left
  Serial.println("Motion: Left Turn (State 4)");
}//m1s4h()

Statem1s4b(){
  if(m1.Timeout(MotionStop)){// Maximum 5 seconds forward motion
    m1.Set(m1s1h,m1s1b);
    Serial.println("changing back to Motion:Stopped (State 1)");
  }
   BlinkM_fadeToRGB(blinkm_addr,0,255,0);// Display GREEN Status
   left();
}//m1s4b()


Statem1s5h(){// m1s1 is --- Turning Right
  Serial.println("Motion: Right Turn (State 5)");
}//m1s5h()

Statem1s5b(){
  if(m1.Timeout(MotionStop)){// Maximum 5 seconds forward motion
    m1.Set(m1s1h,m1s1b);
    Serial.println("changing back to Motion:Stopped (State 1)");
  }
   BlinkM_fadeToRGB(blinkm_addr,0,255,0);// Display GREEN Status
   right();
}//m1s5b()


Statem1s6h(){// m1s1 is --- Reverse
  Serial.println("Motion: Reversing (State 6)");
}//m1s6h()

Statem1s6b(){
  if(m1.Timeout(MotionStop)){// Maximum 5 seconds forward motion
    m1.Set(m1s1h,m1s1b);
    Serial.println("changing back to Motion:Stopped (State 1)");
  }
   BlinkM_fadeToRGB(blinkm_addr,0,255,0);// Display GREEN Status
   reverse();
}//m1s6b()

/************************************************************************/


MotionStop duration is typically set either as a Serial Command parameter when in Manual Mode, or predetermined time lapse for "In Motion" or "Idle"  when in Autonomous Mode (Either Obstacle Avoidance or Light Following).

The exception to this, is in State 3 --- Avoid Obstacle.  In this state, it has been determined that an obstacle blocks the way ahead, and an assessment is done as to whether there is more room to the left or to the right.   "MotionStop" duration is set up to allow just enough time to turn the bot roughly 90 degrees. "MotionStop=200;// Set turn time for Obstacle Avoidance"


Over the next few days, I will convert the rest of the code to use this library, and show the "Light Following Mode"



References:
Arduino-Pi: Of Finite State Machines and Robotics
https://github.com/michaeljball/PenguinBot

Arduino Playground: A novel and relaxed view on finite state machines
http://playground.arduino.cc/Code/FiniteStateMachine
Robot Virtual Worlds – Maze Crawler
Embedded Micro:  basic FSM to control a very simple robot.
http://www.mathertel.de/Arduino/FiniteStateMachine.aspx
http://hacking.majenko.co.uk/finite-state-machine


Using DC Motors and Encoders for 3D printer: Challenging the norm!

$
0
0
http://www.nextdayreprap.co.uk/wiring-reprap-prusa-mendel-build-manual/Every 3D printer I've seen 
(please correct me if I've missed something!) 
uses stepper motors for X/Y/Z axis. 



The RepRap firmware assume that you are using steppers in your build.


That said, RepRap does introduce the concept of "RepStrap

(from http://reprap.org/wiki/Category:RepStrap)


repstrap is a 3D printer cobbled together from whatever parts you can find, which will eventually allow you to print the parts for a reprap machine, or to simply use as a stand alone machine. Derived from the term bootstrap, as in "to pull yourself up by your bootstraps"A RepStrap is a open-hardware rapid prototyping machine which is made by fabrication processes which aren't under the RepRap umbrella yet. These are becoming less and less common as RepRap printed parts become more available, but are still an option. You can build a 3D printer RepStrap using a tablesaw, orusing a lasercutter, and use this to make fun, beautiful, useful things.


Old commercial ink/laser printers used to use stepper motors too.   

These printers typically got resolutions of 300dpi (0.08mm)  or 600dpi (0.04mm)




But.... Newer printers, say within the last decade, use DC motors with a "linear strip encoder".   And these printers typically get better than 1200dpi (0.02mm) 


(yes, I know they use interpolation to get this resolution, but work with me here...)





According to WikiPedia:  Optical linear encoders[1][2] dominate the high resolution market and may employ shuttering/Moirédiffraction or holographic principles. Typical incremental scale periods vary from hundreds down to sub-micrometre and following interpolation can provide resolutions as fine as a nanometre.
And... 

Reprap already has a reference to these... 


OverviewFor those who enjoy scavenging, many components useful for constructing 3D printers can be found in inkjet printers. This often includes optical encoders and strips. This page gives information on finding and using these items.
Finding printers with linear optical encoders in themCheap inkjet printers can be obtained from garage sales or recycling centers. Do not get laser printers, since they do not have the right optical components in them. Not all inkjet printers have optical encoders and strips in them. It is easy to tell by opening the lid (as if to change the ink). You should see a grey plastic strip close to the shiny metal rod and running parallel to it. The printers that people sell cheaply or recycle generally are somewhat inky inside. Do not get ink on the optical strip, though you may be able to clean it off.
The strip runs through the optical sensor, which may be quite hidden. It is probably on the back side of the assembly that holds the ink cartridges.

So...

I'm upping my game. My original goal was to simply copy a basic 3D printer using as much salvaged parts as I could, a few stepper motors, linear rails, switches, etc...  

Had I done my research up front, I probably would not have even started this project, however... I have started, and am facing a new challenge...

My NEW GOAL is to create a 3D printer using DC motors and the salvaged Optical Encoder strips.  

A simple test on the arduino with a pololu dual h-bridge quickly had two printer heads tracking back and forth on their carriages within minutes of wiring them up to their native cables.  I haven't accounted for overrun yet, so they oscillate like crazy before getting to their destination, but this is DEFINITELY doable.


I will likely start with Marlin Firmware and write a hardware abstraction to convert stepper motor output (steps/inch, etc...) to run a closed loop DC motor with Encoder Strip feedback.  



Any suggestions or prior art welcome! 


Let's call these two videos  --- 

Inspiration....    


References:

http://www.nextdayreprap.co.uk/wiring-reprap-prusa-mendel-build-manual/
http://en.wikipedia.org/wiki/Stepper_motor
http://reprap.org/wiki/Firmware
http://reprap.org/wiki/Category:RepStrap
http://benkrasnow.blogspot.ca/2010/02/linear-position-tracking-with.html
http://hackaday.com/2009/11/12/linear-optical-encoder/
http://reprap.org/wiki/Optical_encoders_01
http://www.electromate.com/db_support/downloads/lin.pdf
http://mil.ufl.edu/projects/gnuman/gnuman_pre2005/spec_sheets/heds_encoder.pdf
https://www.youtube.com/watch?v=0QLZCfqUeg4
http://makezine.com/2009/11/11/linear-optical-encoder-from-printer/
http://junkplusarduino.blogspot.ca/p/svg-image-plotter.html
http://madpenguin.ca/blog/2011/05/14/use-an-inkjet-printer-to-learn-emc2-and-servo-motor-control-part-1/
Arduino.cc: Agilent Optical encoder



Followup on 3D printer from Scavenged DC Motor / Encoders Blog

$
0
0
One word...  Wow!

You guys are both supportive and educational at the same time.  

Yes... many of you either told me not to bother because I was simply reinventing the wheel (I actually appreciate that the most because many of you provided links to prior art!), or that I was just plain foolish to attempt this... (but if you are going to criticize, back it with facts/links)...

But MANY of you (over 100 as of this writing) have provided me with positive reinforcement, and sent me links and articles showing others who have succeeded on this journey in one way or another.

I just want to talk a minute about the differences between a "Stepper Motor"  a "DC Motor" and a "Servo Motor".  

I've linked each of the above to Wikipedia so that I don't have to go into great detail of how each actually works.  I'm more interested in the differences each presents in the context of fine grain positioning control.


A Stepper motor has multiple coils that when energised in a certain sequence, produces defined accurate "steps" of the shaft. The NEMA 17 Stepper Motor shown here, seems to be the DIY 3D printer industry's  favorite. It provides 1.8° per step accuracy. As long as your software can initialize the linear travel with endstop switches, mechanical or optical, then it will know at all times where along the axis it was left. There is little issue with "drift".  When you cut the power to a stepper motor, it stays exactly where you placed it. Yes, of course you can "push" the carriage without power but for the most part an energized step will place the shaft in a known spot.  Speed is managed by the frequency in which you cycle the steps.


A DC motor, on the other hand is pretty much "free running".  You apply a DC voltage to the winding, and the shaft will spin in one direction.  Reverse the polarity and the shaft will spin in the other direction.  Torque is based on electric current capabilities, and is a factor of the wire gauge and number of windings.  Speed is related to the voltage applied.  Most DC motors in a control context are driven via Pulse Width Modulation or PWM, but still have no feedback as to the position of the shaft, or in my case linear carriage.  When you remove power from a DC motor, it will coast until it stops.  To remedy this, you need to employ Dynamic Braking. Accuracy is non Existent.

 

A Servo Motor, starts with a DC motor, but provides a feedback mechanism for the angle of rotation.  The hobbyist style servos shown to the right here, rely on a potentiometer, attached to the motor shaft by gears.  The position of the shaft is directly related to the value of the potentiometer. 

Commercial Servo Motors will employ magnetic, electric, or optical Encoders to sense rotational (or linear) movement, and provide this feedback to a servo controller. 

 A Servo Motor is basically a closed loop system that provides direct feedback to the controller upon any movement of the shaft.  Accuracy is a product of the resolution of the encoder in use.






To that end, what I am proposing in my project, is to make a set of Linear Servo Motors.
I will drive them with Pulse Width Modulation as a DC motor.  I will provide feedback, monitoring the encoder position through Interrupts, and thus essentially create a linear servo motor.  In addition, I will apply the appropriate functions to slow the carriage in advance of the destination so as not to overrun the target. Photo interrupters will be used at each end of the carriage for periodic self calibration.


Here are a few of the links shared with me:

Here is an example of a Thing-o-matic makerbot 3d printer with DC motors and linear encoders:




Apparently the GeckoDrive is a popular solution in this space, it takes the outputs meant for a stepper, and manages a DC motor / encoder loop.




Then there's Rapy, a DC motor powered 3D printer made in Korea
It uses two DC Gear motors and one Rotary Optical Encoder per axis.




Somewhat unrelated, but a very interesting belt drive linear motion system using 80/20 extrusion as the linear rail.











More to read:

Converting an Ordinary DC Motor to a Servomotor
http://www.motioncontroltips.com/category/encoders/l-encoders/
https://www.youtube.com/watch?v=wBnbQrs6JsQ
http://blog.machinekit.io/p/machinekit_16.html?m=1
(http://www.geckodrive.com/geckodrive-brush-dc-drives/g320x.html
http://www.geckodrive.com/gecko/images/cms_files/images/G320XStepDirectionCircuit.jpg \
Makeatronics: Building a 3D printer
Makeatronics: 3D Printer Motor Control - Part 1
https://groups.google.com/forum/#!topic/makerbot/hgdUMBRkU38
http://martingautron.com/inputs/books/diy/3d-print.html
https://www.youtube.com/user/ENKTechnologies
GeckoDrive: Step Motor Basics Guide


PenguinBot Self Defence - or How to Arm a Penguin

$
0
0


Over on Hack-A-day, one of the readers joked that I should have Nerf Launchers on the PenguinBot:



And given that today *IS*World Penguin Day , I thought I would spend some time this weekend to add some more playfulness to PenguinBot.
(Disclaimer: I am not affiliated with World Penguin Day or GreenPeace. I apologise if you find this article is in poor taste.)

In a previous update, I had mentioned that I was working on changing Operating Modes from full manual, to Wandering with Object Avoidance, to Following a light with sound activation or Hand Claps.  

I live in a house with three young children...  There is no such thing as "Ambient Sound" in my house to set a reasonable threshold.  (Seriously!!!) 



Additionally, Penguins were not meant to be tethered!  Not even for programming! 

So... I've installed Bluetooth!  

I can now command this Penguin from the comfort of my chair! 



And to appease the readers of Hack A Day ....  
I'm installing a Penguin Self Defence system. 

No more will my PenguinBot be at the mercy of marauding sharks! 

I found this in my junk drawer.  I believe it came off of a kids remote  control car. It has a simple geared DC motor.
As I only need to run it in one direction, I can turn it on and off with a transistor.  


As you can see here, the Nerf Launcher fits nicely on the opposite side as the battery pack.  AND as an added bonus, it offsets the weight of the batteries which had the annoying effect of causing the forward motion to pull to the left. 

Below find the new and improved schematic diagram of PenguinBot V2.0.


I will be wiring this up over the weekend, adding some sequencing to randomly shoot in the direction of bright lights, and uploading the videos.

Cheers, and have a great weekend.  You bet I will...







References:
http://worldpenguinday.com/
http://playground.arduino.cc/Learning/Tutorial01
http://www.instructables.com/id/Android-talks-to-Arduino/
http://learn.adafruit.com/downloads/pdf/adafruit-arduino-lesson-13-dc-motors.pdf
https://sites.google.com/site/stembotics/projects/bluetooth
http://en.wikipedia.org/wiki/Obstacle_avoidance
http://www.instructables.com/id/Arduino-Object-Avoidance-Robot/


Arduino Sketch to manage high resolution - high speed linear encoders

$
0
0

In the ongoing saga of building my RepStrap 3D printer from salvaged printer parts...

The first axis is sorted out:



This is the printhead carriage salvaged out of a inkjet printer.  It basically consists of a DC gear motor driving a tensioned belt, pulling the print carriage across a high resolution optical encoder strip





There have been plenty of people do this before me, but this is my kick at repurposing salvaged printer parts.  


This is the print head circuit board from the carriage.  In the center, you will see the solder pads for the Optical Encoder Sensor.  My original intent was to keep the flat cables intact, and pull the signals off of those, but at about 50mil pitch... I can't even think about soldering that... 
(yeah, I'm getting old)

As they have already done the work of wiring the IR LED with a resistor to VCC, all I really need is four wires.  VCC/GND/Encoder Phase A and B. 
The Optical Encoder provides logic level outputs to the Interrupt pins on the Arduino.  The sketch below only addresses the X-AXIS for demonstration purposes, and only uses one Interrupt on Phase A, while polling the signal level on Phase B.  This provides half the resolution capable from this Quadrature encoder. 

In a future article, we will demonstrate using PinChange Interrupts to get the full resolution from this arrangement.

/***************************************************************************************
*  Lin_Enc_01.ino   04-29-2014   unix_guru at hotmail.com   @unix_guru on twitter
*  http://arduino-pi.blogspot.com
*
*  This sketch allows you to run two salvaged printer carriages for X/Y axis using their 
*  linear encoder strips for tracking. 
*  Both interrupt routines are below, but for the sake of demonstration, I've only set up
*  the X-AXIS for now.
*
*****************************************************************************************/

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

#define frontstop = 100            // Right most encoder boundary
#define backstop = 3600            // Left most encoder boundary


// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 

// Select which 'port' M1, M2, M3 or M4. In this case, M1
Adafruit_DCMotor *myMotor = AFMS.getMotor(1);


const int encoder1PinA = 2;        // X-AXIS  encoder 1 on pins 2 and 4
const int encoder1PinB = 4;
volatile int encoder1Pos = 0;

const int encoder2PinA = 3;        // Y-AXIS  encoder 2 on pins 3 and 5
const int encoder2PinB = 5;
volatile int encoder2Pos = 0;

boolean CarriageDir = 0;           // Carriage Direction '0' is Right to left
byte spd = 220;                    // Carriage speed from 0-255
int newpos = 0;                    // Taget position for carriage
int posrchd = 1;                   // Flag for target reached

int Pos1, Pos2;


void setup() {
  Serial.begin(115200);
  Serial.println("Linear Encoder Test  04-29-2014");

  AFMS.begin();  // Set up Motors
  
  myMotor->run(BACKWARD);        // Bring carriage to home position. 
  myMotor->setSpeed(spd); 
  delay(100); 
  myMotor->run(FORWARD);        // Bring carriage to home position. 
  myMotor->setSpeed(0); 
  
  attachInterrupt(0, doEncoder1, CHANGE);  // encoder pin on interrupt 0 (pin 2)
  // attachInterrupt(1, doEncoder2, CHANGE);  // encoder pin on interrupt 1 (pin 3)
  
  randomSeed(analogRead(0));
}

void loop() {
static int oldPos1, oldPos2;
uint8_t oldSREG = SREG;

uint8_t i;

  cli();
  Pos1 = encoder1Pos;  
  Pos2 = encoder2Pos;
  SREG = oldSREG;
  
  if(Pos1 != oldPos1){
     Serial.print("Encoder 1=");
     Serial.println(Pos1,DEC);
     oldPos1 = Pos1;
  }
  if(Pos2 != oldPos2){
     Serial.print("Encoder 2=");
     Serial.println(Pos2,DEC);
     oldPos2 = Pos2;
  }  
  

  //sweep_carriage();
  
  if(posrchd) {                           // If target has been reached clear flag, and get new target
    newpos =  random(200,3500);
    posrchd = 0;
  }    
    
  posrchd = go_to_target(newpos);
  
}


/***************************************************************************************
The following code was taken from   http://forum.arduino.cc/index.php?topic=41615.20;wap2
to utilize the fast port based encoder logic.  Thank you Lefty!
please go there for a full explanation of how this works.  I have truncated the comments 
here for brevity.
***************************************************************************************/
void doEncoder1() {                                  // ************** X- AXIS ****************
    if (PIND & 0x04) {                              // test for a low-to-high interrupt on channel A, 
        if ( !(PIND & 0x10)) {                      // check channel B for which way encoder turned, 
           encoder1Pos = ++encoder1Pos;               // CW rotation
           PORTD = PIND | 0x40;                     // set direction output pin to 1 = forward, 
          }
        else {
           encoder1Pos = --encoder1Pos;               // CCW rotation
           PORTD =PIND & 0xBF;                      // Set direction output pin to 0 = reverse, 
          }
    }
    else {                                          // it was a high-to-low interrupt on channel A
        if (PIND & 0x10) {                          // check channel B for which way encoder turned, 
           encoder1Pos = ++encoder1Pos;               // CW rotation
           PORTD = PIND | 0x40;                     // Set direction output pin to 1 = forward, 
           }
        else {
           encoder1Pos = --encoder1Pos;               // CCW rotation
           PORTD =PIND & 0xBF;                      // Set direction output pin to 0 = reverse, 
           }
         }
    PORTD = PIND | 0x80;                            //  digitalWrite(encoderstep, HIGH);   generate step pulse high
    PORTD = PIND | 0x80;                            //  digitalWrite(encoderstep, HIGH);   add a small delay
    PORTD = PIND & 0x7F;                            //  digitalWrite(encoderstep, LOW);    reset step pulse
}                                                   // End of interrupt code for encoder #1
                                                   
void doEncoder2(){                                  // ************** X- AXIS ****************
  if (PIND & 0x08) {                                // test for a low-to-high interrupt on channel A, 
     if (!(PIND & 0x20)) {                          // check channel B for which way encoder turned, 
      encoder2Pos = ++encoder2Pos;                  // CW rotation
      PORTB = PINB | 0x01;                          // Set direction output pin to 1 = forward, 
     }
     else {
      encoder2Pos = --encoder2Pos;                  // CCW rotation
      PORTD =PIND & 0xFE;                           // Set direction output pin to 0 = reverse, 
     }
  }
  else {                                            // it was a high-to-low interrupt on channel A
     if (PIND & 0x20) {                             // check channel B for which way encoder turned, 
      encoder2Pos = ++encoder2Pos;                  // CW rotation
      PORTB = PINB | 0x01;                          // Set direction output pin to 1 = forward, 
      }
     else {
      encoder2Pos = --encoder2Pos;                  // CCW rotation
      PORTB =PINB & 0xFE;                           // Set direction output pin to 0 = reverse, 
     }
  }
  PORTB = PINB | 0x02;                              // digitalWrite(encoder2step, HIGH);   generate step pulse high
  PORTB = PINB | 0x02;                              // digitalWrite(encoder2step, HIGH);   used to add a small delay
  PORTB = PINB & 0xFD;                              // digitalWrite(encoder2step, LOW);    reset step pulse
}                                                   // End of interrupt code for encoder #2



/***************************************************************************************
go_to_target() determines the distance and direction from current position to target 
position, then maps speed to decellerate close to the target so as not to overshoot.
***************************************************************************************/


int go_to_target(int target)
{
  int temp = 0;
  int delta = abs(Pos1-target);                   // Distance to target
  spd = map(delta,3600,0,255,150);                // Decellerate as you get closer
  if(target < 3600 && target > 100) {
     if(Pos1 < target) {
       myMotor->run(FORWARD);
       myMotor->setSpeed(spd); 
       temp = 0;
     } else if(Pos1 > target) {
       myMotor->run(BACKWARD);
       myMotor->setSpeed(spd); 
       temp = 0;
     }  else temp =1;
  }
  return temp;
}


/***************************************************************************************
sweep_carriage() is just a test routine to track back and forth testing overshoot. 
I will likely remove it soon.
***************************************************************************************/

void sweep_carriage()
{
    if(CarriageDir == 0) {              // Carriage Moving Right to Left
    if (Pos1 < 3600) {      
      myMotor->run(FORWARD);
      if (Pos1 > 3400) { 
       myMotor->setSpeed(spd-80); 
      } else myMotor->setSpeed(spd);  
    } else {      
      myMotor->setSpeed(0);  
      CarriageDir = !CarriageDir;  
    }
  } else {                              // Carriage Moving Left to Right
    if (Pos1 > 100) {
      myMotor->run(BACKWARD);
      if (Pos1 < 300) { 
       myMotor->setSpeed(spd-80); 
      } else myMotor->setSpeed(spd);  
     } else {      
      myMotor->setSpeed(0);  
      CarriageDir = !CarriageDir;  
    }
  }
}



This is the endstop sensor circuit.  I haven't wired it up to the Arduino as yet, but it is pretty straight forward. 







In the true nature of this project, I searched for a used motor controller to go with my Arduino .   Pictured here, is the Adafruit I2C Motor Shield V2 that was on my original robot.  This project will breath new life into it. 

This is a wonderful board in that it contains TWO dual h-bridge FET drivers for four DC motors, or two steppers, or one stepper and two motors... 



Wiring up the Optical Encoder.  Only four wires are needed...



12volt DC gear motor used to drive the carriage assembly.








References:

http://playground.arduino.cc/Main/RotaryEncoders#OnSpeed
http://reprap.org/wiki/Optical_encoders_01
http://mechatronics.mech.northwestern.edu/design_ref/sensors/encoders.html
http://forum.arduino.cc/index.php/topic,17695.0.html

My Repscrap: DC motors and rotary encoders for Z-Axis too?

$
0
0
 I took a few pictures this morning, to give me something to think about while I was at the cottage for the weekend. 

Pictured here, to the right, is the frame I made up to house the Y-Axis table. You can see the carriage assembly, motor and rail running horizontally in this image.  There are ball bearing slider rails in the "top" and "bottom" sections of the frame that will be attached to the Y-Axis carriage and table.

The frame itself is made from 3 inch extruded Vinyl exterior railing. It is 28" long to accommodate the travel of the Y-Axis table, and it is 16" wide.  I will be putting threaded adjustable feet in each corner for leveling. The ball bearing slider rails fit very snugly inside the extrusion.

I have laid the X-Axis carriage and rail across it, simply to visualize the mounting options and location. 

Now:  On to the good stuff... 

 Pictured here, are two identical paper feed roller assemblies from scavenged Canon inkjet printers. 
They are driven by brushless 12v DC motors, and have a high resolution rotary optical encoder on the end of the hollow paper feed shafts.  

My train of thought is something like this... 

If I were to cut  the hollow shafts short... say 3 inches in length, then support them with bushings, I could cement a threaded rod inside them, and theoretically create a Z-Axis identical in function to many of the existing Reprap stepper motor Z-Axis implementations.  But with 0.08mm resolution.... 



This is RepRrap Prusa implementation using two Z-Axis steppers...










This is just another view of the potential Z-Axis assemblies I have to work with.














I have another couple days until I'm home again to work on them.  I would *love* to get some input and ideas as to how I should/could implement this.


Comments please...





Further Progress on framing my RepScrap 3D printer.

$
0
0
Well, this is what the RepScrap looks like today.



I finished the Y-Axis table late last week, and tested the moment.  It's a little sluggish with the printer motor driving the bed, but will do for now.  I foresee burning this poor motor out, so will probably have to replace it with a gear motor at some point in time. 

The Y-Axis bed is the glass top of the all-in-one printer's scanner.   The aluminum cross bars come from the bottom track of a sliding closet door.


 As I mentioned in my previous posting, the frame for this was scavenged from exterior railing extrusions.    

Here, I am truing up frame that houses the Z-axis ball bearing slider rail, shown below.  These drawer sliders seem to be very tight, as in, there doesn't seem to be much slop at all. 








 At the top of the Z-Axis frame, I am placing the two DC-Motor /Encoder assemblies used for the paper feed on the inkjet printers. 

I am opting to run a threaded rod through each to drive the Z-axis gantry.  It had been suggested over on Reddit, that I might have success with a taught wire or line looped around the rough tubing of the sheet feed roller, but I think the threaded rod will be easier to design and troubleshoot.

  

And below, is my test circuit for the motor/encoder interface.

One thing that I am doing instead of having a separate Optical Endstop for each axis, is that I am making a larger black bar at each end of the linear optical encoder strip.  This can then be identified as a discrepancy during the encoder interrupt routine, and trip the endstop routine.




And a few more pictures of the Z-Axis gantry assembly with the X-Axis rail attached.






Using the Arduino PID Library for position control of X and Y axis on RepScrap printer

$
0
0
I've updated the test code I'm using to manage my X and Y axis DC motor / linear encoder closed loop controller. 



I am currently using the Arduino PID Library by Brett Beauregard  for this, and having great success.  Videos to come tomorrow. 

I am *NOT* going to explain what PID is, or how PID works.... I couldn't possibly do it justice.  I'll simply point you to Brett's wonderful explanation:



In the following example, I set up two axis, X and Y, each using a DC motor run from the Adafruit Motor Shield V2.  This shield provides PWM control for up to four separate DC motors via I2C communications.

I then set up two Quadrature encoders, one for each axis, using the Hardware Interrupts 0 and 1 (Arduino digital pins 2 and 3) and high speed digital port reads for one half of each encoder, and then validate the state of the other phase pin of the encoder during the interrupt routine: 
Graciously borrowed from http://forum.arduino.cc/index.php?topic=41615.20;wap2

The ZERO endstop for each axis is set up using the Arduino PinChangeInterrupt library watching a pin attached to a photo-interrupter.


I would certainly accept any advice on a proper sequence to initialize each axis to the ZERO endstop.

Right now, I arbitrarily send the carriage forward for 100ms assuming this is enough time to get on the positive side of the endstop, if we were beyond it.  Then I set my current position to the maximum possible location, and start travelling back to the endstop, knowing that once I actually reach it, the interrupt routine will Zero out my position, and initialize my PID setpoint to zero as well, thus stopping travel at ZERO. 
Is there a more efficient way of doing this? 


Inside the loop portion of my code, I run the PID controls as per the library, providing motor speed control via the Adafruit motor class, and periodically check to see if both X and Y axis have reached their goal.  At which time, I randomly select a new target for each.  When I get to the real application of this, the random selection of X and Y axis targets will be replaced by GRBL coordinates. 


And without further ado, here is my working code for precise position control in X and Y axis using the Arduino PID library:

/***************************************************************************************
*  Lin_Enc_02.ino   05-12-2014   unix_guru at hotmail.com   @unix_guru on twitter
*  http://arduino-pi.blogspot.com
*
*  This sketch allows you to run two salvaged printer carriages for X/Y axis using their 
*  linear encoder strips for tracking. 
*  This example uses the Arduino PID Library found at:
*  https://github.com/br3ttb/Arduino-PID-Library/archive/master.zip
*
*  Hardware Interrupt 0 on Digital pin2 is used to determine X-Axis position
*  Hardware Interrupt 1 on Digital pin3 is used to determine Y-Axis position
*  PinchangeInterrupt is used to identify the Zero Endstop for X and Y axis

*****************************************************************************************/

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"
#include <PID_v1.h> 
#include <PinChangeInt.h>


#define frontstop = 100                          // Right most encoder boundary
#define backstop = 3600                         // Left most encoder boundary


// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 

// Select which 'port' M1, M2, M3 or M4. In this case, M1
Adafruit_DCMotor *XaxisMotor = AFMS.getMotor(1);
Adafruit_DCMotor *YaxisMotor = AFMS.getMotor(2);


const int XaxisENCPinA = 2;                  // X-AXIS  encoder 1 on pins 2 and 4
const int XaxisENCPinB = 4;
const int XaxisENDSTOP = 10;               // Endstop photointerrupter for X-Axis
volatile double XaxisENCPos = 0;

const int YaxisENCPinA = 3;                  // Y-AXIS  encoder 2 on pins 3 and 5
const int YaxisENCPinB = 5;
const int YaxisENDSTOP = 11;               // Endstop photointerrupter for Y-Axis
volatile double YaxisENCPos = 0;


double XaxisSpd,  YaxisSpd;                  // Carriage speed from 0-255
double XaxisPos, YaxisPos;                   // Current Carriage position

/*working variables for PID routines*/
// Tuning parameters
float KpX=0,  KpY=0;                          //Initial Proportional Gain 
float KiX=10, KiY=10;                         //Initial Integral Gain 
float KdX=0,  KdY=0;                          //Initial Differential Gain 

double XaxisSetpoint, YaxisSetpoint;      // Taget position for carriage

// Instantiate X and Y axis PID controls
PID XaxisPID(&XaxisPos, &XaxisSpd, &XaxisSetpoint, KpX, KiX, KdX, DIRECT); 
PID YaxisPID(&YaxisPos, &YaxisSpd, &YaxisSetpoint, KpY, KiY, KdY, DIRECT); 
const int sampleRate = 1; 

long int reportTime;

void setup() {
  Serial.begin(115200);
  Serial.println("Linear Encoder Test  05-12-2014");

  AFMS.begin();  // Set up Motors
  
  XaxisMotor->run(BACKWARD);                  // Bring carriage to home position. 
  XaxisMotor->setSpeed(70); 
  delay(100);                                                // Get endstop limiter working here
  XaxisMotor->run(FORWARD);                    // Bring carriage to home position. 
  XaxisMotor->setSpeed(0); 
  

  YaxisMotor->run(BACKWARD);                  // Bring carriage to home position. 
  YaxisMotor->setSpeed(70); 
  delay(100);                                                // Get endstop limiter working here
  YaxisMotor->run(FORWARD);                    // Bring carriage to home position. 
  YaxisMotor->setSpeed(0); 
  
  attachInterrupt(0, doXaxisENC, CHANGE);     // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(1, doYaxisENC, CHANGE);     // encoder pin on interrupt 1 (pin 3)

  PCintPort::attachInterrupt(XaxisENDSTOP,doXaxisEndstop,FALLING); //X-axis Endstop ISR
  PCintPort::attachInterrupt(YaxisENDSTOP,doYaxisEndstop,FALLING); //Y-axis Endstop ISR

  randomSeed(analogRead(0));                          // Used to select random setpoints for testing

  XaxisPID.SetMode(AUTOMATIC);                //Turn on the PID loop 
  XaxisPID.SetSampleTime(sampleRate);         //Sets the sample rate 

  YaxisPID.SetMode(AUTOMATIC);                //Turn on the PID loop 
  YaxisPID.SetSampleTime(sampleRate);         //Sets the sample rate 

  reportTime = millis()+2000;
}

void loop() {
uint8_t oldSREG = SREG;                           // Store interrupt status register

  cli();
  XaxisPos = XaxisENCPos;  
  YaxisPos = YaxisENCPos;
  SREG = oldSREG;                                    // Restore interrupt status register
  

  // Temporary to create random X and Y axis setpoints for testing
  if(millis() > reportTime) {                               // Only validate this every 2 seconds
    if(XaxisPos == XaxisSetpoint && YaxisPos == YaxisSetpoint) {   
      // If both X-axis and Y-axis have reached their target - get new targets
      XaxisSetpoint =  random(200,3500);             // Keep target within bounds of Endpoints
      YaxisSetpoint =  random(200,3500);             // Keep target within bounds of Endpoints
    }    
    reportTime = millis()+2000;
}
  
  
  // Manage X-axis positioning
  XaxisPID.Compute();                          //Run the PID loop 
  if(XaxisSetpoint < XaxisPos) XaxisMotor->run(BACKWARD);  // Determine direction of travel
  else  XaxisMotor->run(FORWARD);      
  XaxisMotor->setSpeed(XaxisSpd);              // Apply PID speed to motor


  // Manage Y-axis positioning
  YaxisPID.Compute();                          //Run the PID loop 
  if(YaxisSetpoint < YaxisPos) YaxisMotor->run(BACKWARD);  // Determine direction of travel
  else  YaxisMotor->run(FORWARD);      
  YaxisMotor->setSpeed(YaxisSpd);              // Apply PID speed to motor

}


/***************************************************************************************
The following code was taken from   http://forum.arduino.cc/index.php?topic=41615.20;wap2
to utilize the fast port based encoder logic.  Thank you Lefty!
please go there for a full explanation of how this works.  I have truncated the comments 
here for brevity.

***************************************************************************************/

void doXaxisENC() {                                  // ************** X- AXIS ****************
    if (PIND & 0x04) {                              // test for a low-to-high interrupt on channel A, 
        if ( !(PIND & 0x10)) {                      // check channel B for which way encoder turned, 
           XaxisENCPos = ++XaxisENCPos;               // CW rotation
          }
        else {
           XaxisENCPos = --XaxisENCPos;               // CCW rotation
          }
    }
    else {                                          // it was a high-to-low interrupt on channel A
        if (PIND & 0x10) {                          // check channel B for which way encoder turned, 
           XaxisENCPos = ++XaxisENCPos;               // CW rotation
           }
        else {
           XaxisENCPos = --XaxisENCPos;               // CCW rotation
        }
     }
}                                                   // End of interrupt code for encoder #1


                                                   
void doYaxisENC(){                                  // ************** X- AXIS ****************
  if (PIND & 0x08) {                                // test for a low-to-high interrupt on channel A, 
     if (!(PIND & 0x20)) {                          // check channel B for which way encoder turned, 
      YaxisENCPos = ++YaxisENCPos;                  // CW rotation
     }
     else {
      YaxisENCPos = --YaxisENCPos;                  // CCW rotation
     }
  }
  else {                                            // it was a high-to-low interrupt on channel A
     if (PIND & 0x20) {                             // check channel B for which way encoder turned, 
      YaxisENCPos = ++YaxisENCPos;                  // CW rotation
      }
     else {
      YaxisENCPos = --YaxisENCPos;                  // CCW rotation
     }
  }
}                                                   // End of interrupt code for encoder #2


void doXaxisEndstop() {
  XaxisENCPos=0;                                    // X-Axis Endstop indicates ZERO position 
}

void doYaxisEndstop() {
  YaxisENCPos=0;                                    // Y-Axis Endstop indicates ZERO position 
}

            https://github.com/michaeljball/RepScrap

Updated diagram for reference:



References:

DIYDrones: Tutorial series for new Arduino PID library
http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/
http://robotics.stackexchange.com/questions/1232/how-can-i-use-the-arduino-pid-library-to-drive-a-robot-in-a-straight-line
Tim Wescott's PID without a PHD
http://abigmagnet.blogspot.ca/2008/10/dc-motor-control-part-one.html
http://www.pdx.edu/nanogroup/sites/www.pdx.edu.nanogroup/files/2013_Arduino%20PID%20Lab_0.pdf

https://www.youtube.com/watch?v=ZZYgZjMnGXU
https://www.youtube.com/watch?v=wbmEUi2p-nA
http://blog.solutions-cubed.com/pid-motor-control-with-an-arduino/
http://forum.arduino.cc/index.php/topic,45169.0.html

http://playground.arduino.cc/Code/PIDLibrary
http://playground.arduino.cc/Code/PIDAutotuneLibrary

Arduino Playground: PinChangeInterrupt Library
https://code.google.com/p/arduino-pinchangeint/downloads/list

LetsMakeRobots: PID Control by Big Face
LetsMakeRobots: PID Tutorials for Line Following by Enigmerald
LetsMakeRobots: PID without a PHD by BDK6
LetsMakeRobots: Using Motor Encoders to Control Speed by Oddbot








Upcoming fun project with dual Brushbot and ATtiny84...

$
0
0
While I'm waiting on parts (extruder, beated bed,  and hotend) for my RepScrap 3D printer, I thought I would have a bit more fun...

Someone sent me this hilarious video, and it started me thinking...

I frequently attend various  vendor trade shows,  and invariably, the vendors hand out useless trinkets as advertising...  I usually do one of three things with these... 

    1) anything electronic gets tossed into the parts bin, 
    2) anything of a "toy" nature goes to my children (yes, I got that order straight) 
    3) everything else gets tossed them into the garbage.




 So I happen to have a pair of these "electronic devices" in my parts bin.  I think they are commercially known as "Hexbot Nanos

They would effectively replace the toothbrush head and pager motor in the above video....

But I also received one of these little wind up distractions to the left here...  

Mechanical spring wound clockworks makes him do a little dance...  

For some strange reason, he hadn't quite made it to the kids yet...  hmmm....






It looks like those Hexbots might just fit the bottom of his feet..... 

Maybe I could run them directly from an ATtiny84 as in THIS blog... 
yes, I know I should add a transistor to drive each motor, but when I looked up the current draw on a free running pager motor, low and behold they are around 20-40ma... well within the range of the ATtiny84 pins capability.


 Current and RPM specs:

VoltageRPMCurrent (free)Current (stall)
1.5V970017.5mA120mA
3.0V1842022mA260mA
5.0V3190032.1mA420mA







Add a Sharp IR proximity sensor onto his chest, a small LiPo battery on his back for balance, the gratuitous leds on the head, and I think we just may have ourselves the next project.... It doesn't get much simpler...

(ok, ok... yes, I'll likely wire in a connector for the AVR programmer... but that's it...  well... and maybe find another pager motor to replace the spring wound mechanism that makes him dance... but THATs it... really...

maybe...)


I thought I would put this picture in, just to show the scale... 





References and prior art:





Viewing all 47 articles
Browse latest View live