Project showcase
Robotic Arm And 3D Modeling

Robotic Arm And 3D Modeling

A 3D printed robotic arm, controlled through an Arduino, also being 3D rendered in Python OpenGL, using the Ci20 board.

  • 5,283 views
  • 3 comments
  • 21 respects

Components and supplies

Necessary tools and machines

3drag
3D Printer (generic)

About this project

Overview

The main idea of this project was to build a robotic arm that can be controlled through user input, while also using the Ci20 to create a 3D rendering of the arm on screen.

3D Render

In this area of my project, I used the python opengl library to create a 3D render of the robotic arm I built. This was a very challenging project. Prior to this I had no experience in 3D graphics processing. It took me quite some time to understand how to build this type of program. With the help of python opengl tutorials by Sentdex on Youtube, I was able to gain a better understanding of this subject, giving me a starting base of knowledge to expand upon with this project.

There were a few things in the overlying subject of 3D graphics that I was initially confused on. The main two being:

  • Camera Positioning
  • Object Translation and Rotation

When it comes to camera positioning there is a very important idea to grasp. This idea is that, the camera does not move; it is the world that moves around it. The camera is located at (0,0,0) facing negative z. In order to 'move' the camera, the entire world must be translated and rotated with reference to it. In the case of the camera, the objects on screen are all being moved with respect to it so it appears as though the camera is doing the moving. This also creates the situation where coordinates in the translate and rotate functions must be inverted in order to have their desired effects.

The other area I had problems with was translation and rotation of objects. I had a hard time understanding how to move objects, because the functions for moving objects are the same as 'moving' the camera, only in this case they are actually moving. An aspect that is important to note in 3D graphics is, the order translations and rotations take place in, because their order affects their outcome. Changing the order of a translation and rotation can have drastically different results.

The bulk of the program was inspired by the Sentdex python opengl tutorials, but after the first few I set out on my own to learn my way, through trial and error, and simply doing it to see what works.

This program creates three 3D objects: The axes, the humerus, and the radius. The first is simply a cross section of planes, 50 by 50 units, colored red, blue, and green. I added this to add a reference point for the motion of the arm as it is translated and rotated. The humerus is the upper bone of the arm, and remains static. The radius is one of the two bones in the fore arm. I would have added the ulna bone, but was strapped for time on this project. The radius can be controlled through the G and H keys, rotating and translating it up and down.

This was an area that took a good chunk of my time to figure out; how to sync up the joint of the elbow in the 3D model. The main problem is that the radius's local origin is not based in the upper part of the object, but in the bottom part. This means that it rotates from the bottom not the top. When the radius is rotated it must also be translated back to the elbow joint in order for it to meet up. When trying to figure out this part, I really over thought it. I was using inverse trig functions to try and determine the distance vertically and horizontally it had to be translated. But it turned out I didn't need that, all I needed were two functions.

So my code went from this:

glRotatef(a,1,0,0)
a = rad(a)
glTranslatef(10-10*math.atan(a),0,0)
glTranslatef(0,20+20*math.atan(a),0)

To this:

glRotatef(a,1,0,0)
glTranslatef(20,0,0)

Which was surprising, that the simple code is exactly what I needed for it to work. I learned most of the things for this project through trial and error, and now I know what not to do.

Program Notes

These are some notes that I took as I programmed or when I had a problem that I didn't understand. They were mainly just for my reference, but some of them have coherent information on them, if they are legible.

Robotic Arm

I designed all of the parts for this arm myself in Solidworks. They were then printed on my school's 3D printer. This was actually my first experience of printing something that I designed from a 3D printer, all other times I merely left it digital.

I learned quite a few things from this aspect of my project. I learned key lessons in design and engineering. Mainly through my mistakes and tries, I learned good habits for designing something.

