ros-controls / ros_control

Generic and simple controls framework for ROS
http://wiki.ros.org/ros_control
BSD 3-Clause "New" or "Revised" License
478 stars 306 forks source link

Boilerplate Template #198

Open davetcoleman opened 9 years ago

davetcoleman commented 9 years ago

I'm thinking of making a boilerplate template for a simple hardware interface, does this already exist somewhere?

fmessmer commented 9 years ago

...don't know...but I'd be interested in using/testing one, too :wink:

davetcoleman commented 9 years ago

Very beta: https://github.com/davetcoleman/ros_control_boilerplate

carlosjoserg commented 9 years ago

That looks good, we are mostly using that way (without the debugging part) in our hardware, all namespaced.

One question, in line 70 of the hardware interface you have getParam('joints'). Where do you load that parameter? If you use an aditional file to define that as usual, I was thinking in how to avoid the duplication of that spec., either using the transmission spec. (if any) and it is also in the controllers.yaml, so why not to take it from any of the two?

Very beta: https://github.com/davetcoleman/ros_control_boilerplate

— Reply to this email directly or view it on GitHub https://github.com/ros-controls/ros_control/issues/198#issuecomment-78145318 .

davetcoleman commented 9 years ago

Okay, its mostly done now. I have documentation, a screenshot, a video, and a full RRBot example to run: https://github.com/davetcoleman/ros_control_boilerplate

I would really appreciate people who have worked on joint limits, estops, and other fancy features to add them in here too so everyone else can learn from them. I haven't used them myself yet.

carlosjoserg commented 9 years ago

Thanks, I believe it is very useful to have an example like this. I usually ask before creating a PR, so one comment related to my previous comment:

Is there away to avoid listing joint names for the HW interface (real/sim)?

@davetcoleman proposes to do it here, @adolfo-rt mentioned that they query the Gazebo model for joints here, and @ipa-fxm proposes a joint filtering here.

IMHO, hardware_interface (real/sim) should talk/query the URDF (robot_description of the hardware that hardware_interface refers to) systematically, and not written by hand in a parameter file.

This makes me think of a boilerplate template that can apply to both scenarios: real and simulation, since, IMO, both should be very similar, only differing in additional features one might add to the simulated hardware to be as close as possible to the real hardware (e.g. add noise to measurements, add gravity compensation effort terms available in industrial robots such as the kuka lwr 4+, etc)

My suggestion is to use the transmission spec.. For one single robot, shouldn't be a problem, and I can propose a PR for the RRBot example above, if you think it can be useful.

fmessmer commented 9 years ago

Thanks @davetcoleman! I'm tempted to use this template as a starting point for implementing a ros_control-enabled version of the universal_robot driver (see here)

fmessmer commented 9 years ago

@carlosjoserg see my comment here

davetcoleman commented 9 years ago

Is there away to avoid listing joint names for the HW interface (real/sim)?

You could just hard code them in the code... I don't think one would usually want to just parse all of the joints our of the URDF, because often times there will be multiple hardware interfaces for different parts of the robot. Therefore you need to somewhere define the scope of this particular HWI. But by no means if the rosparam version that I propose in the boilerplate a standard people have to follow, its just one method (but its not bad, and very ROS-ified).

I think using Gazebo stuff is way out of the scope of this boilerplate (though I do require you pull the gazebo_ros_demos pkg because I wanted to reuse the RRBot example I made a few years ago). A real HWI shouldn't have any Gazebo deps. @ipa-fxm's filterJointsParam is also Gazebo-specific.

There might be a good way to use the Transmissions tags to specify which hardware interfaces use what joints, but thats not clear to me atm.

This makes me think of a boilerplate template that can apply to both scenarios: real and simulation, since, IMO, both should be very similar, only differing in additional features one might add to the simulated hardware to be as close as possible to the real hardware

The Gazebo plugin for ros_control is very different, in that it has to have hooks to the physics simulator's timesteps, force and position control interfaces of Gazebo, and it doesn't even have a main (its just a plugin for Gazebo). I don't think there will be much overlap there.

Please feel free to show me an example using the transmission step, but I believe needs to be able to apply different parts of the robot to different HWIs.

Thanks!

carlosjoserg commented 9 years ago

because often times there will be multiple hardware interfaces for different parts of the robot.

That's is my use case right now, that's why I'm concerned. Here it is.

Therefore you need to somewhere define the scope of this particular HWI

I find namespaces a great solution, also for the robot_description

