Skip to content

Commit c2c96c8

Browse files
committed
Make this a bit more similar to main - before merging back.
1 parent 7f4fc82 commit c2c96c8

File tree

3 files changed

+116
-55
lines changed

3 files changed

+116
-55
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.venv
2+
__pycache__
3+
.DS_Store
4+
*.pyc
5+
.idea

readme.md

100644100755
Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
1-
Python Code to drive the Maplin/OWI USB Robot arm
1+
# Python Code to drive the Maplin/OWI "Edge" USB Robot arm
22

3-
Tested on:
4-
* Linux
5-
* OSX (Lion, Mountain Lion)
6-
May work on Windows,.
3+
The main repository for this is https://github.com/orionrobots/python_usb_robot_arm.
74

8-
Requirements
9-
============
10-
* Python 2.7
11-
* Libusb
12-
* pyusb
5+
[Video Demo](https://www.youtube.com/watch?v=dAvWBOTtGnU)
6+
7+
## Quick Raspberry Pi Installation
8+
9+
On a terminal at the Raspberry Pi enter these commands:
10+
11+
curl https://raw.githubusercontent.com/orionrobots/python_usb_robot_arm/main/setup_arm.sh | sudo bash
12+
13+
I suggest review the setup_arm.sh script above to see what it does.
14+
15+
## Requirements for Other OS
16+
17+
This has previously been tested on Linux, OSX and Windows. OSX and Windows require signed drivers which may not easily be available.
18+
19+
* Python 3 or 2.7
20+
* Libusb (on linux, mac or windows - <http://sourceforge.net/projects/libusb-win32/files/latest/download>) - the apt-get package will work.
21+
* pyusb via pip
22+
23+
## Usage
1324

14-
Usage
15-
=====
1625
As a library:
1726

18-
>>> import arm
27+
>>> import usb_arm
1928

2029
To initialise libusb and the arm
2130

@@ -32,16 +41,15 @@ It will turn on for 1 second, and automatically turn off. The moveArm function a
3241
move. You can optionally specify another time, but since the Maplin arm doesn't have any sensors, beware that if
3342
it reaches limits before the time finishes, then it won't stop.
3443

35-
Actual movement
36-
---------------
44+
### Actual movement
3745

3846
>>> arm.move(usb_arm.ElbowUp)
3947

4048
The elbow will move up.
4149
The movements possible:
4250

43-
OpenGrips
44-
CloseGrips
51+
GripsOpen (OpenGrips)
52+
GripsClose (CloseGrips)
4553
WristUp
4654
WristDown
4755
ElbowUp
@@ -53,8 +61,10 @@ BaseCtrClockWise
5361

5462
Stop
5563

56-
Combining Movements
57-
-------------------
64+
LedOn
65+
66+
## Combining Movements
67+
5868
Movements are based upon the BitPattern class, and you can feed arbitrary bitpatterns to it, but all those the
5969
arm is currently capable of are represented above.
6070

@@ -65,8 +75,7 @@ or operator:
6575

6676
The arm should turn clockwise and bring the elbow up simultaneously for half a second.
6777

68-
Gear Lash
69-
---------
78+
### Gear Lash
7079

7180
The unmodified arm has a few flaws - it has fairly loose gear chains in the "servos" it uses for the movements.
7281
To see what I mean try the following:
@@ -79,15 +88,15 @@ which you will need to account for as you use the arm and in programmed sequence
7988

8089
You should now know enough to move the arm to any location.
8190

82-
Sequences of Actions
83-
--------------------
91+
### Sequences of Actions
92+
8493
You can create programmed sequences of actions for the robot. However, before you issue one of these, ensure you
8594
know the position of the arm, and wont move it past its limits - which could cause damage to it,
8695

8796
Sequences are created as arrays of commands. Each command is an array of the bitpattern, followed by the
8897
optional time (defaulting to 1 second):
8998

90-
>>> actions = [[usb_arm.ElbowDown, 0.5], [usb_arm.CloseGrips, 0.5], [usb_arm.ElbowUp]]
99+
>>> actions = [[usb_arm.ElbowDown, 0.5], [usb_arm.GripsClose, 0.5], [usb_arm.ElbowUp]]
91100

92101
To issue the action list:
93102

@@ -100,11 +109,20 @@ There are a couple of canned actions already in the module:
100109
block_right
101110
left_and_blink
102111

103-
Troubleshooting
104-
===============
112+
## An Example Script
113+
114+
Using this in a python file couldn't be easier. For example you could put this in demo_arm.py:
115+
116+
import usb_arm
117+
arm = usb_arm.Arm()
118+
actions = [[usb_arm.ElbowDown, 0.5], [usb_arm.GripsClose, 0.5], [usb_arm.ElbowUp]]
119+
arm.doActions(actions)
105120

106-
Linux - permissions
107-
-------------------
121+
You can then run this with python3 demo_arm.py.
122+
123+
## Troubleshooting
124+
125+
### Linux - permissions
108126

109127
You will either need to run as root (not recommended) or modify your system to allow all users access to the device.
110128

@@ -116,4 +134,16 @@ and add:
116134

117135
Plug in the device and you should be able to access it. Tested on Ubuntu and Mint Linux versions.
118136

137+
## License
138+
139+
CC BY SA 3.0 - http://creativecommons.org/licenses/by-sa/3.0/
140+
Creative Commons By Attribution Share-Alike v3.0
141+
142+
## Related Work
119143

144+
* The original reverse engineering of the UBS protocol was done by
145+
[Vadim Zaliva](http://www.crocodile.org/lord/) and published on [his blog](http://notbrainsurgery.livejournal.com/38622.html)
146+
* [An alternative Objective-C control program](https://armctrl.codeplex.com)
147+
* Device assembly manual <https://www.robotshop.com/media/files/pdf/owi-535_manual.pdf>
148+
* [OWI (manufacturer) information](http://www.owirobots.com/cart/catalog/OWI-535USB-ROBOTIC-ARM-KIT-with-USB-PC-INTERFACE-Assembled-103.html)
149+
* [PCB Scans](https://kyllikki.github.io/EdgeRobotArm/)

usb_arm.py renamed to usb_arm/__init__.py

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
"""Maplin USB Robot arm control"""
2-
import os
3-
os.environ['DYLD_LIBRARY_PATH'] = '/opt/local/lib'
1+
"""Maplin USB Robot arm control.
2+
Usage -
3+
>>> import usb_arm
4+
>>> arm = usb_arm.Arm()
5+
>>> arm.move(usb_arm.OpenGrips)
6+
>>> arm.doActions(block_left) # WARNING - ARM SHOULD BE ALL THE WAY RIGHT BEFORE TRYING THIS
7+
8+
Trouble:
9+
"NO back end found" - you need to install a libusb driver on your system.
10+
"""
411
import usb.core
512
from time import sleep
613

@@ -25,18 +32,30 @@ def __or__(self, other):
2532
self.base | other.base,
2633
self.led | other.led)
2734

28-
CloseGrips = BitPattern(1, 0, 0)
29-
OpenGrips = BitPattern(2, 0, 0)
30-
Stop = BitPattern(0, 0, 0)
31-
WristUp = BitPattern(0x4, 0, 0)
32-
WristDown = BitPattern(0x8, 0, 0)
33-
ElbowUp = BitPattern(0x10, 0, 0)
34-
ElbowDown = BitPattern(0x20, 0, 0)
35-
ShoulderUp = BitPattern(0x40, 0, 0)
36-
ShoulderDown = BitPattern(0x80, 0, 0)
37-
BaseClockWise = BitPattern(0, 1, 0)
35+
def __eq__(self, other):
36+
return self.arm == other.arm and self.base == other.base and self.led == other.led
37+
38+
def __repr__(self):
39+
return "<BitPattern arm:%s base:%s led:%s>" % (self.arm, self.base, self.led)
40+
41+
def __str__(self):
42+
return self.__repr__()
43+
44+
45+
GripsClose = BitPattern(1, 0, 0)
46+
CloseGrips = GripsClose
47+
GripsOpen = BitPattern(2, 0, 0)
48+
OpenGrips = GripsOpen
49+
Stop = BitPattern(0, 0, 0)
50+
WristUp = BitPattern(0x4, 0, 0)
51+
WristDown = BitPattern(0x8, 0, 0)
52+
ElbowUp = BitPattern(0x10, 0, 0)
53+
ElbowDown = BitPattern(0x20, 0, 0)
54+
ShoulderUp = BitPattern(0x40, 0, 0)
55+
ShoulderDown = BitPattern(0x80, 0, 0)
56+
BaseClockWise = BitPattern(0, 1, 0)
3857
BaseCtrClockWise = BitPattern(0, 2, 0)
39-
LedOn = BitPattern(0, 0, 1)
58+
LedOn = BitPattern(0, 0, 1)
4059

4160

4261
class Arm(object):
@@ -45,6 +64,8 @@ class Arm(object):
4564

4665
def __init__(self):
4766
self.dev = usb.core.find(idVendor=0x1267)
67+
if not self.dev:
68+
raise RuntimeError("USB Arm Not found. Ensure it is plugged in and powered on")
4869
self.dev.set_configuration()
4970

5071
def tell(self, msg):
@@ -57,20 +78,22 @@ def safe_tell(self, fn):
5778
case of an exception"""
5879
try:
5980
fn()
60-
except:
81+
except Exception:
6182
self.tell(Stop)
6283
raise
6384

6485
def move(self, pattern, time=1):
6586
"""Perform a pattern move with timing and stop"""
66-
self.tell(pattern)
67-
sleep(time)
68-
self.tell(Stop)
87+
try:
88+
self.tell(pattern)
89+
sleep(time)
90+
finally:
91+
self.tell(Stop)
6992

7093
def doActions(self, actions):
7194
"""Params: List of actions - each is a list/tuple of BitPattern and time
7295
(defaulting to 1 if not set)"""
73-
#Validate
96+
# Validate
7497
for action in actions:
7598
if not 1 <= len(action) <= 2:
7699
raise ValueError("Wrong number of parameters in action %s" %
@@ -90,11 +113,14 @@ def doActions(self, actions):
90113
raise
91114

92115

93-
block_left = [[ShoulderDown], [CloseGrips, 0.4], [ShoulderUp],
94-
[BaseClockWise, 10.2], [ShoulderDown],
95-
[OpenGrips, 0.4], [ShoulderUp, 1.2]]
96-
block_right = [[ShoulderDown], [CloseGrips, 0.4], [ShoulderUp],
97-
[BaseCtrClockWise, 10.2], [ShoulderDown],
98-
[OpenGrips, 0.4], [ShoulderUp, 1.2]]
99-
left_and_blink = list(block_left)
100-
left_and_blink.extend([[LedOn, 0.5], [Stop, 0.5]] * 3)
116+
def makeGrabAndMove(baseDir):
117+
return [[CloseGrips, 1.1],
118+
[ShoulderUp | ElbowUp | WristDown | baseDir],
119+
[baseDir, 8.5],
120+
[ShoulderDown | ElbowDown | WristUp | baseDir],
121+
[OpenGrips]]
122+
123+
124+
blink = [[LedOn, 0.5], [Stop, 0.5]] * 3
125+
block_left = makeGrabAndMove(BaseClockWise) + blink
126+
block_right = makeGrabAndMove(BaseCtrClockWise) + blink

0 commit comments

Comments
 (0)