The design of this robotic arm is inspired by human anatomy. It is comprized of three main bones and a number of joints to give a realistic range of motion. There are two sets of pulleys that are driven through the two microservos. The main idea for replicating the motion of a human forearm, relies on the pulleys. They act as tendons to pull up the radius and ulna bones. The two pulleys can act in tandom to pull the arm up or down. But the major point in this design is that when the servos go in opposite directions, it creates a twisting motion, similar to how the wrist works.

There are somethings that I could have designed better, mainly the connections for the pulleys and servo mounts. The current belts are actually very large specialty o-rings. They may be a good size and fit, but they do tend to slip. I could have also used flat belts and toothed pulleys to have a more reliable transfer of motion. I had also run into some problems along the way as I designed this arm. For a little while I was left without servos because the ones I had initially installed on the arm had broken. They broke due to an error of my own, while attempting to adjust their trim pots. After they broke I took the opportunity to buy much stronger servos, as well as back ups in case the new ones failed.

I learn much from this project, but can always learn much more. This project helped to serve as a foundation for me to continue on with other projects, as well as update and maintain this one.

Arm Design Sketches

Arm Solidworks Assembly

Finished Arm

Code

Python OpenGL Robotic Arm ModelPython
A program written using the python opengl library. It features a controllable arm model and movable camera.
Key Controls:
- L and R arrows: translate x
- U and D arrows: translate y
- J and L keys: translate z

- A and D keys: x rotation
- W and S keys: y rotation
- I and K keys: z rotation

- G and H keys: Arm rotation
import serial
import time
import sys
import math
import pygame
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *


#Initialize Serial Port (On uart0)
port = serial.Serial("/dev/ttyS0",baudrate=9600,timeout=1)

#Map function variables
in_min=0
in_max=1024
out_min=0
out_max=90








def axes():
	axesVerticies=(
		(0,0,0),
		(50,0,0),
		(0,50,0),
		(0,0,50),
		(50,50,0),
		(0,50,50),
		(50,0,50)	
		)
	axesEdges= (
		(0,1),
		(0,2),
		(0,3),
		(1,4),
		(1,6),
		(2,4),
		(2,5),
		(3,5),
		(3,6)
		)
	axesSurfaces= (
		(0,1,6,3),
		(0,1,4,2),
		(0,2,5,3)
		)
	axesColors = (
		(1,.5,.5),#red
		(.5,1,.5),#green
		(.5,.5,1),#blue
		(1,.5,.5),
		(.5,1,.5),
		(.5,.5,1),
		(1,.5,.5),
		(.5,1,.5),
		(.5,.5,1),
		(1,.5,.5),
		(.5,1,.5),
		(.5,.5,1)
		)

	glBegin(GL_QUADS)
	
	i=0
	for surface in axesSurfaces:
		i+=1
		glColor3fv(axesColors[i])
		#Color the different surfaces red, blue, and green
		for vertex in surface:
			glVertex3fv(axesVerticies[vertex])
				
	glEnd()


	#Build Axes
	glBegin(GL_LINES)
	for edge in axesEdges:
		for vertex in edge:
			glVertex3fv(axesVerticies[vertex])	
	glEnd()








	
def radius(a):
	radiusVerticies= (
		(0,0,0),
		(0,3,0),
		(20,0,0),
		(20,5,0),
		(0,0,.5),
		(0,3,.5),
		(20,0,.5),
		(20,5,.5)
		)
	radiusEdges= (
		(0,1),
		(0,2),
		(0,4),
		(1,3),
		(1,5),
		(2,3),
		(2,6),	
		(3,7),
		(4,5),
		(4,6),
		(5,7),
		(6,7)
		)
	radiusSurfaces= (
		(0,1,2,3),
		(4,5,6,7),
		(0,1,4,5),
		(2,3,6,7),
		(0,2,4,6),
		(1,3,5,7)
		)
	
	glRotatef(90,0,0,1)#Position the Radius Vertically
	glRotatef(90,1,0,0)
	glTranslatef(25,25,25)#Move to center

	glRotate(-a,0,0,-1)#Rotate Radius by a, a is changed on keypress
	glTranslatef(-20,0,0)#Keep the Radius the same distance away after rotation


	glBegin(GL_QUADS)

	for surface in radiusSurfaces:
		glColor3fv((1,1,1))#Color the Radius White

	glEnd()

	#Build the Radius
	glBegin(GL_LINES)
	for edge in radiusEdges:
		for vertex in edge:
			glVertex3fv(radiusVerticies[vertex])
	glEnd()

	
	
	
	
	
	
	
	

