0
$\begingroup$

Rosanswers logo

Hello,

I'm using ROS Kinetic. I'd like to have multiple instances of a dynamic_reconfigure server in a node, and I'm wondering how to achieve that. I have different robots that I define in YAML files with a specific number of motors for each robot. I have a dynamic_reconfigure config file in my package that allows me to set min, max, offset and enable parameters for a motor.

But I need to be able to tune those parameters for each motor defined in the YAML file, and that is unknown at compile time. I can't dynamically allocate some array of server because at runtime I don't have the callbacks created.

A way, which I don't really like, could be to add a string or int field to the parameters, and type the motor name or index to apply the values to that specific motor in the code. There could be a bool parameter also to apply the new values in the client node when set to true.

Another way could be to create a nodelet class of a motor with its own dynamic reconfigure server and callback, and have a vector of them in the main node, and push back as many as needed.

The other way could be to use services instead, but then I couldn't use the rqt_reconfigure gui.

Is there a good practice for doing this kind of thing ?

Thanks!


Originally posted by Cyril Jourdan on ROS Answers with karma: 157 on 2018-04-18

Post score: 0


Original comments

Comment by lucasw on 2018-04-18:
Passing extra arguments to callbacks: #q63991 (also a complete non-class example #q289061)

Comment by Cyril Jourdan on 2018-04-19:
Thanks! I see that working for topics, but will it work for dynamic_reconfigure, will rqt_reconfigure show me a list of motors with their parameters using that design ? I'll give it a try.

Comment by Cyril Jourdan on 2018-04-19:
Thanks for your hint lucasw, I've published an answer in my question.

Comment by mgruhler on 2018-04-19:
@Cyril_J great you got it working. You can post your solution as an answer and accept that as correct. This will help other users stumbling upon the same problem...

$\endgroup$

1 Answer 1

0
$\begingroup$

Rosanswers logo

I tried something pointed out by @lucasw and it works! I found that a dynamic_reconfigure server is linked to a NodeHandle and there can only be one instance otherwise you get the error: "tried to advertise a service that is already advertised in this node". So I created a vector of NodeHandle that I link to each server. Also I had to make sure to call ros::init(...) before creating any instance of my class that has the CallbackType as a member otherwise I got the error: Couldn't find an AF_INET address...

My class is for filtering motor commands, here are a few lines of code that focus on solving this problem: In my header file :

class CommandFilter
{
    /* some functions */

    void motorDynConfigCallback(osa_control::MotorDynConfig &config, uint32_t level, const int idx);

private:
    std::vector<dynamic_reconfigure::Server<osa_control::MotorDynConfig>*> motor_dyn_config_server_list_;
    osa_control::MotorDynConfig motor_param_;
    /* other class members */
};

In my implementation file:

bool CommandFilter::init()
{
/* some init code */

for(int i=0; i<ptr_robot_description_->getRobotDof(); i++)
{
        const std::string dof_name = ptr_robot_description_->getControllerList().at(i)->getName();

        ros::NodeHandle node_handle(dof_name.c_str());

        dynamic_reconfigure::Server<osa_control::MotorDynConfig> *s = new dynamic_reconfigure::Server<osa_control::MotorDynConfig>(node_handle);
        dynamic_reconfigure::Server<osa_control::MotorDynConfig>::CallbackType f;
        f = boost::bind(&CommandFilter::motorDynConfigCallback, this, _1, _2, i);
            
        s->setCallback(f);
        motor_dyn_config_server_list_.push_back(s);
}

/* some init code */
}

void CommandFilter::motorDynConfigCallback(osa_control::MotorDynConfig &config, uint32_t level, const int idx)
{
    ROS_INFO("Dynamic Reconfigure Request for dof%d [%s]: %s %d %d %d", idx+1, ptr_robot_description_->getControllerList().at(idx)->getName().c_str(), config.enable?"True":"False", config.min_pos, config.max_pos, config.offset_pos);

    motor_param_ = config;
}

Note that it's a first cut solution and the code can be improved, but it works and give me exactly what I needed.


Originally posted by Cyril Jourdan with karma: 157 on 2018-04-19

This answer was ACCEPTED on the original site

Post score: 1


Original comments

Comment by gvdhoorn on 2018-04-19:
ROS1 has a limitation of 1 node per process. Multiple nodehandles will not create more nodes, but could introduce additional namespaces. Afaik the only thing required for multiple dyn_recfg servers is exactly that: multiple namespaces so topics don't clash.

Comment by gvdhoorn on 2018-04-19:\

ros::NodeHandle *node_handle = new ros::NodeHandle(dof_name.c_str());

This line created an additional namespace.

Comment by Cyril Jourdan on 2018-04-19:
Yes exactly. @gvdhoorn Is there a way to create just the namespace ?

Comment by gvdhoorn on 2018-04-19:
You can just directly pass in a NodeHandle instance. No need to keep them around yourself. The dynamic_reconfigure::Server instance owns them.

But you do need the NodeHandle instance.

Comment by Cyril Jourdan on 2018-04-19:
Thanks for your advice @gvdhoorn, I've simplified the code and updated my answer. I works just as well.

Comment by lucasw on 2018-05-04:
It's worth pointing out: https://github.com/ros/dynamic_reconfigure/blob/master/include/dynamic_reconfigure/server.h#L75 the constructor to the server can be given a mutex and a nodehandle

 new dynamic_reconfigure::Server<foo::Bar>(reconfig_mutex, node_handle)

Comment by Loy on 2018-09-17:
You must construct the dynamic_reconfigure Server via the new operator. It doesn't work when you just use config_server(nh). Using the latter syntax, it will get destroyed when it goes out of scope.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.