ros_control
ros_control copied to clipboard
Boilerplate Template
I'm thinking of making a boilerplate template for a simple hardware interface, does this already exist somewhere?
...don't know...but I'd be interested in using/testing one, too :wink:
Very beta: https://github.com/davetcoleman/ros_control_boilerplate
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 .
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.
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.
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)
@carlosjoserg see my comment here
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!
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?
@adolfo-rt i'd really appreciate your feedback on this. also, could you send me a code sample of joint limits and estop functionality?
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
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.
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.
@adolfo-rt thanks for your feedback, I have incorporated those changes here
- Uses Monotonic time
- Write example (commented out)
- Separated control loop into its own class
- Changes to
waitForShutdown()
@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!
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.
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"
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 "hardware_interface::RobotHW", or a "Hardware Interface".
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:
- A specific type of interface, velocity, position etc.
- The namespace which contains these.
- The actual end-user implementation of a control loop for hardware.
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...
- RobotHW: Hardware resource manager.
-
Hardware interface resource layer:
ActuatorStateInterface
,JointCommandInterface
,JointStateInterface
, etc. Often individually referred to as "Hardware interfaces". -
"Hardware interface": This boilerplate?
read
/update
/write
control loop registering "hardware interface" resource layer pointers from "RobotHW" resource manager.
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
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.
@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.)
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.
@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
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?
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.
Thanks for the clarification.
So...trajectory_toolbox and ros_control_boilerplate? Can you do the separation or do you need assistance?
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!
@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.
In my opinion, yes.