$search
00001 /********************************************************************* 00002 * Software License Agreement (BSD License) 00003 * 00004 * Copyright (c) 2008, Willow Garage, Inc. 00005 * All rights reserved. 00006 * 00007 * Redistribution and use in source and binary forms, with or without 00008 * modification, are permitted provided that the following conditions 00009 * are met: 00010 * 00011 * * Redistributions of source code must retain the above copyright 00012 * notice, this list of conditions and the following disclaimer. 00013 * * Redistributions in binary form must reproduce the above 00014 * copyright notice, this list of conditions and the following 00015 * disclaimer in the documentation and/or other materials provided 00016 * with the distribution. 00017 * * Neither the name of the Willow Garage nor the names of its 00018 * contributors may be used to endorse or promote products derived 00019 * from this software without specific prior written permission. 00020 * 00021 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00022 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00023 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00024 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00025 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00026 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00027 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00028 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00029 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00030 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00031 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00032 * POSSIBILITY OF SUCH DAMAGE. 00033 *********************************************************************/ 00034 00035 #include <iomanip> 00036 00037 #include <math.h> 00038 #include <stddef.h> 00039 00040 #include <ethercat_hardware/wg06.h> 00041 00042 #include <dll/ethercat_dll.h> 00043 #include <al/ethercat_AL.h> 00044 #include <dll/ethercat_device_addressed_telegram.h> 00045 #include <dll/ethercat_frame.h> 00046 00047 #include <boost/static_assert.hpp> 00048 00049 PLUGINLIB_DECLARE_CLASS(ethercat_hardware, 6805006, WG06, EthercatDevice); 00050 00051 00052 WG06::WG06() : 00053 pressure_checksum_error_(false), 00054 accelerometer_samples_(0), 00055 accelerometer_missed_samples_(0), 00056 first_publish_(true), 00057 last_pressure_time_(0), 00058 pressure_publisher_(NULL), 00059 accel_publisher_(NULL), 00060 ft_sample_count_(0), 00061 ft_missed_samples_(0), 00062 diag_last_ft_sample_count_(0), 00063 raw_ft_publisher_(NULL) 00064 { 00065 00066 } 00067 00068 WG06::~WG06() 00069 { 00070 if (pressure_publisher_) delete pressure_publisher_; 00071 if (accel_publisher_) delete accel_publisher_; 00072 } 00073 00074 void WG06::construct(EtherCAT_SlaveHandler *sh, int &start_address) 00075 { 00076 WG0X::construct(sh, start_address); 00077 00078 has_accel_and_ft_ = false; 00079 00080 // As good a place as any for making sure that compiler actually packed these structures correctly 00081 BOOST_STATIC_ASSERT(sizeof(WG06StatusWithAccel) == WG06StatusWithAccel::SIZE); 00082 BOOST_STATIC_ASSERT(sizeof(FTDataSample) == FTDataSample::SIZE); 00083 BOOST_STATIC_ASSERT(sizeof(WG06Pressure) == WG06Pressure::SIZE); 00084 BOOST_STATIC_ASSERT(sizeof(WG06StatusWithAccelAndFT) == WG06StatusWithAccelAndFT::SIZE); 00085 00086 unsigned int base_status = sizeof(WG0XStatus); 00087 00088 command_size_ = sizeof(WG0XCommand); 00089 status_size_ = sizeof(WG0XStatus); 00090 if (fw_major_ == 0) 00091 { 00092 // Do nothing - status memory map is same size as WG05 00093 } 00094 if (fw_major_ == 1) 00095 { 00096 // Include Accelerometer data 00097 status_size_ = base_status = sizeof(WG06StatusWithAccel); 00098 } 00099 else if (fw_major_ == 2) 00100 { 00101 // Include Accelerometer and Force/Torque sensor data 00102 ROS_ERROR("WG06 FW major version %d not officially supported", fw_major_); 00103 status_size_ = base_status = sizeof(WG06StatusWithAccelAndFT); 00104 has_accel_and_ft_ = true; 00105 } 00106 else 00107 { 00108 ROS_ERROR("Unsupported WG06 FW major version %d", fw_major_); 00109 } 00110 status_size_ += sizeof(WG06Pressure); 00111 00112 00113 EtherCAT_FMMU_Config *fmmu = new EtherCAT_FMMU_Config(3); 00114 //ROS_DEBUG("device %d, command 0x%X = 0x10000+%d", (int)sh->get_ring_position(), start_address, start_address-0x10000); 00115 (*fmmu)[0] = EC_FMMU(start_address, // Logical start address 00116 command_size_,// Logical length 00117 0x00, // Logical StartBit 00118 0x07, // Logical EndBit 00119 COMMAND_PHY_ADDR, // Physical Start address 00120 0x00, // Physical StartBit 00121 false, // Read Enable 00122 true, // Write Enable 00123 true); // Enable 00124 00125 start_address += command_size_; 00126 00127 //ROS_DEBUG("device %d, status 0x%X = 0x10000+%d", (int)sh->get_ring_position(), start_address, start_address-0x10000); 00128 (*fmmu)[1] = EC_FMMU(start_address, // Logical start address 00129 base_status, // Logical length 00130 0x00, // Logical StartBit 00131 0x07, // Logical EndBit 00132 STATUS_PHY_ADDR, // Physical Start address 00133 0x00, // Physical StartBit 00134 true, // Read Enable 00135 false, // Write Enable 00136 true); // Enable 00137 00138 start_address += base_status; 00139 00140 (*fmmu)[2] = EC_FMMU(start_address, // Logical start address 00141 sizeof(WG06Pressure), // Logical length 00142 0x00, // Logical StartBit 00143 0x07, // Logical EndBit 00144 PRESSURE_PHY_ADDR, // Physical Start address 00145 0x00, // Physical StartBit 00146 true, // Read Enable 00147 false, // Write Enable 00148 true); // Enable 00149 00150 start_address += sizeof(WG06Pressure); 00151 00152 sh->set_fmmu_config(fmmu); 00153 00154 EtherCAT_PD_Config *pd = new EtherCAT_PD_Config(5); 00155 00156 // Sync managers 00157 (*pd)[0] = EC_SyncMan(COMMAND_PHY_ADDR, command_size_, EC_BUFFERED, EC_WRITTEN_FROM_MASTER); 00158 (*pd)[0].ChannelEnable = true; 00159 (*pd)[0].ALEventEnable = true; 00160 00161 (*pd)[1] = EC_SyncMan(STATUS_PHY_ADDR, base_status); 00162 (*pd)[1].ChannelEnable = true; 00163 00164 (*pd)[2] = EC_SyncMan(MBX_COMMAND_PHY_ADDR, MBX_COMMAND_SIZE, EC_QUEUED, EC_WRITTEN_FROM_MASTER); 00165 (*pd)[2].ChannelEnable = true; 00166 (*pd)[2].ALEventEnable = true; 00167 00168 (*pd)[3] = EC_SyncMan(MBX_STATUS_PHY_ADDR, MBX_STATUS_SIZE, EC_QUEUED); 00169 (*pd)[3].ChannelEnable = true; 00170 00171 (*pd)[4] = EC_SyncMan(PRESSURE_PHY_ADDR, sizeof(WG06Pressure)); 00172 (*pd)[4].ChannelEnable = true; 00173 00174 sh->set_pd_config(pd); 00175 } 00176 00177 00178 int WG06::initialize(pr2_hardware_interface::HardwareInterface *hw, bool allow_unprogrammed) 00179 { 00180 if ( ((fw_major_ == 1) && (fw_minor_ >= 1)) || (fw_major_ >= 2) ) 00181 { 00182 app_ram_status_ = APP_RAM_PRESENT; 00183 } 00184 00185 int retval = WG0X::initialize(hw, allow_unprogrammed); 00186 00187 if (!retval && use_ros_) 00188 { 00189 bool poor_measured_motor_voltage = false; 00190 double max_pwm_ratio = double(0x2700) / double(PWM_MAX); 00191 double board_resistance = 5.0; 00192 if (!WG0X::initializeMotorModel(hw, "WG006", max_pwm_ratio, board_resistance, poor_measured_motor_voltage)) 00193 { 00194 ROS_FATAL("Initializing motor trace failed"); 00195 sleep(1); // wait for ros to flush rosconsole output 00196 return -1; 00197 } 00198 00199 // Publish pressure sensor data as a ROS topic 00200 string topic = "pressure"; 00201 if (!actuator_.name_.empty()) 00202 topic = topic + "/" + string(actuator_.name_); 00203 pressure_publisher_ = new realtime_tools::RealtimePublisher<pr2_msgs::PressureState>(ros::NodeHandle(), topic, 1); 00204 00205 // Register accelerometer with pr2_hardware_interface::HardwareInterface 00206 for (int i = 0; i < 2; ++i) 00207 { 00208 pressure_sensors_[i].state_.data_.resize(22); 00209 pressure_sensors_[i].name_ = string(actuator_info_.name_) + string(i ? "r_finger_tip" : "l_finger_tip"); 00210 if (hw && !hw->addPressureSensor(&pressure_sensors_[i])) 00211 { 00212 ROS_FATAL("A pressure sensor of the name '%s' already exists. Device #%02d has a duplicate name", pressure_sensors_[i].name_.c_str(), sh_->get_ring_position()); 00213 return -1; 00214 } 00215 } 00216 00217 // Publish accelerometer data as a ROS topic, if firmware is recent enough 00218 if (fw_major_ >= 1) 00219 { 00220 topic = "accelerometer"; 00221 if (!actuator_.name_.empty()) 00222 topic = topic + "/" + string(actuator_.name_); 00223 accel_publisher_ = new realtime_tools::RealtimePublisher<pr2_msgs::AccelerometerState>(ros::NodeHandle(), topic, 1); 00224 00225 // Register accelerometer with pr2_hardware_interface::HardwareInterface 00226 { 00227 accelerometer_.name_ = actuator_info_.name_; 00228 if (hw && !hw->addAccelerometer(&accelerometer_)) 00229 { 00230 ROS_FATAL("An accelerometer of the name '%s' already exists. Device #%02d has a duplicate name", accelerometer_.name_.c_str(), sh_->get_ring_position()); 00231 return -1; 00232 } 00233 } 00234 } 00235 00236 // FW version 2 supports Force/Torque sensor. 00237 // Provide Force/Torque data to controllers as an AnalogIn vector 00238 if (fw_major_ >= 2) 00239 { 00240 // TODO: Memory interface to version 2 is not finalized. 00241 ROS_ERROR("WG06 FW version 2 not supported"); 00242 return -1; 00243 00244 ft_raw_analog_in_.name_ = actuator_.name_ + "_ft_raw"; 00245 if (hw && !hw->addAnalogIn(&ft_raw_analog_in_)) 00246 { 00247 ROS_FATAL("An analog in of the name '%s' already exists. Device #%02d has a duplicate name", 00248 ft_raw_analog_in_.name_.c_str(), sh_->get_ring_position()); 00249 return -1; 00250 } 00251 // FT provides 6 values : 3 Forces + 3 Torques 00252 ft_raw_analog_in_.state_.state_.resize(6); 00253 00254 // For now publish RAW F/T values for engineering purposes. In future this publisher may be disabled by default. 00255 topic = "raw_ft"; 00256 if (!actuator_.name_.empty()) 00257 topic = topic + "/" + string(actuator_.name_); 00258 raw_ft_publisher_ = new realtime_tools::RealtimePublisher<ethercat_hardware::RawFTData>(ros::NodeHandle(), topic, 1); 00259 if (raw_ft_publisher_ == NULL) 00260 { 00261 ROS_FATAL("Could not allocate raw_ft publisher"); 00262 return -1; 00263 } 00264 // Allocate space for raw f/t data values 00265 raw_ft_publisher_->msg_.samples.reserve(MAX_FT_SAMPLES); 00266 00267 if (!actuator_.name_.empty()) 00268 { 00269 ft_analog_in_.state_.state_.resize(6); 00270 ros::NodeHandle nh("~" + string(actuator_.name_)); 00271 FTParamsInternal ft_params; 00272 if ( ft_params.getRosParams(nh) ) 00273 { 00274 ft_params_ = ft_params; 00275 ft_params_.print(); 00276 // If we have ft_params, publish F/T values. 00277 topic = "ft"; 00278 if (!actuator_.name_.empty()) 00279 topic = topic + "/" + string(actuator_.name_); 00280 ft_publisher_ = new realtime_tools::RealtimePublisher<geometry_msgs::Wrench>(ros::NodeHandle(), topic, 1); 00281 if (ft_publisher_ == NULL) 00282 { 00283 ROS_FATAL("Could not allocate ft publisher"); 00284 return -1; 00285 } 00286 } 00287 } 00288 00289 } 00290 00291 00292 } 00293 00294 return retval; 00295 } 00296 00297 void WG06::packCommand(unsigned char *buffer, bool halt, bool reset) 00298 { 00299 if (reset) 00300 { 00301 pressure_checksum_error_ = false; 00302 } 00303 00304 WG0X::packCommand(buffer, halt, reset); 00305 00306 if (reset) 00307 { 00308 last_num_encoder_errors_ = actuator_.state_.num_encoder_errors_; 00309 } 00310 00311 WG0XCommand *c = (WG0XCommand *)buffer; 00312 00313 if (accelerometer_.command_.range_ > 2 || 00314 accelerometer_.command_.range_ < 0) 00315 accelerometer_.command_.range_ = 0; 00316 00317 if (accelerometer_.command_.bandwidth_ > 6 || 00318 accelerometer_.command_.bandwidth_ < 0) 00319 accelerometer_.command_.bandwidth_ = 0; 00320 00321 c->digital_out_ = (digital_out_.command_.data_ != 0) | 00322 ((accelerometer_.command_.bandwidth_ & 0x7) << 1) | 00323 ((accelerometer_.command_.range_ & 0x3) << 4); 00324 c->checksum_ = rotateRight8(computeChecksum(c, command_size_ - 1)); 00325 } 00326 00327 bool WG06::unpackState(unsigned char *this_buffer, unsigned char *prev_buffer) 00328 { 00329 bool rv = true; 00330 00331 int status_bytes = 00332 has_accel_and_ft_ ? sizeof(WG06StatusWithAccelAndFT) : // Has FT sensor and accelerometer 00333 accel_publisher_ ? sizeof(WG06StatusWithAccel) : 00334 sizeof(WG0XStatus); 00335 00336 WG06Pressure *pressure = (WG06Pressure *)(this_buffer + command_size_ + status_bytes); 00337 00338 unsigned char* this_status = this_buffer + command_size_; 00339 if (!verifyChecksum(this_status, status_bytes)) 00340 { 00341 status_checksum_error_ = true; 00342 rv = false; 00343 goto end; 00344 } 00345 00346 if (!unpackPressure(pressure)) 00347 { 00348 rv = false; 00349 //goto end; // all other tasks should not be effected by bad pressure sensor data 00350 } 00351 00352 if (accel_publisher_) 00353 { 00354 WG06StatusWithAccel *status = (WG06StatusWithAccel *)(this_buffer + command_size_); 00355 WG06StatusWithAccel *last_status = (WG06StatusWithAccel *)(prev_buffer + command_size_); 00356 if (!unpackAccel(status, last_status)) 00357 { 00358 rv=false; 00359 } 00360 } 00361 00362 if (has_accel_and_ft_) 00363 { 00364 WG06StatusWithAccelAndFT *status = (WG06StatusWithAccelAndFT *)(this_buffer + command_size_); 00365 WG06StatusWithAccelAndFT *last_status = (WG06StatusWithAccelAndFT *)(prev_buffer + command_size_); 00366 if (!unpackFT(status, last_status)) 00367 { 00368 rv = false; 00369 } 00370 } 00371 00372 00373 if (!WG0X::unpackState(this_buffer, prev_buffer)) 00374 { 00375 rv = false; 00376 } 00377 00378 end: 00379 return rv; 00380 } 00381 00382 00388 bool WG06::unpackPressure(WG06Pressure *p) 00389 { 00390 00391 if (!verifyChecksum(p, sizeof(*p))) 00392 { 00393 pressure_checksum_error_ = true; 00394 return false; 00395 } 00396 else { 00397 for (int i = 0; i < 22; ++i ) { 00398 pressure_sensors_[0].state_.data_[i] = 00399 ((p->l_finger_tip_[i] >> 8) & 0xff) | 00400 ((p->l_finger_tip_[i] << 8) & 0xff00); 00401 pressure_sensors_[1].state_.data_[i] = 00402 ((p->r_finger_tip_[i] >> 8) & 0xff) | 00403 ((p->r_finger_tip_[i] << 8) & 0xff00); 00404 } 00405 00406 if (p->timestamp_ != last_pressure_time_) 00407 { 00408 if (pressure_publisher_ && pressure_publisher_->trylock()) 00409 { 00410 pressure_publisher_->msg_.header.stamp = ros::Time::now(); 00411 pressure_publisher_->msg_.l_finger_tip.resize(22); 00412 pressure_publisher_->msg_.r_finger_tip.resize(22); 00413 for (int i = 0; i < 22; ++i ) { 00414 pressure_publisher_->msg_.l_finger_tip[i] = pressure_sensors_[0].state_.data_[i]; 00415 pressure_publisher_->msg_.r_finger_tip[i] = pressure_sensors_[1].state_.data_[i]; 00416 } 00417 pressure_publisher_->unlockAndPublish(); 00418 } 00419 } 00420 last_pressure_time_ = p->timestamp_; 00421 } 00422 00423 return true; 00424 } 00425 00426 00432 bool WG06::unpackAccel(WG06StatusWithAccel *status, WG06StatusWithAccel *last_status) 00433 { 00434 int count = uint8_t(status->accel_count_ - last_status->accel_count_); 00435 accelerometer_samples_ += count; 00436 // Only most recent 4 samples of accelerometer data is available in status data 00437 // 4 samples will be enough with realtime loop running at 1kHz and accelerometer running at 3kHz 00438 // If count is greater than 4, then some data has been "missed". 00439 accelerometer_missed_samples_ += (count > 4) ? (count-4) : 0; 00440 count = min(4, count); 00441 accelerometer_.state_.samples_.resize(count); 00442 accelerometer_.state_.frame_id_ = string(actuator_info_.name_) + "_accelerometer_link"; 00443 for (int i = 0; i < count; ++i) 00444 { 00445 int32_t acc = status->accel_[count - i - 1]; 00446 int range = (acc >> 30) & 3; 00447 float d = 1 << (8 - range); 00448 accelerometer_.state_.samples_[i].x = 9.81 * ((((acc >> 0) & 0x3ff) << 22) >> 22) / d; 00449 accelerometer_.state_.samples_[i].y = 9.81 * ((((acc >> 10) & 0x3ff) << 22) >> 22) / d; 00450 accelerometer_.state_.samples_[i].z = 9.81 * ((((acc >> 20) & 0x3ff) << 22) >> 22) / d; 00451 } 00452 00453 if (accel_publisher_->trylock()) 00454 { 00455 accel_publisher_->msg_.header.frame_id = accelerometer_.state_.frame_id_; 00456 accel_publisher_->msg_.header.stamp = ros::Time::now(); 00457 accel_publisher_->msg_.samples.resize(count); 00458 for (int i = 0; i < count; ++i) 00459 { 00460 accel_publisher_->msg_.samples[i].x = accelerometer_.state_.samples_[i].x; 00461 accel_publisher_->msg_.samples[i].y = accelerometer_.state_.samples_[i].y; 00462 accel_publisher_->msg_.samples[i].z = accelerometer_.state_.samples_[i].z; 00463 } 00464 accel_publisher_->unlockAndPublish(); 00465 } 00466 return true; 00467 } 00468 00469 00475 bool WG06::unpackFT(WG06StatusWithAccelAndFT *status, WG06StatusWithAccelAndFT *last_status) 00476 { 00477 ft_raw_analog_in_.state_.state_.resize(NUM_FT_CHANNELS); 00478 ft_analog_in_.state_.state_.resize(6); 00479 00480 // Fill in raw analog output with most recent data sample 00481 { 00482 const FTDataSample &sample(status->ft_samples_[0]); 00483 for (unsigned i=0; i<NUM_FT_CHANNELS; ++i) 00484 { 00485 ft_raw_analog_in_.state_.state_[i] = double(sample.data_[i]); 00486 } 00487 } 00488 00489 // perform offset and gains multiplication on raw data 00490 // and then multiple by calibration matrix to get force and torque values. 00491 // The calibration matrix is based on "raw" deltaR/R values from strain gauges 00492 // 00493 // Force/Torque = Coeff * ADCVoltage 00494 // 00495 // Coeff = RawCoeff / ( ExcitationVoltage * AmplifierGain ) 00496 // = RawCoeff / ( 2.5V * AmplifierGain ) 00497 // 00498 // ADCVoltage = Vref / 2^16 00499 // = 2.5 / 2^16 00500 // 00501 // Force/Torque = RawCalibrationCoeff / ( ExcitationVoltage * AmplifierGain ) * (ADCValues * 2.5V/2^16) 00502 // = (RawCalibration * ADCValues) / (AmplifierGain * 2^16) 00503 { 00504 double in[6]; 00505 for (unsigned i=0; i<6; ++i) 00506 { 00507 // Take average of last 3 samples to get better noise performance 00508 static const unsigned AVG_WINDOW_LEN = 3; 00509 double sum = 0.0; 00510 for (unsigned j=0; j<AVG_WINDOW_LEN; ++j) 00511 { 00512 sum += double(status->ft_samples_[j].data_[i]); 00513 } 00514 double avg = sum / double(AVG_WINDOW_LEN); 00515 in[i] = (avg - ft_params_.offset(i)) / ( ft_params_.gain(i) * double(1<<16) ); 00516 } 00517 for (unsigned i=0; i<6; ++i) 00518 { 00519 double sum=0.0; 00520 for (unsigned j=0; j<6; ++j) 00521 { 00522 sum += ft_params_.calibration_coeff(i,j) * in[j]; 00523 } 00524 ft_analog_in_.state_.state_[i] = sum; 00525 } 00526 } 00527 00528 00529 unsigned new_samples = (unsigned(status->ft_sample_count_) - unsigned(last_status->ft_sample_count_)) & 0xFF; 00530 ft_sample_count_ += new_samples; 00531 int missed_samples = std::max(int(0), int(new_samples) - 4); 00532 ft_missed_samples_ += missed_samples; 00533 00534 // Put all new samples in buffer and publish it 00535 if ((raw_ft_publisher_ != NULL) && (raw_ft_publisher_->trylock())) 00536 { 00537 unsigned usable_samples = min(new_samples, MAX_FT_SAMPLES); 00538 raw_ft_publisher_->msg_.samples.resize(usable_samples); 00539 raw_ft_publisher_->msg_.sample_count = ft_sample_count_; 00540 raw_ft_publisher_->msg_.missed_samples = ft_missed_samples_; 00541 for (unsigned sample_num=0; sample_num<usable_samples; ++sample_num) 00542 { 00543 // put data into message so oldest data is first element 00544 const FTDataSample &sample(status->ft_samples_[sample_num]); 00545 ethercat_hardware::RawFTDataSample &msg_sample(raw_ft_publisher_->msg_.samples[usable_samples-sample_num-1]); 00546 msg_sample.sample_count = ft_sample_count_ - sample_num; 00547 msg_sample.data.resize(NUM_FT_CHANNELS); 00548 for (unsigned ch_num=0; ch_num<NUM_FT_CHANNELS; ++ch_num) 00549 { 00550 msg_sample.data[ch_num] = sample.data_[ch_num]; 00551 } 00552 msg_sample.vhalf = sample.vhalf_; 00553 } 00554 raw_ft_publisher_->msg_.sample_count = ft_sample_count_; 00555 raw_ft_publisher_->unlockAndPublish(); 00556 } 00557 00558 if ( (ft_publisher_ != NULL) && (ft_publisher_->trylock()) ) 00559 { 00560 geometry_msgs::Wrench &msg(ft_publisher_->msg_); 00561 msg.force.x = ft_analog_in_.state_.state_[0]; 00562 msg.force.y = ft_analog_in_.state_.state_[1]; 00563 msg.force.z = ft_analog_in_.state_.state_[2]; 00564 msg.torque.x = ft_analog_in_.state_.state_[3]; 00565 msg.torque.y = ft_analog_in_.state_.state_[4]; 00566 msg.torque.z = ft_analog_in_.state_.state_[5]; 00567 ft_publisher_->unlockAndPublish(); 00568 } 00569 00570 return true; 00571 } 00572 00573 00574 void WG06::multiDiagnostics(vector<diagnostic_msgs::DiagnosticStatus> &vec, unsigned char *buffer) 00575 { 00576 diagnostic_updater::DiagnosticStatusWrapper &d(diagnostic_status_); 00577 diagnosticsWG06(d, buffer); 00578 vec.push_back(d); 00579 diagnosticsAccel(d, buffer); 00580 vec.push_back(d); 00581 00582 if (has_accel_and_ft_) 00583 { 00584 WG06StatusWithAccelAndFT *status = (WG06StatusWithAccelAndFT *)(buffer + command_size_); 00585 // perform f/t sample 00586 diagnosticsFT(d, status); 00587 vec.push_back(d); 00588 } 00589 } 00590 00591 00592 void WG06::diagnosticsAccel(diagnostic_updater::DiagnosticStatusWrapper &d, unsigned char *buffer) 00593 { 00594 stringstream str; 00595 str << "Accelerometer (" << actuator_info_.name_ << ")"; 00596 d.name = str.str(); 00597 char serial[32]; 00598 snprintf(serial, sizeof(serial), "%d-%05d-%05d", config_info_.product_id_ / 100000 , config_info_.product_id_ % 100000, config_info_.device_serial_number_); 00599 d.hardware_id = serial; 00600 00601 d.summary(d.OK, "OK"); 00602 d.clear(); 00603 00604 pr2_hardware_interface::AccelerometerCommand acmd(accelerometer_.command_); 00605 00606 const char * range_str = 00607 (acmd.range_ == 0) ? "+/-2G" : 00608 (acmd.range_ == 1) ? "+/-4G" : 00609 (acmd.range_ == 2) ? "+/-8G" : 00610 "INVALID"; 00611 00612 const char * bandwidth_str = 00613 (acmd.bandwidth_ == 6) ? "1500Hz" : 00614 (acmd.bandwidth_ == 5) ? "750Hz" : 00615 (acmd.bandwidth_ == 4) ? "375Hz" : 00616 (acmd.bandwidth_ == 3) ? "190Hz" : 00617 (acmd.bandwidth_ == 2) ? "100Hz" : 00618 (acmd.bandwidth_ == 1) ? "50Hz" : 00619 (acmd.bandwidth_ == 0) ? "25Hz" : 00620 "INVALID"; 00621 00622 // Board revB=1 and revA=0 does not have accelerometer 00623 bool has_accelerometer = (board_major_ >= 2); 00624 double sample_frequency = 0.0; 00625 ros::Time current_time(ros::Time::now()); 00626 if (!first_publish_) 00627 { 00628 sample_frequency = double(accelerometer_samples_) / (current_time - last_publish_time_).toSec(); 00629 { 00630 if (((sample_frequency < 2000) || (sample_frequency > 4000)) && has_accelerometer) 00631 { 00632 d.mergeSummary(d.WARN, "Bad accelerometer sampling frequency"); 00633 } 00634 } 00635 } 00636 accelerometer_samples_ = 0; 00637 last_publish_time_ = current_time; 00638 first_publish_ = false; 00639 00640 d.addf("Accelerometer", "%s", accelerometer_.state_.samples_.size() > 0 ? "Ok" : "Not Present"); 00641 d.addf("Accelerometer range", "%s (%d)", range_str, acmd.range_); 00642 d.addf("Accelerometer bandwidth", "%s (%d)", bandwidth_str, acmd.bandwidth_); 00643 d.addf("Accelerometer sample frequency", "%f", sample_frequency); 00644 d.addf("Accelerometer missed samples", "%d", accelerometer_missed_samples_); 00645 } 00646 00647 00648 void WG06::diagnosticsWG06(diagnostic_updater::DiagnosticStatusWrapper &d, unsigned char *buffer) 00649 { 00650 WG0X::diagnostics(d, buffer); 00651 00652 // TODO, maybe do more error checking on pressure sensor, and put diagnostics in its own topic 00653 if (pressure_checksum_error_) 00654 { 00655 d.mergeSummary(d.ERROR, "Checksum error on pressure data"); 00656 } 00657 } 00658 00659 00660 void WG06::diagnosticsFT(diagnostic_updater::DiagnosticStatusWrapper &d, WG06StatusWithAccelAndFT *status) 00661 { 00662 stringstream str; 00663 str << "Force/Torque sensor (" << actuator_info_.name_ << ")"; 00664 d.name = str.str(); 00665 char serial[32]; 00666 snprintf(serial, sizeof(serial), "%d-%05d-%05d", config_info_.product_id_ / 100000 , config_info_.product_id_ % 100000, config_info_.device_serial_number_); 00667 d.hardware_id = serial; 00668 00669 d.summary(d.OK, "OK"); 00670 d.clear(); 00671 00672 d.addf("F/T sample count", "%llu", ft_sample_count_); 00673 d.addf("F/T missed samples", "%llu", ft_missed_samples_); 00674 std::stringstream ss; 00675 const FTDataSample &sample(status->ft_samples_[0]); //use newest data sample 00676 for (unsigned i=0;i<NUM_FT_CHANNELS;++i) 00677 { 00678 ss.str(""); ss << "FT In"<< (i+1); 00679 d.addf(ss.str(), "%d", int(sample.data_[i])); 00680 } 00681 d.addf("FT Vhalf", "%d", int(sample.vhalf_)); 00682 00683 const std::vector<double> &ft_data( ft_analog_in_.state_.state_ ); 00684 if (ft_data.size() == 6) 00685 { 00686 d.addf("Force X", "%f", ft_data[0]); 00687 d.addf("Force Y", "%f", ft_data[1]); 00688 d.addf("Force Z", "%f", ft_data[2]); 00689 d.addf("Torque X", "%f", ft_data[3]); 00690 d.addf("Torque Y", "%f", ft_data[4]); 00691 d.addf("Torque Z", "%f", ft_data[5]); 00692 } 00693 } 00694 00695 00696 00697 FTParamsInternal::FTParamsInternal() 00698 { 00699 // Initial offset = 0.0 00700 // Gains = 1.0 00701 // Calibration coeff = identity matrix 00702 for (int i=0; i<6; ++i) 00703 { 00704 offset(i) = 0.0; 00705 gain(i) = 1.0; 00706 for (int j=0; j<6; ++j) 00707 { 00708 calibration_coeff(i,j) = (i==j) ? 1.0 : 0.0; 00709 } 00710 } 00711 } 00712 00713 00714 void FTParamsInternal::print() const 00715 { 00716 for (int i=0; i<6; ++i) 00717 { 00718 ROS_INFO("offset[%d] = %f", i, offset(i)); 00719 } 00720 for (int i=0; i<6; ++i) 00721 { 00722 ROS_INFO("gain[%d] = %f", i, gain(i)); 00723 } 00724 for (int i=0; i<6; ++i) 00725 { 00726 ROS_INFO("coeff[%d] = [%f,%f,%f,%f,%f,%f]", i, 00727 calibration_coeff(i,0), calibration_coeff(i,1), 00728 calibration_coeff(i,2), calibration_coeff(i,3), 00729 calibration_coeff(i,4), calibration_coeff(i,5) 00730 ); 00731 } 00732 } 00733 00734 00735 bool FTParamsInternal::getDoubleArray(XmlRpc::XmlRpcValue params, const char* name, double *results, unsigned len) 00736 { 00737 if(!params.hasMember(name)) 00738 { 00739 ROS_ERROR("Expected ft_param to have '%s' element", name); 00740 return false; 00741 } 00742 00743 XmlRpc::XmlRpcValue values = params[name]; 00744 if (values.getType() != XmlRpc::XmlRpcValue::TypeArray) 00745 { 00746 ROS_ERROR("Expected FT param '%s' to be list type", name); 00747 return false; 00748 } 00749 if (values.size() != int(len)) 00750 { 00751 ROS_ERROR("Expected FT param '%s' to have %d elements", name, len); 00752 return false; 00753 } 00754 for (unsigned i=0; i<len; ++i) 00755 { 00756 if (values[i].getType() != XmlRpc::XmlRpcValue::TypeDouble) 00757 { 00758 ROS_ERROR("Expected FT param %s[%d] to be floating point type", name, i); 00759 return false; 00760 } else { 00761 results[i] = static_cast<double>(values[i]); 00762 } 00763 } 00764 00765 return true; 00766 } 00767 00768 00778 bool FTParamsInternal::getRosParams(ros::NodeHandle nh) 00779 { 00780 if (!nh.hasParam("ft_params")) 00781 { 00782 ROS_WARN("'ft_params' not available for force/torque sensor in namespace '%s'", 00783 nh.getNamespace().c_str()); 00784 return false; 00785 } 00786 00787 XmlRpc::XmlRpcValue params; 00788 nh.getParam("ft_params", params); 00789 if (params.getType() != XmlRpc::XmlRpcValue::TypeStruct) 00790 { 00791 ROS_ERROR("expected ft_params to be struct type"); 00792 return false; 00793 } 00794 00795 if (!getDoubleArray(params, "offsets", offsets_, 6)) 00796 { 00797 return false; 00798 } 00799 00800 if (!getDoubleArray(params, "gains", gains_, 6)) 00801 { 00802 return false; 00803 } 00804 00805 XmlRpc::XmlRpcValue coeff_matrix = params["calibration_coeff"]; 00806 if (coeff_matrix.getType() != XmlRpc::XmlRpcValue::TypeArray) 00807 { 00808 ROS_ERROR("Expected FT param 'calibration_coeff' to be list type"); 00809 return false; 00810 } 00811 if (coeff_matrix.size() != 6) 00812 { 00813 ROS_ERROR("Expected FT param 'calibration_coeff' to have 6 elements"); 00814 return false; 00815 } 00816 00817 for (int i=0; i<6; ++i) 00818 { 00819 XmlRpc::XmlRpcValue coeff_row = coeff_matrix[i]; 00820 if (coeff_row.getType() != XmlRpc::XmlRpcValue::TypeArray) 00821 { 00822 ROS_ERROR("Expected FT param calibration_coeff[%d] to be list type", i); 00823 return false; 00824 } 00825 if (coeff_row.size() != 6) 00826 { 00827 ROS_ERROR("Expected FT param calibration_coeff[%d] to have 6 elements", i); 00828 return false; 00829 } 00830 00831 for (int j=0; j<6; ++j) 00832 { 00833 if (coeff_row[j].getType() != XmlRpc::XmlRpcValue::TypeDouble) 00834 { 00835 ROS_ERROR("Expected FT param calibration_coeff[%d,%d] to be floating point type", i,j); 00836 return false; 00837 } else { 00838 calibration_coeff(i,j) = static_cast<double>(coeff_row[j]); 00839 } 00840 } 00841 } 00842 00843 return true; 00844 } 00845 00846 00847 const unsigned WG06::NUM_FT_CHANNELS; 00848 const unsigned WG06::MAX_FT_SAMPLES;