Task-space Admittance Control
System and Controller Configurations
IndySDK provides several libraries such as position controller, admittance trajectory generator, several filters, and the shared memory. In this example, task-space admittance control is implemented with such libraries.
This example covers the following items. How to...
- Transform F/T sensor coordinate.
- Use H-infinity position controller.
- Initialize the nominal system.
- Use forward dynamic position controller.
- Realize three dimensional admittance trajectory in the task-space.
- Use low-pass filter.
Example Code
As a first step, generate the task control component. Then, add several header files to use the shared memory and filters as follows:
| // for the use of shared memeory
#include "NRMKFramework/Indy/Sharedmemory/SharedData.h"
#include "NRMKFramework/Indy/Sharedmemory/SharedData.h"
NRMKFramework::ShmemManager * indyShm = NULL;
NRMKIndy::SharedData::RobotControlSharedData ctrData;
// for the use of lowpass filter and filtered derviative
#include <Util/Filter.h>
#include <math.h>
|
Before designing the task-space admittance controller, define several member functions and variables in the TaskControlComponent class as follows:
| // degree of freedom (6dof)
enum
{
JOINT_DOF = AbstractRobot6D::JOINT_DOF
};
// new type definitaions
typedef typename ROBOT::JointVec JointVec;
typedef typename ROBOT::JointMat JointMat;
typedef NRMKFoundation::FilterLowPass<double> LowPassFilter;
typedef NRMKFoundation::FilterDerivative<double> FilteredDerivative;
|
| // member functions
void readFTSensor(ROBOT & robot);
void initNominalSystem(ROBOT & robot);
void computeJDet(ROBOT & robot);
void setBias() {_ftBias = _ftSensor;}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | private:
// getting nominal robot dynamics
JointMat _Mn, _Cn;
JointVec _Gn;
private:
// getting admittance trajectory
TaskPosition _tposPxy;
TaskVelocity _tvelPxy;
TaskAcceleration _taccPxy;
private:
// with respect to ft sensor
LieGroup::Wrench _ftBias;
LieGroup::Wrench _ftSensor;
LieGroup::Rotation _ft_sensor_orientation;
LieGroup::Wrench _ftTransformed, _ftTransformed_filtered;
LowPassFilter _ftLowPass[6];
private:
// reset control, instaneous time, control gains
bool _reset_ctrl;
double _delT;
TaskVec _kp, _kv, _ki;
|
In the constructor, allocate the memory to the pointer variable, which allows to acquire the F/T sensor data from the shared memory (for more details, see IndyFramework or F/T Sensor and Gripper Example)
| TaskControlComponent::TaskControlComponent()
: AbstractController(USERNAME, EMAIL, SERIAL)
, _robotNom(NULL)
{
indyShm = new NRMKFramework::ShmemManager(INDY_SHM_NAME, INDY_SHM_LEN);
for(int i=0; i<6; i++) _ftBias[i] = 0;
}
|
In the destructor, release the memory allocated for the nominal robot and shared memory:
| TaskControlComponent::~TaskControlComponent()
{
if (_robotNom != NULL) delete _robotNom;
if (indyShm != NULL)
delete indyShm;
}
|
Source File
Based on position control libraries (i.e., H-infinity and Forward Dynamic controllers), design admittance controller to track generated admittance trajectories.
To this end, fill in the initialize() function as follows:
- set the coordinate of the F/T sensor to be aligned with the coordinate of the end-effector.
- Initialize the position controller to be used.
- Update the sampling time (delt) to the global variable.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | void TaskControlComponent::initialize(ROBOT & robot, double delt)
{
// create a nominal robot
_robotNom = AbstractController::createRobotObj();
// align ft sensor coordinates with robot's tool frame's coordinates
_ft_sensor_orientation = LieGroup::Rotation(-1,0,0,0,-1,0,0,0,1);
robot.setTFTSensor(_ft_sensor_orientation, LieGroup::Displacement::Zero());
// initialize position controller
#if defined(HINF_CTR)
robot.initHinfController(delt);
robot.resetHinfController();
#else
robot.initForwardDynController(delt);
robot.resetForwardDynController();
#endif
// update sampling time
_delT = delt;
//initialize task-space error
robot.initTaskErr(delt);
}
|
In the setGains() function, update the control gains to use them globally as follows:
| void TaskControlComponent::setGains(TaskVec const & kp, TaskVec const & kv, TaskVec const & ki)
{
_kp = kp;
_kv = kv;
_ki = ki;
}
|
For each control task, to reset the controller and the bias value of F/T sensor, implement the following code into the reset() function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | void TaskControlComponent::reset()
{
// get ft sensor values from shared memory
NRMKIndy::SharedData::getControlData(*indyShm, ctrData);
for (int i=0; i<6; i++) _ftSensor[i] = ctrData.Fext[i];
// compensate ft sensor bias
setBias();
for(int j=0; j<JOINT_DOF; j++){
for(int i=0; i<2; i++){
_ftLowPass[j].filtered(i) = 0.0;
_ftLowPass[j].raw(i) = 0.0;
}
}
// reset flag defined for controller reset
_reset_ctrl = true;
}
|
In the computeControlTorq() function, the admittance controller is implemented like follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 | int TaskControlComponent::computeControlTorq(ROBOT & robot, LieGroup::Vector3D const & gravDir, TaskPosition const & posDesired, TaskVelocity const & velDesired, TaskAcceleration const & accDesired, JointVec & torque)
{
if (indyShm == NULL)
{
printf("Shared memory is missed\n");
}
// reset admittance trajectory and position controller
if (_reset_ctrl)
{
printf("initialized\n");
//reset nominal system "robotNom"; the function is defined below
initNominalSystem(robot);
//reset admittancd trajectory
robot.resetAdmittanceTraj(_delT);
//reset position controllers
#if defined(HINF_CTR)
robot.resetHinfController();
#else
robot.resetForwardDynController();
#endif
_reset_ctrl = false;
}
// read ft sensor value; the function is defined below
readFTSensor(robot);
// change ft sensor direction x,y,z,u,v,w to u,v,w,x,y,z
for (int i=0; i<3; i++) _ftTransformed[i] = _ftSensor[i+3];
for (int i=0; i<3; i++) _ftTransformed[i+3] = _ftSensor[i];
// lowpass filtering of ft sensor values
LieGroup::Wrench _ftTransFiltered, _ftTransFilteredLow;
for (int i=0; i<INDY_NUM_TASK_AXES; i++)
{
_ftLowPass[i].filter(_ftTransformed[i], _ftTransformed_filtered[i]);
}
// calculate admittance trajecotry
// based on desired trajectories and ft sensor values
robot.updateAdmittanceTraj(posDesired, velDesired, accDesired, _tposPxy,
_tvelPxy, _taccPxy, _ftTransformed, _ftTransformed_filtered, -1);
// set control gains and update admittance trajecotry to controllers
#if defined(HINF_CTR)
robot.setHinfControlGain(_kp, _kv, _ki);
robot.HinfController(gravDir, _tposPxy, _tvelPxy, _taccPxy);
#else
robot.setForwardDynControlGain(_kp, _kv, _ki);
robot.ForwardDynController(*_robotNom, _Mn, _Cn, _Gn, _tposPxy, _tvelPxy,
gravDir);
#endif
torque = robot.tau();
return 0; //return 1: Close to Singularity
}
|
resetNominalSystem() and readFTSensor() functions used in the computeControlTorq() are respectively given as follows:
First, define resetNominalSystem() function to update the values of the robot object to those of the robotNom object. Note that resetNominalSystem() is used when restarting the task.
| void TaskControlComponent::initNominalSystem(ROBOT & robot)
{
_robotNom->setTtarget(robot.Ttarget().R(), robot.Ttarget().r());
_robotNom->setTReference(robot.Tref().R(),robot.Tref().r());
_robotNom->q() = robot.q();
_robotNom->qdot() = robot.qdot();
_robotNom->update();
_robotNom->updateJacobian();
}
|
Moreover, define the readFTSensor() function to transform the coordinate of the F/T sensor to be aligned with the global coordinate of the robot.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | void TaskControlComponent::readFTSensor(ROBOT & robot)
{
NRMKIndy::SharedData::getControlData(*indyShm, ctrData);
TaskPosition _tposT;
TaskVelocity _tvelT;
LieGroup::Wrench _ftSensorTemp;
_ftSensorTemp.setZero();
robot.computeFK(_tposT, _tvelT);
for (int i=0; i<6; i++) _ftSensorTemp[i] = ctrData.Fext[i];
_ftSensorTemp = _ftSensorTemp - _ftBias;
LieGroup::HTransform T_ft_target = robot.Ttarget().icascade(robot.Tft());
LieGroup::Wrench _ftSensorTemp2 = T_ft_target.itransform(_ftSensorTemp);
LieGroup::Wrench _ftSensorTransformed = LieGroup::Wrench(_tposT.R()*_ftSensorTemp2.f(), _ftSensorTemp2.n());
for (int i=0; i<3; i++) _ftSensor[i] = _ftSensorTransformed[i+3];
for (int i=0; i<3; i++) _ftSensor[i+3] = _ftSensorTransformed[i];
}
|