CyberGloveProsthesisControl.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package RobotAPI::ProsthesisKinestheticTeachIn
17 * @author Julia Starke ( julia dot starke at kit dot edu )
18 * @date 2020
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
24
25#include <chrono>
26#include <filesystem>
27#include <fstream>
28#include <iostream>
29
30#include <VirtualRobot/math/Helpers.h>
31
34
37
40
41using namespace armarx;
42using namespace ProsthesisKinestheticTeachIn;
43
44// DO NOT EDIT NEXT LINE
45CyberGloveProsthesisControl::SubClassRegistry
46 CyberGloveProsthesisControl::Registry(CyberGloveProsthesisControl::GetName(),
48
49void
51{
52 // put your user code for the enter-point here
53 // execution time should be short (<100ms)
54 ARMARX_IMPORTANT << "onEnter";
55}
56
57void
58CyberGloveProsthesisControl::shapeHand(float fingers, float thumb)
59{
60 getHandUnit()->setJointAngles({{"Fingers", fingers}, {"Thumb", thumb}});
61}
62
63// possibly add debug observer code here
64//class ProsthesisPositionHelper;
65//typedef boost::shared_ptr<ProsthesisPositionHelper> ProsthesisPositionHelperPtr;
66//class ProsthesisPositionHelper
67//{
68//public:
69
70// ProsthesisPositionHelper(ObserverInterfacePrx robotUnitObserver)
71// : robotUnitObserver(robotUnitObserver)
72// {
73// thumbDF = DatafieldRefPtr::dynamicCast(robotUnitObserver->getDatafieldRefByName("SensorDevices", "RightHandThumb_position"));
74// fingersDF = DatafieldRefPtr::dynamicCast(robotUnitObserver->getDatafieldRefByName("SensorDevices", "RightHandFingers_position"));
75// }
76
77// ~ProsthesisPositionHelper()
78// {
79// }
80
81// float getThumbPosition()
82// {
83// return thumbDF->getDataField()->getFloat();
84// }
85
86// float getFingersPosition()
87// {
88// return fingersDF->getDataField()->getFloat();
89// }
90
91// ObserverInterfacePrx robotUnitObserver;
92// DatafieldRefPtr thumbDF;
93// DatafieldRefPtr fingersDF;
94//};
95
96
97void
99{
100 // put your user code for the execution-phase here
101 // runs in seperate thread, thus can do complex operations
102 // should check constantly whether isRunningTaskStopped() returns true
103
104 // get a private kinematic instance for this state of the robot (tick "Robot State Component" proxy checkbox in statechart group)
105 // VirtualRobot::RobotPtr robot = getLocalRobot();
106
107 // DebugDrawerHelper debugDrawerHelper(getDebugDrawerTopic(), "RemoteTeachIn", robot);
108 // ProsthesisPositionHelperPtr prosthesisPositionHelper;
109
111 rootLayoutBuilder.addChild(RemoteGui::makeLabel("message"))
112 .addChild(RemoteGui::makeLabel("status").value("status"))
113 .addChild(RemoteGui::makeLabel("instruction"))
114 .addChild(
116 .addChild(
117 RemoteGui::makeButton("calibrate_cyber_glove").label("Calibrate Cyber Glove"))
118 .addChild(RemoteGui::makeButton("start_recording").label("Start Recording"))
119 .addChild(RemoteGui::makeButton("stop_recording").label("Stop Recording"))
120 .addChild(
121 RemoteGui::makeButton("set_latest_timestamp").label("Set Latest Timestamp")))
122 .addChild(RemoteGui::makeLineEdit("file_name").value("Set file name"))
123 //.addChild(RemoteGui::makeLabel("longMessage").value("test"))
124 .addChild(new RemoteGui::VSpacer());
125
126 getRemoteGui()->createTab("CyberGloveProsthesisControl", rootLayoutBuilder);
127 RemoteGui::TabProxy guiTab = RemoteGui::TabProxy(getRemoteGui(), "CyberGloveProsthesisControl");
128
129 //std::string packageName = "ProsthesisKinestheticTeachIn";
130 //armarx::CMakePackageFinder finder(packageName);
131 //std::string dataDir = finder.getDataDir() + "/" + packageName + "/recordings/";
132 std::filesystem::path myPath = std::filesystem::current_path();
133 std::string dataDir = myPath.string() + "/prosthesisKinestheticTeaching/recordings/";
134 ARMARX_IMPORTANT << "Data recording directory: " << dataDir;
135 //SimpleJsonLoggerPtr logger;
136 std::ofstream logger;
137 std::string timestamp = "";
138 std::string latest_timestamp = "";
139
140 PhaseType currentPhase = PhaseType::CalibOpen;
141
142 int lastClickCountCalibrateCyberGlove = guiTab.getValue<int>("calibrate_cyber_glove").get();
143 int lastClickCountRecordOn = guiTab.getValue<int>("start_recording").get();
144 int lastClickCountRecordOff = guiTab.getValue<int>("stop_recording").get();
145 int lastClickCountLatestTimestamp = guiTab.getValue<int>("set_latest_timestamp").get();
146
147 bool record = false;
148
149 CyberGloveValues openValues;
150 CyberGloveValues closedValues;
151 bool storeCalibrationValuesForCyberGlove = false;
152
153 std::chrono::time_point<std::chrono::system_clock> start, end;
154
155
156 // uncomment this if you need a continous run function. Make sure to use sleep or use blocking wait to reduce cpu load.
157 while (!isRunningTaskStopped()) // stop run function if returning true
158 {
159 // do your calculations
160 // synchronize robot clone to most recent state
161 //RemoteRobot::synchronizeLocalClone(robot, getRobotStateComponent());
162 PhaseType nextPhase = currentPhase;
163
164 guiTab.receiveUpdates();
165 int calibrateCyberGloveClickCount = guiTab.getValue<int>("calibrate_cyber_glove").get();
166 bool calibrateCyberGloveButtonWasClicked =
167 calibrateCyberGloveClickCount > lastClickCountCalibrateCyberGlove;
168 lastClickCountCalibrateCyberGlove = calibrateCyberGloveClickCount;
169
170 int startRecordingClickCount = guiTab.getValue<int>("start_recording").get();
171 bool startRecordingButtonClicked = startRecordingClickCount > lastClickCountRecordOn;
172 lastClickCountRecordOn = startRecordingClickCount;
173
174 int stopRecordingClickCount = guiTab.getValue<int>("stop_recording").get();
175 bool stopRecordingButtonClicked = stopRecordingClickCount > lastClickCountRecordOff;
176 lastClickCountRecordOff = stopRecordingClickCount;
177
178 int setLatestTimestampClickCount = guiTab.getValue<int>("set_latest_timestamp").get();
179 bool setLatestTimeStampButtonClicked =
180 setLatestTimestampClickCount > lastClickCountLatestTimestamp;
181 lastClickCountLatestTimestamp = setLatestTimestampClickCount;
182 ARMARX_IMPORTANT << "Timestamp click count: " << setLatestTimestampClickCount;
183
184 CyberGloveValues currentValues = getCyberGloveObserver()->getLatestValues();
185
186 float thumbValue = 0;
187 float indexValue = 0;
188
189 auto calcThumbSum = [](const CyberGloveValues& v)
190 { return v.thumbAbd + v.thumbCMC + v.thumbIP + v.thumbMCP; };
191 auto calcIndexSum = [](const CyberGloveValues& v)
192 { return v.indexDIP + v.indexMCP + v.indexPIP; };
193
194 if (setLatestTimeStampButtonClicked)
195 {
196 if (!latest_timestamp.empty())
197 {
198 timestamp = latest_timestamp;
199 //guiTab.getValue<std::string>("longMessage").set("Resume latest timestamp. Latest timestamp is " + latest_timestamp);
200 getMessageDisplay()->setMessage("Resume latest timestamp",
201 "Latest timestamp is " + latest_timestamp);
202 }
203 else
204 {
205 //guiTab.getValue<std::string>("longMessage").set("No timestamp stored.");
206 getMessageDisplay()->setMessage("No timestamp stored.",
207 "Take a point cloud to generate a timestamp.");
208 }
209 }
210
211 if (startRecordingButtonClicked)
212 {
213 if (timestamp.empty())
214 {
215 IceUtil::Time now = IceUtil::Time::now();
216 time_t timer = now.toSeconds();
217 struct tm* ts;
218 char buffer[80];
219 ts = localtime(&timer);
220 strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S", ts);
221 std::string str(buffer);
222 timestamp = str;
223 latest_timestamp = timestamp;
224 }
225 std::string fileName = guiTab.getValue<std::string>("file_name").get();
226 //logger.reset(new SimpleJsonLogger(dataDir + fileName + "-temp-" + timestamp + ".json", false));
227 logger.open(dataDir + fileName + "-temp-" + timestamp + ".csv");
228 start = std::chrono::system_clock::now();
229 logger << "Timestamp, indexTarget, thumbTarget, indexActual, thumbActual" << std::endl;
230 record = true;
231 storeCalibrationValuesForCyberGlove = true;
232 getMessageDisplay()->setMessage("Start Recording Trajectory", "");
233 }
234
235 if (stopRecordingButtonClicked)
236 {
237 timestamp = "";
238 //logger->close();
239 logger.close();
240 record = false;
241
242 getMessageDisplay()->setMessage("Stop Recording Trajectory", "");
243 }
244
245 if (currentPhase == PhaseType::CalibOpen)
246 {
247 getMessageDisplay()->setMessage("Calibrating open hand", "");
248 guiTab.getValue<std::string>("message").set("Put your hand flat on the table!");
249 if (calibrateCyberGloveButtonWasClicked)
250 {
251 openValues = currentValues;
252 nextPhase = PhaseType::CalibClose;
253 }
254 }
255 if (currentPhase == PhaseType::CalibClose)
256 {
257 getMessageDisplay()->setMessage("Calibrating closed hand", "");
258 guiTab.getValue<std::string>("message").set("Make a fist!");
259 if (calibrateCyberGloveButtonWasClicked)
260 {
261 closedValues = currentValues;
262 nextPhase = PhaseType::TeachIn;
263 }
264 }
265 if (currentPhase == PhaseType::TeachIn)
266 {
267 guiTab.getValue<std::string>("message").set("Teach-In!");
268 thumbValue = math::Helpers::Clamp(0,
269 1,
270 math::Helpers::ILerp(calcThumbSum(openValues),
271 calcThumbSum(closedValues),
272 calcThumbSum(currentValues)));
273 indexValue = math::Helpers::Clamp(0,
274 1,
275 math::Helpers::ILerp(calcIndexSum(openValues),
276 calcIndexSum(closedValues),
277 calcIndexSum(currentValues)));
278
279 if (!std::isfinite(thumbValue))
280 {
281 thumbValue = 0;
282 }
283 if (!std::isfinite(indexValue))
284 {
285 indexValue = 0;
286 }
287
288 //SimpleJsonLoggerEntry e;
289 shapeHand(indexValue, thumbValue);
290
291 // e.AddTimestamp();
292 NameValueMap handMotorValues = getHandUnit()->getCurrentJointValues();
293 // e.Add("indexValueTarget", indexValue);
294 // e.Add("thumbValueTarget", thumbValue);
295 // e.Add("indexValueActual", handMotorValues["Fingers"]);
296 // e.Add("thumbValueActual", handMotorValues["Thumb"]);
297
298 if (record)
299 {
300
301 end = std::chrono::system_clock::now();
302 double elapsed_milliseconds =
303 std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
304 logger << elapsed_milliseconds << "," << indexValue << "," << thumbValue << ","
305 << handMotorValues["Fingers"] << "," << handMotorValues["Thumb"]
306 << std::endl;
307
308 // if (storeCalibrationValuesForCyberGlove)
309 // {
310 // ARMARX_IMPORTANT << "Recording raw joint values for calibration.";
311 // e = getRawJointValuesForCalibration(openValues, closedValues);
312 // storeCalibrationValuesForCyberGlove = false;
313 // }
314 // else
315 // {
316 // addCurrentRawJointValues(currentValues, e);
317 // }
318 // logger->log(e);
319 }
320 }
321
322 {
323 std::stringstream ss;
324 ss << "index: " << currentValues.indexMCP << " " << currentValues.indexPIP << " "
325 << currentValues.indexDIP << "\n";
326 ss << "thumb: " << currentValues.thumbMCP << " " << currentValues.thumbIP << " "
327 << currentValues.thumbCMC << " " << currentValues.thumbAbd << "\n";
328 ss << std::floor(indexValue * 100) << "% " << std::floor(thumbValue * 100) << "%\n";
329 guiTab.getValue<std::string>("status").set(ss.str());
330 }
331 //ARMARX_IMPORTANT << "index: " << getCyberGloveObserver()->getLatestValues().indexMCP << " thumb: " << getCyberGloveObserver()->getLatestValues().thumbMCP;
332
333 guiTab.sendUpdates();
334
335 currentPhase = nextPhase;
336
338 }
339
340 shapeHand(0, 0);
341 //logger->close();
342 logger.close();
343}
344
345//void CyberGloveProsthesisControl::onBreak()
346//{
347// // put your user code for the breaking point here
348// // execution time should be short (<100ms)
349//}
350
351void
353{
354 // put your user code for the exit point here
355 // execution time should be short (<100ms)
356}
357
358// DO NOT EDIT NEXT FUNCTION
364
365//void CyberGloveProsthesisControl::addCurrentRawJointValues(CyberGloveValues& currentValues, SimpleJsonLoggerEntry& e)
366//{
367// std::map<std::string, float> currentRawJointValues;
368// currentRawJointValues["thumbAbd"] = currentValues.thumbAbd;
369// currentRawJointValues["thumbCMC"] = currentValues.thumbCMC;
370// currentRawJointValues["thumbIP"] = currentValues.thumbIP;
371// currentRawJointValues["thumbMCP"] = currentValues.thumbMCP;
372
373// currentRawJointValues["indexDIP"] = currentValues.indexDIP;
374// currentRawJointValues["indexMCP"] = currentValues.indexMCP;
375// currentRawJointValues["indexPIP"] = currentValues.indexPIP;
376
377// currentRawJointValues["middleAbd"] = currentValues.middleAbd;
378// currentRawJointValues["middleDIP"] = currentValues.middleDIP;
379// currentRawJointValues["middleMCP"] = currentValues.middleMCP;
380// currentRawJointValues["middlePIP"] = currentValues.middlePIP;
381
382// currentRawJointValues["ringAbd"] = currentValues.ringAbd;
383// currentRawJointValues["ringDIP"] = currentValues.ringDIP;
384// currentRawJointValues["ringMCP"] = currentValues.ringMCP;
385// currentRawJointValues["ringPIP"] = currentValues.ringPIP;
386
387// currentRawJointValues["littleAbd"] = currentValues.littleAbd;
388// currentRawJointValues["littleDIP"] = currentValues.littleDIP;
389// currentRawJointValues["littleMCP"] = currentValues.littleMCP;
390// currentRawJointValues["littlePIP"] = currentValues.littlePIP;
391
392// currentRawJointValues["palmArch"] = currentValues.palmArch;
393
394// currentRawJointValues["wristFlex"] = currentValues.wristFlex;
395// currentRawJointValues["wristAbd"] = currentValues.wristAbd;
396
397// e.Add("rawJointValues", currentRawJointValues);
398//}
399
400//SimpleJsonLoggerEntry CyberGloveProsthesisControl::getRawJointValuesForCalibration(CyberGloveValues& openValues, CyberGloveValues& closedValues)
401//{
402// SimpleJsonLoggerEntry entriesForCalibration;
403// entriesForCalibration.AddTimestamp();
404// std::map<std::string, float> rawJointValuesOpen;
405// rawJointValuesOpen["thumbAbd"] = openValues.thumbAbd;
406// rawJointValuesOpen["thumbCMC"] = openValues.thumbCMC;
407// rawJointValuesOpen["thumbIP"] = openValues.thumbIP;
408// rawJointValuesOpen["thumbMCP"] = openValues.thumbMCP;
409
410// rawJointValuesOpen["indexDIP"] = openValues.indexDIP;
411// rawJointValuesOpen["indexMCP"] = openValues.indexMCP;
412// rawJointValuesOpen["indexPIP"] = openValues.indexPIP;
413
414// rawJointValuesOpen["middleAbd"] = openValues.middleAbd;
415// rawJointValuesOpen["middleDIP"] = openValues.middleDIP;
416// rawJointValuesOpen["middleMCP"] = openValues.middleMCP;
417// rawJointValuesOpen["middlePIP"] = openValues.middlePIP;
418
419// rawJointValuesOpen["ringAbd"] = openValues.ringAbd;
420// rawJointValuesOpen["ringDIP"] = openValues.ringDIP;
421// rawJointValuesOpen["ringMCP"] = openValues.ringMCP;
422// rawJointValuesOpen["ringPIP"] = openValues.ringPIP;
423
424// rawJointValuesOpen["littleAbd"] = openValues.littleAbd;
425// rawJointValuesOpen["littleDIP"] = openValues.littleDIP;
426// rawJointValuesOpen["littleMCP"] = openValues.littleMCP;
427// rawJointValuesOpen["littlePIP"] = openValues.littlePIP;
428
429// rawJointValuesOpen["palmArch"] = openValues.palmArch;
430
431// rawJointValuesOpen["wristFlex"] = openValues.wristFlex;
432// rawJointValuesOpen["wristAbd"] = openValues.wristAbd;
433
434// entriesForCalibration.Add("rawJointValuesForCalibrationOpen", rawJointValuesOpen);
435
436// std::map<std::string, float> rawJointValuesClosed;
437// rawJointValuesClosed["thumbAbd"] = closedValues.thumbAbd;
438// rawJointValuesClosed["thumbCMC"] = closedValues.thumbCMC;
439// rawJointValuesClosed["thumbIP"] = closedValues.thumbIP;
440// rawJointValuesClosed["thumbMCP"] = closedValues.thumbMCP;
441
442// rawJointValuesClosed["indexDIP"] = closedValues.indexDIP;
443// rawJointValuesClosed["indexMCP"] = closedValues.indexMCP;
444// rawJointValuesClosed["indexPIP"] = closedValues.indexPIP;
445
446// rawJointValuesClosed["middleAbd"] = closedValues.middleAbd;
447// rawJointValuesClosed["middleDIP"] = closedValues.middleDIP;
448// rawJointValuesClosed["middleMCP"] = closedValues.middleMCP;
449// rawJointValuesClosed["middlePIP"] = closedValues.middlePIP;
450
451// rawJointValuesClosed["ringAbd"] = closedValues.ringAbd;
452// rawJointValuesClosed["ringDIP"] = closedValues.ringDIP;
453// rawJointValuesClosed["ringMCP"] = closedValues.ringMCP;
454// rawJointValuesClosed["ringPIP"] = closedValues.ringPIP;
455
456// rawJointValuesClosed["littleAbd"] = closedValues.littleAbd;
457// rawJointValuesClosed["littleDIP"] = closedValues.littleDIP;
458// rawJointValuesClosed["littleMCP"] = closedValues.littleMCP;
459// rawJointValuesClosed["littlePIP"] = closedValues.littlePIP;
460
461// rawJointValuesClosed["palmArch"] = closedValues.palmArch;
462
463// rawJointValuesClosed["wristFlex"] = closedValues.wristFlex;
464// rawJointValuesClosed["wristAbd"] = closedValues.wristAbd;
465
466// entriesForCalibration.Add("rawJointValuesForCalibrationClosed", rawJointValuesClosed);
467
468// return entriesForCalibration;
469//}
std::string timestamp()
std::string str(const T &t)
static XMLStateFactoryBasePtr CreateInstance(XMLStateConstructorParams stateData)
ValueProxy< T > getValue(std::string const &name)
static void SleepMS(float milliseconds)
Definition TimeUtil.h:203
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
detail::VBoxLayoutBuilder makeVBoxLayout(std::string const &name="")
detail::HBoxLayoutBuilder makeHBoxLayout(std::string const &name="")
detail::ButtonBuilder makeButton(std::string const &name)
detail::LabelBuilder makeLabel(std::string const &name)
detail::LineEditBuilder makeLineEdit(std::string const &name)
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceInternal::Handle< XMLStateFactoryBase > XMLStateFactoryBasePtr
Definition XMLState.h:64
bool isfinite(const std::vector< T, Ts... > &v)
Definition algorithm.h:366
Derived & addChild(WidgetPtr const &child)