Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSDK-1653 Fix arm behavior when OOB #1822

Merged
merged 27 commits into from
Feb 3, 2023
Merged

Conversation

nfranczak
Copy link
Member

@nfranczak nfranczak commented Jan 31, 2023

To rescue an arm which has been initialized as out of bounds (OOB), we allow the following:

  • EndPosition and JointPositions should be able to report the robot's present state.
  • MoveToPosition errors as long as in OOB.
  • MoveToJointPositions works only if we are bringing the OOB joint more in bounds.

Once in bounds, MoveToJointPositions cannot move a joint to an OOB state.

The OOB error is surfaced through the arm.CreateStatus method. This in turn prevents the arm card from showing up on the control card of the UI.

  • Add ticket to fleet mgmt which allows for the arm control card to still show despite the OOB error.
    RSDK-1836

The unit test for this lives inside components/arm/arm_test.go.
We construct an injected arm and define its LocalArm as a fake ur5e.
To initialize in an OOB state we define a mock MoveToJointPositions method.

@viambot viambot added the safe to test This pull request is marked safe to test from a trusted zone label Jan 31, 2023
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Jan 31, 2023
@spieswl
Copy link
Contributor

spieswl commented Jan 31, 2023

Can you add some details about how you tested this change? For example, #1788 (comment)

Copy link
Member

@raybjork raybjork left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual logic looks like what we want, and I haven't taken a close look at the testing yet. But the implementation needs to be changed to act on general arms and not just reconfigurable ones though

r.mu.RLock()
defer r.mu.RUnlock()
return r.actual.MoveToJointPositions(ctx, positionDegs, extra)
}

// checkDesiredJointPositions validates that the desired joint positions either bring the joint back
// in bounds or do not move the joint more out of bounds.
func (r *reconfigurableArm) checkDesiredJointPositions(ctx context.Context, desiredJoints []float64) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] In general we don't ever want to be modifying the reconfigurable arm itself, and this method along with the other functionality introduced should work on the generic arm.Arm type

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strongly agree with this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Nick, take a look at the Move() and Plan() functions at the end of arm.go; there are arm-generic Plan and Move functions there, that each individual arm driver calls for MoveToPosition. We probably want something similar to that for joint movement.

