Written by Madelyn Brown, Joshua Parkinson, and Ben Grumann
Our Project-
We took upon the task of bringing the Dexter Robot Arm, that had previously been collecting dust in the back of the lab, back to life. Going into the project we had no idea what we were even working with. There was little to no documentation left on the arm as to what model it was, how to operate it, and what software it used. Our overall project goal was to make the arm usable enough to work in future projects and demonstrations.
Our Goals & Goal Status-
To consider the robot operable we had to accomplish several small things. We had to check that the robot was functional to where it turned on and initialized properly, as well as deciphering which programming app it used to be jogged. Each joint on the robot was to be functional so it can use its full range of motion, and a program was to be written demonstrating each joint’s movement [See Content Included for sample code]. The robot was also to have a functional end effector, as well as the capability to perform basic tasks and movements, including palletization. Finally, we decided that the robot should have a clean, well managed workspace to where it can perform within a full range of motion.
After some testing, we were able to successfully turn the robot on, connect it to the software we found on the given laptop, and send simple tasks to the arm, those of which were included within the software. We then began writing our own code, getting the robot to do more complex jobs, such as drawing on a whiteboard and accepting user input using HTML code. After a lot of practicing, writing a quick job to demonstrate joint movement came fairly easily, and the only joints that weren’t operating as expected were J4 and J5, the joints on the end effector. We were also unable to receive a signal from the robot to control the servo on the original end effector, however, to continue allowing the robot to perform tasks, we created a second end effector that doesn’t use a servo. We were also able to in several cases operate successfully around the loose joints in the robot. Finally, we were able to clean up the robot’s workspace to a much neater environment than when we had begun work, allowing it to move freely [See Content Included for workspace image].
Unfortunately, we were unable to complete the code needed to perform the palletizing tasks. We had several pieces of it prepared, such as the end effector, the palette parts to stack, and the user-input based code we planned to fine tune. However, we ran short on time, and failed to bring these pieces together. Despite this, we had completed a few value-added goals, such as more complex tasks (whiteboard art) and active user input (beginning of palletizing code). Due to this, we are certain this is a strong start to next semester’s work on this project.
Replication Page & Links-
For the sake of ease, during our research process, we made sure to document every important detail a new user may find helpful when operating the robot for the first time. This documentation will be linked (and pasted) below, and it will run through how to set up the robot, connect to it, and run simple codes using the software. This document also includes many helpful links and resources we used during our experimentation.
–
Dexter Robotic Arm Info Doc
Written Collaboratively by Maddie Brown, Joshua Parkinson, and Ben Grumann
There aren’t a lot of resources or documentation available on this project, so we’ve put together this document to compile the most important information regarding the operation of the robotic arm in the OSHE lab.
Basic Information/Links
This robot is an older model of a Dexter 5 axis arm by Haddington Dynamics. The arm is mostly 3D printed and entirely open source. To program it, it uses Dexter Development Environment [DDE] which can be downloaded from the link below:
[Dexter Development Environment]
There are a couple other links that we found useful when first working on the arm.
Hackaday page with assembly videos: [Dexter | Hackaday.io]
Official Chemtron website with specs: [Dexter: An Award Winning Robotic Arm]
OnShape page with a full model of the arm (newer model): [CAD Arm]
Dexter github page: [Github]
End Effector Wiring Documentation: [End Effector Page]
Thingiverse 3D Printable Parts: [Dexter Arm Files]
Startup
To boot up the robot, make sure that it’s plugged into the front power supply and the E-Stop button isn’t pressed. It should be oriented with the circuit board facing towards the front, and all other joints pointed straight up. Small white markers on each joint indicate its intended ‘home’ position. There should be a few lights on the board that turn on when the robot is plugged in [Green, Blue, and currently a flickering Red, as well as green lights near the joint cables], and the fan at the front of the circuit board will start up. After ~30 seconds, the robot will do a small ‘dance’ to initialize, and then it should be good to plug in and program. Wait until the robot has initialized to plug it in, or else it could confuse the programming.
Connection
To connect to the robot, Dexter uses an ethernet cable. The cable plugs directly into the board, then directly into the machine that can run DDE. Dexter can also be connected to a computer via a micro usb cable, though that connection is more convoluted [requires outside programs], so it’s easiest to use the ethernet cable and a USB adapter if your laptop doesn’t have a port.
Upon bootup, DDE should have some sample code already that initializes ‘dexter0’, or the default robot. The IP should be set to 192.168.1.142 . To run this code, and all jobs in general, highlight the lines you wish to run, then hit the ‘Eval&Start’ button near the bottom left. This should initialize the robot.
In order for your ethernet port to recognize Dexter is connected to it, you have to manually set the IP. Go to Settings > Network & Internet > Ethernet, or just press Start and type in Ethernet. Set the IP Assignment to Manual, and edit the IPv4 settings to:
IPv4 Address: 192.168.1.140 (Cannot be the same as the Dexter IP)
IPv4 Mask: 255.255.255.0
Re-Initialize Dexter in the DDE terminal for good measure, then run a test job to make sure the robot is connected properly. Test jobs can be found under the ‘Jobs’ menu near the top of the workspace. We usually run the ‘NEUTRAL_ANGLES’ instruction under ‘Run Instruction’ to make sure everything is operable.
Dexter Development Environment
DDE is a little difficult to understand, and even through our current research, we still don’t know its full capabilities. The interface itself can be a bit overwhelming, so we’ll run through the quick basics of running code. All of this information and more is available within the program through the help center, which is located on the top right side of the screen.
Initializing Dexter- In depth
In order for DDE to run code, a robot must be defined within the software. Luckily, this code is given to you when you boot up the program, but just in case, here’s what you should be seeing:
The Dexter IP address is the IP of the robot, and will be fixed. Unless you plan on changing the robot’s IP, this doesn’t need to be altered. ‘Dexter0’ is the name the robot will be given, and can be changed if needed.
To run these lines, they have to be selected. You can run each line individually by pressing ‘ALT’ and clicking the code, or click and drag to select all of them. Then, near the bottom of the screen, hit the ‘Eval&Start’ button. This will be used to run jobs in the future.
Jobs
When you want to run a command in DDE, it needs to be defined as a ‘job’. There are plenty of sample codes that demonstrate this syntax, and can be inserted into the file under ‘Jobs’ > ‘Insert Instruction’ menu at the top of the screen. Under ‘Jobs’ > ‘Run Instruction’ there are commands that will run immediately after clicking them. The ones we used the most were ‘HOME_ANGLES’ and ‘NEUTRAL_ANGLES’. Home brings the robot to the position it was initialized at, and Neutral brings all joints (except J1) to a 45 degree angle.
To move the robot, you can use the commands ‘Dexter.move_all_joints([])’ and ‘Dexter.move_to([])’. The first command takes inputs as angles for all joints, while the second takes a coordinate in space, though the move_to command is a bit finicky and very precise. While DDE gives you a supposed range and limit of motion, sometimes the robot has trouble finding its way there. We found the move_all_joints command to be simplest to work with, though tedious.
More syntax examples are available through sample code or searching the Help Center.
Link to some of the code we’ve used previously: [Sample Codes] (Also available near the bottom of this document)
Simulation Mode
In the bottom right of the DDE interface, there is a small box that is used for robot simulations. This is helpful when testing code to make sure the robot doesn’t damage itself when running. To turn simulation mode on and off, go to ‘Jobs’ > ‘Simulate?’ > and select which option you prefer. The robot will not move if simulation is set to true, and will instead show its process on screen.
Help Center
The Help Center within DDE has been very helpful when providing us with syntax, ideas, and examples to follow when we work on our own code. Many other questions can be answered there, and if we haven’t covered the specifics of what you’d like to do, it’s likely you’ll find the information there.
Search for commands with the top search bar, or any other keyword, and where in the documentation your keyword is located will light up orange.
–
OSF Repository/Content Included-
[OSF Repository Link] https://osf.io/x8z5j/
SAMPLE CODES:
Joint Test- 90 degree rotation and back.
new Job({name: “jointTest”,
do_list: [Dexter.set_parameter(“MaxSpeed”, 20),
Dexter.move_all_joints([0, 0, 0, 0, 0]),
Dexter.move_all_joints([90, 0, 0, 0, 0]),
Dexter.move_all_joints([0, 0, 0, 0, 0]),
Dexter.move_all_joints([0, 80, 0, 0, 0]),
Dexter.move_all_joints([0, 0, 0, 0, 0]),
Dexter.move_all_joints([0, 0, 90, 0, 0]),
Dexter.move_all_joints([0, 0, 0, 0, 0]),
Dexter.move_all_joints([0, 0, 0, -90, 0]),
Dexter.move_all_joints([0, 0, 0, 0, 0])
]})
Hard-coded job that draws on a whiteboard.
new Job({name: “amogus”,
do_list: [Dexter.set_parameter(“MaxSpeed”, 20),
Dexter.move_all_joints([0, 0, 0, 0, 0]),
Dexter.move_all_joints([90, -35, 100, 0, 0]), //xyz
Dexter.move_all_joints([90, -33.5, 100, 0, 0]), //upper right eye
Dexter.wait_until(5),
Dexter.move_all_joints([90, -36, 110, 0, 0]),//bottom right eye
Dexter.move_all_joints([80, -36, 109, 0, 0]),//bottom left eye
Dexter.move_all_joints([80, -33, 100, 0, 0]),//upper left eye
Dexter.move_all_joints([90, -33.5, 100, 0, 0]),
Dexter.move_all_joints([90, -40, 100, 0, 0]),
Dexter.move_all_joints([95, -33, 95, 0, 0]),
Dexter.move_all_joints([95, -32, 97, 0, 0]),//upper right of body
Dexter.move_all_joints([95, -41, 123, 0, 0]),//lower right of body
Dexter.move_all_joints([85, -42, 122, 0, 0]),//lower left of right leg
Dexter.move_all_joints([85, -39, 114, 0, 0]),//upper left of right leg
Dexter.move_all_joints([80, -39, 114, 0, 0]),//upper right of left leg
Dexter.move_all_joints([80, -41.5, 122.5, 0, 0]),//lower right of left leg
Dexter.move_all_joints([69, -39, 120, 0, 0]),//lower left of left leg
Dexter.move_all_joints([69, -35, 109, 0, 0]),//midpoint up left
Dexter.move_all_joints([69, -30, 96, 0, 0]),//upper left of body
Dexter.move_all_joints([95, -32, 97, 0, 0]),
Dexter.move_all_joints([80, -50, 114, 0, 0]),
Dexter.move_all_joints([0, 0, 0, 0, 0])
]})
Follow Me mode Test
Function setFollowMe(){
var ret(MD = []
ret(MD.push(make_ins(“w”, DIFF_FORCE_SPEED_FACTOR_ANGLE, DEF_SPEED_FACTOR_DIFF))
ret(MD.push(make_ins(“w”, DIFF_FORCE_SPEED_FACTOR_ROT, DEF_SPEED_FACTOR_DIFF))
ret(MD.push(make_ins(“w”, PID_P, 0))
ret(MD.push(make_ins(“w”, PID_ADDRESS, 3))
ret(MD.push(make_ins(“w”, PID_ADDRESS, 4))
ret(MD.push(make_ins(“w”, PID_ADDRESS, 0))
ret(MD.push(make_ins(“w”, PID_ADDRESS, 1))
ret(MD.push(make_ins(“w”, PID_ADDRESS, 2))
ret(MD.push(make_ins(“w”, SPEED_FACTORA, DEF_SPEED_FACTOR_A))
ret(MD.push(make_ins(“S”, “J1Friction”,5 ))
ret(MD.push(make_ins(“S”, “J2Friction”,5 ))
ret(MD.push(make_ins(“S”, “J3Friction”,5 ))
ret(MD.push(make_ins(“S”, “J4Friction”,15 ))
ret(MD.push(make_ins(“S”, “J5Friction”,15 ))
ret(MD.push(make_ins(“w”, 67, 0))
ret(MD.push(make_ins(“w”, 68, 0))
ret(MD.push(make_ins(“w”, 69, 0)) //Nice
ret(MD.push(make_ins(“w”, 70, 0))
ret(MD.push(make_ins(“w”, 71, 0))
ret(MD.push(make_ins(“w”, 79, 50 ^ 200 ))
ret(MD.push(make_ins(“w”, 80, 50 ^ 200 ))
ret(MD.push(make_ins(“w”, 81, 50 ^ 200 ))
ret(MD.push(make_ins(“w”, 42, 12448 ))
Return ret(MD
}
Palletizing Code [WORK IN PROGRESS]
function handle_print_job_1_dialog_input(vals){
if(vals.clicked_button_value == “Continue”){
Job.print_job_1.user_data.color = vals.pallets
Job.print_job_1.user_data.color_status = “ok”
Job.print_job_1.user_data.stack = vals.stack
Job.print_job_1.user_data.stack_status = “ok”
}
else if(vals.clicked_button_value == “Cancel”){
Job.print_job_1.user_data.color_status = “cancel”
}
}
function print_job_1_dialog(){
show_window({content:`
<span style=”font-size:18px; font-family:Arial; font-style:normal; color:#000000; font-weight:200;”>
How many pallets are there?
<br/>
Please choose a value from 0-6.</span>
<br/>
<span style=”font-size:18px; font-family:Comic Sans MS; font-style:normal; color:#000000; font-weight:200;”>
<input name=”pallets” type=”range” id=”Pallets” list=”tickmarks”
min=”0″ max=”6″ oninput=”rangeValue.innerText = this.value”/>
<label for=”Pallets”># of pallets</label>
<datalist id=”tickmarks”>
<option value=”0″ label=”0″></option>
<option value=”1″ label=”1″></option>
<option value=”2″ label=”2″></option>
<option value=”3″ label=”3″></option>
<option value=”4″ label=”4″></option>
<option value=”5″ label=”5″></option>
<option value=”6″ label=”6″></option>
</datalist>
<br/>
How many should I stack?</span>
<br/>
<span style=”font-size:18px; font-family:Comic Sans MS; font-style:normal; color:#000000; font-weight:200;”>
<input name=”stack” type=”range” id=”Stack” list=”tickmarks”
min=”0″ max=”6″ oninput=”rangeValue.innerText = this.value”/>
<label for=”stack”># to stack</label>
<datalist id=”tickmarks”>
<option value=”0″ label=”0″></option>
<option value=”1″ label=”1″></option>
<option value=”2″ label=”2″></option>
<option value=”3″ label=”3″></option>
<option value=”4″ label=”4″></option>
<option value=”5″ label=”5″></option>
<option value=”6″ label=”6″></option>
</datalist>
</span>
<br/>
<input type=”submit” value=”Continue”/>
<input type=”submit” value=”Cancel”/>
`,
title: “How many pallets should I stack!”,
background_color: “#B6FFFF”,
height: 225,
callback: handle_print_job_1_dialog_input
}
)
}
new Job({name: “print_job_1”,
do_list:
[
Dexter.move_all_joints([0, 45, 90, -45, 0]),
function(){print_job_1_dialog()},
Robot.wait_until(function(){return this.user_data.color_status != undefined}),
[j3],
/* function(vals){if(this.user_data.color == 1){
[j3],
prompt(“Are you sure?”,”1″)
}},
*/
//Dexter.move_all_joints([0, 0, 0, 0, 0]),
function(){if (this.user_data.color_status == “cancel”){
return Robot.error(“ran out of filament.”)
}
this.user_data.color
}]})
Job.print_job_1.start()
Conclusion/Future Steps-
To summarize, we believe that the Dexter arm is on a good path for the future. Now that we have the means of easily communicating with it, in future semesters, we can focus more on interesting projects to draw more attention to Robotics and the enterprise as a whole. We plan on making more end effectors, utilizing more features of Dexter and DDE, and just overall having fun with our research. With this and future documentation, we hope that others using Dexter will branch off of our research and do complex things with this technology as well.