-
-
Save machmotioncommunity/169248a95c2213458cb1d9ca9d26e9e6 to your computer and use it in GitHub Desktop.
proof-of-concept of homing; tweaking parameters for RSI API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Attempt to home to an arbitrary I/O. | |
// Everything in this proof-of-concept is hardcoded (upstairs test station) | |
// | |
#include <iostream> | |
#include <iomanip> | |
#include <string> | |
#include <cstdint> | |
#include <sstream> | |
#include <chrono> | |
#include <thread> | |
#include <cmath> | |
// convenience for string/object-stringified concatenation. | |
template<typename T> | |
void __cat(std::stringstream& tgt, const T& only) { | |
tgt << only; | |
} | |
// recursive function | |
template<typename T, typename... R> | |
void __cat(std::stringstream& tgt, const T& first, R... rest) { | |
tgt << first; | |
__cat(tgt, rest...); | |
} | |
// entry point for recursive call. | |
template<typename... R> | |
std::string _cat(R... rest) { | |
std::stringstream t; | |
__cat(t, rest...); | |
return t.str(); | |
} | |
inline std::string _cat() { return std::string(); } | |
#define FIXED(d,p,w) _cat(std::setw(w),std::setprecision(p),std::fixed,d) | |
#define PTR_ADDR(p) _cat("0x",std::hex,(void*)p) | |
#define RSIAPP | |
// (20190827;8.1.3) rsi.h generates warnings (needless "typedef" before an enum). | |
#pragma warning( push ) | |
#pragma warning( disable : 4091 ) | |
#include <rsi.h> | |
#pragma warning( pop ) | |
namespace r = RSI::RapidCode; // Don't hide the namespace, just abbreviate it. | |
#pragma comment(lib,"rsiqvc.lib") | |
#define CONTEXT() _cat("; [" , __FUNCTION__ , "] [" , __FILE__ , ":" , __LINE__ , "]") | |
#define EXCEPTION(...) std::exception(_cat(__VA_ARGS__,"; ",CONTEXT()).c_str()) | |
inline std::string as_hex_string( const void* bytes, size_t size ) { | |
std::stringstream buf; | |
for (size_t i=0; i<size; i++) | |
buf << std::hex << std::setw(2) << std::setfill('0') << (uint16_t)(((const char*)bytes)[i]); | |
return buf.str(); | |
} | |
const int TARGET_AXIS_IDX = 0; // drive to home | |
const double AXIS_COUNTS_PER_UNIT = 1<<17; // 1 PUU = 1 revolution | |
const int TARGET_IO_NODE_IDX = 2; // node hosting I/O | |
const int TARGET_IO_INPUT_IDX = 6; // (general) I/O index on node | |
// just for comparison (host/FW addrs) | |
const int TARGET_IO_NETWORK_IDX = 44; | |
const int TARGET_IO_NETWORK_BIT_INDEX = 1; | |
const r::RSIHomeMethod HOMING_METHOD = r::RSIHomeMethodRISING_HOME; | |
//const r::RSIHomeMethod HOMING_METHOD = r::RSIHomeMethodNEGATIVE_LIMIT; | |
//const r::RSIHomeMethod HOMING_METHOD = r::RSIHomeMethodPOSITIVE_LIMIT; | |
//const r::RSIHomeMethod HOMING_METHOD = r::RSIHomeMethodPOSITIVE_LIMIT_THEN_LEFT_INDEX; | |
//const r::RSIHomeMethod HOMING_METHOD = r::RSIHomeMethodRISING_HOME_POSITIVE_START; | |
const double HOME_OFFSET = 0.0; | |
const double HOME_VELOCITY = 1; // 60 RPM -> rev/sec | |
const double HOME_ACCEL = 1; // 60 RPM/sec -> rev/sec | |
const double HOME_DECEL = HOME_ACCEL; // same for now | |
const double HOME_JERK_PCT = 100.0; | |
const r::RSIAction HOME_ACTION = r::RSIActionSTOP; // default | |
// I don't want to use this. | |
const double HOME_TRAVEL_DISTANCE = -100.0; // 0; // 1000.0; | |
const bool HOME_MOVE_TO_ZERO = true; // default | |
#define DO_TRACE_HOME | |
//#define USE_DEPRECATED | |
void run_test() { | |
const char* rsi_val = getenv("RSI"); | |
if (rsi_val == NULL) { | |
throw EXCEPTION("The environment variable RSI must be defined.\n"); | |
} | |
r::MotionController* mc; | |
try { | |
mc = r::MotionController::CreateFromSoftware(const_cast<char*>(rsi_val)); | |
} | |
catch ( const std::exception& e ) { | |
throw EXCEPTION("Error instantiating MotionController object: ",e.what(),"\n"); | |
} | |
// Network:Operational | |
try { | |
size_t attempts = 0; | |
size_t max_attempts = 3; | |
while (mc->NetworkStateGet() != r::RSINetworkStateOPERATIONAL) { | |
if (attempts > max_attempts) { | |
throw EXCEPTION("Maximum # attempts (",max_attempts,") to start the network failed. Aborting...\n"); | |
} | |
mc->NetworkStart(); | |
} | |
} | |
catch ( const std::exception& e ) { | |
throw EXCEPTION("Exception raised attempting to start the EtherCAT network: ",e.what(),"\n"); | |
} | |
auto axis_count = mc->AxisCountGet(); | |
if (axis_count < 1) { | |
throw EXCEPTION("No axes found on the network: ",axis_count,"\n"); | |
} | |
// axis to home | |
r::Axis* axis = mc->AxisGet(TARGET_AXIS_IDX); | |
std::cout << "Axis #" << axis->DriveIndexGet() << "\n " | |
<< axis->NetworkNode->NameGet() << ": " << axis->NetworkNode->VendorIdGet() << "/" << axis->NetworkNode->ProductCodeGet() << "\n"; | |
#ifdef DO_TRACE_HOME | |
axis->TraceFileSet("RapidCodeHomeTrace.txt"); | |
axis->TraceMaskOffSet(r::RSITraceALL); | |
axis->Trace(true); | |
#endif // DO_TRACE_HOME | |
axis->UserUnitsSet(AXIS_COUNTS_PER_UNIT); | |
double puu = axis->UserUnitsGet(); | |
std::cout << " PUU: " << puu << "\n"; | |
// I/O | |
r::IO* io_node = mc->IOGet(TARGET_IO_NODE_IDX); | |
r::IOPoint* io_point = r::IOPoint::CreateDigitalInput(io_node,TARGET_IO_INPUT_IDX); | |
// | |
// Configure Homing | |
// parameters: method, speed/accel, ... | |
// | |
axis->HomeMethodSet(HOMING_METHOD); | |
std::cout << " Homing Method: " << static_cast<int32_t>(axis->HomeMethodGet()) << "\n"; | |
axis->HomeOffsetSet(HOME_OFFSET); | |
std::cout << " Home Offset: " << axis->HomeOffsetGet() << "\n"; | |
// There may be multiple velocities to set for some homing methonds. | |
// virtual void HomeVelocitySet(RSIHomeStage homeStage, double velocity) = 0 ; | |
// | |
// RSIHomeStageSTAGE_ONE = 0, ///< Searching Move to Find Limit. (ie Home Active) | |
// RSIHomeStageSTAGE_TWO = 1, ///< Searching Move from Limit Active to Limit Edge. (ie Returning to Home Edge) | |
// RSIHomeStageSTAGE_THREE = 2, ///< Searching Move from Edge to Index. | |
// RSIHomeStageSTAGE_FOUR = 3, ///< Covers motion to Zero after HomeOffsetSet has been used. | |
#ifdef USE_DEPRECATED | |
std::cout << "Using Deprecated APIs...\n"; | |
axis->HomeVelocitySet(HOME_VELOCITY); | |
std::cout << " Home Velocity: " << axis->HomeVelocityGet() << " (PUU/sec)\n"; | |
axis->HomeSlowVelocitySet(HOME_VELOCITY); | |
std::cout << " Home Slow Velocity: " << axis->HomeSlowVelocityGet() << " (PUU/sec)\n"; | |
axis->HomeAccelerationSet(HOME_ACCEL); | |
std::cout << " Home Acceleration: " << axis->HomeAccelerationGet() << " (PUU/sec/sec)\n"; | |
axis->HomeDecelerationSet(HOME_DECEL); | |
std::cout << " Home Deceleration: " << axis->HomeDecelerationGet() << " (PUU/sec/sec)\n"; | |
axis->HomeJerkPercentSet(HOME_JERK_PCT); | |
std::cout << " Home Jerk Percent: " << axis->HomeJerkPercentGet() << " %\n"; | |
#else // USE_DEPRECATED | |
std::cout << "Using New APIs...\n"; | |
for (auto& stage : std::vector<r::RSIHomeStage>{r::RSIHomeStageSTAGE_ONE, r::RSIHomeStageSTAGE_TWO, | |
r::RSIHomeStageSTAGE_THREE, r::RSIHomeStageSTAGE_FOUR}) { | |
std::cout << " Homing Stage " << stage << ":\n"; | |
axis->HomeVelocitySet(stage,HOME_VELOCITY); | |
std::cout << " Home Velocity: " << axis->HomeVelocityGet(stage) << " (PUU/sec)\n"; | |
axis->HomeAccelerationSet(stage,HOME_ACCEL); | |
std::cout << " Home Acceleration: " << axis->HomeAccelerationGet(stage) << " (PUU/sec/sec)\n"; | |
axis->HomeDecelerationSet(stage,HOME_DECEL); | |
std::cout << " Home Deceleration: " << axis->HomeDecelerationGet(stage) << " (PUU/sec/sec)\n"; | |
axis->HomeJerkPercentSet(stage,HOME_JERK_PCT); | |
std::cout << " Home Jerk Percent: " << axis->HomeJerkPercentGet(stage) << " %\n"; | |
axis->HomeTravelDistanceSet(stage, HOME_TRAVEL_DISTANCE); | |
std::cout << " (using) Home Travel Distance: " << HOME_TRAVEL_DISTANCE << " (PUU)\n"; | |
} | |
#endif // USE_DEPRECATED | |
//axis->HomeBehaviorSet(HOME_ACTION); | |
//std::cout << " Home Behavior: " << axis->HomeBehaviorGet() << "\n"; // 2: stop | |
// I don't think these will be used for this test. | |
axis->HomeActionSet(HOME_ACTION); | |
std::cout << " Home Action: " << axis->HomeActionGet() << "\n"; | |
//axis->HardwareNegLimitActionSet(HOME_ACTION); | |
//std::cout << " Neg Limit Action: " << axis->HardwareNegLimitActionGet() << "\n"; | |
//axis->HardwarePosLimitActionSet(HOME_ACTION); | |
//std::cout << " Pos Limit Action: " << axis->HardwarePosLimitActionGet() << "\n"; | |
axis->ErrorLimitActionSet(r::RSIActionNONE); | |
std::cout << " Position Error Action: " << axis->ErrorLimitActionGet() << "\n"; | |
// limits of travel | |
// I/O used for home switch | |
int32_t iopoint_bit_num = static_cast<int32_t>(floor((log(io_point->MaskGet()) / log(2)) + .5)); | |
auto use_addr = io_point->AddressGet(); | |
int32_t use_bit_num = iopoint_bit_num; | |
// use network I/O | |
//use_addr = mc->NetworkInputAddressGet(TARGET_IO_NETWORK_IDX); | |
//use_bit_num = 0; | |
std::cout << "\nHome Switch I/O: " | |
<< io_node->NetworkNode->NameGet() << ": " << io_node->NetworkNode->VendorIdGet() << "/" << io_node->NetworkNode->ProductCodeGet() | |
<< " #" << TARGET_IO_INPUT_IDX << "/Bit-" << iopoint_bit_num << "\n" | |
" Host(" << PTR_ADDR(io_point->AddressGet()) << ")" | |
" F/W(" << PTR_ADDR(mc->FirmwareAddressGet(io_point->AddressGet())) << ")\n" | |
<< " Mask(" << PTR_ADDR(io_point->MaskGet()) << ") Digital?(" << io_point->IsDigital() << ") IsOutput?(" << io_point->IsOutput() << ")\n" | |
<< " State: " << (io_point->Get()?"SET":"clear") | |
<< "\n" | |
<< " (-~-> Node) " << "Host(" << PTR_ADDR(io_node->NetworkNode->DigitalInAddressGet(TARGET_IO_INPUT_IDX)) << ")" | |
" F/W(" << PTR_ADDR(mc->FirmwareAddressGet(io_node->NetworkNode->DigitalInAddressGet(TARGET_IO_INPUT_IDX))) << ")" | |
" Mask(" << PTR_ADDR(io_node->NetworkNode->DigitalInMaskGet(TARGET_IO_INPUT_IDX)) << ")" | |
<< "\n" | |
<< " (-~-> NetworkInput) " << " Host(" << PTR_ADDR(mc->NetworkInputAddressGet(TARGET_IO_NETWORK_IDX)) << ")" | |
" F/W(" << PTR_ADDR(mc->FirmwareAddressGet(mc->NetworkInputAddressGet(TARGET_IO_NETWORK_IDX))) << ")" | |
<< "\n" | |
<< " Using: Host(" << PTR_ADDR(use_addr) << ") F/W(" << PTR_ADDR(mc->FirmwareAddressGet(use_addr)) << "), Bit #" << use_bit_num << "\n" | |
; | |
axis->HomeLimitCustomConfigSet(use_addr,use_bit_num); | |
// prepare for homing | |
axis->Abort(); | |
axis->ClearFaults(); | |
axis->AmpEnableSet(true); | |
// Home the axis. | |
std::cout << "\n\nHoming...\n"; | |
std::cout.flush(); | |
double home_start_pos = axis->ActualPositionGet(); // PUU | |
auto home_started_time = std::chrono::high_resolution_clock::now(); | |
try { | |
// Axis::Home() is synchronous. We want to monitor things while it's taking place. | |
bool abort = false; | |
std::thread homer([axis,&abort](){ | |
try { | |
std::cout << " -->\n"; | |
std::cout.flush(); | |
axis->Home(HOME_MOVE_TO_ZERO); | |
} | |
catch (const std::exception& e) { | |
std::cerr << "Exception raised in homing: " << e.what() << "\n"; | |
abort = true; | |
} | |
}); | |
// status: monitor distance moved, velocity, position | |
std::string prev; | |
while (! axis->HomeStateGet() && ! abort) { | |
// progress indicator | |
double curr_pos = axis->ActualPositionGet(); | |
double home_traveled = curr_pos - home_start_pos; | |
auto status = _cat("D:",FIXED(home_traveled,2,8), | |
" V:",FIXED(axis->ActualVelocityGet(),2,8), | |
" IO:",(io_point->Get()?"ON":"off")); | |
if (prev.size() > 0) { | |
std::cout << std::string(prev.size(),'\b'); | |
} | |
std::cout << status; | |
std::cout.flush(); | |
prev = status; | |
std::this_thread::sleep_for(std::chrono::milliseconds(100)); | |
} | |
auto home_finished_time = std::chrono::high_resolution_clock::now(); | |
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(home_finished_time-home_started_time).count()/1000.0; | |
if (!abort) | |
std::cout << "Homed: " << FIXED(elapsed,3,0) << " seconds.\n"; | |
else | |
std::cout << "Homing ended. " << FIXED(elapsed,3,0) << " seconds.\n"; | |
// cleanup | |
homer.join(); | |
} | |
catch ( const std::exception& e ) { | |
auto elapsed = std::chrono::high_resolution_clock::now() - home_started_time; | |
throw EXCEPTION("Error during homing: ",e.what(),"\n" | |
" +",(std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count()/1000.0)," sec"); | |
} | |
#ifdef DO_TRACE_HOME | |
axis->Trace(false); | |
#endif // DO_TRACE_HOME | |
} | |
int main( int argc, const char** argv ) { | |
try { | |
run_test(); | |
} | |
catch ( const std::exception& e ) { | |
std::cerr << "Exception raised running test: " << e.what() << "\n"; | |
return 1; | |
} | |
std::cout << "Finished!\n"; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment