canopen_ros.cpp
Go to the documentation of this file.
00001 
00060 #include "ros/ros.h"
00061 #include <urdf/model.h>
00062 #include "std_msgs/String.h"
00063 #include "sensor_msgs/JointState.h"
00064 #include "control_msgs/JointTrajectoryControllerState.h"
00065 #include "brics_actuator/JointVelocities.h"
00066 #include "cob_srvs/Trigger.h"
00067 #include "cob_srvs/SetOperationMode.h"
00068 #include <diagnostic_msgs/DiagnosticArray.h>
00069 #include <iostream>
00070 #include <map>
00071 #include <boost/bind.hpp>
00072 #include <boost/filesystem.hpp>
00073 #include <ipa_canopen_core/canopen.h>
00074 #include <XmlRpcValue.h>
00075 #include <ipa_canopen_ros/JointLimits.h>
00076 
00077 typedef boost::function<bool(cob_srvs::Trigger::Request&, cob_srvs::Trigger::Response&)> TriggerType;
00078 typedef boost::function<void(const brics_actuator::JointVelocities&)> JointVelocitiesType;
00079 typedef boost::function<bool(cob_srvs::SetOperationMode::Request&, cob_srvs::SetOperationMode::Response&)> SetOperationModeCallbackType;
00080 
00081 struct BusParams
00082 {
00083     std::string baudrate;
00084     uint32_t syncInterval;
00085 };
00086 
00087 std::map<std::string, BusParams> buses;
00088 
00089 std::string deviceFile;
00090 
00091 JointLimits* joint_limits_;
00092 std::vector<std::string> chainNames;
00093 std::vector<std::string> jointNames;
00094 
00095 bool CANopenInit(cob_srvs::Trigger::Request &req, cob_srvs::Trigger::Response &res, std::string chainName)
00096 {
00097     bool all_initialized = true;
00098 
00099     for (auto device : canopen::devices)
00100     {
00101         if (not device.second.getInitialized())
00102         {
00103             all_initialized = false;
00104         }
00105     }
00106 
00107     if(all_initialized)
00108     {
00109         res.success.data = true;
00110         res.error_message.data = "already initialized";
00111         ROS_INFO("already initialized");
00112         return true;
00113     }
00114 
00115     bool init_success = canopen::init(deviceFile, canopen::syncInterval);
00116     std::this_thread::sleep_for(std::chrono::milliseconds(100));
00117 
00118 
00119     for (auto device : canopen::devices)
00120     {
00121         if(init_success)
00122         {
00123             res.success.data = true;
00124             res.error_message.data = "Sucessfuly initialized";
00125             ROS_INFO("The device was sucessfuly initialized");
00126 
00127         }
00128         else
00129         {
00130             res.success.data = false;
00131             res.error_message.data = "Module could not be initialized";
00132             ROS_WARN("Module could not be initialized. Check for possible errors and try to initialize it again.");
00133         }
00134 
00135 
00136     return true;
00137 
00138     }
00139 }
00140 
00141 
00142 bool CANopenRecover(cob_srvs::Trigger::Request &req, cob_srvs::Trigger::Response &res, std::string chainName)
00143 {
00144 
00145     for (auto device : canopen::devices)
00146     {
00147         if (not device.second.getInitialized())
00148         {
00149             res.success.data = false;
00150             res.error_message.data = "not initialized yet";
00151             ROS_INFO("not initialized yet");
00152             return true;
00153         }
00154     }
00155 
00156     bool recover_success = canopen::recover(deviceFile, canopen::syncInterval);
00157     std::this_thread::sleep_for(std::chrono::milliseconds(100));
00158 
00159 
00160     for (auto device : canopen::devices)
00161     {
00162 
00163         if(recover_success)
00164         {
00165 
00166             res.success.data = true;
00167             res.error_message.data = "Sucessfuly recovered";
00168             ROS_INFO("The device was sucessfuly recovered");
00169             return true;
00170         }
00171         else
00172         {
00173             res.success.data = false;
00174             res.error_message.data = "Module could not be recovered";
00175             ROS_WARN("Module could not be recovered. Check for possible errors and try to recover it again.");
00176             return true;
00177         }
00178 
00179     }
00180 
00181 }
00182 
00183 
00184 bool CANOpenHalt(cob_srvs::Trigger::Request &req, cob_srvs::Trigger::Response &res, std::string chainName)
00185 {
00186 
00187 
00188 
00189     canopen::halt(deviceFile, canopen::syncInterval);
00190     std::this_thread::sleep_for(std::chrono::milliseconds(100));
00191 
00192     res.success.data = true;
00193     res.error_message.data = "";
00194     return true;
00195 }
00196 
00197 
00198 bool setOperationModeCallback(cob_srvs::SetOperationMode::Request &req, cob_srvs::SetOperationMode::Response &res, std::string chainName)
00199 {
00200     res.success.data = true;  // for now this service is just a dummy, not used elsewhere
00201     return true;
00202 }
00203 
00204 void setVel(const brics_actuator::JointVelocities &msg, std::string chainName)
00205 {
00206     if (!canopen::atFirstInit & !canopen::recover_active)
00207     {
00208         std::vector<double> velocities;
00209         std::vector<double> positions;
00210 
00211 
00212         for (auto it : msg.velocities)
00213         {
00214             velocities.push_back( it.value);
00215         }
00216 
00217         int counter = 0;
00218 
00219         for (auto device : canopen::devices)
00220         {
00221 
00222             double pos = (double)device.second.getDesiredPos();// + joint_limits_->getOffsets()[counter];
00223             positions.push_back(pos);
00224             counter++;
00225         }
00226 
00227         joint_limits_->checkVelocityLimits(velocities);
00228         joint_limits_->checkPositionLimits(positions, velocities);
00229 
00230         canopen::deviceGroups[chainName].setVel(velocities);
00231     }
00232 }
00233 
00234 void readParamsFromParameterServer(ros::NodeHandle n)
00235 {
00236     std::string param;
00237 
00238     param = "devices";
00239     XmlRpc::XmlRpcValue busParams;
00240     if (n.hasParam(param))
00241     {
00242         n.getParam(param, busParams);
00243     }
00244     else
00245     {
00246         ROS_ERROR("Parameter %s not set, shutting down node...", param.c_str());
00247         n.shutdown();
00248     }
00249 
00250     // TODO: check for content of busParams
00251     for (int i=0; i<busParams.size(); i++)
00252     {
00253         BusParams busParam;
00254         auto name = static_cast<std::string>(busParams[i]["name"]);
00255         busParam.baudrate = static_cast<std::string>(busParams[i]["baudrate"]);
00256         canopen::baudRate = busParam.baudrate;
00257         busParam.syncInterval = static_cast<int>(busParams[i]["sync_interval"]);
00258         buses[name] = busParam;
00259     }
00260 
00261 
00262     param = "chains";
00263     XmlRpc::XmlRpcValue chainNames_XMLRPC;
00264     if (n.hasParam(param))
00265     {
00266         n.getParam(param, chainNames_XMLRPC);
00267     }
00268     else
00269     {
00270         ROS_ERROR("Parameter %s not set, shutting down node...", param.c_str());
00271         n.shutdown();
00272     }
00273 
00274     // TODO: check for content of chainNames_XMLRPC
00275     for (int i=0; i<chainNames_XMLRPC.size(); i++)
00276         chainNames.push_back(static_cast<std::string>(chainNames_XMLRPC[i]));
00277 
00278     for (auto chainName : chainNames) {
00279         param = "/" + chainName + "/joint_names";
00280         XmlRpc::XmlRpcValue jointNames_XMLRPC;
00281         if (n.hasParam(param))
00282         {
00283             n.getParam(param, jointNames_XMLRPC);
00284         }
00285         else
00286         {
00287             ROS_ERROR("Parameter %s not set, shutting down node...", param.c_str());
00288             n.shutdown();
00289         }
00290 
00291         // TODO: check for content of jointNames_XMLRPC
00292         for (int i=0; i<jointNames_XMLRPC.size(); i++)
00293             jointNames.push_back(static_cast<std::string>(jointNames_XMLRPC[i]));
00294 
00295         param = "/" + chainName + "/module_ids";
00296         XmlRpc::XmlRpcValue moduleIDs_XMLRPC;
00297         if (n.hasParam(param))
00298         {
00299             n.getParam(param, moduleIDs_XMLRPC);
00300         }
00301         else
00302         {
00303             ROS_ERROR("Parameter %s not set, shutting down node...", param.c_str());
00304             n.shutdown();
00305         }
00306 
00307         // TODO: check for content of muduleIDs
00308         std::vector<uint8_t> moduleIDs;
00309         for (int i=0; i<moduleIDs_XMLRPC.size(); i++)
00310             moduleIDs.push_back(static_cast<int>(moduleIDs_XMLRPC[i]));
00311 
00312         param = "/" + chainName + "/devices";
00313         XmlRpc::XmlRpcValue devices_XMLRPC;
00314         if (n.hasParam(param))
00315         {
00316             n.getParam(param, devices_XMLRPC);
00317         }
00318         else
00319         {
00320             ROS_ERROR("Parameter %s not set, shutting down node...", param.c_str());
00321             n.shutdown();
00322         }
00323 
00324         // TODO: check for content of devices_XMLRPC
00325         std::vector<std::string> devices;
00326         for (int i=0; i<devices_XMLRPC.size(); i++)
00327             devices.push_back(static_cast<std::string>(devices_XMLRPC[i]));
00328 
00329         for (unsigned int i=0; i<jointNames.size(); i++)
00330         {
00331             canopen::devices[ moduleIDs[i] ] = canopen::Device(moduleIDs[i], jointNames[i], chainName, devices[i]);
00332         }
00333 
00334         canopen::deviceGroups[ chainName ] = canopen::DeviceGroup(moduleIDs, jointNames);
00335 
00336     }
00337 
00338 }
00339 
00340 void setJointConstraints(ros::NodeHandle n)
00341 {
00342     /******************************************
00343      *
00344      *
00345      *
00346      */
00347 
00349     joint_limits_ = new JointLimits();
00350     int DOF = jointNames.size();
00351 
00352     std::string param_name = "/robot_description";
00353     std::string full_param_name;
00354     std::string xml_string;
00355 
00356     n.searchParam(param_name, full_param_name);
00357     if (n.hasParam(full_param_name))
00358     {
00359         n.getParam(full_param_name.c_str(), xml_string);
00360     }
00361 
00362     else
00363     {
00364         ROS_ERROR("Parameter %s not set, shutting down node...", full_param_name.c_str());
00365         n.shutdown();
00366     }
00367 
00368     if (xml_string.size() == 0)
00369     {
00370         ROS_ERROR("Unable to load robot model from parameter %s",full_param_name.c_str());
00371         n.shutdown();
00372     }
00373     //ROS_INFO("%s content\n%s", full_param_name.c_str(), xml_string.c_str());
00374 
00376     urdf::Model model;
00377 
00378     if (!model.initString(xml_string))
00379     {
00380         ROS_ERROR("Failed to parse urdf file");
00381         n.shutdown();
00382     }
00383     ROS_INFO("Successfully parsed urdf file");
00384 
00386     std::vector<double> MaxVelocities(DOF);
00387     std::vector<double> LowerLimits(DOF);
00388     std::vector<double> UpperLimits(DOF);
00389     std::vector<double> Offsets(DOF);
00390 
00391     for (int i = 0; i < DOF; i++)
00392     {
00393         if(!model.getJoint(jointNames[i].c_str())->limits)
00394         {
00395             ROS_ERROR("Parameter limits could not be found in the URDF contents.");
00396             n.shutdown();
00397             exit(1);
00398         }
00399         else if(!model.getJoint(jointNames[i].c_str())->limits->velocity)
00400         {
00401             ROS_ERROR("Limits has no velocity attribute");
00402             n.shutdown();
00403             exit(1);
00404         }
00405         if(!model.getJoint(jointNames[i].c_str())->limits->lower)
00406         {
00407             ROS_ERROR("Limits has no lower attribute");
00408             n.shutdown();
00409             exit(1);
00410         }
00411         else if(!model.getJoint(jointNames[i].c_str())->limits->upper)
00412         {
00413             ROS_ERROR("Limits has no upper attribute");
00414             n.shutdown();
00415             exit(1);
00416         }
00417         //Get maximum velocities out of urdf model
00418         MaxVelocities[i] = model.getJoint(jointNames[i].c_str())->limits->velocity;
00419 
00421         LowerLimits[i] = model.getJoint(jointNames[i].c_str())->limits->lower;
00422 
00423          // Get upper limits out of urdf model
00424         UpperLimits[i] = model.getJoint(jointNames[i].c_str())->limits->upper;
00425 
00427         if(!model.getJoint(jointNames[i].c_str())->calibration)
00428         {
00429             ROS_ERROR("Parameter calibration could not be found in the URDF contents.");
00430             n.shutdown();
00431             exit(1);
00432         }
00433         else if(!model.getJoint(jointNames[i].c_str())->calibration->rising)
00434         {
00435             ROS_ERROR("Calibration has no rising attribute");
00436             n.shutdown();
00437             exit(1);
00438         }
00439         Offsets[i] = model.getJoint(jointNames[i].c_str())->calibration->rising.get()[0];
00440     }
00441 
00443 
00444     joint_limits_->setDOF(DOF);
00445     joint_limits_->setUpperLimits(UpperLimits);
00446     joint_limits_->setLowerLimits(LowerLimits);
00447     joint_limits_->setMaxVelocities(MaxVelocities);
00448     joint_limits_->setOffsets(Offsets);
00449 
00450     /********************************************
00451      *
00452      *
00453      ********************************************/
00454 }
00455 
00456 
00457 int main(int argc, char **argv)
00458 {
00459     // todo: allow identical module IDs of modules when they are on different CAN buses
00460 
00461 
00462     ros::init(argc, argv, "canopen_ros");
00463     ros::NodeHandle n(""); // ("~");
00464 
00465     readParamsFromParameterServer(n);
00466 
00467     std::cout << "Sync Interval" << buses.begin()->second.syncInterval << std::endl;
00468     canopen::syncInterval = std::chrono::milliseconds( buses.begin()->second.syncInterval );
00469     // ^ todo: this only works with a single CAN bus; add support for more buses!
00470     deviceFile = buses.begin()->first;
00471 
00472     //canopen::pre_init();
00473 
00474     /********************************************/
00475 
00476     // add custom PDOs:
00477     canopen::sendPos = canopen::defaultPDOOutgoing_interpolated;
00478     for (auto it : canopen::devices) {
00479         canopen::incomingPDOHandlers[ 0x180 + it.first ] = [it](const TPCANRdMsg mS) { canopen::defaultPDO_incoming_status( it.first, mS ); };
00480         canopen::incomingPDOHandlers[ 0x480 + it.first ] = [it](const TPCANRdMsg mP) { canopen::defaultPDO_incoming_pos( it.first, mP ); };
00481         canopen::incomingEMCYHandlers[ 0x081 + it.first ] = [it](const TPCANRdMsg mE) { canopen::defaultEMCY_incoming( it.first, mE ); };
00482     }
00483 
00484     // set up services, subscribers, and publishers for each of the chains:
00485     std::vector<TriggerType> initCallbacks;
00486     std::vector<ros::ServiceServer> initServices;
00487     std::vector<TriggerType> recoverCallbacks;
00488     std::vector<ros::ServiceServer> recoverServices;
00489     std::vector<TriggerType> stopCallbacks;
00490     std::vector<ros::ServiceServer> stopServices;
00491     std::vector<SetOperationModeCallbackType> setOperationModeCallbacks;
00492     std::vector<ros::ServiceServer> setOperationModeServices;
00493 
00494     std::vector<JointVelocitiesType> jointVelocitiesCallbacks;
00495     std::vector<ros::Subscriber> jointVelocitiesSubscribers;
00496     std::map<std::string, ros::Publisher> currentOperationModePublishers;
00497     std::map<std::string, ros::Publisher> statePublishers;
00498     ros::Publisher jointStatesPublisher = n.advertise<sensor_msgs::JointState>("/joint_states", 1);
00499     ros::Publisher diagnosticsPublisher = n.advertise<diagnostic_msgs::DiagnosticArray>("/diagnostics", 1);
00500 
00501     for (auto it : canopen::deviceGroups)
00502     {
00503         ROS_INFO("Configuring %s", it.first.c_str());
00504 
00505         initCallbacks.push_back( boost::bind(CANopenInit, _1, _2, it.first) );
00506         initServices.push_back( n.advertiseService("/" + it.first + "/init", initCallbacks.back()) );
00507         recoverCallbacks.push_back( boost::bind(CANopenRecover, _1, _2, it.first) );
00508         recoverServices.push_back( n.advertiseService("/" + it.first + "/recover", recoverCallbacks.back()) );
00509         stopCallbacks.push_back( boost::bind(CANOpenHalt, _1, _2, it.first) );
00510         stopServices.push_back( n.advertiseService("/" + it.first + "/halt", stopCallbacks.back()) );
00511         setOperationModeCallbacks.push_back( boost::bind(setOperationModeCallback, _1, _2, it.first) );
00512         setOperationModeServices.push_back( n.advertiseService("/" + it.first + "/set_operation_mode", setOperationModeCallbacks.back()) );
00513 
00514         jointVelocitiesCallbacks.push_back( boost::bind(setVel, _1, it.first) );
00515         jointVelocitiesSubscribers.push_back( n.subscribe<brics_actuator::JointVelocities>("/" + it.first + "/command_vel", 1, jointVelocitiesCallbacks.back()) );
00516 
00517         currentOperationModePublishers[it.first] = n.advertise<std_msgs::String>("/" + it.first + "/current_operationmode", 1);
00518 
00519         statePublishers[it.first] = n.advertise<control_msgs::JointTrajectoryControllerState>("/" + it.first + "/state", 1);
00520     }
00521 
00522     double lr = 1000.0 / std::chrono::duration_cast<std::chrono::milliseconds>(canopen::syncInterval).count();
00523 
00524     ros::Rate loop_rate(lr);
00525 
00526     setJointConstraints(n);
00527 
00528     while (ros::ok())
00529     {
00530 
00531         // iterate over all chains, get current pos and vel and publish as topics:
00532         int counter = 0;
00533         std::vector <double> positions;
00534         std::vector <double> desired_positions;
00535 
00536         for (auto device : canopen::devices)
00537         {
00538 
00539             double pos = (double)device.second.getActualPos() + joint_limits_->getOffsets()[counter];
00540             double des_pos = (double)device.second.getDesiredPos() + joint_limits_->getOffsets()[counter];
00541             positions.push_back(pos);
00542             desired_positions.push_back(des_pos);
00543             counter++;
00544         }
00545 
00546         for (auto dg : (canopen::deviceGroups))
00547         {
00548             sensor_msgs::JointState js;
00549             js.name = dg.second.getNames();
00550             js.header.stamp = ros::Time::now(); // todo: possibly better use timestamp of hardware msg?
00551 
00552             js.position = positions;//dg.second.getActualPos();
00553             //std::cout << "Position" << js.position[0] << std::endl;
00554             js.velocity = dg.second.getActualVel();
00555             js.effort = std::vector<double>(dg.second.getNames().size(), 0.0);
00556             jointStatesPublisher.publish(js);
00557 
00558             control_msgs::JointTrajectoryControllerState jtcs;
00559             jtcs.header.stamp = js.header.stamp;
00560             jtcs.actual.positions = js.position;
00561             jtcs.actual.velocities = js.velocity;
00562             jtcs.desired.positions = desired_positions;//dg.second.getDesiredPos();
00563             jtcs.desired.velocities = dg.second.getDesiredVel();
00564             statePublishers[dg.first].publish(jtcs);
00565 
00566             std_msgs::String opmode;
00567             opmode.data = "velocity";
00568             currentOperationModePublishers[dg.first].publish(opmode);
00569             counter++;
00570         }
00571 
00572         // publishing diagnostic messages
00573         diagnostic_msgs::DiagnosticArray diagnostics;
00574         diagnostic_msgs::DiagnosticStatus diagstatus;
00575         std::vector<diagnostic_msgs::DiagnosticStatus> diagstatus_msg;
00576 
00577         diagnostic_msgs::KeyValue keyval;
00578         std::vector<diagnostic_msgs::KeyValue> keyvalues;
00579 
00580 
00581 
00582         diagnostics.status.resize(1);
00583 
00584         for (auto dg : (canopen::devices))
00585         {
00586             std::string name = dg.second.getName();
00587             //ROS_INFO("Name %s", name.c_str() );
00588 
00589             keyval.key = "Node ID";
00590             uint16_t node_id = dg.second.getCANid();
00591             std::stringstream result;
00592             result << node_id;
00593             keyval.value = result.str().c_str();
00594             keyvalues.push_back(keyval);
00595 
00596             keyval.key = "Device Name";
00597             std::vector<char> dev_name = dg.second.getManufacturerDevName();
00598             keyval.value = std::string(dev_name.begin(), dev_name.end());
00599             keyvalues.push_back(keyval);
00600 
00601             /*
00602             keyval.key = "Hardware Version";
00603             std::vector<char> manhw = dg.second.getManufacturerHWVersion();
00604             keyval.value = std::string(manhw.begin(), manhw.end());
00605             keyvalues.push_back(keyval);
00606 
00607             keyval.key = "Software Version";
00608             std::vector<char> mansw = dg.second.getManufacturerSWVersion();
00609             keyval.value = std::string(mansw.begin(), mansw.end());
00610             keyvalues.push_back(keyval);
00611 
00612 
00613 
00614             keyval.key = "Vendor ID";
00615             std::vector<uint16_t> vendor_id = dg.second.getVendorID();
00616             std::stringstream result1;
00617             for (auto it : vendor_id)
00618             {
00619                 result1 <<  std::hex << it;
00620             }
00621             keyval.value = result1.str().c_str();
00622             keyvalues.push_back(keyval);
00623 
00624             keyval.key = "Revision Number";
00625             uint16_t rev_number = dg.second.getRevNumber();
00626             std::stringstream result2;
00627             result2 << rev_number;
00628             keyval.value = result2.str().c_str();
00629             keyvalues.push_back(keyval);
00630 
00631             keyval.key = "Product Code";
00632             std::vector<uint16_t> prod_code = dg.second.getProdCode();
00633             std::stringstream result3;
00634             std::copy(prod_code.begin(), prod_code.end(), std::ostream_iterator<uint16_t>(result3, " "));
00635             keyval.value = result3.str().c_str();
00636             keyvalues.push_back(keyval);
00637             */
00638 
00639             bool error_ = dg.second.getFault();
00640             bool initialized_ = dg.second.getInitialized();
00641 
00642             if(initialized_)
00643             {
00644                 keyval.key = "Current mode of operation";
00645                 int8_t mode_display = dg.second.getCurrentModeofOperation();
00646                 keyval.value = canopen::modesDisplay[mode_display];
00647                 keyvalues.push_back(keyval);
00648 
00649                 keyval.key = "Errors Register";
00650                 keyval.value = dg.second.getErrorRegister();
00651                 keyvalues.push_back(keyval);
00652 
00653                 keyval.key = "Current driver temperature";
00654                 double driver_temperature = dg.second.getDriverTemperature();
00655                 keyval.value = std::to_string(driver_temperature);
00656                 keyvalues.push_back(keyval);
00657             }
00658 
00659             //ROS_INFO("Fault: %d", error_);
00660             //ROS_INFO("Referenced: %d", initialized_);
00661 
00662             // set data to diagnostics
00663             if(error_)
00664             {
00665                 diagstatus.level = 2;
00666                 diagstatus.name = chainNames[0];
00667                 diagstatus.message = "Fault occured.";
00668                 diagstatus.values = keyvalues;
00669                 break;
00670             }
00671             else
00672             {
00673                 if (initialized_)
00674                 {
00675                     diagstatus.level = 0;
00676                     diagstatus.name = chainNames[0];
00677                     diagstatus.message = "canopen chain initialized and running";
00678                     diagstatus.values = keyvalues;
00679                 }
00680                 else
00681                 {
00682                     diagstatus.level = 1;
00683                     diagstatus.name = chainNames[0];
00684                     diagstatus.message = "canopen chain not initialized";
00685                     diagstatus.values = keyvalues;
00686                     break;
00687                 }
00688             }
00689         }
00690         diagstatus_msg.push_back(diagstatus);
00691         // publish diagnostic message
00692         diagnostics.status = diagstatus_msg;
00693         diagnostics.header.stamp = ros::Time::now();
00694         diagnosticsPublisher.publish(diagnostics);
00695 
00696         ros::spinOnce();
00697         loop_rate.sleep();
00698     }
00699 
00700     return 0;
00701 }
00702 


ipa_canopen_ros
Author(s): Tobias Sing, Thiago de Freitas
autogenerated on Mon Oct 6 2014 00:59:31