def humerus():
	humerusVerticies= (
		(0,0,0),
		(0,5,0),
		(25,-1,0),
		(25,6,0),
		(0,0,.5),
		(0,5,.5),
		(25,-1,.5),
		(25,6,.5)
		)
	humerusEdges= (
		(0,1),
		(0,2),
		(0,4),
		(1,3),
		(1,5),
		(2,3),
		(2,6),	
		(3,7),
		(4,5),
		(4,6),
		(5,7),
		(6,7)
		)
	humerusSurfaces= (
		(0,1,2,3),
		(4,5,6,7),
		(0,1,4,5),
		(2,3,6,7),
		(0,2,4,6),
		(1,3,5,7)
	  )
	
	#Position the Humerus Vertically
	glRotatef(90,0,0,1)
	glRotatef(90,1,0,0)
	glTranslatef(25,25,25)
	
	
	glBegin(GL_QUADS)

	for surface in humerusSurfaces:
		glColor3fv((1,1,1))#Color the Humerus White

	glEnd()

	#Build the Humerus
	glBegin(GL_LINES)

	for edge in humerusEdges:
		for vertex in edge:
			glVertex3fv(humerusVerticies[vertex])
	glEnd()







def map(rcv,in_min,in_max,out_min,out_max):
	#This function is from the arduino. It maps the values
	#of one input, onto a different range, creating the output
	return(rcv-in_min*(out_max-out_min)/(in_max-in_min)+out_min)







def serialInput():
	#This function reads the serial port, uart0
	#It then processes the data into a usable variable
	#by the radius function
	
	#This Function is broken and does not work currently
	rcv = port.readline()
	int(rcv)
	a = map(rcv,in_min,in_max,out_min,out_max)
	return(a)

	
	
	
	
	
def text(x,y,z):
	#Print out the position of the camera
	#Rotation isn't implemented, so it won't be accurate
	print("Position:",x,y,z)
	sys.stdout.write("\033[F")
	
	
	
	
	

def main():

	#Build Pygame Window
	pygame.init()
	display = (800,800)
	pygame.display.set_mode(display, DOUBLEBUF|OPENGL)


	gluPerspective(45, (display[0]/display[1]), 0.1, 300.0)

	#Set Camera Position
	glRotatef(-45,-1,0,0)#Rotate Camera down
	glRotatef(45,0,-1,0)#Rotate Camera turn
	glTranslatef(-100,-150,-100)#Translate the camera to the corner


