same output on port 3001
i wonder what the difference between those interfaces is
same output on port 3001
i wonder what the difference between those interfaces is
Heres a reference guide on the error codes:
I’m on my phone right now… but it doesn’t look like -2808 is immediately listed
“Error codes available for use by GPL application programs (in addition to -786 and -1038)”
gpl = Guidance Programming Language
here:
through telnet interface (password for me is Help)
GPL: Show Thread
ICT, Idle
TcpAsyncCom1, Running
TcpCom1, Running
What’s the current state of this? At a glance it looks pretty feature complete on github but pretty recent commits. I have an arm coming in the next week or so
pf is fully working! we are using it in production, including one with a linear rail.
I just reverse engineered the inverse kinematics last friday (because the brooks cartesian api has some ambiguity). this will live on the v1b1 branch until we merge v1b1 into main. I will post an update here when I merge this (this afternoon)
Amazing! Thanks, I’m looking forward to trying this out
pushed this yesterday pf kinematics · PyLabRobot/pylabrobot@0fa5e4e · GitHub
Wow, this is awesome Rick. Been thinking about doing this, much simpler than I thought it would be :). Curious what sort of issues you had with Cartesian commands? For the most part I’ve mostly preferred using joint space for the same reason. eg. not being able to determine elbow orientation between locations. (unless you force the path)
yeah of course
l1 and l2 are initially fitted using linear regression on the arm model, by querying joint and cartesian locations and just learning the function. Then I was actually able to validate this by looking at the technical drawings of the pf400 (https://www.brooks.com/getmedia/968fcb26-d347-4404-b9ab-bbd910447975/PreciseFlex_400.pdf), which I should have done from the start but didnt think of.
L3 is dependent on the gripper. I am not sure how that is configured for the stock dataset that I trained, must somewhere be on the arm. But this one is actually parameterized since it depends on the gripper:
from pylabrobot.brooks.precise_flex import PreciseFlex400
# Default (stock single gripper, 162 mm)
pf = PreciseFlex400(host="192.168.0.1")
# Custom gripper length (e.g. 185 mm for an alternate end-effector)
pf = PreciseFlex400(host="192.168.0.1", gripper_length=185.0)
await pf.setup()
Yeap! I know what you are talking about. Great solution.
@rickwierenga can you tell me more about this, is there a way to easily query these values? I assume they vary by robot to robot?
@dataclass
class PF400Params:
"""Calibrated link lengths; sub-mm FK residual on a held-out probe set."""
l1: float = 302.0 # shoulder -> elbow [mm]
l2: float = 289.0 # elbow -> wrist [mm]
l3: float = 162.0 # wrist -> TCP [mm]
z_tool_offset: float = 0.0
eps: float = 1e-6
initially I fitted those parameters by linear regression against the model for this arm, but then I was able to validate them against the technical drawing (https://www.brooks.com/getmedia/968fcb26-d347-4404-b9ab-bbd910447975/PreciseFlex_400.pdf). In hindsight I should have just used that model from the start but oh well.
The gripper (l3) is machine specific. I am not sure where the 162 came from for the “training set” I made for fitting this model, must be stored on the machine somewhere. But it’s parameterized now so should be easy to change. Here’s the API for that:
from pylabrobot.brooks.precise_flex import PreciseFlex400
# Defaults (stock PF400 single gripper): gripper_length=162.0, gripper_z_offset=0.0
pf = PreciseFlex400(host="192.168.0.1")
# Custom end-effector
pf = PreciseFlex400(
host="192.168.0.1",
gripper_length=185.0,
gripper_z_offset=12.5,
)
await pf.setup()
also renamed z_tool_offset to gripper_z_offset and l3 to gripper_length for clarity.
Ahh! I see, that’s great. Even more validation that both calculations match
. For anyone’s future reference image below
How are the pickup and drop functions intended to be used? I’ve been playing around with this and can open and close, move to joint or cartesian locations, but the pickup and drop commands are sending the robot down in to its base where it crashes against the framing it’s on. I’m wondering if I am misinterpreting them or if something’s off about a configuration somewhere. I’m using a 750 z 750 reach with no rail, for what it’s worth.
I’m still working on integrating it with the resource model. the pf on the v1b1 branch is already much better, on v0 it was experimental. I should have that ready soon. in the meantime, we have been using open/close/move_to to get things working quickly. obviously not the best api, but that’s what worked while we figure out/build exactly what nice pickup and drop need. (I’m also doing the kx2 dev in parallel so they will have the same api)
this unfortunately is a limitation in the firmware/tcc, it will happily crash into its own base sometimes. We can avoid that with PLR by figuring out when exactly that happens, and then going to an intermediary position where it doesn’t happen. One thing that helps today is specifying the gripper orientation to be clockwise/counter clockwise but it’s not bulletproof
I am unfortunately all too familiar with the need to move things quickly and the debt it incurs. The system I’m building is pretty simple so moves to and grip will be fine, though, so I’m good to go! Just wanted to know if there was something I was missing. Thanks again!
I’ve been very curious about the KX2s. There’s a relative abundance of used PFs right now so I couldn’t justify getting a new one, but I’ve had enough working with the Precise arms that I am eager to find one that isn’t so fiddly
It’s more of a “building it step by step” thing and doing it across different arms at the same time.
KX2 is nice because you can just control the motors directly rather than having to deal with installing custom firmware and getting it to run. It’ll always just work. On the hardware side, it really depends on your usecase. KX2 rotates arounds its base but only has one extending joint whereas pf has 2 joints so is better in tight spaces.