diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f99a969ea560724d139a1bb81d46b405981cc3d..a9d5c834a76b8c037b820257a143991ea4f25c08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,7 @@ IF(BUILD_PYTHON_INTERFACE) python/${SOTTALOSBALANCE_PYNAME}/test/test_COMTraj_checkfeedback_gazebo.py python/${SOTTALOSBALANCE_PYNAME}/test/appli_COMTraj_checkfeedback_gazebo.py python/${SOTTALOSBALANCE_PYNAME}/test/test_ffSubscriber.py + python/${SOTTALOSBALANCE_PYNAME}/test/appli_ffSubscriber.py python/${SOTTALOSBALANCE_PYNAME}/test/test_zmpEstimator.py python/${SOTTALOSBALANCE_PYNAME}/test/test_dcmComControl.py python/${SOTTALOSBALANCE_PYNAME}/test/test_dcmComZmpControl.py @@ -170,6 +171,8 @@ IF(BUILD_PYTHON_INTERFACE) python/${SOTTALOSBALANCE_PYNAME}/test/test_comAdmittance.py python/${SOTTALOSBALANCE_PYNAME}/test/test_admittance_single_joint.py python/${SOTTALOSBALANCE_PYNAME}/test/appli_admittance_single_joint.py + python/${SOTTALOSBALANCE_PYNAME}/test/test_admittance_single_joint_velocity_based.py + python/${SOTTALOSBALANCE_PYNAME}/test/appli_admittance_single_joint_velocity_based.py python/${SOTTALOSBALANCE_PYNAME}/test/test_singleTraj.py python/${SOTTALOSBALANCE_PYNAME}/test/test_jointTrajGen.py python/${SOTTALOSBALANCE_PYNAME}/test/test_jointControl.py diff --git a/include/sot/talos_balance/admittance-controller.hh b/include/sot/talos_balance/admittance-controller.hh index 1699238fae707f8b4079cc0be0bebfffea451947..c3f97a48fa32f172bd915137c8c26d024787bfd7 100644 --- a/include/sot/talos_balance/admittance-controller.hh +++ b/include/sot/talos_balance/admittance-controller.hh @@ -62,7 +62,7 @@ namespace dynamicgraph { void init(const double & dt, const unsigned & n); - void setPosition(const dynamicgraph::Vector &); + void setPosition(const dynamicgraph::Vector & position); /* --- SIGNALS --- */ DECLARE_SIGNAL_IN(Kp, dynamicgraph::Vector); @@ -82,6 +82,7 @@ namespace dynamicgraph { getLogger().sendMsg("[AdmittanceController-"+name+"] "+msg, t, file, line); } + bool m_useState; protected: int m_n; bool m_initSucceeded; /// true if the entity has been successfully initialized diff --git a/python/sot_talos_balance/test/appli_admittance_single_joint_velocity_based.py b/python/sot_talos_balance/test/appli_admittance_single_joint_velocity_based.py new file mode 100644 index 0000000000000000000000000000000000000000..9d2f2c543612d25172e212f32a63cb98c47d9919 --- /dev/null +++ b/python/sot_talos_balance/test/appli_admittance_single_joint_velocity_based.py @@ -0,0 +1,50 @@ +from sot_talos_balance.create_entities_utils import create_joint_admittance_controller +from dynamic_graph.sot.core.meta_tasks_kine import MetaTaskKine6d, MetaTaskKineCom, gotoNd +from sot_talos_balance.meta_task_joint import MetaTaskKineJoint +from dynamic_graph import plug +from dynamic_graph.sot.core import SOT + +N_JOINTS = 32 +N_CONFIG = N_JOINTS + 6 + +dt = robot.timeStep + +JOINT = 25 +QJOINT = JOINT + 6 + +target = -10.0 + +# --- Joint +robot.taskJoint = MetaTaskKineJoint(robot.dynamic,QJOINT) +robot.taskJoint.featureDes.errorIN.value = [0.0] +robot.taskJoint.task.controlGain.value = 0 +robot.taskJoint.task.setWithDerivative(True) + +# --- Admittance controller +Kp = [0.1] +robot.admittance_control = create_joint_admittance_controller(JOINT,Kp,dt,robot) +plug(robot.admittance_control.qRef,robot.taskJoint.featureDes.errorIN) +plug(robot.admittance_control.dqRef,robot.taskJoint.featureDes.errordotIN) + +# --- CONTACTS +#define contactLF and contactRF +robot.contactLF = MetaTaskKine6d('contactLF',robot.dynamic,'LF',robot.OperationalPointsMap['left-ankle']) +robot.contactLF.feature.frame('desired') +robot.contactLF.gain.setConstant(100) +robot.contactLF.keep() +locals()['contactLF'] = robot.contactLF + +robot.contactRF = MetaTaskKine6d('contactRF',robot.dynamic,'RF',robot.OperationalPointsMap['right-ankle']) +robot.contactRF.feature.frame('desired') +robot.contactRF.gain.setConstant(100) +robot.contactRF.keep() +locals()['contactRF'] = robot.contactRF + +robot.sot = SOT('sot') +robot.sot.setSize(robot.dynamic.getDimension()) +plug(robot.sot.control,robot.device.control) + +robot.sot.push(robot.contactRF.task.name) +robot.sot.push(robot.contactLF.task.name) +robot.device.control.recompute(0) + diff --git a/python/sot_talos_balance/test/appli_ffSubscriber.py b/python/sot_talos_balance/test/appli_ffSubscriber.py new file mode 100644 index 0000000000000000000000000000000000000000..24eada78a5524e6316e0d3b97230a5a1f7770ff9 --- /dev/null +++ b/python/sot_talos_balance/test/appli_ffSubscriber.py @@ -0,0 +1,62 @@ +from sot_talos_balance.create_entities_utils import create_com_trajectory_generator +from dynamic_graph.sot.core.meta_tasks_kine import MetaTaskKine6d, MetaTaskKineCom, gotoNd +from dynamic_graph import plug +from dynamic_graph.sot.core import SOT + +from dynamic_graph.ros import RosSubscribe + + +from dynamic_graph.tracer_real_time import TracerRealTime +from sot_talos_balance.create_entities_utils import addTrace, dump_tracer + +dt = robot.timeStep; +robot.comTrajGen = create_com_trajectory_generator(dt,robot); + +robot.subscriber = RosSubscribe("base_subscriber") +robot.subscriber.add("vector","position","/sot/base_link/position") +robot.subscriber.add("vector","velocity","/sot/base_link/velocity") + +# --- COM +robot.taskCom = MetaTaskKineCom(robot.dynamic) +robot.dynamic.com.recompute(0) +robot.taskCom.featureDes.errorIN.value = robot.dynamic.com.value +robot.taskCom.task.controlGain.value = 10 + +# --- CONTACTS +#define contactLF and contactRF +robot.contactLF = MetaTaskKine6d('contactLF',robot.dynamic,'LF',robot.OperationalPointsMap['left-ankle']) +robot.contactLF.feature.frame('desired') +robot.contactLF.gain.setConstant(100) +robot.contactLF.keep() +locals()['contactLF'] = robot.contactLF + +robot.contactRF = MetaTaskKine6d('contactRF',robot.dynamic,'RF',robot.OperationalPointsMap['right-ankle']) +robot.contactRF.feature.frame('desired') +robot.contactRF.gain.setConstant(100) +robot.contactRF.keep() +locals()['contactRF'] = robot.contactRF + +robot.sot = SOT('sot') +robot.sot.setSize(robot.dynamic.getDimension()) +plug(robot.sot.control,robot.device.control) +plug(robot.comTrajGen.x, robot.taskCom.featureDes.errorIN) + +robot.sot.push(robot.contactRF.task.name) +robot.sot.push(robot.contactLF.task.name) +robot.sot.push(robot.taskCom.task.name) +robot.device.control.recompute(0) + +# --- TRACER +robot.tracer = TracerRealTime("zmp_tracer") +robot.tracer.setBufferSize(80*(2**20)) +robot.tracer.open('/tmp','dg_','.dat') +robot.device.after.addSignal('{0}.triger'.format(robot.tracer.name)) +robot.device.after.addSignal('{0}.position'.format(robot.subscriber.name)) # force recalculation +robot.device.after.addSignal('{0}.velocity'.format(robot.subscriber.name)) # force recalculation + +addTrace(robot.tracer, robot.dynamic, 'com') +addTrace(robot.tracer, robot.subscriber, 'position') +addTrace(robot.tracer, robot.subscriber, 'velocity') + +robot.tracer.start() + diff --git a/python/sot_talos_balance/test/test_admittance_single_joint_velocity_based.py b/python/sot_talos_balance/test/test_admittance_single_joint_velocity_based.py new file mode 100644 index 0000000000000000000000000000000000000000..ad68a4216b8b6c5339a9d9f2c7bc1c34310985d0 --- /dev/null +++ b/python/sot_talos_balance/test/test_admittance_single_joint_velocity_based.py @@ -0,0 +1,16 @@ +from sot_talos_balance.utils.run_test_utils import run_test, runCommandClient, evalCommandClient +from time import sleep + +run_test('appli_admittance_single_joint_velocity_based.py') + +sleep(1.0) +runCommandClient('robot.admittance_control.tauDes.value = [target]') +runCommandClient('robot.sot.push(robot.taskJoint.task.name)') + +sleep(5.0) +des_tau = evalCommandClient('target') +tau = evalCommandClient('robot.device.ptorque.value[JOINT]') + +print("Desired torque: %f" % des_tau) +print("Current torque: %f" % tau) + diff --git a/python/sot_talos_balance/test/test_ffSubscriber.py b/python/sot_talos_balance/test/test_ffSubscriber.py index 8b038cd5325f6eb016d8e78e620bc80d52f10664..40acd0a429fc7be4e221abdf79ac3e6d62e76129 100644 --- a/python/sot_talos_balance/test/test_ffSubscriber.py +++ b/python/sot_talos_balance/test/test_ffSubscriber.py @@ -1,121 +1,70 @@ -from sot_talos_balance.create_entities_utils import create_com_trajectory_generator -from dynamic_graph.sot.core.meta_tasks_kine import MetaTaskKine6d, MetaTaskKineCom, gotoNd -from dynamic_graph.sot.core.matrix_util import matrixToTuple -from dynamic_graph import plug -from dynamic_graph.sot.core import SOT -from numpy import eye +from sot_talos_balance.utils.run_test_utils import run_test, runCommandClient, evalCommandClient +from sot_talos_balance.utils.gazebo_utils import GazeboLinkStatePublisher from time import sleep -import os -from dynamic_graph.ros import RosSubscribe - -from dynamic_graph.tracer_real_time import TracerRealTime -from sot_talos_balance.create_entities_utils import addTrace, dump_tracer import matplotlib.pyplot as plt import numpy as np -def main(robot): - dt = robot.timeStep; - robot.comTrajGen = create_com_trajectory_generator(dt,robot); - - # --- COM - robot.taskCom = MetaTaskKineCom(robot.dynamic) - robot.dynamic.com.recompute(0) - robot.taskCom.featureDes.errorIN.value = robot.dynamic.com.value - robot.taskCom.task.controlGain.value = 10 - - # --- CONTACTS - #define contactLF and contactRF - robot.contactLF = MetaTaskKine6d('contactLF',robot.dynamic,'LF',robot.OperationalPointsMap['left-ankle']) - robot.contactLF.feature.frame('desired') - robot.contactLF.gain.setConstant(100) - robot.contactLF.keep() - locals()['contactLF'] = robot.contactLF - - robot.contactRF = MetaTaskKine6d('contactRF',robot.dynamic,'RF',robot.OperationalPointsMap['right-ankle']) - robot.contactRF.feature.frame('desired') - robot.contactRF.gain.setConstant(100) - robot.contactRF.keep() - locals()['contactRF'] = robot.contactRF - - robot.subscriber = RosSubscribe("base_subscriber") - robot.subscriber.add("vector","position","/sot/base_link/position") - robot.subscriber.add("vector","velocity","/sot/base_link/velocity") - - robot.sot = SOT('sot') - robot.sot.setSize(robot.dynamic.getDimension()) - plug(robot.sot.control,robot.device.control) - - robot.sot.push(robot.contactRF.task.name) - robot.sot.push(robot.taskCom.task.name) - robot.sot.push(robot.contactLF.task.name) - robot.device.control.recompute(0) - - # --- TRACER - robot.tracer = TracerRealTime("zmp_tracer") - robot.tracer.setBufferSize(80*(2**20)) - robot.tracer.open('/tmp','dg_','.dat') - robot.device.after.addSignal('{0}.triger'.format(robot.tracer.name)) - robot.device.after.addSignal('{0}.position'.format(robot.subscriber.name)) # force recalculation - robot.device.after.addSignal('{0}.velocity'.format(robot.subscriber.name)) # force recalculation - - addTrace(robot.tracer, robot.dynamic, 'com') - addTrace(robot.tracer, robot.subscriber, 'position') - addTrace(robot.tracer, robot.subscriber, 'velocity') - - plug(robot.comTrajGen.x, robot.taskCom.featureDes.errorIN); - - robot.tracer.start() - - sleep(1.0); - os.system("rosservice call \start_dynamic_graph") - sleep(2.0); - robot.comTrajGen.move(1,-0.025,4.0); - sleep(20.0); - robot.comTrajGen.startSinusoid(1,0.05,8.0); - sleep(5.0); - - dump_tracer(robot.tracer) - - # --- DISPLAY - com_data = np.loadtxt('/tmp/dg_'+robot.dynamic.name+'-com.dat') - pos_data = np.loadtxt('/tmp/dg_'+robot.subscriber.name+'-position.dat') - vel_data = np.loadtxt('/tmp/dg_'+robot.subscriber.name+'-velocity.dat') - - plt.figure() - plt.plot(com_data[:,1],'b-') - plt.plot(com_data[:,2],'r-') - plt.plot(com_data[:,3],'g-') - plt.title('COM') - plt.legend(['x','y','z']) - - plt.figure() - plt.plot(pos_data[:,1],'b-') - plt.plot(pos_data[:,2],'r-') - plt.plot(pos_data[:,3],'g-') - plt.title('Position measure') - plt.legend(['x','y','z']) - - plt.figure() - plt.plot(pos_data[:,4],'b-') - plt.plot(pos_data[:,5],'r-') - plt.plot(pos_data[:,6],'g-') - plt.title('Orientation measure') - plt.legend(['yaw','pitch','roll']) - - plt.figure() - plt.plot(vel_data[:,1],'b-') - plt.plot(vel_data[:,2],'r-') - plt.plot(vel_data[:,3],'g-') - plt.title('Linear velocity measure') - plt.legend(['x','y','z']) - - plt.figure() - plt.plot(vel_data[:,4],'b-') - plt.plot(vel_data[:,5],'r-') - plt.plot(vel_data[:,6],'g-') - plt.title('Angular velocity measure') - plt.legend(['x','y','z']) - - plt.show() +pub = GazeboLinkStatePublisher('base_link',1000) +print("Starting Gazebo link state publisher...") +pub.start() +print("Gazebo link state publisher started") +raw_input("Wait before running the test") + +run_test('appli_ffSubscriber.py') + +sleep(2.0) +runCommandClient('robot.comTrajGen.move(1,-0.025,4.0)') +sleep(5.0) +runCommandClient('robot.comTrajGen.startSinusoid(1,0.05,8.0)') +sleep(5.0) +runCommandClient('dump_tracer(robot.tracer)') + +# --- DISPLAY +com_data = np.loadtxt('/tmp/dg_' + evalCommandClient('robot.dynamic.name') + '-com.dat') +pos_data = np.loadtxt('/tmp/dg_' + evalCommandClient('robot.subscriber.name') + '-position.dat') +vel_data = np.loadtxt('/tmp/dg_' + evalCommandClient('robot.subscriber.name') + '-velocity.dat') + +plt.ion() + +plt.figure() +plt.plot(com_data[:,1],'b-') +plt.plot(com_data[:,2],'r-') +plt.plot(com_data[:,3],'g-') +plt.title('COM') +plt.legend(['x','y','z']) + +plt.figure() +plt.plot(pos_data[:,1],'b-') +plt.plot(pos_data[:,2],'r-') +plt.plot(pos_data[:,3],'g-') +plt.title('Position measure') +plt.legend(['x','y','z']) + +plt.figure() +plt.plot(pos_data[:,4],'b-') +plt.plot(pos_data[:,5],'r-') +plt.plot(pos_data[:,6],'g-') +plt.title('Orientation measure') +plt.legend(['yaw','pitch','roll']) + +plt.figure() +plt.plot(vel_data[:,1],'b-') +plt.plot(vel_data[:,2],'r-') +plt.plot(vel_data[:,3],'g-') +plt.title('Linear velocity measure') +plt.legend(['x','y','z']) + +plt.figure() +plt.plot(vel_data[:,4],'b-') +plt.plot(vel_data[:,5],'r-') +plt.plot(vel_data[:,6],'g-') +plt.title('Angular velocity measure') +plt.legend(['x','y','z']) + +raw_input("Wait before leaving the simulation") +print("Stopping Gazebo link state publisher...") +pub.stop() +sleep(0.1) +print("Gazebo link state publisher stopped") diff --git a/src/admittance-controller.cpp b/src/admittance-controller.cpp index f38322ea1c39585d83e34241bdf42f7f5c05c955..d144c2fd99bbae7b36a9eb639b04720c3d3636eb 100644 --- a/src/admittance-controller.cpp +++ b/src/admittance-controller.cpp @@ -34,6 +34,9 @@ namespace dynamicgraph using namespace dg::command; //Size to be aligned "-------------------------------------------------------" + +#define PROFILE_ADMITTANCECONTROLLER_QREF_COMPUTATION "AdmittanceController: qRef computation " + #define PROFILE_ADMITTANCECONTROLLER_DQREF_COMPUTATION "AdmittanceController: dqRef computation " #define INPUT_SIGNALS m_KpSIN << m_stateSIN << m_tauSIN << m_tauDesSIN @@ -66,6 +69,8 @@ namespace dynamicgraph /* Commands. */ addCommand("init", makeCommandVoid2(*this, &AdmittanceController::init, docCommandVoid2("Initialize the entity.","time step","Number of elements"))); addCommand("setPosition", makeCommandVoid1(*this, &AdmittanceController::setPosition, docCommandVoid1("Set initial reference position.","Initial position"))); + addCommand("useExternalState", makeDirectSetter(*this,&m_useState, docDirectGetter("use external state","bool"))); + addCommand("isUsingExternalState", makeDirectGetter(*this,&m_useState, docDirectGetter("use external state","bool"))); } void AdmittanceController::init(const double & dt, const unsigned & n) @@ -74,8 +79,6 @@ namespace dynamicgraph return SEND_MSG("n must be at least 1", MSG_TYPE_ERROR); if(!m_KpSIN.isPlugged()) return SEND_MSG("Init failed: signal Kp is not plugged", MSG_TYPE_ERROR); - if(!m_stateSIN.isPlugged()) - return SEND_MSG("Init failed: signal state is not plugged", MSG_TYPE_ERROR); if(!m_tauSIN.isPlugged()) return SEND_MSG("Init failed: signal tau is not plugged", MSG_TYPE_ERROR); if(!m_tauDesSIN.isPlugged()) @@ -87,9 +90,10 @@ namespace dynamicgraph m_initSucceeded = true; } - void AdmittanceController::setPosition(const dynamicgraph::Vector & state) + void AdmittanceController::setPosition(const dynamicgraph::Vector & position) + { - m_q = state; + m_q = position; } /* ------------------------------------------------------------------- */ @@ -110,7 +114,6 @@ namespace dynamicgraph const Vector & tau = m_tauSIN(iter); const Vector & Kp = m_KpSIN(iter); - assert(state.size()==m_n && "Unexpected size of signal state"); assert(tau.size()==m_n && "Unexpected size of signal tau"); assert(tauDes.size()==m_n && "Unexpected size of signal tauDes"); assert(Kp.size()==m_n && "Unexpected size of signal Kp"); @@ -132,14 +135,30 @@ namespace dynamicgraph return s; } + getProfiler().start(PROFILE_ADMITTANCECONTROLLER_QREF_COMPUTATION); + const Vector & dqRef = m_dqRefSOUT(iter); assert(dqRef.size()==m_n && "Unexpected size of signal dqRef"); + if(m_useState) + { + if(!m_stateSIN.isPlugged()) + { + SEND_MSG("Signal state is requested, but is not plugged", MSG_TYPE_ERROR); + return s; + } + const Vector & state = m_stateSIN(iter); + assert(state.size()==m_n && "Unexpected size of signal state"); + m_q = state; + } + m_q += dqRef*m_dt; s = m_q; + getProfiler().stop(PROFILE_ADMITTANCECONTROLLER_QREF_COMPUTATION); + return s; }