#I had problems figuring out how to properly arrange these functions
#There order will effect their outcomes; Changing one value can
#have drastically different results.
#Rotating the camera first is important, because if you
#translate it first, the rotation after it is then multiplied
#with the translation, causing it to do things you wouldn't 
#expect.
	
	#Variables

	#Radius Angle
	a=0
	
	#Coordinates
	x=0
	y=0
	z=0
	

	#Keystates
	KLEFT=False
	KRIGHT=False
	KUP=False
	KDOWN=False
	KJ=False		
	KL=False
	KA=False
	KD=False
	KW=False
	KS=False
	KI=False
	KK=False	
	KG=False
	KH=False
	
	#Infinite while loop
	while True:
		#Event Handling loop
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				pygame.quit()
				quit()
				#Kill Window

			#KEYEVENT KEYDOWN HANDLING
			if event.type == pygame.KEYDOWN:
				#TRANSLATE
				if event.key == pygame.K_LEFT:
					KLEFT=True
				if event.key == pygame.K_RIGHT:
					KRIGHT=True
				if event.key == pygame.K_UP:
					KUP=True
				if event.key == pygame.K_DOWN:
					KDOWN=True
				if event.key == pygame.K_j:
					KJ=True
				if event.key == pygame.K_l:
					KL=True
				
				#ROTATE
				if event.key == pygame.K_a:
					KA=True			
				if event.key == pygame.K_d:
					KD=True		        	
				if event.key == pygame.K_w:
					KW=True			
				if event.key == pygame.K_s:
					KS=True
				if event.key == pygame.K_i:
					KI=True			
				if event.key == pygame.K_k:
					KK=True
				#Arm Control
				#Optional key usage to move arm model

				if event.key == pygame.K_g:
					KG=True
				if event.key == pygame.K_h:
					KH=True
					
			#KEYEVENT KEYUP HANDLING
			if event.type == pygame.KEYUP:
				#TRANSLATE
				if event.key == pygame.K_LEFT:
					KLEFT=False
				if event.key == pygame.K_RIGHT:
					KRIGHT=False
				if event.key == pygame.K_UP:
					KUP=False
				if event.key == pygame.K_DOWN:
					KDOWN=False
				if event.key == pygame.K_j:
					KJ=False
				if event.key == pygame.K_l:
					KL=False
		
				#ROTATE
				if event.key == pygame.K_a:
					KA=False
				if event.key == pygame.K_d:
					KD=False
				if event.key == pygame.K_w:
					KW=False	
				if event.key == pygame.K_s:
					KS=False
				if event.key == pygame.K_i:
					KI=False
				if event.key == pygame.K_k:
					KK=False

				#Arm Angle
				#Optional key usage to move arm model
				
				if event.key == pygame.K_g:
					KG=False
				if event.key == pygame.K_h:
					KH=False
				

		#KEY PRESS ACTIONS

		#TRANSLATE
		if KLEFT==True:
			glTranslatef(5,0,0)
			x+=5
		if KRIGHT==True:
			glTranslatef(-5,0,0)
			x-=5
		if KUP==True:
			glTranslatef(0,-5,0)
			y+=5
		if KDOWN==True:
			glTranslatef(0,5,0)
			y-=5
		if KJ==True:
			glTranslatef(0,0,-5)
			z+=5
		if KL==True:
			glTranslatef(0,0,5)
			z-=5

		#ROTATE
		if KA==True:
			glRotate(5,0,1,0)
		if KD==True:
			glRotate(5,0,-1,0)
		if KW==True:
			glRotate(5,-1,0,0)
		if KS==True:
			glRotate(5,1,0,0)
		if KI==True:
			glRotate(5,0,0,-1)
		if KK==True:
			glRotate(5,0,0,1)
		
		#Arm Angle
		#Optional key usage to move arm model
		if KG==True:
			a+=5
	
		if KH==True:
			a-=5
		
				

#Another area I had trouble with was understanding how the camera
#translation, and rotation, all worked together. An assumption
#I had from when I started learning about 3D programming,
#was that when translate or rotate was called, the camera
#was doing the moving. This caused me major confusion, due
#to me then not understanding how to translate and rotate
#objects.

#This is an important distinction:
#	-The world is not static and the camera moves
#	-But the camera is static and the world moves
#Once I realized the second statement is true, then everything
#became much clearer, and I could continue working on my project
#with much less confusion.
		




		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

		#Get joystick input
		#serialInput()

	
		#Draw shapes
		axes()

		glPushMatrix()
		radius(a)
		glPopMatrix()

		glPushMatrix()
		humerus()
		glPopMatrix()

		
		text(x,y,z)

		pygame.display.flip()
		pygame.time.wait(10)
	

main()
Python Serial TestPython
This was a test program for testing out the serial port of the Ci20.
import serial
port = serial.Serial("/dev/bus/usb/003",baudrate=9600,timeout=3.0)
#"/dev/bus/usb/003"
while True:
	rcv = port.readline()
	print(rcv)	