@viambot viambot added viam-org Is part of the viamrobotics organization. "safe to test" and removed safe to test This pull request is marked safe to test from a trusted zone labels Jan 31, 2023
@@ -88,8 +88,11 @@ func TestForwardKinematics(t *testing.T) {
// Test out of bounds. Note that ComputePosition will return nil on OOB.
newPos = []float64{-45, 0, 0, 0, 0, 999}
pos, err = ComputePosition(m, &pb.JointPositions{Values: newPos})
test.That(t, pos, test.ShouldBeNil)
test.That(t, err, test.ShouldNotBeNil)
checkOrien := &spatial.R4AA{Theta: -2.48798057005674, RX: 0.23071941493324336, RY: -0.7100813450467474, RZ: 0.6652465971273088}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Seems like this could more closely match the form of what is taking place on L79-86.

@@ -24,7 +25,7 @@ func ComputePosition(model referenceframe.Frame, joints *pb.JointPositions) (spa
}

pose, err := model.Transform(model.InputFromProtobuf(joints))
if err != nil {
if err != nil && !strings.Contains(err.Error(), referenceframe.OOBErrString) {
Copy link
Contributor

@spieswl spieswl Jan 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major, q] So ComputePosition has been modified to NOT return nil if we have an OOB error. Is this what we want to be doing? It feels wrong that we could be computing a position with known bad inputs and returning that pose. There may also be some value in continuing with this change, but I would like us to think about the implications here.

Copy link
Member

@raybjork raybjork Jan 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted the last comment because I realized that it was not right. You are correct good callout, we should probably be floating the OOBError higher in the stack because now its being squashed in frame.go

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed; this should live elsewhere.

The current defined behavior for Transform on a Frame is that if OOB, it will return an Error, and the OOB pose.

However, this is not a method on a Frame, but a public method. We shouldn't be changing the behavior here, since it's public. We should re-examine whether we want to use a public method at the point in question, and if so, we should create a new one that explicitly will return OOB positions.

Copy link
Member Author

@nfranczak nfranczak Jan 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

part of the ticket description states:

This should be updated such that if an arm is currently in an out-of-bounds state, it is still able to report its present state

When looking that the implementation of EndPosition I see that it calls motionplan.ComputePosition.
Hence, if we error on ComputePosition we error on EndPosition.
Example here:
https://github.com/viamrobotics/rdk/blob/main/components/arm/universalrobots/ur.go#L322

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to do some thinking abt this.. 🤔

Copy link
Member Author

@nfranczak nfranczak Feb 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok after a night's rest I am back with some new thoughts.
to-reiterate, part of the ticket description states:

This should be updated such that if an arm is currently in an out-of-bounds state, it is still able to report its present state

Hence, when in an OOB state, arm.EndPosition and arm.JointPositions should still return the desired information.

There may also be some value in continuing with this change, but I would like us to think about the implications here.

The addition of !strings.Contains(err.Error(), referenceframe.OOBErrString) to the conditional inside motionplan/kinematic.go/ComputePosition() makes it such that components/arm/arm.go/CreateStatus() does not return nil, err when in an OOB state.

If we do return nil, err, this means that the arm card on the UI is blank.
Screen Shot 2023-01-31 at 3 47 06 PM
Hence, to rescue an OOB arm we must rely on the use of the SDK.

In my opinion, the loss of the arm UI card is detrimental to the user experience for this specific case. However, this can be fixed through submitting a ticket to fleet mgmt and having a more in depth discussion about how to shape the UI when OOB..

You are correct good callout, we should probably be floating the OOBError higher in the stack because now its being squashed in frame.go
We shouldn't be changing the behavior here, since it's public. We should re-examine whether we want to use a public method at the point in question, and if so, we should create a new one that explicitly will return OOB positions.

I agree that we should float the OOBError higher in the stack. Because we "shouldn't be changing the behavior here, since it's public", I propose new public method inside motionplan/kinematic.go called ComputeOOBPositions(). Then, ComputePositions() would error on OOB, while ComputeOOBPositions() would not error on OOB.

This means that all arm driver implementations of EndPosition() could be changed from:

return motionplan.ComputePosition(e.model, joints)

to

return motionplan.ComputeOOBPosition(e.model, joints)

In turn, all arm driver implementations of MoveToPosition() could be changed from:

model := e.ModelFrame()
joints, err := e.JointPositions(ctx, nil)
if err != nil {
	return err
}
// check that joint positions are not out of bounds
if _, err = model.Transform(model.InputFromProtobuf(joints)); err != nil {
	return err
}

to

joints, err := e.JointPositions(ctx, nil)
if err != nil {
	return err
}
if _, err = motionplan.ComputePosition(e.model, joints); err != nil {
	return err
}

This way, the error would be surfaced in CreateStatus.

I am sure there are multiple other solutions, so I welcome feedback to what I have proposed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great. ComputeOOBPosition() is the correct path to go down here. Thus ComputePosition() maintains its current behavior, and a user must be explicit about the call if they want OOB to be allowable.

I have one concern here: if we implement the above as-specified, while the arm card will now be valid, if a joint is indeed OOB, then the user needs to know that that is the case, and be told exactly what to do to fix it, since position moves will be failing.

Can we update the error statement to specify exactly which joint is OOB, in addition to the current "val needs to be within bounds [min, max]"?

@@ -88,8 +88,11 @@ func TestForwardKinematics(t *testing.T) {
// Test out of bounds. Note that ComputePosition will return nil on OOB.
Copy link
Contributor

@spieswl spieswl Jan 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Pending what we decide on the ComputePosition change, we would want this comment to be updated.

Copy link
Contributor

@spieswl spieswl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Biggest outstanding issue is Ray's comment on arm.go


// checkDesiredJointPositions validates that the desired joint positions either bring the joint back
// in bounds or do not move the joint more out of bounds.
func checkDesiredJointPositions(ctx context.Context, a Arm, desiredJoints []float64) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each individual arm driver should call this on MoveToJointPositions (and it should be removed from reconfigurableArm

@Otterverse Otterverse added safe to test This pull request is marked safe to test from a trusted zone and removed "safe to test" labels Jan 31, 2023
@nfranczak
Copy link
Member Author

question: I am unsure how DoCommand works. Should a check be placed on that method as well?

@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Jan 31, 2023
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Feb 2, 2023
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Feb 2, 2023
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Feb 2, 2023
// check that joint positions are not out of bounds
_, err = motionplan.ComputePosition(model, joints)
if err != nil && strings.Contains(err.Error(), referenceframe.OOBErrString) {
err = errors.New("cartesian movements are not allowed when arm joints are out of bounds")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we concatenate the returned err from above with this string?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you got it. now the error statement is of form:

cartesian movements are not allowed when arm joints are out of bounds - joint A input out of bounds, input B needs to be within range [C D]

Copy link
Member

@biotinker biotinker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM pending one final error contents update

Copy link
Contributor

@spieswl spieswl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Nice job!

@spieswl spieswl dismissed raybjork’s stale review February 3, 2023 15:33

Hey Ray, pulling you off since you're OoO for today and we don't want to bug you.

@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Feb 3, 2023
@@ -60,6 +62,10 @@ const (
SubtypeName = resource.SubtypeName("arm")
)

// MTPoob is a string that all MoveToPosition errors should contain if the method is called
// and there are joints which are out of bounds.
const MTPoob = "cartesian movements are not allowed when arm joints are out of bounds"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do we think of MTPoob as the name for this error?
Open to feedback!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I think the name MTPoob is hilarious and we should keep it :)

// check that joint positions are not out of bounds
_, err = motionplan.ComputePosition(model, joints)
if err != nil && strings.Contains(err.Error(), referenceframe.OOBErrString) {
return errors.New(strings.Join(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than importing the strings lib and using join, this can just be:

return errors.New(MTPoob + ": " + err.Error())

@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Feb 3, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Feb 3, 2023

Code Coverage

Note: merge base coverage results not available, comparing against closest 9ed8da6~1=(a345ee0) instead

Package Line Rate Delta Health
go.viam.com/rdk/components/arm 60% +4.24%
go.viam.com/rdk/components/arm/universalrobots 41% -0.52%
go.viam.com/rdk/components/arm/xarm 2% -0.02%
go.viam.com/rdk/components/arm/yahboom 7% 0.00%
go.viam.com/rdk/components/audioinput 55% 0.00%
go.viam.com/rdk/components/base 63% 0.00%
go.viam.com/rdk/components/base/agilex 62% 0.00%
go.viam.com/rdk/components/base/boat 41% 0.00%
go.viam.com/rdk/components/base/wheeled 76% 0.00%
go.viam.com/rdk/components/board 66% 0.00%
go.viam.com/rdk/components/board/arduino 10% 0.00%
go.viam.com/rdk/components/board/commonsysfs 48% 0.00%
go.viam.com/rdk/components/board/fake 38% 0.00%
go.viam.com/rdk/components/board/numato 19% 0.00%
go.viam.com/rdk/components/board/pi 50% 0.00%
go.viam.com/rdk/components/camera 65% -0.25%
go.viam.com/rdk/components/camera/align 64% 0.00%
go.viam.com/rdk/components/camera/fake 64% 0.00%
go.viam.com/rdk/components/camera/ffmpeg 77% 0.00%
go.viam.com/rdk/components/camera/transformpipeline 78% +0.31%
go.viam.com/rdk/components/camera/videosource 45% -0.58%
go.viam.com/rdk/components/encoder 4% 0.00%
go.viam.com/rdk/components/encoder/fake 76% 0.00%
go.viam.com/rdk/components/gantry 60% 0.00%
go.viam.com/rdk/components/gantry/multiaxis 84% 0.00%
go.viam.com/rdk/components/gantry/oneaxis 86% 0.00%
go.viam.com/rdk/components/generic 83% 0.00%
go.viam.com/rdk/components/gripper 68% 0.00%
go.viam.com/rdk/components/input 87% 0.00%
go.viam.com/rdk/components/input/gpio 84% 0.00%
go.viam.com/rdk/components/motor 77% 0.00%
go.viam.com/rdk/components/motor/dimensionengineering 63% 0.00%
go.viam.com/rdk/components/motor/dmc4000 69% 0.00%
go.viam.com/rdk/components/motor/fake 57% 0.00%
go.viam.com/rdk/components/motor/gpio 64% +2.16%
go.viam.com/rdk/components/motor/gpiostepper 57% 0.00%
go.viam.com/rdk/components/motor/tmcstepper 62% 0.00%
go.viam.com/rdk/components/movementsensor 75% 0.00%
go.viam.com/rdk/components/movementsensor/cameramono 46% 0.00%
go.viam.com/rdk/components/movementsensor/gpsnmea 36% 0.00%
go.viam.com/rdk/components/movementsensor/gpsrtk 28% 0.00%
go.viam.com/rdk/components/posetracker 86% 0.00%
go.viam.com/rdk/components/sensor 86% 0.00%
go.viam.com/rdk/components/sensor/ultrasonic 26% 0.00%
go.viam.com/rdk/components/servo 68% 0.00%
go.viam.com/rdk/components/servo/gpio 71% 0.00%
go.viam.com/rdk/config 75% 0.00%
go.viam.com/rdk/control 57% 0.00%
go.viam.com/rdk/data 79% 0.00%
go.viam.com/rdk/examples/customresources/demos/complexmodule 0% 0.00%
go.viam.com/rdk/examples/customresources/demos/remoteserver 0% 0.00%
go.viam.com/rdk/grpc 25% 0.00%
go.viam.com/rdk/ml 67% 0.00%
go.viam.com/rdk/ml/inference 70% 0.00%
go.viam.com/rdk/module 64% 0.00%
go.viam.com/rdk/module/modmanager 79% 0.00%
go.viam.com/rdk/motionplan 69% +0.15%
go.viam.com/rdk/operation 82% 0.00%
go.viam.com/rdk/pointcloud 73% 0.00%
go.viam.com/rdk/protoutils 59% 0.00%
go.viam.com/rdk/referenceframe 72% +0.03%
go.viam.com/rdk/registry 89% 0.00%
go.viam.com/rdk/resource 84% 0.00%
go.viam.com/rdk/rimage 78% 0.00%
go.viam.com/rdk/rimage/depthadapter 94% 0.00%
go.viam.com/rdk/rimage/transform 75% 0.00%
go.viam.com/rdk/rimage/transform/cmd/extrinsic_calibration 67% 0.00%
go.viam.com/rdk/robot 86% +0.95%
go.viam.com/rdk/robot/client 81% 0.00%
go.viam.com/rdk/robot/framesystem 65% 0.00%
go.viam.com/rdk/robot/impl 80% 0.00%
go.viam.com/rdk/robot/server 55% 0.00%
go.viam.com/rdk/robot/web 61% 0.00%
go.viam.com/rdk/robot/web/stream 87% 0.00%
go.viam.com/rdk/services/armremotecontrol 71% 0.00%
go.viam.com/rdk/services/armremotecontrol/builtin 52% 0.00%
go.viam.com/rdk/services/baseremotecontrol 71% 0.00%
go.viam.com/rdk/services/baseremotecontrol/builtin 79% 0.00%
go.viam.com/rdk/services/datamanager 79% 0.00%
go.viam.com/rdk/services/datamanager/builtin 78% 0.00%
go.viam.com/rdk/services/datamanager/datacapture 70% 0.00%
go.viam.com/rdk/services/datamanager/datasync 0% 0.00%
go.viam.com/rdk/services/motion 63% 0.00%
go.viam.com/rdk/services/motion/builtin 88% 0.00%
go.viam.com/rdk/services/navigation 53% 0.00%
go.viam.com/rdk/services/sensors 77% 0.00%
go.viam.com/rdk/services/sensors/builtin 97% 0.00%
go.viam.com/rdk/services/shell 14% 0.00%
go.viam.com/rdk/services/slam 85% 0.00%
go.viam.com/rdk/services/slam/builtin 70% 0.00%
go.viam.com/rdk/services/vision 80% 0.00%
go.viam.com/rdk/services/vision/builtin 74% 0.00%
go.viam.com/rdk/session 97% 0.00%
go.viam.com/rdk/spatialmath 84% 0.00%
go.viam.com/rdk/subtype 92% 0.00%
go.viam.com/rdk/utils 72% 0.00%
go.viam.com/rdk/vision 26% 0.00%
go.viam.com/rdk/vision/chess 80% 0.00%
go.viam.com/rdk/vision/delaunay 87% 0.00%
go.viam.com/rdk/vision/keypoints 92% 0.00%
go.viam.com/rdk/vision/objectdetection 82% 0.00%
go.viam.com/rdk/vision/odometry 60% 0.00%
go.viam.com/rdk/vision/odometry/cmd 0% 0.00%
go.viam.com/rdk/vision/segmentation 49% 0.00%
go.viam.com/rdk/web/server 25% 0.00%
Summary 66% (21646 / 33046) +0.05%

@nfranczak nfranczak requested a review from biotinker February 3, 2023 16:38
Copy link
Member

@biotinker biotinker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@nfranczak nfranczak merged commit 3089e39 into viamrobotics:main Feb 3, 2023
@nfranczak nfranczak deleted the RSDK-1653 branch February 3, 2023 22:42
@nfranczak nfranczak mentioned this pull request Feb 9, 2023
randhid pushed a commit to randhid/rdk that referenced this pull request Feb 25, 2023
biotinker pushed a commit to biotinker/robotcore that referenced this pull request Mar 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
safe to test This pull request is marked safe to test from a trusted zone viam-org Is part of the viamrobotics organization.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants