22#include <yarp/os/all.h>
23#include <yarp/dev/PolyDriver.h>
24#include <yarp/dev/IPositionControl.h>
25#include <yarp/dev/IControlMode.h>
26#include <yarp/dev/IControlLimits.h>
27#include <yarp/math/Rand.h>
28#include <yarp/math/NormRand.h>
30#include "iCubBlinker_IDL.h"
33using namespace yarp::os;
34using namespace yarp::math;
38 INTERACTION_MODE_UNKNOWN=0,
39 INTERACTION_MODE_IDLE, INTERACTION_MODE_CONVERSATION,
40 INTERACTION_MODE_CALIBRATION_CLOSURE,INTERACTION_MODE_CALIBRATION_OPENING
43string int2hex(
const int _i)
45 std::ostringstream hex;
46 hex << std::hex << _i;
50string int2string(
const int _a)
52 std::ostringstream ss;
57string double2string(
const double _a)
59 std::ostringstream ss;
65class Blinker:
public RFModule,
public iCubBlinker_IDL
71 yarp::dev::PolyDriver _poly;
72 yarp::dev::IPositionControl* _iPos{
nullptr };
73 double _minPoly{ 0.0 }, _maxPoly{ 0.0 };
75 size_t _joint_eylids{ 0 };
81 string blinkingBehavior;
82 string blinkingPeriod;
97 InteractionMode int_mode;
98 double fixed_blinkper;
100 double blinkper_nrm, blinkper_sgm;
101 double closure_nrm, closure_sgm;
102 double sustain_nrm, sustain_sgm;
103 double opening_nrm, opening_sgm;
106 bool sendRawValue(
const string &_value)
108 if (emotionsPort.getOutputCount()>0)
111 cmd.addString(_value);
112 emotionsPort.write(cmd);
123 if (blinkingBehavior==
"naturalistic")
125 return doBlinkNaturalistic();
127 else if (blinkingBehavior==
"fast")
129 return doBlinkFast();
133 yError(
"Blinking behavior is neither naturalistic or fast!");
144 yDebug()<<
"Sending position"<<_maxPoly;
145 res &= _iPos->positionMove(_joint_eylids, _maxPoly);
147 bool motion_done =
false;
148 while (!motion_done) {
150 auto ok = _iPos->checkMotionDone(_joint_eylids, &motion_done);
152 yError()<<
"Unable to check if the motion of the eyelids is done";
157 yDebug()<<
"Sending position"<<_minPoly;
158 res &= _iPos->positionMove(_joint_eylids, _minPoly);
161 yDebug(
"Sending raw value: %i",sMin);
162 res = res && sendRawValue((
"S" + int2string(sMin)));
164 yDebug(
"Sending raw value: %i",sMax);
165 res = res && sendRawValue((
"S" + int2string(sMax)));
174 bool doBlinkNaturalistic()
176 double t_cl = NormRand::scalar(closure_nrm,closure_sgm);
178 while (t_cl<closure_nrm-closure_sgm || t_cl>closure_nrm+closure_sgm)
180 t_cl = NormRand::scalar(closure_nrm,closure_sgm);
183 double t_su = NormRand::scalar(sustain_nrm,sustain_sgm);
185 while (t_su<sustain_nrm-sustain_sgm || t_su>sustain_nrm+sustain_sgm)
187 t_su = NormRand::scalar(sustain_nrm,sustain_sgm);
190 double t_op = NormRand::scalar(opening_nrm,opening_sgm);
192 while (t_op<opening_nrm-opening_sgm || t_op>opening_nrm+opening_sgm)
194 t_op = NormRand::scalar(opening_nrm,opening_sgm);
197 yDebug(
"Starting a naturalistic blink. T_cl %g \t T_su %g \t T_op %g Total %g",
198 t_cl,t_su,t_op,t_cl+t_su+t_op);
204 for (
int i = 0; i < 11; i++)
206 auto targetPos = _minPoly + i * (_maxPoly - _minPoly) / 10;
207 string rawvalue =
"S" + int2string(targetPos);
208 yDebug() <<
"Sending postion: " << targetPos <<
" " << i * 10;
209 _iPos->positionMove(_joint_eylids, targetPos);
210 bool motion_done =
false;
211 while (!motion_done) {
213 auto ok = _iPos->checkMotionDone(_joint_eylids, &motion_done);
215 yError()<<
"Unable to check if the motion of the eyelids is done";
220 Time::delay(t_op / 10.0);
225 for (
int i = 0; i < 11; i++)
227 auto targetPos = _maxPoly - i * (_maxPoly - _minPoly) / 10;
228 string rawvalue =
"S" + int2string(targetPos);
229 yDebug() <<
"Sending postion: " << targetPos <<
" " << 100 - i * 10;
230 _iPos->positionMove(_joint_eylids, targetPos);
231 bool motion_done =
false;
232 while (!motion_done) {
234 auto ok = _iPos->checkMotionDone(_joint_eylids, &motion_done);
236 yError()<<
"Unable to check if the motion of the eyelids is done";
241 Time::delay(t_cl / 10.0);
246 for (
int i = 0; i < 11; i++)
248 int valueToSend = sMax - i * (sMax - sMin) / 10;
249 string rawvalue =
"S" + int2string(valueToSend);
250 yDebug(
"Sending raw value: %s %i", rawvalue.c_str(), 100 - i * 10);
251 sendRawValue(rawvalue);
252 Time::delay(t_cl / 10.0);
257 for (
int i = 0; i < 11; i++)
259 int valueToSend = sMin + i * (sMax - sMin) / 10;
260 string rawvalue =
"S" + int2string(valueToSend);
261 yDebug(
"Sending raw value: %s %i", rawvalue.c_str(), i * 10);
262 sendRawValue(rawvalue);
263 Time::delay(t_op / 10.0);
271 bool set_calib_values()
273 yWarning(
"[iCubBlinker] Sending calib values does nothing currently!");
279 bool setInteractionMode_IDLE()
281 Bottle idle_mode=(rf->findGroup(
"idle_mode"));
283 if (idle_mode.size()>0)
285 bool res = retrieveInteractionMode_params(idle_mode);
288 int_mode = INTERACTION_MODE_IDLE;
294 yError(
"[iCubBlinker] no idle_mode group found in .ini file!");
302 bool setInteractionMode_CONVERSATION()
304 Bottle conversation_mode=(rf->findGroup(
"conversation_mode"));
306 if (conversation_mode.size()>0)
308 bool res = retrieveInteractionMode_params(conversation_mode);
311 int_mode = INTERACTION_MODE_CONVERSATION;
317 yError(
"[iCubBlinker] no conversation_mode group found in ini file!");
325 bool retrieveInteractionMode_params(Bottle &int_mode)
328 blinkper_nrm = int_mode.check(
"blinkper_nrm",Value(5.2)).asFloat64();
329 blinkper_sgm = int_mode.check(
"blinkper_sgm",Value(3.7)).asFloat64();
331 closure_nrm = int_mode.check(
"closure_nrm",Value(0.111)).asFloat64();
332 closure_sgm = int_mode.check(
"closure_sgm",Value(0.031)).asFloat64();
334 sustain_nrm = int_mode.check(
"sustain_nrm",Value(0.020)).asFloat64();
335 sustain_sgm = int_mode.check(
"sustain_sgm",Value(0.005)).asFloat64();
337 opening_nrm = int_mode.check(
"opening_nrm",Value(0.300)).asFloat64();
338 opening_sgm = int_mode.check(
"opening_sgm",Value(0.123)).asFloat64();
344 string InteractionMode_params_toString()
348 res <<
"[" << name <<
"] blinker period:\t[ nrm " << blinkper_nrm <<
"\tsgm " << blinkper_sgm <<
" ]" << endl;
349 res <<
"[" << name <<
"] closure speed: \t[ nrm " << closure_nrm <<
"\tsgm " << closure_sgm <<
" ]" << endl;
350 res <<
"[" << name <<
"] sustain speed: \t[ nrm " << sustain_nrm <<
"\tsgm " << sustain_sgm <<
" ]" << endl;
351 res <<
"[" << name <<
"] opening speed: \t[ nrm " << opening_nrm <<
"\tsgm " << opening_sgm <<
" ]" << endl;
357 bool computeNextBlinkPeriod()
359 if (blinkingPeriod==
"fixed")
362 yInfo(
"[iCubBlinker] Next blink in %g [s]",dt);
365 else if (blinkingPeriod==
"gaussian")
371 while (dt<blinkper_nrm-blinkper_sgm || dt>blinkper_nrm+blinkper_sgm)
373 dt = NormRand::scalar(blinkper_nrm,blinkper_sgm);
374 yDebug(
"[iCubBlinker] Computing next blink.. %g %g %g %i",dt,blinkper_nrm-blinkper_sgm,blinkper_nrm+blinkper_sgm,i); i++;
376 yInfo(
"[iCubBlinker] Next blink in %g [s]",dt);
381 yError(
"blinkingPeriod was neither gaussian nor fixed!");
388 bool configure(ResourceFinder &_mainRF)
390 rf =
const_cast<ResourceFinder*
>(&_mainRF);
392 name =rf->check(
"name", Value(
"iCubBlinker")).asString().c_str();
393 robot=rf->check(
"robot",Value(
"icub")).asString().c_str();
394 blinkingBehavior=rf->check(
"blinkingBehavior",Value(
"fast")).asString().c_str();
395 blinkingPeriod=rf->check(
"blinkingPeriod",Value(
"fixed")).asString().c_str();
396 fixed_blinkper=rf->check(
"fixedBlinkPer",Value(5.0)).asFloat64();
398 isBlinking=rf->check(
"autoStart");
400 yarp::os::Property rcb_face_conf{ {
"device",Value(
"remote_controlboard")},
401 {
"local", Value(getName(
"/face/remoteControlBoard"))},
402 {
"remote",Value(
"/icub/face")},
403 {
"part",Value(
"face")} };
405 if (_poly.open(rcb_face_conf))
407 yarp::dev::IControlMode* iCM{
nullptr };
408 yarp::dev::IControlLimits* iCtrlLim{
nullptr };
409 auto ok = _poly.view(iCM);
410 ok &= _poly.view(_iPos);
411 ok &= _poly.view(iCtrlLim);
413 ok &= iCM->setControlMode(_joint_eylids, VOCAB_CM_POSITION);
415 ok &= iCtrlLim->getLimits(_joint_eylids, &_minPoly, &_maxPoly);
416 _maxPoly = _maxPoly - 25.0;
419 ok &= _iPos->setRefSpeed(_joint_eylids, 50.0);
420 ok &= _iPos->setRefAcceleration(_joint_eylids, std::numeric_limits<double>::max());
424 yError() <<
"Fail to configure correctly the remote_controlboard";
430 yWarning() <<
"Failed to open the remote_controlboard device for commanding the eyelids, you can ignore this warning if your robot doesn't have the rfe board";
431 auto ok = emotionsPort.open((
"/" + name +
"/emotions/raw").c_str());
432 if (rf->check(
"autoConnect"))
434 ok &= Network::connect(emotionsPort.getName().c_str(),
"/" + robot +
"/face/raw/in");
438 yError() <<
"Configuration phase failed, please check if yarprobotinterface (iCub with RFE) or the the serial device (iCub without RFE) are running.";
445 doubleBlink=rf->check(
"doubleBlink");
447 rpcPort.open((
"/"+name+
"/rpc").c_str());
450 Bottle calib=(rf->findGroup(
"calibration"));
452 if (calib.size() > 0)
454 sMin = calib.check(
"sMin",Value(00)).asInt32();
455 sMax = calib.check(
"sMax",Value(70)).asInt32();
457 yInfo(
"[iCubBlinker] Eyelid calibs loaded: (%i %i)", sMin, sMax);
469 int_mode = INTERACTION_MODE_IDLE;
470 set_interaction_mode(
"idle");
472 computeNextBlinkPeriod();
481 bool attach(RpcServer &source)
483 return this->yarp().attachAsServer(source);
494 emotionsPort.close();
500 double getPeriod() {
return 0.01; }
504 yInfo(
"[iCubBlinker] start command received");
511 yInfo(
"[iCubBlinker] stop command received");
518 string res=isBlinking?
"on":
"off";
519 res=res+
"_"+get_interaction_mode();
532 return doBlinkFast();
535 bool blink_naturalistic()
538 return doBlinkNaturalistic();
544 return doBlink() && doBlink();
548 string print_params()
553 res <<
"[" << name <<
"] robot: " << robot << endl;
554 res <<
"[" << name <<
"] blinkingBehavior: " << blinkingBehavior;
555 res <<
"\tblinkingPeriod: " << blinkingPeriod <<
"\tFixed Blink period: " << fixed_blinkper << endl;
556 res <<
"[" << name <<
"] isBlinking: " << (isBlinking?
"true":
"false") <<
"\tdoubleBlink: " << get_double_blink() << endl;
557 res <<
"[" << name <<
"] calibration [sMin sMax]: [" << sMin <<
" " << sMax <<
"]" << endl;
558 res <<
"[" << name <<
"] InteractionMode: " << get_interaction_mode() << endl;
559 res << InteractionMode_params_toString();
561 printf(
"%s\n", res.str().c_str());
569 string path = rf->getHomeContextPath().c_str();
570 string inifile = path+
"/"+rf->find(
"from").asString();
571 yInfo(
"Saving calib configuration to %s",inifile.c_str());
574 myfile.open(inifile.c_str(),ios::trunc);
576 if (myfile.is_open())
578 myfile <<
"name \t" << name << endl;
579 myfile <<
"robot \t" << robot << endl;
580 if (rf->check(
"autoStart"))
582 myfile <<
"autoStart" << endl;
584 myfile <<
"calib \t(" << sMin <<
"\t" << sMax <<
")\n";
593 string fileName=rf->findFile(rf->find(
"from").asString().c_str());
596 yWarning(
"[iCubBlinker::load] No filename has been found. Skipping..");
600 yInfo(
"[iCubBlinker::load] File loaded: %s", fileName.c_str());
601 yWarning(
"[iCubBlinker::load] Only the calib values will be loaded.");
602 Property data; data.fromConfigFile(fileName.c_str());
603 Bottle b; b.read(data);
604 Bottle calib=(b.findGroup(
"calibration"));
606 if (calib.size() > 0)
608 sMin = calib.check(
"sMin",Value(00)).asInt32();
609 sMax = calib.check(
"sMax",Value(70)).asInt32();
611 yInfo(
"[iCubBlinker::load] Eyelid calibs loaded: (%i %i)", sMin, sMax);
622 bool set_interaction_mode(
const std::string& val)
628 res = setInteractionMode(INTERACTION_MODE_IDLE);
630 else if (val ==
"conversation")
632 res = setInteractionMode(INTERACTION_MODE_CONVERSATION);
635 yInfo(
"[iCubBlinker] Interaction mode set to %s",get_interaction_mode().c_str());
636 printf(
"%s\n",InteractionMode_params_toString().c_str());
642 bool setInteractionMode(InteractionMode _int_mode)
644 if (_int_mode==INTERACTION_MODE_IDLE)
646 return setInteractionMode_IDLE();
648 else if (_int_mode==INTERACTION_MODE_CONVERSATION)
650 return setInteractionMode_CONVERSATION();
654 int_mode = _int_mode;
660 string get_interaction_mode()
662 if (int_mode == INTERACTION_MODE_UNKNOWN)
664 return "interaction_mode_unknown";
666 else if (int_mode == INTERACTION_MODE_IDLE)
670 else if (int_mode == INTERACTION_MODE_CONVERSATION)
672 return "conversation";
674 else if (int_mode == INTERACTION_MODE_CALIBRATION_OPENING ||
675 int_mode == INTERACTION_MODE_CALIBRATION_CLOSURE)
677 return "calibration";
686 yError(
"Not yet implemented!");
691 bool set_blinking_behavior(
const std::string& val)
695 if (val==
"naturalistic" || val==
"fast")
697 blinkingBehavior=val;
702 yError(
"Invalid blinking behavior requested! %s",val.c_str());
705 yInfo(
"Blinking behavior set to %s",get_blinking_behavior().c_str());
711 string get_blinking_behavior()
713 return blinkingBehavior;
717 bool set_blinking_period(
const std::string& val)
721 if (val==
"gaussian" || val==
"fixed")
728 yError(
"Invalid blinking period requested! %s",val.c_str());
731 yInfo(
"Blinking period set to %s",get_blinking_period().c_str());
737 string get_blinking_period()
739 if (blinkingPeriod==
"gaussian")
741 return blinkingPeriod;
745 return blinkingPeriod +
" (" + double2string(fixed_blinkper) +
" [s])";
750 bool set_double_blink(
const string &val)
769 string get_double_blink()
771 return (doubleBlink?
"on":
"off");
777 lock_guard<mutex> lg(mtx);
779 if (Time::now()-t0>=dt)
786 if ((++doubleBlinkCnt)%5==0)
793 computeNextBlinkPeriod();
803int main(
int argc,
char *argv[])
806 rf.setDefaultContext(
"funny-things");
807 rf.setDefaultConfigFile(
"iCubBlinker.ini");
808 rf.configure(argc,argv);
810 if (rf.check(
"help"))
813 yInfo(
"[ICUB BLINKER] Options:");
814 yInfo(
" --context path: where to find the called resource (default funnyThings).");
815 yInfo(
" --from from: the name of the .ini file (default iCubBlinker.ini).");
816 yInfo(
" --name name: the name of the module (default iCubBlinker).");
817 yInfo(
" --robot robot: the name of the robot. Default icub.");
818 yInfo(
" --autoStart flag: If the module should autostart the blinking behavior. Default no.");
819 yInfo(
" --autoConnect flag: If the module should autoconnect itself to the face expression port. Default no.");
820 yInfo(
" --blinkingBehavior string: String that specifies the desired blinking behavior (either naturalistic or fast).");
821 yInfo(
" --blinkingPeriod string: String that specifies the desired blinking period (either gaussian or fixed).");
822 yInfo(
" --fixedBlinkPer double: the blinking period in case of fixed blinkingPeriod (default 5.0).");
823 yInfo(
" --calibration::sMin int: Manually set the minimum value for the blinking behavior (default 00).");
824 yInfo(
" --calibration::sMax int: Manually set the maximum value for the blinking behavior (default 70).");
830 if (!yarp.checkNetwork())
832 yError(
"YARP server not available!");
837 return blinker.runModule(rf);