int main(int argc, char** argv) { ros::init(argc, argv, "elektron_base_node"); ros::NodeHandle n; ros::NodeHandle nh("~"); bool publish_odom_tf; bool dump; double ls, rs; ros::Publisher odom_pub = n.advertise<nav_msgs::Odometry> ("odom", 1); tf::TransformBroadcaster odom_broadcaster; ros::Subscriber twist_sub = n.subscribe("cmd_vel", 1, &twistCallback); ros::Rate loop_rate(200); std::string dev; if (!nh.getParam("device", dev)) { dev = "/dev/ttyACM0"; } if (!nh.getParam("publish_odom_tf", publish_odom_tf)) { publish_odom_tf = false; } if (!nh.getParam("dump", dump)) { dump = false; } if (!nh.getParam("lin_scale", ls)) { ls = 1.0; } if (!nh.getParam("rot_scale", rs)) { rs = 1.0; } nav_msgs::Odometry odom; odom.header.frame_id = "odom"; odom.child_frame_id = "base_link"; geometry_msgs::TransformStamped odom_trans; odom_trans.header.frame_id = "odom"; odom_trans.child_frame_id = "base_link"; // NFv2 Configuration NFv2_Config(&NFComBuf, NF_TerminalAddress); CommPort = new SerialComm(dev); if (! (CommPort->isConnected()) ) { ROS_INFO("connection failed"); ROS_ERROR("Connection to device %s failed", dev.c_str()); return 0; } else { ROS_INFO("connection ok"); pthread_t listenerThread; if(pthread_create(&listenerThread, NULL, &listener, NULL)) ROS_ERROR("Listener thread error"); else ROS_INFO("Listener thread started"); ros::Timer timer1 = n.createTimer(ros::Duration(0.1), readDeviceVitalsTimerCallback); while (ros::ok()) { double x, y, th, xvel, thvel; ros::Time current_time = ros::Time::now(); // If communication with Elektron requested if(commandCnt > 0) { txCnt = NF_MakeCommandFrame(&NFComBuf, txBuf, (const uint8_t*)commandArray, commandCnt, NF_RobotAddress); // Clear communication request commandCnt = 0; // Send command frame to Elektron CommPort->write(txBuf, txCnt); timeoutCount = 0; }/*else { if(timeoutCount < TIMEOUT) { txCnt = NF_MakeCommandFrame(&NFComBuf, txBuf, (const uint8_t*)commandArray, commandCnt, NF_RobotAddress); CommPort->write(txBuf, txCnt); commandCnt = 0; timeoutCount++; } else { NFComBuf.SetDrivesSpeed.data[0] = 0; NFComBuf.SetDrivesSpeed.data[1] = 0; commandArray[commandCnt++] = NF_COMMAND_SetDrivesSpeed; NFComBuf.SetDrivesMode.data[0] = NF_DrivesMode_SPEED; NFComBuf.SetDrivesMode.data[1] = NF_DrivesMode_SPEED; commandArray[commandCnt++] = NF_COMMAND_SetDrivesMode; txCnt = NF_MakeCommandFrame(&NFComBuf, txBuf, (const uint8_t*)commandArray, commandCnt, NF_RobotAddress); CommPort->write(txBuf, txCnt); commandCnt = 0; } } */ x = 0; y = 0; th = 0; xvel = 0; thvel = 0; //since all odometry is 6DOF we'll need a quaternion created from yaw geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th); if (publish_odom_tf) { //first, we'll publish the transform over tf odom_trans.header.stamp = current_time; odom_trans.transform.translation.x = x; odom_trans.transform.translation.y = y; odom_trans.transform.translation.z = 0.0; odom_trans.transform.rotation = odom_quat; //send the transform odom_broadcaster.sendTransform(odom_trans); } //next, we'll publish the odometry message over ROS odom.header.stamp = current_time; //set the position odom.pose.pose.position.x = x; odom.pose.pose.position.y = y; odom.pose.pose.position.z = 0.0; odom.pose.pose.orientation = odom_quat; odom.pose.covariance[0] = 0.00001; odom.pose.covariance[7] = 0.00001; odom.pose.covariance[14] = 10.0; odom.pose.covariance[21] = 1.0; odom.pose.covariance[28] = 1.0; odom.pose.covariance[35] = thvel + 0.001; //set the velocity odom.child_frame_id = "base_link"; odom.twist.twist.linear.x = xvel; odom.twist.twist.linear.y = 0.0; odom.twist.twist.angular.z = thvel; //publish the message //odom_pub.publish(odom); ros::spinOnce(); loop_rate.sleep(); } } return 0; }
//do communication cycle uint64_t HI_moxa::HI_read_write_hardware(void) { static int64_t receive_attempts = 0; // UNUSED: static int64_t receive_timeouts = 0; static int error_msg_power_stage = 0; static int error_msg_hardware_panic = 0; static int error_msg_overcurrent = 0; static int last_synchro_state[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; static int comm_timeouts[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; static int synchro_switch_filter[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; const int synchro_switch_filter_th = 2; bool robot_synchronized = true; bool power_fault; bool all_hardware_read = true; uint64_t ret = 0; uint8_t drive_number; static int status_disp_cnt = 0; static std::stringstream temp_message; // If Hardware Panic, send PARAM_DRIVER_MODE_ERROR to motor drivers if (hardware_panic) { for (drive_number = 0; drive_number <= last_drive_number; drive_number++) { // only set error parameter, do not wait for answer servo_data[drive_number].commandCnt = 0; NFComBuf.SetDrivesMode.data[drive_number] = NF_DrivesMode_ERROR; servo_data[drive_number].commandArray[servo_data[drive_number].commandCnt++] = NF_COMMAND_SetDrivesMode; txCnt = NF_MakeCommandFrame(&NFComBuf, txBuf + 5, (const uint8_t*) servo_data[drive_number].commandArray, servo_data[drive_number].commandCnt, drives_addresses[drive_number]); // Clear communication request servo_data[drive_number].commandCnt = 0; // Send command frame SerialPort[drive_number]->write(txBuf, txCnt + 5); } if (error_msg_hardware_panic == 0) { std::cout << "[error] hardware panic" << std::endl; error_msg_hardware_panic++; } std::cout << temp_message.str() << std::endl; struct timespec delay; delay.tv_nsec = 2000000; delay.tv_sec = 0; nanosleep(&delay, NULL); return ret; } else { // Make command frames and send them to drives for (drive_number = 0; drive_number <= last_drive_number; drive_number++) { // Set communication requests servo_data[drive_number].commandArray[servo_data[drive_number].commandCnt++] = NF_COMMAND_ReadDrivesPosition; servo_data[drive_number].commandArray[servo_data[drive_number].commandCnt++] = NF_COMMAND_ReadDrivesCurrent; servo_data[drive_number].commandArray[servo_data[drive_number].commandCnt++] = NF_COMMAND_ReadDrivesStatus; // Make command frame servo_data[drive_number].txCnt = NF_MakeCommandFrame(&NFComBuf, servo_data[drive_number].txBuf + howMuchItSucks, (const uint8_t*) servo_data[drive_number].commandArray, servo_data[drive_number].commandCnt, drives_addresses[drive_number]); // Clear communication requests servo_data[drive_number].commandCnt = 0; } for (drive_number = 0; drive_number <= last_drive_number; drive_number++) { // Send command frame SerialPort[drive_number]->write(servo_data[drive_number].txBuf, servo_data[drive_number].txCnt + howMuchItSucks); } } receive_attempts++; struct timespec delay; delay.tv_nsec = 1500000; delay.tv_sec = 0; nanosleep(&delay, NULL); // Tu kiedys byl SELECT all_hardware_read = true; // Read data from all drives for (drive_number = 0; drive_number <= last_drive_number; drive_number++) { rxCnt = 0; while (1) { if (SerialPort[drive_number]->read(&(rxBuf[rxCnt]), 1) > 0 && (rxCnt < 255)) { if (NF_Interpreter(&NFComBuf, rxBuf, &rxCnt, rxCommandArray, &rxCommandCnt) > 0) { // TODO: Check Status break; } } else { comm_timeouts[drive_number]++; if (all_hardware_read) { all_hardware_read = false; std::cout << "[error] timeout in " << (int) receive_attempts << " communication cycle on drives"; } std::cout << " " << (int) drive_number << "(" << port_names[drive_number].c_str() << ")"; break; } } } // If Hardware Panic, after receiving data, wait till the end of comm cycle and return. if (hardware_panic) { struct timespec delay; delay.tv_nsec = 2000000; delay.tv_sec = 0; nanosleep(&delay, NULL); return ret; } if (all_hardware_read) { for (drive_number = 0; drive_number <= last_drive_number; drive_number++) comm_timeouts[drive_number] = 0; } else { std::cout << std::endl; } // Inicjalizacja flag robot_synchronized = true; power_fault = false; for (drive_number = 0; drive_number <= last_drive_number; drive_number++) { // Wypelnienie pol odebranymi danymi // NFComBuf.ReadDrivesPosition.data[] contains last received value servo_data[drive_number].previous_absolute_position = servo_data[drive_number].current_absolute_position; servo_data[drive_number].current_absolute_position = NFComBuf.ReadDrivesPosition.data[drive_number]; // Ustawienie flagi wlaczonej mocy if ((NFComBuf.ReadDrivesStatus.data[drive_number] & NF_DrivesStatus_PowerStageFault)!= 0) { power_fault = true; } // Ustawienie flagi synchronizacji if ((NFComBuf.ReadDrivesStatus.data[drive_number] & NF_DrivesStatus_Synchronized)== 0) { robot_synchronized = false; } // Sprawdzenie, czy wlasnie nastapila synchronizacja kolejnej osi if (last_synchro_state[drive_number] == 0 && (NFComBuf.ReadDrivesStatus.data[drive_number] & NF_DrivesStatus_Synchronized)!= 0) { servo_data[drive_number].first_hardware_reads = FIRST_HARDWARE_READS_WITH_ZERO_INCREMENT; last_synchro_state[drive_number] = 1; } // W pierwszych odczytach danych z napedu przyrost pozycji musi byc 0. if ((servo_data[drive_number].first_hardware_reads > 0)) { servo_data[drive_number].previous_absolute_position = servo_data[drive_number].current_absolute_position; servo_data[drive_number].first_hardware_reads--; } // Sprawdzenie przyrostu pozycji enkodera servo_data[drive_number].current_position_inc = (double) (servo_data[drive_number].current_absolute_position - servo_data[drive_number].previous_absolute_position); if ((robot_synchronized) && ((int) ridiculous_increment[drive_number] != 0)) { /*if (drive_number == 0) { std::cout << "inc: " << servo_data[drive_number].current_position_inc << " cur: " << servo_data[drive_number].current_absolute_position << " prev: " << servo_data[drive_number].previous_absolute_position << " time: " << boost::get_system_time() << std::endl; }*/ if ((servo_data[drive_number].current_position_inc > ridiculous_increment[drive_number]) || (servo_data[drive_number].current_position_inc < -ridiculous_increment[drive_number])) { hardware_panic = true; temp_message << "[error] ridiculous increment on drive " << (int) drive_number << ", " << port_names[drive_number].c_str() << ", c.cycle " << (int) receive_attempts << ": read = " << servo_data[drive_number].current_position_inc << ", max = " << ridiculous_increment[drive_number] << std::endl; //master.msg->message(lib::FATAL_ERROR, temp_message.str()); std::cerr << temp_message.str() << std::cerr.flush(); } } // Sprawdzenie ograniczenia nadpradowego if ((NFComBuf.ReadDrivesStatus.data[drive_number] & NF_DrivesStatus_Overcurrent)!= 0) { if (error_msg_overcurrent == 0) { //master.msg->message(lib::NON_FATAL_ERROR, "Overcurrent"); std::cout << "[error] overcurrent on drive " << (int) drive_number << ", " << port_names[drive_number].c_str() << ": read = " << NFComBuf.ReadDrivesCurrent.data[drive_number] << "mA" << std::endl; error_msg_overcurrent++; } } // Wykrywanie sekwencji timeoutow komunikacji if (comm_timeouts[drive_number] >= MAX_COMM_TIMEOUTS) { hardware_panic = true; temp_message << "[error] multiple communication timeouts on drive " << (int) drive_number << "(" << port_names[drive_number].c_str() << "): limit = " << MAX_COMM_TIMEOUTS << std::endl; //master.msg->message(lib::FATAL_ERROR, temp_message.str()); std::cerr << temp_message.str() << std::cerr.flush(); } } //master.controller_state_edp_buf.is_synchronised = robot_synchronized; //master.controller_state_edp_buf.robot_in_fault_state = power_fault; if (power_fault) { hardware_panic = true; std::cout << "[error] power_fault" << std::endl; if (error_msg_power_stage == 0) { temp_message << "[error] power_fault" << std::endl; std::cerr << temp_message.str() << std::cerr.flush(); //master.msg->message(lib::NON_FATAL_ERROR, "Wylaczono moc - robot zablokowany"); error_msg_power_stage++; } } else { error_msg_power_stage = 0; } for (drive_number = 0; drive_number <= last_drive_number; drive_number++) { if ((NFComBuf.ReadDrivesStatus.data[drive_number] & NF_DrivesStatus_LimitSwitchUp)!= 0)ret |= (uint64_t) (UPPER_LIMIT_SWITCH << (5 * (drive_number))); // Zadzialal wylacznik "gorny" krancowy if ((NFComBuf.ReadDrivesStatus.data[drive_number] & NF_DrivesStatus_LimitSwitchDown)!= 0)ret |= (uint64_t) (LOWER_LIMIT_SWITCH << (5 * (drive_number))); // Zadzialal wylacznik "dolny" krancowy if ((NFComBuf.ReadDrivesStatus.data[drive_number] & NF_DrivesStatus_EncoderIndexSignal)!= 0)ret |= (uint64_t) (SYNCHRO_ZERO << (5 * (drive_number))); // Impuls zera rezolwera if ((NFComBuf.ReadDrivesStatus.data[drive_number] & NF_DrivesStatus_Overcurrent)!= 0)ret |= (uint64_t) (OVER_CURRENT << (5 * (drive_number))); // Przekroczenie dopuszczalnego pradu if ((NFComBuf.ReadDrivesStatus.data[drive_number] & NF_DrivesStatus_SynchroSwitch)!= 0) { if (synchro_switch_filter[drive_number] == synchro_switch_filter_th) ret |= (uint64_t) (SYNCHRO_SWITCH_ON << (5 * (drive_number))); // Zadzialal wylacznik synchronizacji else synchro_switch_filter[drive_number]++; } else { synchro_switch_filter[drive_number] = 0; } } if (status_disp_cnt++ == STATUS_DISP_T) { status_disp_cnt = 0; } delay.tv_nsec = 2000000; delay.tv_sec = 0; nanosleep(&delay, NULL); return ret; }
//read motor current from communication buffer int HI_moxa::set_parameter_now(int drive_number, const int parameter, ...) { struct timespec delay; uint8_t setParamCommandCnt = 0; uint8_t setParamCommandArray[10]; va_list newValue; va_start(newValue, parameter); switch (parameter) { case NF_COMMAND_SetDrivesMisc: NFComBuf.SetDrivesMisc.data[drive_number] = (uint32_t) va_arg(newValue, int); setParamCommandArray[setParamCommandCnt++] = NF_COMMAND_SetDrivesMisc; break; case NF_COMMAND_SetDrivesMaxCurrent: NFComBuf.SetDrivesMaxCurrent.data[drive_number] = (int16_t) va_arg(newValue, int); setParamCommandArray[setParamCommandCnt++] = NF_COMMAND_SetDrivesMaxCurrent; break; case NF_COMMAND_SetDrivesMode: NFComBuf.SetDrivesMode.data[drive_number] = (uint8_t) va_arg(newValue, int); setParamCommandArray[setParamCommandCnt++] = NF_COMMAND_SetDrivesMode; break; case NF_COMMAND_SetCurrentRegulator: NFComBuf.SetCurrentRegulator.data[drive_number] = va_arg(newValue, NF_STRUCT_Regulator); setParamCommandArray[setParamCommandCnt++] = NF_COMMAND_SetCurrentRegulator; break; default: std::cout << "[error] HI_moxa::set_parameter_now() invalid parameter " << (int) parameter << std::endl; return -1; break; } va_end(newValue); // Add Read Drive Status request setParamCommandArray[setParamCommandCnt++] = NF_COMMAND_ReadDrivesStatus; // Make command frame txCnt = NF_MakeCommandFrame(&NFComBuf, txBuf + 5, (const uint8_t*) setParamCommandArray, setParamCommandCnt, drives_addresses[drive_number]); // Clear communication request setParamCommandCnt = 0; for (int param_set_attempt = 0; param_set_attempt < MAX_PARAM_SET_ATTEMPTS; param_set_attempt++) { // Send command frame SerialPort[drive_number]->write(txBuf, txCnt + 5); // hardware panic; do not print error information; do not wait for response if (parameter == NF_COMMAND_SetDrivesMode && NFComBuf.SetDrivesMode.data[drive_number] == NF_DrivesMode_ERROR) return 0; // Give some time for a response to return delay.tv_nsec = 1700000; delay.tv_sec = 0; nanosleep(&delay, NULL); rxCnt = 0; while (1) { if (SerialPort[drive_number]->read(&(rxBuf[rxCnt]), 1) > 0 && (rxCnt < 255)) { if (NF_Interpreter(&NFComBuf, rxBuf, &rxCnt, rxCommandArray, &rxCommandCnt) > 0) { // TODO: Check status; return 0; } } else { std::cout << "[error] param (" << parameter << ") set ack timeout for drive (" << drive_number << ")" << std::endl; break; } } } throw std::runtime_error("HI_Moxa: Unable to communicate with motor controllers. Try switching the power on."); return 1; }
uint64_t HI_moxa::write_hardware(void) { static int error_msg_hardware_panic = 0; uint64_t ret = 0; uint8_t drive_number; // static std::stringstream temp_message; // If Hardware Panic, send PARAM_DRIVER_MODE_ERROR to motor drivers if (hardware_panic) { for (drive_number = 0; drive_number <= last_drive_number; drive_number++) { // only set error parameter, do not wait for answer servo_data[drive_number].commandCnt = 0; NFComBuf.SetDrivesMode.data[drive_number] = NF_DrivesMode_ERROR; servo_data[drive_number].commandArray[servo_data[drive_number].commandCnt++] = NF_COMMAND_SetDrivesMode; txCnt = NF_MakeCommandFrame( &NFComBuf, txBuf, (const uint8_t*) servo_data[drive_number].commandArray, servo_data[drive_number].commandCnt, drives_addresses[drive_number]); // Clear communication request servo_data[drive_number].commandCnt = 0; // Send command frame SerialPort[drive_number]->write(txBuf, txCnt); } if (error_msg_hardware_panic == 0) { std::cout << "[error] hardware panic" << std::endl; error_msg_hardware_panic++; } // std::cout << temp_message.str() << std::endl; /* struct timespec delay; delay.tv_nsec = 2000000; delay.tv_sec = 0; nanosleep(&delay, NULL); */ return ret; } else { // przygotowanie pod test zablokowania komunikacji po bledzie if (all_hardware_read) //if (1) { // Make command frames and send them to drives for (drive_number = 0; drive_number <= last_drive_number; drive_number++) { // Set communication requests // std::cout << "write drive_number: " << drive_number ; servo_data[drive_number].commandArray[servo_data[drive_number] .commandCnt++] = NF_COMMAND_ReadDrivesPosition; servo_data[drive_number].commandArray[servo_data[drive_number] .commandCnt++] = NF_COMMAND_ReadDrivesCurrent; servo_data[drive_number].commandArray[servo_data[drive_number] .commandCnt++] = NF_COMMAND_ReadDrivesStatus; // Make command frame servo_data[drive_number].txCnt = NF_MakeCommandFrame( &NFComBuf, servo_data[drive_number].txBuf + howMuchItSucks, (const uint8_t*) servo_data[drive_number].commandArray, servo_data[drive_number].commandCnt, drives_addresses[drive_number]); // Clear communication requests servo_data[drive_number].commandCnt = 0; } for (drive_number = 0; drive_number <= last_drive_number; drive_number++) { // Send command frame if (receiveFailCnt[drive_number] == 0) SerialPort[drive_number]->write( servo_data[drive_number].txBuf, servo_data[drive_number].txCnt + howMuchItSucks); #define SPN #ifdef SPN static int rwhprints = 18; if (rwhprints) { rwhprints--; std::cout << "RWH: "; for (int i = 0; i < servo_data[drive_number].txCnt + howMuchItSucks; i++) { std::cout << std::hex << (unsigned int) servo_data[drive_number].txBuf[i]; std::cout << " "; } std::cout << std::endl; } #endif } } else { // std::cout << "write hardware !all_hardware_read " << std::endl; } } ret = 1; return ret; }