I think using Gazebo stuff is way out of the scope of this boilerplate The Gazebo plugin for ros_control is very different

I'm not talking about the plugin itself, but the class defining the hardware interfaces, real and sim. IMO, the only differences should be read/write to real robot/simulated robot, and as I said before, additional stuff to be as close as possible to the real robot.

I'm just thinking that the boilerplate can be already a best-practice template to achieve a reliable implementation of your own scheme.

...There might be a good way to use the Transmissions tags to specify which hardware interfaces use what joints, but thats not clear to me atm. ...Please feel free to show me an example using the transmission step, but I believe needs to be able to apply different parts of the robot to different HWIs.

I'm already doing it in simulation, but with an odd hack which is name-spacing the transmissions. I'm trying to find a better solution. When I posted ros-simulation/gazebo_ros_pkgs#297 I already described my proposed solution.

I can help on the boilerplate for the RRBot example, but it won't fit either in gazebo_ros_demos nor in a ros-controls-demo-like repo , I see it more within something like this, don't you think?

davetcoleman commented 9 years ago

@adolfo-rt i'd really appreciate your feedback on this. also, could you send me a code sample of joint limits and estop functionality?

adolfo-rt commented 9 years ago

Hey all.

I think this is a great idea, and a lot of people will it very useful. I'll try to report back before the end of the week. I already took a quick peek, but I'd like to spend some quiet time with it ;-)

Once again, thanks for the great initiatives @davetcoleman

carlosjoserg commented 9 years ago

Hi again, and sorry if I bother here with a long post, I just want to give you an opinion form an user's point of view.

Context

I understand that you all wish to generalize everything, and that's great. But IMO, with HW is not an easy task, as you surely know. Templates are good, of course. I even consider the gazebo_ros_control plugin as a nice template, actually. But sometimes, we (users, at least my case) would like to see how the tools are applied in a simple example, for which I think that the RRBOT is ideal. And the beauty of the new ros-controls is the ability to customize your own robot within an easy-to-use framework compatible with almost everything, instead of generalization.

I also understand that there are 1000 ways of doing things, but for newbies (as me), we usually tend to follow examples until we make our minds and fully understand what we are doing, and finally decide what was the best way to do what you did. So, having a good example from the beginning surely is helpful.

Suggestion

So, inspired by this issue and this scheme, and based on my recent experience dealing with these tools, what I did here is the skeleton of a comprehensive RRBOT example to show the recent capabilities of ROS(controls)/Gazebo/MoveIt! all at once and with best-practice specifications (and who else better than you to contribute on that):

https://github.com/carlosjoserg/rrbot

Note that, in the rrbot_hw package I merged both the gazebo_ros_control plugin + defaultrobothwsim (old implementation, e.g. w/o estops) and the boilerplate template here, only that I changed names as if it were customized for the RRBOT case, so stuff can be added later on.

Note also that, I created a single rrbot_hw.h to define the hardware from where rrbot_hw_sim.h and rrbot_hw_real.h are derived to avoid repetition of interfaces and shared memory, and bring simulation and reality as close as possible and effortless. I'm sure more stuff can be moved to the base class.

This promotes a plugin per robot type, and not a general gazebo_ros_control plugin, but I don't see it bad either. Similarly, there are different depth camera plugins as well.

Still joint name load to hardware interfaces using URDF/Transmissions and multi-robot control is not there. However, there is already a robot made out of two RRBOTs to test different approaches on this topic.

adolfo-rt commented 9 years ago

I can't make inline comments on the code, because it's not a PR, so I'll be brief. Manually cross-referencing is time-consuming. I'm going to highlight only the most relevant best practices

generic_hardware_interface.cpp

Control period

In void GenericHardwareInterface::update(const ros::TimerEvent& e): Don't compute the control period from system time differences. Extract from this robot control sig thread:

"I recommend using a monotonic source for computing control periods, as opposed to using system time differences (current - previous). The latter is not a good practice because system time is subject to changing at different rates (eg. ntp slew) or even discretely (eg. user calling date --set=some_arbitrary_date)."