Arduino Joystick to Servo ControlArduino
This program takes the input of the joysticks and maps the servos position to it.
#include <Servo.h>

Servo servo1;
Servo servo2;

void setup() {
  
  Serial.begin(9600);
  while(!Serial){
    //Wait until Serial port opens before sending data
  }
  pinMode(A0,INPUT);
  pinMode(A1,INPUT);
  pinMode(10,OUTPUT);
  pinMode(9,OUTPUT);
  servo1.attach(10);
  servo2.attach(9);
}

void loop() {

  servoFunction();
  
}

void servoFunction(){
  //Read Joystick Position
  int x = analogRead(A0);
  int y = analogRead(A1);
  
  //Send Joystick Position to the serial port
  Serial.println(x);
  Serial.println(' ');
  Serial.println(y);

  //Map motor position to joystick position
  int s1position = map(x, 0, 1023, 500, 2300);
  int s2position = map(y, 1023, 0, 600, 2400);
  //Range of servo1: 500-2300
  //Range of servo2: 600-2400
  //Each Servo has a specific range for its end stops
  //This was determined through trial and error
  
  //Set motor position to joystick position
  servo1.writeMicroseconds(s1position);
  servo2.writeMicroseconds(s2position);
  
}

Custom parts and enclosures

elbow_bracket.SLDPRT
elbow_bracket.SLDPRT
elbow_hinge.SLDPRT
elbow_hinge.SLDPRT
elbow_pulley.SLDPRT
elbow_pulley.SLDPRT
Humerus.SLDPRT
Humerus.SLDPRT
shoulder_bracket.SLDPRT
shoulder_bracket.SLDPRT
shoulder_hinge.SLDPRT
shoulder_hinge.SLDPRT
ulna.SLDPRT
ulna.SLDPRT
upper_pulley.SLDPRT
upper_pulley.SLDPRT
wrist_bracket.SLDPRT
wrist_bracket.SLDPRT
wrist_connector.SLDPRT
wrist_connector.SLDPRT
wrist_hinge_axis_1.SLDPRT
wrist_hinge_axis_1.SLDPRT
wrist_hinge_axis_2.SLDPRT
wrist_hinge_axis_2.SLDPRT
elbow_bracket.STL
elbow_hinge.STL
elbow_pulley.STL
Humerus.STL
shoulder_bracket.STL
shoulder_hinge.STL
ulna.STL
upper_pulley.STL
wrist_bracket.STL
wrist_connector.STL
wrist_hinge_axis_1.STL
wrist_hinge_axis_2.STL

Schematics

Arduino and Ci20 Schematic
Depicted in this schematic is the circuit to control the servos and connect them to the arduino. (Not Depicted: The Arduino's Tx line is also connected to the Ci20's UART0 RX Pin, located on GPIO pin 10 of the main header. The Ci20 also supplies 3.3v to the arduino's VIN pin, and the ground pins of the arduino and Ci20 are also connected.)
Schematic

Comments

Similar projects you might like

Simple Programmable Robotic Arm

Project showcase by Ryan Chan

  • 49,059 views
  • 63 comments
  • 145 respects

Robotic Arm Controlled by Human Arm

Project showcase by zezarandrade

  • 5,849 views
  • 0 comments
  • 11 respects

Android Controlled 6DoF Robotic Arm

Project in progress by Team Mans

  • 11,065 views
  • 0 comments
  • 17 respects

Littlearm 2C: Build a 3D Printed Arduino Robot Arm

Project tutorial by Slant Concepts

  • 4,403 views
  • 1 comment
  • 32 respects

Robotic Arm

Project tutorial by Team tRee

  • 3,830 views
  • 0 comments
  • 11 respects

BT Mobile Ctrl Trunk Robotic Arm Using String

Project tutorial by jegatheesan

  • 1,276 views
  • 0 comments
  • 3 respects
Add projectSign up / Login