Difference between revisions of "5. Joint PD Control Without Hand Library"
Alexalspach (Talk | contribs) (→Reading and Writing) |
|||
(39 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
In the past 4 tutorials, we learned how to control the Allegro Hand using the included grasping and motions library. In this tutorials, we will take a step back and control the hand at a lower level. We will go over basic filtering of the encoder data and PD position control of the joints. | In the past 4 tutorials, we learned how to control the Allegro Hand using the included grasping and motions library. In this tutorials, we will take a step back and control the hand at a lower level. We will go over basic filtering of the encoder data and PD position control of the joints. | ||
− | For this lesson, we must roll back our controller code to the point before we implemented any code to move the hand joints. It may be a better idea to start a new project with the following code. Remember, you configure the project according to the DLL project properties from tutorial 1. | + | For this lesson, we must roll back our controller code to the point before we implemented any code to move the hand joints. It may be a better idea to start a new project with the following code. Remember, you configure the project according to the DLL project properties from tutorial 1. Your code should reflect the code developed in the three steps of tutorial 2: |
*[[2._Allegro_Hand_Joint_Position_Control#Allegro_Hand_Controller_Basis | 2.1 Allegro Hand Controller Basis]] | *[[2._Allegro_Hand_Joint_Position_Control#Allegro_Hand_Controller_Basis | 2.1 Allegro Hand Controller Basis]] | ||
Line 9: | Line 9: | ||
<div class="toccolours mw-collapsible mw-collapsed"> | <div class="toccolours mw-collapsible mw-collapsed"> | ||
− | ''' | + | '''myAHController.cpp''' |
<div class="mw-collapsible-content"> | <div class="mw-collapsible-content"> | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
− | #include " | + | #include "myAHController.h" |
− | #include " | + | #include "myAHControllerCmd.h" |
− | + | ||
− | + | ||
− | + | myAHController::myAHController(rDC rdc) | |
− | + | ||
:rControlAlgorithmEx(rdc) | :rControlAlgorithmEx(rdc) | ||
− | , _jdof(0) // everything is NULL to start | + | , _jdof(0) // everything is NULL to start |
, _hand(NULL) | , _hand(NULL) | ||
, _is_left_hand(false) | , _is_left_hand(false) | ||
− | , _demo_mode( | + | , _demo_mode(true) // will be used when |
− | , _demo_start_time(0) // we make our own motion | + | , _demo_start_time(0) // we make our own motion |
{ | { | ||
} | } | ||
− | + | ||
− | + | myAHController::~myAHController() | |
{ | { | ||
− | if (_hand) | + | if (_hand) // if there is already a hand, |
delete _hand; // delete it before creating a new one | delete _hand; // delete it before creating a new one | ||
} | } | ||
− | + | void myAHController::init(int mode) | |
− | void | + | |
{ | { | ||
− | // this property is read from the | + | // this property is read from the myAHController XDL file |
// to determine if the hand we are using will be the right or the left | // to determine if the hand we are using will be the right or the left | ||
const TCHAR* prop = NULL; | const TCHAR* prop = NULL; | ||
prop = getProperty(_T("whichHand")); | prop = getProperty(_T("whichHand")); | ||
// if using a right hand | // if using a right hand | ||
− | if (prop && _tcsicmp(prop, _T(" | + | if (prop && _tcsicmp(prop, _T("right")) == 0) |
{ | { | ||
− | _is_left_hand = false; | + | _is_left_hand = false; // used to tell left from right |
_hand = bhCreateRightHand(); // create the right hand | _hand = bhCreateRightHand(); // create the right hand | ||
− | |||
} | } | ||
// if using a left hand | // if using a left hand | ||
Line 54: | Line 51: | ||
_is_left_hand = true; | _is_left_hand = true; | ||
_hand = bhCreateLeftHand(); // create the left hand | _hand = bhCreateLeftHand(); // create the left hand | ||
− | |||
} | } | ||
assert(_hand); // if hand was not created, abort | assert(_hand); // if hand was not created, abort | ||
− | _hand->SetTimeInterval(0.003); | + | _hand->SetTimeInterval(0.003); // control period is set (333Hz) |
_jdof = JDOF; // 16 DOF | _jdof = JDOF; // 16 DOF | ||
− | _arrangeJointDevices(); | + | _arrangeJointDevices(); // finds all hand motors and encoders |
− | _q.resize(_jdof); | + | _q.resize(_jdof); // array (16) holds current joint positions |
_qdot.resize(_jdof); // array (16) holds current joint velocities | _qdot.resize(_jdof); // array (16) holds current joint velocities | ||
_torque.resize(_jdof); // array (16) holds current joint torques | _torque.resize(_jdof); // array (16) holds current joint torques | ||
− | _q.zero(); | + | _q.zero(); // all positions, vel and torque set to zero |
_qdot.zero(); | _qdot.zero(); | ||
_torque.zero(); | _torque.zero(); | ||
Line 74: | Line 70: | ||
_demo_q_des.zero(); // used to create motion sequences | _demo_q_des.zero(); // used to create motion sequences | ||
− | memset(_x, 0, sizeof(_x[0])*4); | + | memset(_x, 0, sizeof(_x[0])*4); // sets x, y and z position to (0,0,0) |
− | memset(_y, 0, sizeof(_y[0])*4); | + | memset(_y, 0, sizeof(_y[0])*4); // for all four fingers |
memset(_z, 0, sizeof(_z[0])*4); | memset(_z, 0, sizeof(_z[0])*4); | ||
} | } | ||
− | + | ||
− | void | + | void myAHController::_arrangeJointDevices() |
{ | { | ||
− | |||
// _jdof is 16. | // _jdof is 16. | ||
for (int i=0; i<_jdof; i++) | for (int i=0; i<_jdof; i++) | ||
Line 91: | Line 86: | ||
// find all 16 motors on hand (motor1, motor2, ..., motor16) | // find all 16 motors on hand (motor1, motor2, ..., motor16) | ||
_stprintf(devname, _T("motor%d"), i + 1); // device name built and stored | _stprintf(devname, _T("motor%d"), i + 1); // device name built and stored | ||
− | _motor[i] = findDevice(devname); | + | _motor[i] = findDevice(devname); // device located |
− | + | ||
// find all 16 encoders on hand (enc1, enc2, ..., enc16) | // find all 16 encoders on hand (enc1, enc2, ..., enc16) | ||
_stprintf(devname, _T("enc%d"), i + 1); | _stprintf(devname, _T("enc%d"), i + 1); | ||
_enc[i] = findDevice(devname); | _enc[i] = findDevice(devname); | ||
− | |||
} | } | ||
} | } | ||
− | + | ||
− | void | + | void myAHController::update(const rTime& t) |
{ | { | ||
_cur_time = t; // controller is updated every control period | _cur_time = t; // controller is updated every control period | ||
rControlAlgorithm::update(t); | rControlAlgorithm::update(t); | ||
− | |||
− | |||
} | } | ||
− | + | ||
− | void | + | void myAHController::_readDevices() |
{ | { | ||
// all 16 encoder pos. values are read and stored in _q[] | // all 16 encoder pos. values are read and stored in _q[] | ||
Line 124: | Line 115: | ||
} | } | ||
− | void | + | void myAHController::_writeDevices() |
{ | { | ||
// all 16 motor _torque[] values are written to the motor device | // all 16 motor _torque[] values are written to the motor device | ||
Line 137: | Line 128: | ||
} | } | ||
} | } | ||
− | + | ||
− | void | + | void myAHController::_compute(const double& t) |
{ | { | ||
− | // | + | // Computes control inputs |
} | } | ||
− | + | ||
− | int | + | int myAHController::command(const short& cmd, const int& arg) |
{ | { | ||
// Handles user-defined commands according to cmd. | // Handles user-defined commands according to cmd. | ||
// Further information can be retrieved from the second argument. | // Further information can be retrieved from the second argument. | ||
− | + | ||
// The variable cmd will be received from Allegro Application Studio | // The variable cmd will be received from Allegro Application Studio | ||
// and will be used to envoke hand actions | // and will be used to envoke hand actions | ||
return 0; | return 0; | ||
} | } | ||
− | + | ||
rControlAlgorithm* CreateControlAlgorithm(rDC& rdc) | rControlAlgorithm* CreateControlAlgorithm(rDC& rdc) | ||
{ | { | ||
− | return new | + | return new myAHController(rdc); |
} | } | ||
Line 163: | Line 154: | ||
<br> | <br> | ||
<div class="toccolours mw-collapsible mw-collapsed"> | <div class="toccolours mw-collapsible mw-collapsed"> | ||
− | ''' | + | '''myAHController.h''' |
<div class="mw-collapsible-content"> | <div class="mw-collapsible-content"> | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
− | |||
− | |||
+ | #ifndef __MY_AH_CONTROLLER_H__ | ||
+ | #define __MY_AH_CONTROLLER_H__ | ||
+ | |||
#include <list> | #include <list> | ||
#include "rControlAlgorithm/rControlAlgorithm.h" | #include "rControlAlgorithm/rControlAlgorithm.h" | ||
#include "BHand/BHand.h" | #include "BHand/BHand.h" | ||
− | + | ||
− | #define JDOF 16 | + | #define JDOF 16 |
− | + | ||
− | // | + | // myAHController inherited from algorithm interface class |
− | class REXPORT | + | class REXPORT myAHController : public rControlAlgorithmEx |
{ | { | ||
public: | public: | ||
− | + | myAHController(rDC rdc); | |
− | ~ | + | ~myAHController(); |
− | + | ||
virtual void init(int mode = 0); | virtual void init(int mode = 0); | ||
virtual void update(const rTime& t); | virtual void update(const rTime& t); | ||
virtual int command(const short& cmd, const int& arg = 0); | virtual int command(const short& cmd, const int& arg = 0); | ||
− | + | ||
private: | private: | ||
virtual void _readDevices(); | virtual void _readDevices(); | ||
virtual void _writeDevices(); | virtual void _writeDevices(); | ||
− | + | ||
virtual void _compute(const rTime& t); | virtual void _compute(const rTime& t); | ||
− | + | ||
void _arrangeJointDevices(); | void _arrangeJointDevices(); | ||
− | + | ||
− | + | ||
private: | private: | ||
− | rTime | + | |
− | + | // algorithm variables go here | |
− | BHand* | + | rTime _cur_time; // current time in controller |
− | bool | + | |
− | + | BHand* _hand; // Allegro Hand | |
− | rID | + | bool _is_left_hand; // bool, left? |
− | rID | + | |
− | + | rID _motor[16]; // motor array | |
− | dVector | + | rID _enc[16]; // encoder array |
− | dVector | + | |
− | dVector | + | dVector _q; // joint current position |
− | + | dVector _qdot; // joint current velocity | |
− | int | + | dVector _torque; // joint torque |
− | + | ||
− | double | + | int _jdof; // degrees of freedom |
− | double | + | |
− | double | + | double _x[4]; // location of each (4) fingertips |
− | + | double _y[4]; // in x, y, and z | |
− | int | + | double _z[4]; |
− | rTime | + | |
− | dVector | + | int _demo_mode; // bool, used later as flag to envoke user control |
− | + | rTime _demo_start_time; // time at which the demo mode starts | |
+ | dVector _demo_q_des; // desired position for position controller | ||
+ | |||
}; | }; | ||
− | + | ||
#endif | #endif | ||
Line 229: | Line 223: | ||
<div class="toccolours mw-collapsible mw-collapsed"> | <div class="toccolours mw-collapsible mw-collapsed"> | ||
− | ''' | + | '''myAHControllerCmd.h''' |
<div class="mw-collapsible-content"> | <div class="mw-collapsible-content"> | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
− | |||
− | |||
+ | #ifndef __MY_AH_CONTROLLER_CMD_H__ | ||
+ | #define __MY_AH_CONTROLLER_CMD_H__ | ||
+ | |||
#include "rCommand/rCmdManipulator.h" | #include "rCommand/rCmdManipulator.h" | ||
− | + | ||
// These commands will be fed into command() | // These commands will be fed into command() | ||
// and can be used to envoke certain actions | // and can be used to envoke certain actions | ||
Line 245: | Line 240: | ||
#define BH_HOME (RCMD_GO_HOME) | #define BH_HOME (RCMD_GO_HOME) | ||
// #define BH_ONE (RCMD_USER + 1) | // #define BH_ONE (RCMD_USER + 1) | ||
− | + | ||
#endif | #endif | ||
Line 270: | Line 265: | ||
− | + | '''Remember:''' The 16 joint torque commands and joint positions are stored in float arrays with 16 entries each and indexed as follows: | |
− | ''' | + | {|style=" border-collapse: separate; border-spacing: 15; border-width: 2px; border-style: solid; border-color: #CCCCCC; padding: 0px; text-align: center; width: 200px" |
− | + | |-style="background: #D0D0D0" | |
− | + | |'''Finger''' | |
+ | |'''Joint''' | ||
+ | |'''Index''' | ||
+ | |-style="background: #F0F0F0" | ||
+ | |1||1||0 | ||
|- | |- | ||
− | | | + | |1||2||1 |
|- | |- | ||
− | |1||1|| | + | |1||3||2 |
+ | |- | ||
+ | |1||4||3 | ||
+ | |-style="background: #F0F0F0" | ||
+ | |2||1||4 | ||
+ | |- | ||
+ | |2||2||5 | ||
+ | |- | ||
+ | |2||3||6 | ||
+ | |- | ||
+ | |2||4||7 | ||
+ | |-style="background: #F0F0F0" | ||
+ | |3||1||8 | ||
+ | |- | ||
+ | |3||2||9 | ||
+ | |- | ||
+ | |3||3||10 | ||
+ | |- | ||
+ | |3||4||11 | ||
+ | |-style="background: #F0F0F0" | ||
+ | |4||1||12 | ||
+ | |- | ||
+ | |4||2||13 | ||
+ | |- | ||
+ | |4||3||14 | ||
+ | |- | ||
+ | |4||4||15 | ||
|} | |} | ||
+ | |||
+ | |||
+ | Let's add a bit of code to apply a small torque to all four (4) joints on the index finger and subsequently read the joint position of each and print it to the command window. | ||
+ | |||
+ | ===myAHController.h=== | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | |||
+ | #ifndef __MY_AH_CONTROLLER_H__ | ||
+ | #define __MY_AH_CONTROLLER_H__ | ||
+ | |||
+ | #include <list> | ||
+ | #include "rControlAlgorithm/rControlAlgorithm.h" | ||
+ | #include "BHand/BHand.h" | ||
+ | |||
+ | #define JDOF 16 | ||
+ | |||
+ | // myAHController inherited from algorithm interface class | ||
+ | class REXPORT myAHController : public rControlAlgorithmEx | ||
+ | { | ||
+ | public: | ||
+ | myAHController(rDC rdc); | ||
+ | ~myAHController(); | ||
+ | |||
+ | virtual void init(int mode = 0); | ||
+ | virtual void update(const rTime& t); | ||
+ | virtual int command(const short& cmd, const int& arg = 0); | ||
+ | |||
+ | private: | ||
+ | virtual void _readDevices(); | ||
+ | virtual void _writeDevices(); | ||
+ | |||
+ | virtual void _compute(const rTime& t); | ||
+ | |||
+ | void _arrangeJointDevices(); | ||
+ | |||
+ | |||
+ | private: | ||
+ | |||
+ | // algorithm variables go here | ||
+ | rTime _cur_time; // current time in controller | ||
+ | |||
+ | BHand* _hand; // Allegro Hand | ||
+ | bool _is_left_hand; // bool, left? | ||
+ | |||
+ | rID _motor[16]; // motor array | ||
+ | rID _enc[16]; // encoder array | ||
+ | |||
+ | dVector _q; // joint current position | ||
+ | dVector _qdot; // joint current velocity | ||
+ | dVector _torque; // joint torque | ||
+ | |||
+ | int _jdof; // degrees of freedom | ||
+ | |||
+ | double _x[4]; // location of each (4) fingertips | ||
+ | double _y[4]; // in x, y, and z | ||
+ | double _z[4]; | ||
+ | |||
+ | int _demo_mode; // bool, used later as flag to envoke user control | ||
+ | rTime _demo_start_time; // time at which the demo mode starts | ||
+ | dVector _demo_q_des; // desired position for position controller | ||
+ | FILE* _fp; | ||
+ | }; | ||
+ | |||
+ | #endif | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | <br> | ||
+ | |||
+ | ===myAHController.cpp=== | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | |||
+ | myAHController::myAHController(rDC rdc) | ||
+ | :rControlAlgorithmEx(rdc) | ||
+ | , _jdof(0) // everything is NULL to start | ||
+ | , _hand(NULL) | ||
+ | , _is_left_hand(false) | ||
+ | , _demo_mode(true) // will be used when | ||
+ | , _demo_start_time(0) // we make our own motion | ||
+ | , _fp(NULL) | ||
+ | { | ||
+ | fopen_s(&fp, "C:\\myAHController.txt", "w"); | ||
+ | } | ||
+ | |||
+ | myAHController::~myAHController() | ||
+ | { | ||
+ | if (_hand) // if there is already a hand, | ||
+ | delete _hand; // delete it before creating a new one | ||
+ | |||
+ | if (_fp) | ||
+ | fclose(_fp); | ||
+ | } | ||
+ | |||
+ | ... | ||
+ | |||
+ | void myAHController::_compute(const double& t) | ||
+ | { | ||
+ | // Computes control inputs | ||
+ | // Joints 0, 1, 2 and 3 are commanded at 0.1 N.m | ||
+ | for (int i=0; i<4; i++) | ||
+ | { | ||
+ | _torque[i] = 0.1; | ||
+ | } | ||
+ | |||
+ | // Joint positions 0, 1, 2 and 3 are printed | ||
+ | for (int i=0; i<4; i++) | ||
+ | { | ||
+ | fprintf(_fp, "%i: %f\t",i,_q[i]); | ||
+ | } | ||
+ | fprintf(_fp, "\n"); // next line | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Compile and execute the shortcut in the Desktop.<br> | ||
+ | It is necessary to execute the shortcut with Administrator Privilege.<br> | ||
+ | You can see the file that has joint positions in C:\myAHController.txt file.<br> | ||
+ | We can now design any controller with a position input and a torque output. | ||
+ | |||
+ | ==PD Control== | ||
+ | |||
+ | One of the simplest and most widely used control algorithms, Proportional-Derivative (PD) control can be easily implemented to control the position of a robotic joint. | ||
+ | |||
+ | To use PD control, we need to calculate the error between the joint's set, or desired, position and the joint's current position. For each joint, this error value and a gain value will comprise the proportional controller. Along with the current error, we will also calculate the error's rate of change from time-step to time-step. This will be calculated using the difference between the current and previous error values divided by the time-step. | ||
+ | [[Category:Programming Guides]] |
Latest revision as of 18:44, 30 April 2015
In the past 4 tutorials, we learned how to control the Allegro Hand using the included grasping and motions library. In this tutorials, we will take a step back and control the hand at a lower level. We will go over basic filtering of the encoder data and PD position control of the joints.
For this lesson, we must roll back our controller code to the point before we implemented any code to move the hand joints. It may be a better idea to start a new project with the following code. Remember, you configure the project according to the DLL project properties from tutorial 1. Your code should reflect the code developed in the three steps of tutorial 2:
myAHController.cpp
#include "myAHController.h" #include "myAHControllerCmd.h" myAHController::myAHController(rDC rdc) :rControlAlgorithmEx(rdc) , _jdof(0) // everything is NULL to start , _hand(NULL) , _is_left_hand(false) , _demo_mode(true) // will be used when , _demo_start_time(0) // we make our own motion { } myAHController::~myAHController() { if (_hand) // if there is already a hand, delete _hand; // delete it before creating a new one } void myAHController::init(int mode) { // this property is read from the myAHController XDL file // to determine if the hand we are using will be the right or the left const TCHAR* prop = NULL; prop = getProperty(_T("whichHand")); // if using a right hand if (prop && _tcsicmp(prop, _T("right")) == 0) { _is_left_hand = false; // used to tell left from right _hand = bhCreateRightHand(); // create the right hand } // if using a left hand else { _is_left_hand = true; _hand = bhCreateLeftHand(); // create the left hand } assert(_hand); // if hand was not created, abort _hand->SetTimeInterval(0.003); // control period is set (333Hz) _jdof = JDOF; // 16 DOF _arrangeJointDevices(); // finds all hand motors and encoders _q.resize(_jdof); // array (16) holds current joint positions _qdot.resize(_jdof); // array (16) holds current joint velocities _torque.resize(_jdof); // array (16) holds current joint torques _q.zero(); // all positions, vel and torque set to zero _qdot.zero(); _torque.zero(); _demo_q_des.resize(_jdof); // desired joint positions _demo_q_des.zero(); // used to create motion sequences memset(_x, 0, sizeof(_x[0])*4); // sets x, y and z position to (0,0,0) memset(_y, 0, sizeof(_y[0])*4); // for all four fingers memset(_z, 0, sizeof(_z[0])*4); } void myAHController::_arrangeJointDevices() { // _jdof is 16. for (int i=0; i<_jdof; i++) { // oversited array intialized for storing the device name string TCHAR devname[32]; // find all 16 motors on hand (motor1, motor2, ..., motor16) _stprintf(devname, _T("motor%d"), i + 1); // device name built and stored _motor[i] = findDevice(devname); // device located // find all 16 encoders on hand (enc1, enc2, ..., enc16) _stprintf(devname, _T("enc%d"), i + 1); _enc[i] = findDevice(devname); } } void myAHController::update(const rTime& t) { _cur_time = t; // controller is updated every control period rControlAlgorithm::update(t); } void myAHController::_readDevices() { // all 16 encoder pos. values are read and stored in _q[] float val; for (int i=0; i<JDOF; i++) { if (_enc[i] != INVALID_RID) { readDeviceValue(_enc[i], &val, 4); _q[i] = (float)val; } } } void myAHController::_writeDevices() { // all 16 motor _torque[] values are written to the motor device float val; for (int i=0; i<JDOF; i++) { val = (float)_torque[i]; if (_motor[i] != INVALID_RID) { writeDeviceValue(_motor[i], &val, 4); } } } void myAHController::_compute(const double& t) { // Computes control inputs } int myAHController::command(const short& cmd, const int& arg) { // Handles user-defined commands according to cmd. // Further information can be retrieved from the second argument. // The variable cmd will be received from Allegro Application Studio // and will be used to envoke hand actions return 0; } rControlAlgorithm* CreateControlAlgorithm(rDC& rdc) { return new myAHController(rdc); }
myAHController.h
#ifndef __MY_AH_CONTROLLER_H__ #define __MY_AH_CONTROLLER_H__ #include <list> #include "rControlAlgorithm/rControlAlgorithm.h" #include "BHand/BHand.h" #define JDOF 16 // myAHController inherited from algorithm interface class class REXPORT myAHController : public rControlAlgorithmEx { public: myAHController(rDC rdc); ~myAHController(); virtual void init(int mode = 0); virtual void update(const rTime& t); virtual int command(const short& cmd, const int& arg = 0); private: virtual void _readDevices(); virtual void _writeDevices(); virtual void _compute(const rTime& t); void _arrangeJointDevices(); private: // algorithm variables go here rTime _cur_time; // current time in controller BHand* _hand; // Allegro Hand bool _is_left_hand; // bool, left? rID _motor[16]; // motor array rID _enc[16]; // encoder array dVector _q; // joint current position dVector _qdot; // joint current velocity dVector _torque; // joint torque int _jdof; // degrees of freedom double _x[4]; // location of each (4) fingertips double _y[4]; // in x, y, and z double _z[4]; int _demo_mode; // bool, used later as flag to envoke user control rTime _demo_start_time; // time at which the demo mode starts dVector _demo_q_des; // desired position for position controller }; #endif
myAHControllerCmd.h
#ifndef __MY_AH_CONTROLLER_CMD_H__ #define __MY_AH_CONTROLLER_CMD_H__ #include "rCommand/rCmdManipulator.h" // These commands will be fed into command() // and can be used to envoke certain actions // by the robot. Allegro Application Studio // will use these to interface with the // cotroller plug-in. #define BH_NONE (RCMD_USER + 0) #define BH_HOME (RCMD_GO_HOME) // #define BH_ONE (RCMD_USER + 1) #endif
Contents |
[edit] Check the code
Let's quickly compile the code above to make sure we copied everything correctly. It should compile with no errors.
Next, open up AHAS via your virtual Allegro Hand shortcut. None of the buttons should work anymore but you should see the command prompt indication that all motors and encoders have been found. These are all we need!
[edit] Reading and Writing
We have two functions, _readDevices() and _writeDevices(), that are called every control period. For the Allegro Hand controller, this means that the encoder values are accessed and motor commands are calculated and written at 333Hz, or once every 0.003 seconds.
When _readDevices() accessed the encoder data, it saves it to the array _q[]" which has 16 floating point entries (once for each joint). The joint positions in _q are in radians.
Similarly, when writing a torque command to the motors, "_writeDevices()" accesses the 16-long floating-point array _torque[]'. The torque values are in the unit Newton-meters.
Alike _readDevices() and "_writeDevices()", the function '_compute() is also called every control iteration. This is where we will implement are controller code, or the computation of motor torque based on joint positions.
Remember: The 16 joint torque commands and joint positions are stored in float arrays with 16 entries each and indexed as follows:
Finger | Joint | Index |
1 | 1 | 0 |
1 | 2 | 1 |
1 | 3 | 2 |
1 | 4 | 3 |
2 | 1 | 4 |
2 | 2 | 5 |
2 | 3 | 6 |
2 | 4 | 7 |
3 | 1 | 8 |
3 | 2 | 9 |
3 | 3 | 10 |
3 | 4 | 11 |
4 | 1 | 12 |
4 | 2 | 13 |
4 | 3 | 14 |
4 | 4 | 15 |
Let's add a bit of code to apply a small torque to all four (4) joints on the index finger and subsequently read the joint position of each and print it to the command window.
[edit] myAHController.h
#ifndef __MY_AH_CONTROLLER_H__ #define __MY_AH_CONTROLLER_H__ #include <list> #include "rControlAlgorithm/rControlAlgorithm.h" #include "BHand/BHand.h" #define JDOF 16 // myAHController inherited from algorithm interface class class REXPORT myAHController : public rControlAlgorithmEx { public: myAHController(rDC rdc); ~myAHController(); virtual void init(int mode = 0); virtual void update(const rTime& t); virtual int command(const short& cmd, const int& arg = 0); private: virtual void _readDevices(); virtual void _writeDevices(); virtual void _compute(const rTime& t); void _arrangeJointDevices(); private: // algorithm variables go here rTime _cur_time; // current time in controller BHand* _hand; // Allegro Hand bool _is_left_hand; // bool, left? rID _motor[16]; // motor array rID _enc[16]; // encoder array dVector _q; // joint current position dVector _qdot; // joint current velocity dVector _torque; // joint torque int _jdof; // degrees of freedom double _x[4]; // location of each (4) fingertips double _y[4]; // in x, y, and z double _z[4]; int _demo_mode; // bool, used later as flag to envoke user control rTime _demo_start_time; // time at which the demo mode starts dVector _demo_q_des; // desired position for position controller FILE* _fp; }; #endif
[edit] myAHController.cpp
myAHController::myAHController(rDC rdc) :rControlAlgorithmEx(rdc) , _jdof(0) // everything is NULL to start , _hand(NULL) , _is_left_hand(false) , _demo_mode(true) // will be used when , _demo_start_time(0) // we make our own motion , _fp(NULL) { fopen_s(&fp, "C:\\myAHController.txt", "w"); } myAHController::~myAHController() { if (_hand) // if there is already a hand, delete _hand; // delete it before creating a new one if (_fp) fclose(_fp); } ... void myAHController::_compute(const double& t) { // Computes control inputs // Joints 0, 1, 2 and 3 are commanded at 0.1 N.m for (int i=0; i<4; i++) { _torque[i] = 0.1; } // Joint positions 0, 1, 2 and 3 are printed for (int i=0; i<4; i++) { fprintf(_fp, "%i: %f\t",i,_q[i]); } fprintf(_fp, "\n"); // next line }
Compile and execute the shortcut in the Desktop.
It is necessary to execute the shortcut with Administrator Privilege.
You can see the file that has joint positions in C:\myAHController.txt file.
We can now design any controller with a position input and a torque output.
[edit] PD Control
One of the simplest and most widely used control algorithms, Proportional-Derivative (PD) control can be easily implemented to control the position of a robotic joint.
To use PD control, we need to calculate the error between the joint's set, or desired, position and the joint's current position. For each joint, this error value and a gain value will comprise the proportional controller. Along with the current error, we will also calculate the error's rate of change from time-step to time-step. This will be calculated using the difference between the current and previous error values divided by the time-step.
Whos here now: Members 0 Guests 0 Bots & Crawlers 1 |