Major Stability Issues When Moving Joints via API

marc.a.grossman
Enthusiast

Major Stability Issues When Moving Joints via API

marc.a.grossman
Enthusiast
Enthusiast

I've got a delta style robot that I've modeled with the hope of mapping out it's forward kinematics using fusion360. The idea here is that I can then create non-standard delta-style robot configurations and check their range of motion with fusion360 and via a python script eventually modify various parameters such as the upper and lower leg lengths in order to optimize for the robot's work window.

 

The issue I'm having seems to be due to a serious lack of stability of fusion360 and the API. Here's the steps that my code does.

  1. gets the joints that I'd like to move
  2. locks two of the joints and moves the third in 1 degree increments
  3. takes a measurement of the end effector position relative to the root component origin
  4. stores the joint positions and the end effector position
  5. repeats for some range of positions... say -20 to 20 degrees of some joint
  6. writes the joint angles, and end effector positions to a .csv so I can post-process

This script is crashing nearly every time. I don't even understand how it could be crashing given that everything is inside a try/except. Can someone please help me out here. I'm at a complete loss.

 

There are a couple things that might be worth pointing out.

  1. this didn't work at all (ie completely crashed) until I discovered the adsk.doEvents method. Now it sometimes works.
  2. This script works pretty darn well when I use a slider instead of a range(start,stop) to cause the one joint I'm driving to move. 
  3. I'm having to lock the two joints I don't want moving using a private method _set_isLocked(True). If there is a better way, please let me know.
  4. The way I'm doing it naturally creates a large number of things that can be undone such as undo toggle joint lock and undo drive joint. I'm starting to wonder if it's not that I'm creating too many things that can be "undone" and that's what's blowing things up. It seems to be more stable when I do fewer moves. You'll see in the for loop where I do range(-20,20)... when I make it range(-5,5) things work. When I do range(-20,20) things crash, and I have no idea why. I have made these same movements with a slider  that drives the joint in another script, but I don't want to have to use sliders.

Furthermore what can be causing this when I'm doing everything inside a try/except. This must be an issue way deep down in something and because everything crashes I have no way of finding out what it is. I've even tried this in offline mode to see if that would help, but with no luck.

 

import adsk.core, adsk.fusion, traceback, math
import csv
import os

pi = math.pi

thetas = []
xyzs = []

rootPath = os.path.expanduser('~')
fullPath = os.path.join(rootPath, 'Documents', 'output.csv')

app = adsk.core.Application.get()
ui = app.userInterface


def getDist2Origin(component):
	try:
		trans = component.transform
		xDist = trans.translation.x
		yDist = trans.translation.y
		zDist = trans.translation.z
		return [xDist,yDist,zDist]
	except:
		if ui:
			ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
	



def moveJoint(free_joint, fixed_joints, newAngle):
	try:
		revoluteMotion = adsk.fusion.RevoluteJointMotion.cast(free_joint.jointMotion)
		for j in fixed_joints:
			if not (j.isLocked):
				j._set_isLocked(True)
		revoluteMotion.rotationValue = pi*newAngle/180.0
		adsk.doEvents()
		#unlock the joints
		for j in fixed_joints:
			if j.isLocked:
				j._set_isLocked(False)
		thetas.append(newAngle)
	except:
		if ui:
			ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


def write2CSV(list_of_lists, filepath):
	try:
		with open(fullPath, 'w', newline='\n') as f:
			writer = csv.writer(f)
			for val in list_of_lists:
				writer.writerow(val)
	except:
		if ui:
			ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


def run(context):
	try:
		rootComp = app.activeProduct.rootComponent

		mobilePlatform = rootComp.occurrences.itemByName('Basic Platform v1:1')



		joint0 = rootComp.joints.itemByName('Shoulder0')
		joint1 = rootComp.joints.itemByName('Shoulder1')
		joint2 = rootComp.joints.itemByName('Shoulder2')
		for theta in range(-20,20):
			moveJoint(joint0, [joint1, joint2], theta)
			dists = getDist2Origin(mobilePlatform)
			# thetas.append([theta, dists])
			# thetas = []

		# write2CSV(thetas, fullPath)


		# with open('output.csv', 'w', newline='') as f:
		# 	writer = csv.writer(f)
		# 	for val in blah:
		# 		writer.writerow([val])
		# prevent this module from being terminate when the script returns, because we are waiting for event handlers to fire
#		adsk.autoTerminate(False)
	except:
		if ui:
			ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
0 Likes
Reply
647 Views
2 Replies
Replies (2)

Anonymous
Not applicable

Interesting project.

 

Have you tried moving over a different range of the same "size", such as from -20 to -10?

 

I suspect the problem is indeed within Fusion itself. I haven't tried driving joints via the API but I've observed enough broken/skipping joint behavior when moving them manually to suspect internal issues.

 

You might consider leaving the joints unlocked and perform a few relaxations to get them back where they are supposed to be (go around moving each joint where you want it to be multiple times in a row - each move should result in fewer disruptions to other joints). If you observe a large joint jump you've hit the bug I'm thinking of.

0 Likes

marc.a.grossman
Enthusiast
Enthusiast

I did eventually find a way to get what I wanted done done. The issues is this:

 

Fusion pretends that you can set joints to negative angle values. Say you've got a revolute joint, and you want it set to -60 degrees. The truth is that behind the scenes, the -60 you set is not treated as -60, instead it's treated as 300 degrees. This is not a totally unexpected behavior, but it is not well documented, and there are some bugs here. 

 

What I ended up doing is locking two of my revolute joints, while unlocking the third and driving it with my_revolute_joint.jointMotion.rotationValue = theta, where theta is in radians naturally as that's the angular unit underlying fusion. Then I lock the joint I drove. I got all kinds of crazy solves doing this that would "kink" my arms and cause me trouble.

 

The final trick was to use joint origins to re-orient the starting positions of my joints so that I never drove them under 0 degrees (0 radian) or above 360 degrees (2pi radian). That seemed to solve my problem.

 

 

Here is some example code of the relevant code section:

def setRevoluteJoints_locked(revolute_joints, thetas):
	#successively unlocks, then moves any joint that is not set to the angle desired by thetas
	#NOTES:
	#did not work correctly without both adsk.doEvents() commands at the end
	#does appear to work with the doEvents() after locking and unlocking commented out
	for idx, joint in enumerate(revolute_joints):
		if joint.jointMotion.rotationValue != thetas[idx]:
			joint.isLocked = False
			joint.jointMotion.rotationValue = thetas[idx]
			joint.isLocked = True
	adsk.doEvents()
	adsk.doEvents()
0 Likes