00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include <cob_hand_bridge/InitFinger.h>
00019 #include <cob_hand_bridge/Status.h>
00020
00021 #include <std_srvs/Trigger.h>
00022
00023 #include <sensor_msgs/JointState.h>
00024 #include <control_msgs/FollowJointTrajectoryAction.h>
00025
00026 #include <diagnostic_updater/publisher.h>
00027
00028 #include <actionlib/server/simple_action_server.h>
00029
00030 #include <ros/ros.h>
00031
00032 #include <boost/thread/mutex.hpp>
00033 #include <boost/shared_ptr.hpp>
00034 #include <boost/scoped_ptr.hpp>
00035
00036 #include <angles/angles.h>
00037
00038 #include <gpio.h>
00039 #include <sdhx.h>
00040
00041 boost::mutex g_mutex;
00042
00043 boost::shared_ptr<cob_hand_bridge::Status> g_status;
00044 boost::shared_ptr<diagnostic_updater::TimeStampStatus> g_topic_status;
00045
00046 ros::Publisher g_js_pub;
00047 sensor_msgs::JointState g_js;
00048
00049 ros::ServiceServer g_init_srv;
00050 ros::ServiceServer g_halt_srv;
00051 ros::ServiceServer g_recover_srv;
00052
00053 typedef actionlib::SimpleActionServer<control_msgs::FollowJointTrajectoryAction> FollowJointTrajectoryActionServer;
00054 boost::scoped_ptr<FollowJointTrajectoryActionServer> g_as;
00055 control_msgs::FollowJointTrajectoryGoalConstPtr g_goal;
00056 cob_hand_bridge::JointValues g_command;
00057 cob_hand_bridge::JointValues g_default_command;
00058 ros::Timer g_command_timer;
00059 ros::Timer g_deadline_timer;
00060 double g_stopped_velocity;
00061 double g_stopped_current;
00062 bool g_initialized;
00063 bool g_motion_stopped;
00064 bool g_control_stopped;
00065 bool g_motors_moved;
00066 std::vector<double> g_goal_tolerance;
00067 std::string g_port;
00068
00069
00070 boost::scoped_ptr<SDHX> g_sdhx;
00071
00072 bool isFingerReady_nolock() {
00073 return g_status && (g_status->status & (g_status->MASK_FINGER_READY | g_status->MASK_ERROR)) == g_status->MASK_FINGER_READY && g_status->rc == 0;
00074 }
00075
00076 bool checkAction_nolock(bool deadline_exceeded){
00077 control_msgs::FollowJointTrajectoryResult result;
00078 if(g_as->isActive()){
00079 bool goal_reached = false;
00080 if(g_motion_stopped) {
00081 goal_reached = true;;
00082 for(size_t i = 0; i < g_status->joints.position_cdeg.size(); ++i){
00083 if(fabs(g_status->joints.position_cdeg[i]-g_command.position_cdeg[i]) > g_goal_tolerance[i]){
00084 goal_reached = false;
00085 result.error_code = result.GOAL_TOLERANCE_VIOLATED;
00086 break;
00087 }
00088 }
00089 }
00090 if(!isFingerReady_nolock()) {
00091 g_deadline_timer.stop();
00092 g_as->setAborted();
00093 }else if(g_motion_stopped && (goal_reached || g_motors_moved)) {
00094 g_deadline_timer.stop();
00095 g_as->setSucceeded(result);
00096 }else if (deadline_exceeded) {
00097 g_deadline_timer.stop();
00098 result.error_code = result.GOAL_TOLERANCE_VIOLATED;
00099 g_as->setAborted(result, "goal not reached in time");
00100 return false;
00101 }
00102 }
00103 return true;
00104 }
00105
00106 bool halt(){
00107 if(!g_sdhx || !g_sdhx->halt()){
00108 ROS_ERROR("Halt service did not succeed");
00109 return false;
00110 }
00111 else
00112 return true;
00113 }
00114
00115 bool init(){
00116 ros::NodeHandle nh_priv("~");
00117 if(!g_sdhx){
00118 g_sdhx.reset(new SDHX);
00119 g_port = nh_priv.param<std::string>("sdhx/port", "/dev/ttyACM0");
00120 if(g_sdhx->init(g_port.c_str(),
00121 nh_priv.param("sdhx/min_pwm0", 0),
00122 nh_priv.param("sdhx/min_pwm1", 0),
00123 nh_priv.param("sdhx/max_pwm0", 0),
00124 nh_priv.param("sdhx/max_pwm1", 0)) ){
00125
00126 g_status->rc = 0;
00127 g_status->status &= ~g_status->MASK_ERROR;
00128 return true;
00129 }
00130 }
00131 return false;
00132 }
00133
00134 bool recover(){
00135 if(g_sdhx) {
00136 if((g_status->status & g_status->MASK_ERROR) == 0 && g_status->rc == 0) {
00137 return true;
00138 }else if(g_sdhx->isInitialized()) {
00139 g_sdhx.reset();
00140 g_status->rc = 0;
00141 if(init()){
00142 g_status->status &= ~g_status->MASK_ERROR;
00143 return true;
00144 }
00145 }else{
00146 return false;
00147 }
00148 }
00149 return false;
00150 }
00151
00152 bool initCallback(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &res){
00153 boost::mutex::scoped_lock lock(g_mutex);
00154
00155 if(!g_status) {
00156 res.message = "hand is not yet connected";
00157 }else if(g_status->status == g_status->NOT_INITIALIZED) {
00158 lock.unlock();
00159
00160 if(init()){
00161 res.success = true;
00162 g_as->start();
00163 }
00164 else{
00165 res.success = false;
00166 res.message = "Not initialized";
00167 }
00168
00169 lock.lock();
00170 g_initialized = res.success;
00171 }else if(!g_initialized){
00172 g_as->start();
00173 res.success = true;
00174 res.message = "finger already initialized, restarting the controller";
00175 g_initialized = recover();
00176 }else{
00177 res.success = true;
00178 res.message = "already initialized";
00179 }
00180
00181 return true;
00182 }
00183
00184 bool haltCallback(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &res){
00185 boost::mutex::scoped_lock lock(g_mutex);
00186 if(!g_status) {
00187 res.message = "hand is not yet connected";
00188 res.success = false;
00189 }else{
00190 res.success = halt();
00191 }
00192 return true;
00193 }
00194
00195 bool recoverCallback(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &res){
00196 boost::mutex::scoped_lock lock(g_mutex);
00197 if(!g_status) {
00198 res.message = "hand is not yet connected";
00199 res.success = false;
00200 }else{
00201 res.success = recover();
00202 }
00203 return true;
00204 }
00205
00206 void statusCallback(const ros::TimerEvent& e){
00207 boost::mutex::scoped_lock lock(g_mutex);
00208 double dt = g_js.header.stamp.toSec() - e.current_real.toSec();
00209 bool first = !g_status;
00210 bool calc_vel = !first && dt != 0 ;
00211 if(first)
00212 g_status.reset ( new cob_hand_bridge::Status() );
00213 g_status->stamp = e.current_real;
00214 g_status->status = g_status->NOT_INITIALIZED;
00215
00216 cob_hand_bridge::JointValues &j= g_status->joints;
00217
00218 if(g_sdhx && g_sdhx->isInitialized()){
00219 int16_t position_cdeg[2];
00220 int16_t velocity_cdeg_s[2];
00221 int16_t current_100uA[2];
00222 if(g_sdhx->getData(position_cdeg, velocity_cdeg_s, current_100uA, boost::chrono::seconds(1))){
00223 g_status->status |= g_status->MASK_FINGER_READY;
00224 }
00225 else
00226 g_status->status |= g_status->MASK_ERROR;
00227 j.position_cdeg[0] = position_cdeg[0]; j.position_cdeg[1] = position_cdeg[1];
00228 j.velocity_cdeg_s[0] = velocity_cdeg_s[0]; j.velocity_cdeg_s[1] = velocity_cdeg_s[1];
00229 j.current_100uA[0] = current_100uA[0]; j.current_100uA[1] = current_100uA[1];
00230 g_status->rc = g_sdhx->getRC();
00231 }
00232
00233 g_topic_status->tick(g_status->stamp);
00234
00235 g_motion_stopped = true;
00236 g_control_stopped = true;
00237
00238 if(g_status->status & g_status->MASK_FINGER_READY){
00239 for(size_t i=0; i < g_status->joints.position_cdeg.size(); ++i){
00240 double new_pos = angles::from_degrees(g_status->joints.position_cdeg[i]/100.0);
00241 if(calc_vel){
00242 g_js.velocity[i] = (new_pos - g_js.position[i]) / dt;
00243 }
00244 if(fabs(g_js.velocity[i]) > g_stopped_velocity){
00245 g_motion_stopped = false;
00246 g_motors_moved = true;
00247 }
00248 if(fabs(g_status->joints.current_100uA[i]) > g_stopped_current){
00249 g_control_stopped = false;
00250 }
00251 g_js.position[i] = new_pos;
00252 }
00253 g_js.header.stamp = g_status->stamp;
00254 g_js_pub.publish(g_js);
00255 }
00256 checkAction_nolock(false);
00257
00258 if(first) g_init_srv = ros::NodeHandle("driver").advertiseService("init", initCallback);
00259
00260 if(g_sdhx) g_sdhx->poll();
00261 }
00262
00263 void reportDiagnostics(diagnostic_updater::DiagnosticStatusWrapper &stat){
00264 boost::mutex::scoped_lock lock(g_mutex);
00265
00266 if(!g_status){
00267 stat.summary(stat.ERROR, "not connected");
00268 return;
00269 }
00270
00271 if(g_status->status == g_status->NOT_INITIALIZED){
00272 stat.summary(stat.WARN, "not initialized");
00273 }else if(g_status->status & g_status->MASK_ERROR){
00274 stat.summary(stat.ERROR, "Bridge has error");
00275 }else{
00276 stat.summary(stat.OK, "Connected and running");
00277 }
00278 stat.add("sdhx_ready", bool(g_status->status & g_status->MASK_FINGER_READY));
00279 stat.add("sdhx_rc", uint32_t(g_status->rc));
00280 stat.add("sdhx_motion_stopped", g_motion_stopped);
00281 stat.add("sdhx_control_stopped", g_control_stopped);
00282
00283 if(g_status->rc > 0){
00284 stat.mergeSummary(stat.ERROR, "SDHx has error");
00285 }
00286 }
00287
00288 void handleDeadline(const ros::TimerEvent &){
00289 boost::mutex::scoped_lock lock(g_mutex);
00290 if(g_as->isActive()){
00291 if(!checkAction_nolock(true)){
00292 g_command.position_cdeg = g_status->joints.position_cdeg;
00293 lock.unlock();
00294 halt();
00295 lock.lock();
00296 }
00297 }
00298 }
00299
00300 void goalCB() {
00301 control_msgs::FollowJointTrajectoryGoalConstPtr goal = g_as->acceptNewGoal();
00302
00303 g_deadline_timer.stop();
00304
00305
00306 control_msgs::FollowJointTrajectoryResult result;
00307 result.error_code = result.INVALID_GOAL;
00308 if(goal->trajectory.points.size()!=1 && (goal->trajectory.points.size()!=2 || !goal->trajectory.points[0].time_from_start.isZero() ) ){
00309 g_as->setAborted(result, "goal is not valid");
00310 return;
00311 }
00312
00313 cob_hand_bridge::JointValues new_command = g_default_command;
00314 size_t found = 0;
00315
00316 for(size_t i=0; i < g_js.name.size(); ++i){
00317 for(size_t j=0; j < goal->trajectory.joint_names.size(); ++j){
00318 if(g_js.name[i] == goal->trajectory.joint_names[j]){
00319 new_command.position_cdeg[i]= angles::to_degrees(goal->trajectory.points.back().positions[j])*100;
00320
00321 if(goal->trajectory.points.back().effort.size() > 0){
00322 if(goal->trajectory.points.back().effort.size() == new_command.current_100uA.size()){
00323 new_command.current_100uA[i] = goal->trajectory.points.back().effort[j] * 1000.0;
00324 }else{
00325 g_as->setAborted(result, "Number of effort values mismatch");
00326 return;
00327 }
00328 }
00329
00330 ++found;
00331 break;
00332 }
00333 }
00334 }
00335
00336 if(found != g_js.name.size()){
00337 g_as->setAborted(result, "Joint names mismatch");
00338 return;
00339 }
00340 std::vector<double> goal_tolerance(g_command.position_cdeg.size(), angles::to_degrees(g_stopped_velocity)*100);
00341
00342 for(size_t i = 0; i < goal->goal_tolerance.size(); ++i){
00343 bool missing = true;
00344 for(size_t j=0; j < g_js.name.size(); ++j){
00345 if(goal->goal_tolerance[i].name == g_js.name[j]){
00346 missing = false;
00347 if(goal->goal_tolerance[i].position > 0.0){
00348 goal_tolerance[j] = goal->goal_tolerance[i].position;
00349 }
00350 break;
00351 }
00352 }
00353 if(missing){
00354 g_as->setAborted(result, "Goal tolerance invalid");
00355 return;
00356 }
00357 }
00358
00359 ros::Time now = ros::Time::now();
00360 ros::Time trajectory_deadline = (goal->trajectory.header.stamp.isZero() ? now : goal->trajectory.header.stamp)
00361 + goal->trajectory.points.back().time_from_start + ros::Duration(goal->goal_time_tolerance);
00362 if(trajectory_deadline <= now){
00363 result.error_code = result.OLD_HEADER_TIMESTAMP;
00364 g_as->setAborted(result, "goal is not valid");
00365 return;
00366 }
00367
00368 boost::mutex::scoped_lock lock(g_mutex);
00369
00370 if(!isFingerReady_nolock()) {
00371 g_as->setAborted(result, "SDHx is not ready for commands");
00372 return;
00373 }
00374
00375 g_command = new_command;
00376
00377 g_goal_tolerance = goal_tolerance;
00378 g_motors_moved = false;
00379 g_command_timer.stop();
00380
00381 if(g_sdhx){
00382 int16_t position_cdeg[2];
00383 int16_t velocity_cdeg_s[2];
00384 int16_t current_100uA[2];
00385 position_cdeg[0]=g_command.position_cdeg[0]; position_cdeg[1]=g_command.position_cdeg[1];
00386 velocity_cdeg_s[0]=g_command.velocity_cdeg_s[0]; velocity_cdeg_s[1]=g_command.velocity_cdeg_s[1];
00387 current_100uA[0]=g_command.current_100uA[0]; current_100uA[1]=g_command.current_100uA[1];
00388 g_sdhx->move(position_cdeg, velocity_cdeg_s, current_100uA);
00389 }
00390
00391 g_deadline_timer.setPeriod(trajectory_deadline-ros::Time::now());
00392 g_deadline_timer.start();
00393 g_command_timer.start();
00394 }
00395
00396 void cancelCB() {
00397 boost::mutex::scoped_lock lock(g_mutex);
00398 g_command.position_cdeg = g_status->joints.position_cdeg;
00399 g_deadline_timer.stop();
00400 lock.unlock();
00401
00402 halt();
00403 g_as->setPreempted();
00404 }
00405
00406 void resendCommand(const ros::TimerEvent &e){
00407 boost::mutex::scoped_lock lock(g_mutex);
00408 if(isFingerReady_nolock()){
00409 if(g_control_stopped || !g_motion_stopped)
00410 if(g_sdhx){
00411 int16_t position_cdeg[2];
00412 int16_t velocity_cdeg_s[2];
00413 int16_t current_100uA[2];
00414 position_cdeg[0]=g_command.position_cdeg[0]; position_cdeg[1]=g_command.position_cdeg[1];
00415 velocity_cdeg_s[0]=g_command.velocity_cdeg_s[0]; velocity_cdeg_s[1]=g_command.velocity_cdeg_s[1];
00416 current_100uA[0]=g_command.current_100uA[0]; current_100uA[1]=g_command.current_100uA[1];
00417 g_sdhx->move(position_cdeg, velocity_cdeg_s, current_100uA);
00418 }
00419 } else {
00420 g_command_timer.stop();
00421 lock.unlock();
00422 halt();
00423 ROS_WARN("finger is not ready, stopped resend timer");
00424 }
00425 }
00426
00427 int main(int argc, char* argv[])
00428 {
00429 ros::init(argc, argv, "cob_hand_bridge_node");
00430
00431 ros::NodeHandle nh;
00432 ros::NodeHandle nh_d("driver");
00433 ros::NodeHandle nh_priv("~");
00434
00435 if(!nh_priv.getParam("sdhx/joint_names", g_js.name)){
00436 ROS_ERROR("Please provide joint names for SDHx");
00437 return 1;
00438 }
00439
00440 if(g_command.position_cdeg.size() != g_js.name.size()){
00441 ROS_ERROR_STREAM("Number of joints does not match " << g_command.position_cdeg.size());
00442 return 1;
00443 }
00444
00445
00446 nh_priv.param("sdhx/stopped_velocity",g_stopped_velocity, 0.05);
00447 if(g_stopped_velocity <= 0.0){
00448 ROS_ERROR_STREAM("stopped_velocity must be a positive number");
00449 return 1;
00450 }
00451
00452 double stopped_current= nh_priv.param("sdhx/stopped_current", 0.1);
00453 if(stopped_current <= 0.0){
00454 ROS_ERROR_STREAM("stopped_current must be a positive number");
00455 return 1;
00456 }
00457 g_stopped_current = stopped_current * 1000.0;
00458
00459 std::vector<double> default_currents;
00460 if(nh_priv.getParam("sdhx/default_currents", default_currents)){
00461 if(default_currents.size() != g_default_command.current_100uA.size()){
00462 ROS_ERROR_STREAM("Number of current values does not match number of joints");
00463 return 1;
00464 }
00465 for(size_t i=0; i< g_default_command.current_100uA.size(); ++i){
00466 g_default_command.current_100uA[i] = default_currents[i] * 1000.0;
00467 }
00468 }else{
00469 g_default_command.current_100uA[0] = 2120;
00470 g_default_command.current_100uA[1] = 1400;
00471 }
00472
00473 g_default_command.velocity_cdeg_s[0] = 1000;
00474 g_default_command.velocity_cdeg_s[1] = 1000;
00475
00476 g_js.position.resize(g_command.position_cdeg.size());
00477 g_js.velocity.resize(g_command.position_cdeg.size());
00478
00479 diagnostic_updater::Updater diag_updater;
00480 diag_updater.setHardwareID(nh_priv.param("hardware_id", std::string("none")));
00481 diag_updater.add("bridge", reportDiagnostics);
00482
00483 diagnostic_updater::TimeStampStatusParam param(
00484 nh_priv.param("status/min_duration", -1.0),
00485 nh_priv.param("status/max_duration", 0.1)
00486 );
00487 g_topic_status.reset ( new diagnostic_updater::TimeStampStatus(param) );
00488 diag_updater.add("connection", boost::bind(&diagnostic_updater::TimeStampStatus::run, g_topic_status, _1));
00489
00490 ros::Timer diag_timer = nh.createTimer(ros::Duration(diag_updater.getPeriod()/2.0),boost::bind(&diagnostic_updater::Updater::update, &diag_updater));
00491
00492 double resend_period = nh_priv.param("sdhx/resend_period", 0.1);
00493 if(resend_period > 0.0) g_command_timer = nh.createTimer(ros::Duration(resend_period), resendCommand, false, false);
00494
00495 g_deadline_timer = nh.createTimer(ros::Duration(1.0), handleDeadline, true, false);
00496
00497 g_js_pub = nh.advertise<sensor_msgs::JointState>("joint_states", 1);
00498
00499 ros::Timer status_timer = nh.createTimer(ros::Duration(1.0/20),statusCallback);
00500
00501 g_halt_srv = nh_d.advertiseService("halt", haltCallback);
00502 g_recover_srv = nh_d.advertiseService("recover", recoverCallback);
00503
00504 g_as.reset(new FollowJointTrajectoryActionServer(ros::NodeHandle("joint_trajectory_controller"), "follow_joint_trajectory",false));
00505 g_as->registerPreemptCallback(cancelCB);
00506 g_as->registerGoalCallback(goalCB);
00507
00508 ros::spin();
00509 return 0;
00510 }