Posix-wise, you can use clock_gettime(CLOCK_MONOTONIC, t)` (more)

Write

The write method is somewhat specific to a particular setup. For a dummy example, I'd simply write commands to a raw data buffer. If you're dealing with only position and velocity interfaces, you could make read report back desired positions or integrate desired velocities, and you'd have a kinematic simulator in place.

Separate control loop from RobotHW

GenericHardwareInterface is the robot abstraction, and it's currently coupled with the control loop. I'd clearly separate these two. For instance, in your control loop you're imposing a separate process for it, and how time and durations are measured. Those two assumptions will not work well for everybody. I'm sure one can think of more.

generic_hardware_main.cpp

It's very important to stress the need of an AsyncSpinner or similar, which you already do.

You could ros::waitForShutdown(); instead of ros::spin(), right?.

General comments

I'd aim at having a basic example that's easy to understand, and then to scale it up with goodies, like joint limits, e-stop handling or transmissions. This exercise will likely help us identify building blocks that should be put in place to reduce the red tape needed to construct a robot hardware. Making sure that everybody understands the few key issues discussed above is already a big gain.

davetcoleman commented 9 years ago

@adolfo-rt thanks for your feedback, I have incorporated those changes here

@carlosjoserg I really haven't dealt with Gazebo in almost two years, so I don't tend to think about how to integrate these two components. I'm also not sure if it would make it easier for beginners, it seems like it would rather just be overwhelming to have the number of packages in the example you present. But I agree a comprehensive example is needed. In an ideal world a ROS Setup Assistant would create not just MoveIt! packages, but all the demo code to get a robot started. But I am not motivated to do that at this time

For further review I'll create a dummy pull request. Here it is Thanks!

mryellow commented 9 years ago

For some perspective from a noob coming through...

I made it here stuck with the line between hardware_interface::RobotHW block, actual hardware and implementation of the control loop.

I saw diff_drive_controller.cpp, reusable it seems, writeFromNonRT, interesting... diff_drive_controller.h hardware_interface, righto so there is RobotHW I guess and it too should be reuable... this Commands type must be involved in how I interface with hardware somewhere along the line...

Too many examples relate to Gazebo, the source for which does help provide some insight but the extra plugin layer obfuscates the process a little.

Now I wonder what this thread is really about and if what I see here is customisation of controllers and hardware interfaces, or the missing layer I'm looking for. At first it looks like duplication of something that must be buried in hardware_interfaces, I wonder if the path is to always rewrite hardware_interface::RobotHW block with custom hardware code embedded in it, surely not.

Then I take a close look at Husky which uses the diff_drive_controller in it's config and VelocityJointInterface in the URDF. Reusing all the parts I'd like to reuse, confirming it does work as expected.

I see husky_base control loop and linking to hardware_interface, along the way it actually writes to hardware, the connection with this thread becomes more clear.

So here we have your control loop, separate to all that goes on inside hardware_interface::RobotHW, and talking directly to it.

All the pieces look to be in their correct places, takes a bit of a leap to get from the ros_control lecture to this boilerplate. A nice diagram needed in the middle there somewhere.

mryellow commented 9 years ago

Ok so... a "controller" (such as diff_drive_controller) can use a type from hardware_interfaces (such as VelocityJointInterface) which is actually called "Hardware Resource Interface Layer". This is then picked up in a "Hardware Interface" via registerInterface, using the VelocityJointInterface pointer directly in custom robot specific read/write methods.

The key bit of the talk being:

void read();
void write();

"This is what is up to you, this is where ros_control can no longer help you"

ros control map

So a robot hardware specific implementation has to fill in the orange section of this chart....

Not used to working in C++ too much and the "hardware interface" bit easily looks like two things called by the same name until you realise the types are "Hardware Resource Interface Layer" even though they exist mostly in the hardware_interfaces repo, they aren't really the "hardware interfaces" themselves strictly speaking.

Which I guess means this boilerplate represents "hardwareinterface::RobotHW", or a "Hardware Interface"_.

mryellow commented 9 years ago

Looking over the API docs can't help but feel I've still got this wrong, or that there is a hole in the lexicon somewhere. Got the pieces in their correct places, but no clear words to describe them. Could simply be my C++ knowledge is somewhat lacking but as I'm the noob coming through, I'm a good case-study for any confusion here.

http://docs.ros.org/indigo/api/hardware_interface/html/c++/namespaces.html

Here "hardware interface" is a namespace containing individual "interface" suffix classes. Custom interfaces can also be injected into this from other packages, such is the case with velocity from transmission.

http://docs.ros.org/indigo/api/hardware_interface/html/c++/classhardware__interface_1_1RobotHW.html

hardware_interface:RobotHW would be the resource manager itself.... Which makes sense in the ros_control diagram above.

http://wiki.ros.org/ros_control

Here it says "A list of available hardware interfaces (via the Hardware Resource Manager) as of this writing.", then lists things like VelocityJointInterface. So if that is a "Hardware Interface", then what is this boilerplate repo? Does it have a name? Is the name "Hardware Interface" reused as it is at the opening of this thread and ambiguity added?

https://github.com/davetcoleman/ros_control_boilerplate/blob/indigo-devel/src/generic_hardware_interface.cpp

"hardware interface", loads "hardware interfaces" from "hardware interface"? Which is commented as "generic hardware interface", loads "hardware interface" from "RobotHW".

Seems like "hardware interface" is interchangeable for:

Shouldn't they have three different names?

Seems like "Real Robot" falls off the description of the architecture and read/update/write end up floating in air between the defined components, just as they are shown in that diagram.

... Lets see if I can get this lexicon straighter in my head from the source...

Yeah... Two things, one name.... Not clearly wrapped as a module in the diagram (given it's not all tightly coupled, but lack of clear definition of the component is confusing).

Strictly:

hardware "resource manager" -> "hardware interface resource layer", containing "hardware interfaces" -> ??? ("Real robot")

Colloquially:

"RobotHW" -> "hardware interface" -> "hardware interface".

edit:

Should it be:

"RobotHW Resource manager" -> "Hardware interface resource layer" containing "Hardware interfaces" -> "Control interface".

As named in Husky

davetcoleman commented 8 years ago

I just want to give an update that I've been using this boilerplate extensively and have added joint limits support and a "simulation" interface for any robot to use. I've just released it as a ROS debian, but I'm happy to take more feedback.

bmagyar commented 8 years ago

@davetcoleman I think your boilerplates were pretty well received. Should we move them over to the ros-controls org and make the Kinetic releases from here? That would give us some of the long-awaited tutorials/documentation that we are missing. (I even got feedback from NASA that they missed tutorials a lot.)

davetcoleman commented 8 years ago

Yea, I could do a PR to ros_control. I think keeping its current name would be most appropriate, calling it just "boilerplate" would be too generic for the ros package namespace.

One thing that is bothering me, though, is that the scope has maybe outgrown its name. I have a number of tools in it that should probably else where - recording and saving trajectories to/from file, keyboard teleop, etc. It has also become another layer of interfaces that are less ROS-agnostic - a lot of the original ros_control pretended like ROS didn't exist, which I find odd considering its name. For example, it does not load the robot_description for you, even though pretty much every controller and hardware interface should need this IMHO. This package also includes a simulation pass-through feature, which I find really useful for working with robots disconnected from hardware but when you don't want all of Gazebo.

So I'm not sure what to do because of this feature creep, let me know your thoughts.

ShawnSchaerer commented 8 years ago

@davetcoleman I have used your boilerplate exclusively for my ROS Control implementation and will upgrade to your latest version soon. I'd like to offer my time to help you in testing etc.

I will go through your latest and give you my opinion on the features and feature creep

bmagyar commented 8 years ago

Awesome! Thanks Shawn for popping in, it is always great to have a fresh set of eyes on the problem.

@davetcoleman, I'm with you on the oddity of being ROS-agnostic, but it seems to work out pretty well. I have just recently saw some controllers being totally using LCM and the rest of MIT's DRC software stack running on a NASA Valkyrie (which is a ros_control-based robot). Those controllers hardly use ROS but are able to run within the same ecosystem which I think it a great feature. I think it's best to keep the minimalism of the framework. If this means that every controller needs to load and parse the robot_description for themselves, then so be it, they may parse them in different ways, etc.

Would it be possible to separate the trajectory tools into a repo named trajectory_toolbox? I'm not sure there's no such package with this name already though...

Could you elaborate on the pass-through feature?

davetcoleman commented 8 years ago

Pass through just means that when you write() to a hardware interface, the same value is immediately returned in the read() call - no actual hardware is controlled but in Rviz, etc it appears as if the robot is moving.

Would it be possible to separate the trajectory tools into a repo named trajectory_toolbox?

Seems like a good idea.

bmagyar commented 8 years ago

Thanks for the clarification.

So...trajectory_toolbox and ros_control_boilerplate? Can you do the separation or do you need assistance?

davetcoleman commented 8 years ago

I've had this lingering on my todo list but I do not think I will get to it anytime soon. Feel free to work on it yourself, thanks!

matthew-reynolds commented 4 years ago

@bmagyar @davetcoleman Is there still a desire to get these pulled into the main repo? If so, I'm happy to start chipping away at it.

bmagyar commented 4 years ago

In my opinion, yes.