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

Robot status topic via controller #141

Merged
merged 18 commits into from
Apr 21, 2020

Conversation

axelschroth
Copy link
Contributor

Implements issue #32

Notes and limitations:

  • controller publishes state messages as long as ExternalControl is running or paused on UR. Otherwise rostopic echo /robot_status does not receive further messages.
  • Tested with UR simulation software -> no tests regarding emergency stops (since this is not possible with ursim)
  • Criteria of published content is based on ur_modern_driver implementation (see Publish industrial_msgs::RobotStatus Zagitta/ur_modern_driver#5)

@gavanderhoorn
Copy link
Contributor

@axelschroth wrote in #32 (comment):

Does anyone have an idea, why no further state messages are published by industrial_robot_status_controller as soon as I stop the robot program (with ExternalControl Cap, see PR)?

The controller will only publish something if its update(..) is called.

You may have to add it to the consistent_controllers list here so it doesn't get stopped.

fixes problem that no messages are published if robot program is not running
@@ -38,6 +38,7 @@
<depend>ur_dashboard_msgs</depend>
<depend>ur_msgs</depend>
<depend>std_srvs</depend>
<depend>industrial_robot_status_interface</depend>
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be a build_depend only?

It's a header-only library, there is nothing at runtime there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, you're right

@gavanderhoorn
Copy link
Contributor

Would you not need to add industrial_robot_status_interface to the COMPONENTS of Catkin in the CMakeLists.txt?

@axelschroth
Copy link
Contributor Author

Would you not need to add industrial_robot_status_interface to the COMPONENTS of Catkin in the CMakeLists.txt?

Done with last commit. Thanks! Any further suggestions?

@gavanderhoorn
Copy link
Contributor

Not right now.

Seeing as CI is 'down' at the moment (#144) and I'm not an maintainer here we'll have to wait for @fmauch to process the PR :)

But thanks for implementing this. 👍

void HardwareInterface::extractRobotStatus()
{
// robot status bit 2: Is teach button pressed
robot_status_resource_.mode = robot_status_bits_[2] ? RobotMode::MANUAL : RobotMode::AUTO;
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess I do have an additional comment: please use some constants here for the bit indices instead of magic nrs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this really the right check? The robot can be in manual mode without the teach button being pressed, right?

I am aware, that usually on industrial robots manual and automatic modes have a different meaning than the one used on UR robots. But boiling it down to the teach button pressed feels misleading to me, as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

@fmauch: this was ported from ur_modern_driver I believe.

Would you happen to have a suggestion on how to better determine whether the controller is in AUTO or MANUAL?

I don't even believe URs really have that concept.

When they're being "freedrived" though they are definitely not in AUTO (or whatever we want to call the equivalent on URs).

Copy link
Contributor

@fmauch fmauch Mar 30, 2020

Choose a reason for hiding this comment

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

I don't even believe URs really have that concept.

They do (on e-Series, on CB3 the same concept was called "running mode" and "programming mode"), but that concept is different (or a little less restrictive). In AUTO mode (which you can activate after setting a password) one can only load and execute predefined programs. (See section "Operational Mode Selection" in the manual)

However, I am currently not aware of a method receiving this information. (which, of course, doesn't necessarily mean, that it doesn't exist)

When they're being "freedrived" though they are definitely not in AUTO (or whatever we want to call the equivalent on URs).

Yes, but it feels as if we would be misusing the field from the message for something semantically different. Which, as you reminded me often, not the best thing to do :-)

Copy link
Contributor

Choose a reason for hiding this comment

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

Would you happen to have a suggestion on how to better determine whether the controller is in AUTO or MANUAL?

Forgot to answer that... To be honest - I don't know. I think, using what UR calls manual and automatic operational mode comes closest to what other industrial robots semantically state with this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree with your points, but as you said @gavanderhoorn , this implementation is based on the interpretation of the ur_modern_driver.
I also thought about the issue when porting to the RTDE interface, but I didn't find any possibility to read this information about the Operational Mode over the RTDE interface (see RTDE Guide )...

Another point to think of: What would we do with the CB-series? I think in the old Polyscope versions, you have to use a configurable input to change to running mode. Yet we could use that as "robot mode" if we have the information.

Copy link
Contributor

Choose a reason for hiding this comment

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

What about limiting this PR to porting the ur_modern_driver implementation, then using a potential follow-up PR after/if we get more information on what @fmauch mentions?

It would be a nice enhancement over the partial implementation from ur_modern_driver.

@axelschroth: if you could address my comment about magic nrs which started this thread, I'd vote for merging it then.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd be happy about that, I will commit the changes about the magic nrs tomorrow.

Copy link
Contributor

Choose a reason for hiding this comment

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

What about limiting this PR to porting the ur_modern_driver implementation, then using a potential follow-up PR after/if we get more information on what @fmauch mentions?

I'd be fine with that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I addressed your suggestion using an enum in the rtde client, because this belongs to the rtde specification. Are you okay with that?

Comment on lines 674 to 676
// note that e-stop is handled by a seperate variable
robot_status_resource_.in_error =
safety_status_bits_[UrRtdeSafetyStatusBits::IS_PROTECTIVE_STOPPED] ? TriState::TRUE : TriState::FALSE;
Copy link
Contributor

Choose a reason for hiding this comment

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

One thing I believe we could include in this PR: in_error should be the union of anything which makes the controller go into an error state. Is IS_EMERGENCY_STOPPED also a cause of IS_PROTECTIVE_STOPPED? If not, then it would need to be || here I believe.

Any other causes we could add to in_error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I can add this. Unfortunately I cannot test emergency stops, since I don't have a real robot here, just ursim. I don't know the behaviour of these bits, therefore I cannot tell which bits provide additional information regarding the error state.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think all of these should be used for that:

  IS_PROTECTIVE_STOPPED = 2,
  IS_ROBOT_EMERGENCY_STOPPED = 6,
  IS_EMERGENCY_STOPPED = 7,
  IS_VIOLATION = 8,
  IS_FAULT = 9,
  IS_STOPPED_DUE_TO_SAFETY = 10

Copy link
Contributor

Choose a reason for hiding this comment

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

I could make a short trip to the lab these days to test the behavior.

@@ -48,6 +48,29 @@ namespace ur_driver
{
namespace rtde_interface
{
enum UrRtdeRobotStatusBits
Copy link
Contributor

Choose a reason for hiding this comment

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

Please make this scoped enums as we use this all over the driver.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, then I'd rather switch to constants like

static const uint8_t ROBOT_STATUS_BIT_IS_POWER_ON = 0;

and so on, because accessing the bitsets with scoped enums gets quite ugly..

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe @fmauch already added a std::underlying_type<> somewhere?

IS_POWER_BUTTON_PRESSED = 3
};

enum UrRtdeSafetyStatusBits
Copy link
Contributor

Choose a reason for hiding this comment

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

Scoped enums, same as above

Comment on lines +142 to +146

robot_status_controller:
type: industrial_robot_status_controller/IndustrialRobotStatusController
handle_name: industrial_robot_status_handle
publish_rate: 10
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be added for the other controller files, as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! Thanks for reviewing

@gavanderhoorn
Copy link
Contributor

Thanks for iterating @axelschroth 👍

Comment on lines 676 to 681
if (safety_status_bits_[toUnderlying(UrRtdeSafetyStatusBits::IS_PROTECTIVE_STOPPED)] ||
safety_status_bits_[toUnderlying(UrRtdeSafetyStatusBits::IS_ROBOT_EMERGENCY_STOPPED)] ||
safety_status_bits_[toUnderlying(UrRtdeSafetyStatusBits::IS_EMERGENCY_STOPPED)] ||
safety_status_bits_[toUnderlying(UrRtdeSafetyStatusBits::IS_VIOLATION)] ||
safety_status_bits_[toUnderlying(UrRtdeSafetyStatusBits::IS_FAULT)] ||
safety_status_bits_[toUnderlying(UrRtdeSafetyStatusBits::IS_STOPPED_DUE_TO_SAFETY)])
Copy link
Contributor

@gavanderhoorn gavanderhoorn Mar 31, 2020

Choose a reason for hiding this comment

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

This may be taking it too far, but: seeing as this set never changes, what about keeping a "mask" bitset around, initialised once and then used as:

if ((safety_status_bits_ | in_error_bitset_).any())
   ...

std::bitset supports the necessary operators.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll leave that up to @axelschroth It is fine like that from my side.

Copy link
Contributor

@gavanderhoorn gavanderhoorn Mar 31, 2020

Choose a reason for hiding this comment

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

We could ignore it in this PR, but seeing as this is run @ the controller's update rate (ie: 125 or 500 Hz), it's 7*500 vs 1*500 operations per second. I'm not sure the compiler will be able to coalesce all the bitset::test(..) invocations into a single masking operation.

Copy link
Contributor

Choose a reason for hiding this comment

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

true... And it should be fairly quick to implement :-)

@fmauch
Copy link
Contributor

fmauch commented Apr 1, 2020

Thanks again @axelschroth I well test that in the lab ASAP

@axelschroth
Copy link
Contributor Author

Thank you guys for reviewing @gavanderhoorn and @fmauch!

@fmauch
Copy link
Contributor

fmauch commented Apr 3, 2020

Testing results:

Robot running:

mode:
  val: 2
e_stopped:
  val: 0
drives_powered:
  val: 1
motion_possible:
  val: 1
in_motion:
  val: -1
in_error:
  val: 1
error_code: 0

EM-Stop hit:

mode:
  val: 2
e_stopped:
  val: 1
drives_powered:
  val: 1
motion_possible:
  val: 0
in_motion:
  val: -1
in_error:
  val: 1
error_code: 0

EM-Stop released:

mode:
  val: 2
e_stopped:
  val: 0
drives_powered:
  val: 1
motion_possible:
  val: 0
in_motion:
  val: -1
in_error:
  val: 1
error_code: 0

Protective stop:

mode:
  val: 2
e_stopped:
  val: 0
drives_powered:
  val: 1
motion_possible:
  val: 1
in_motion:
  val: -1
in_error:
  val: 1
error_code: 0

After Bootup:

mode:
  val: 2
e_stopped:
  val: 0
drives_powered:
  val: 0
motion_possible:
  val: 0
in_motion:
  val: -1
in_error:
  val: 1
error_code: 0

Switched on after bootup (not yet started):

mode:
  val: 2
e_stopped:
  val: 0
drives_powered:
  val: 1
motion_possible:
  val: 0
in_motion:
  val: -1
in_error:
  val: 1
error_code: 0

in_error seems to be wrong, I will look into that, later.

@fmauch
Copy link
Contributor

fmauch commented Apr 3, 2020

motion_possible in protective stop seems wrong, right?

Co-Authored-By: Felix Exner <felix_mauch@web.de>
robot_status_resource_.drives_powered =
robot_status_bits_[toUnderlying(UrRtdeRobotStatusBits::IS_POWER_ON)] ? TriState::TRUE : TriState::FALSE;

robot_status_resource_.motion_possible =
Copy link
Contributor

Choose a reason for hiding this comment

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

I think a union would be good here, as well. Best achieved by combination with the following safety status bits:

  IS_PROTECTIVE_STOPPED = 2,
  IS_RECOVERY_MODE = 3,
  IS_SAFEGUARD_STOPPED = 4,
  IS_SYSTEM_EMERGENCY_STOPPED = 5,
  IS_ROBOT_EMERGENCY_STOPPED = 6,
  IS_EMERGENCY_STOPPED = 7,
  IS_VIOLATION = 8,
  IS_FAULT = 9,
  IS_STOPPED_DUE_TO_SAFETY = 10

Sorry for coming with that this late, it just came up while testing.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we check in_error? Are there any bits in this list which are not in the other?

Copy link
Contributor

Choose a reason for hiding this comment

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

I did not propose IS_SAFEGUARD_STOPPED for in_error as I wouldn't consider that an error status. However, motion will not be possible, while this is active...

Copy link
Contributor

Choose a reason for hiding this comment

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

Hm. Other industrial robots will report this as an error I believe.

If we don't, then we can run into the situation where in_error is false and motors_on is true, but motion_possible is false. That would at least be confusing.

Copy link
Contributor

Choose a reason for hiding this comment

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

I thought, this would be exactly the purpose of this field. Otherwise it would be redundant, right?

But if that's the way this is usually being handled, I am absolutely fine with it.

Copy link
Contributor

@gavanderhoorn gavanderhoorn Apr 7, 2020

Choose a reason for hiding this comment

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

Then motion_possible should be (pseudo):

motion_possible = not (in_error or (IS_SAFEGUARD_STOPPED in robot_status_bits_))

I believe.

Or if we want to be nice: it should also include the status of the external control program.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright, I will modify the expression for motion_possible shortly. However I don't have time to include the status of the external control program, this condition may be added later if that's okay.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, absolutely!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

motion_possible = in_error or (IS_SAFEGUARD_STOPPED in robot_status_bits_)

I guess you mean the negated version of this expression. Motion is not possible if controller is in error or safeguard stop

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, of course.

Updated.

@fmauch
Copy link
Contributor

fmauch commented Apr 14, 2020

I just tested this PR again and noticed one more issue: robot_motion always states true when there is no error and no safeguard stop active, even if the robot hardware is powered down completely. I made a PR to your branch @axelschroth with how I think the behavior is more correct.

@axelschroth
Copy link
Contributor Author

I just tested this PR again and noticed one more issue: robot_motion always states true when there is no error and no safeguard stop active, even if the robot hardware is powered down completely. I made a PR to your branch @axelschroth with how I think the behavior is more correct.

Great, thank you! I merged your PR.

Copy link
Contributor

@gavanderhoorn gavanderhoorn left a comment

Choose a reason for hiding this comment

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

This looks like it's OK now.

👍 @axelschroth and thanks again for iterating.

Adding a check on the status of the control program can be done in a future PR.

@fmauch
Copy link
Contributor

fmauch commented Apr 20, 2020

Perfect, I'll give it another HW test tomorrow and then merge it in if everything is fine. Thanks again for your continuous effors @axelschroth

@fmauch fmauch merged commit ad3cb7c into UniversalRobots:master Apr 21, 2020
@gavanderhoorn
Copy link
Contributor

Nice 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants