AERA
aera_hand_robot_controller.cpp
1 // File: aera_hand_robot_controller.cpp
2 // Date:
3 // Description:
4 // Author:
5 // Modifications:
6 
7 #define DEBUG_STRING_MESSAGE
8 
9 #include <string>
10 #include <sstream>
11 #include <memory>
12 #include <cstdint>
13 #include <webots/Supervisor.hpp>
14 #include <webots/Motor.hpp>
15 #include <webots/PositionSensor.hpp>
16 #include <winsock.h>
17 
18 using namespace std;
19 using namespace webots;
20 
21 typedef string TCPMessage;
22 
23 #ifdef DEBUG_STRING_MESSAGE
24 static vector<string>
25 split(const string& input, char separator)
26 {
27  vector<string> result;
28  stringstream is(input);
29  string str;
30  while (getline(is, str, separator))
31  result.push_back(str);
32 
33  return result;
34 }
35 #endif
36 
37 static SOCKET open_socket(const char* host, int port) {
38  struct sockaddr_in address;
39  struct hostent *server;
40  SOCKET fd;
41  int rc;
42 
43 #ifdef _WIN32
44  // Initialize the socket API.
45  WSADATA info;
46  rc = WSAStartup(MAKEWORD(1, 1), &info); /* Winsock 1.1 */
47  if (rc != 0) {
48  cout << "ERROR: Cannot initialize Winsock" << endl;
49  return INVALID_SOCKET;
50  }
51 #endif
52 
53  // Create the socket.
54  fd = socket(AF_INET, SOCK_STREAM, 0);
55  if (fd == INVALID_SOCKET) {
56  cout << "ERROR: Cannot create socket" << endl;
57  return INVALID_SOCKET;
58  }
59 
60  // Fill in the socket address.
61  memset(&address, 0, sizeof(struct sockaddr_in));
62  address.sin_family = AF_INET;
63  address.sin_port = htons(port);
64  server = gethostbyname(host);
65 
66  if (server)
67  memcpy((char *)&address.sin_addr.s_addr, (char *)server->h_addr, server->h_length);
68  else {
69  cout << "ERROR: Cannot resolve server name: " << host << endl;
70 #ifdef _WIN32
71  closesocket(fd);
72 #else
73  close(fd);
74 #endif
75  return INVALID_SOCKET;
76  }
77 
78  // Connect to the server.
79  rc = connect(fd, (struct sockaddr *)&address, sizeof(struct sockaddr));
80  if (rc == -1) {
81  cout << "ERROR: Cannot connect to the server at " << host << ":" << port << endl;
82 #ifdef _WIN32
83  closesocket(fd);
84 #else
85  close(fd);
86 #endif
87  return -1;
88  }
89 
90  cout << "Connected to server at " << host << ":" << port << endl;
91  return fd;
92 }
93 
94 // Imitate TCPConnection::sendMessage.
95 static int sendMessage(SOCKET fd, const unique_ptr<TCPMessage>& msg)
96 {
97  // Serialize the TCPMessage
98  string out;
99 #ifdef DEBUG_STRING_MESSAGE
100  out = *msg;
101 #else
102  out = msg->SerializeAsString();
103 #endif
104 
105  // First put the length of the message in the first 8 bytes of the output stream
106  string out_buffer = "";
107  for (int i = 0; i < 8; ++i) {
108  out_buffer += (unsigned char)((int)(((uint64_t)out.size() >> (i * 8)) & 0xFF));
109  }
110 
111  // Attach the serialized message to the byte-stream
112  out_buffer += out;
113 
114  // Send message length + message through the socket.
115  int i_send_result = ::send(fd, &out_buffer[0], out_buffer.size(), 0);
116  if (i_send_result == SOCKET_ERROR) {
117  cout << "sendMessage failed" << endl;
118  }
119 
120  return i_send_result;
121 }
122 
123 // Imitate TCPConnection::receiveMessage.
124 static unique_ptr<TCPMessage> receiveMessage(SOCKET fd)
125 {
126  // Number of bytes received
127  int received_bytes = 0;
128 
129  // Length of read bytes in total (used to ensure split messages are read correctly)
130  uint64_t len_res = 0;
131 
132  // First read the length of the message to expect (8 byte uint64_t)
133  const int msg_length_buf_size = 8;
134  string tcp_msg_len_recv_buf;
135  tcp_msg_len_recv_buf.reserve(msg_length_buf_size);
136  // To ensure split message is read correctly
137  while (len_res < msg_length_buf_size) {
138  received_bytes = ::recv(fd, &(tcp_msg_len_recv_buf[len_res]), msg_length_buf_size - len_res, 0);
139  if (received_bytes > 0) {
140  // All good
141  len_res += received_bytes;
142  }
143  else if (received_bytes == 0) {
144  // Client closed the connection
145  len_res = -1;
146  cout << "Connection closing..." << endl;
147  return NULL;
148  }
149  else {
150  // Error occured during receiving
151  cout << "recv failed during recv of data length" << endl;
152  return NULL;
153  }
154  }
155 
156  // Convert the read bytes to uint64_t. Little Endian! Otherwise invert for loop - not implemented, yet.
157  uint64_t msg_len = 0;
158  for (int i = msg_length_buf_size - 1; i >= 0; --i)
159  {
160  msg_len <<= 8;
161  msg_len |= (unsigned char)tcp_msg_len_recv_buf[i];
162  }
163 
164  // Reset read bytes and total read bytes to 0
165  received_bytes = 0;
166  len_res = 0;
167  // Read as many packages as needed to fill the message buffer. Ensures split messages are received correctly.
168  char* buf = new char[msg_len]();
169  while (len_res < msg_len) {
170  received_bytes = recv(fd, &buf[len_res], msg_len - len_res, 0);
171  if (received_bytes > 0) {
172  len_res += received_bytes;
173  }
174  else if (received_bytes == 0) {
175  return NULL;
176  }
177  else {
178  cout << "recv failed during recv of data message" << endl;
179  return NULL;
180  }
181  }
182 
183  // Parse the byte-stream into a TCPMessage
184  unique_ptr<TCPMessage> msg = make_unique<TCPMessage>();
185 #ifdef DEBUG_STRING_MESSAGE
186  *msg = string(buf, msg_len);
187 #else
188  if (!msg->ParseFromArray(buf, msg_len)) {
189  cout << "ERROR: Parsing Message from String failed" << endl;
190  return NULL;
191  }
192 #endif
193 
194  delete[] buf;
195 
196  return msg;
197 }
198 
199 // Check if new data is on the TCP connection to receive.
200 // Return 0 for no, 1 for yes, -1 for error.
201 static int receiveIsReady(SOCKET fd) {
202  timeval tv{ 0, 0 };
203  int rc = 0;
204  FD_SET tcp_client_fd_set;
205  FD_ZERO(&tcp_client_fd_set);
206  FD_SET(fd, &tcp_client_fd_set);
207  rc = ::select(fd + 1, &tcp_client_fd_set, NULL, NULL, &tv);
208  if (rc == 0) {
209  // No messages on the socket.
210  return 0;
211  }
212  else if (rc == SOCKET_ERROR) {
213  return -1;
214  }
215 
216  return 1;
217 }
218 
219 const double position_offset = -15.0;
220 const double position_factor = 0.1;
221 const double bin_size = 5;
222 
223 // Get the quantized "position" from the angle around the robot.
224 static get_position(double angle) {
225  double position = angle / position_factor - position_offset;
226  // Quantize.
227  return floor((position + bin_size/2) / bin_size) * bin_size;
228 }
229 
230 // The arguments of the main function can be specified by the
231 // "controllerArgs" field of the Robot node
232 int main(int argc, char **argv) {
233  SOCKET aera_fd = open_socket("127.0.0.1", 8080);
234  if (aera_fd == INVALID_SOCKET)
235  // Already printed the error.
236  return -1;
237 
238  Supervisor* robot = new Supervisor();
239  Node* sphere = robot->getFromDef("sphere");
240  Node* cube = robot->getFromDef("cube");
241  // Separately, set ned to the Node so we can get its translation, etc.
242  Node* ned = robot->getFromDef("Ned");
243  // We don't expect the robot to move, so get its position now.
244  double ned_x = ned->getField("translation")->getSFVec3f()[0];
245  double ned_y = ned->getField("translation")->getSFVec3f()[1];
246 
247  // Expect timeStep to be 1 ms.
248  int timeStep = (int)robot->getBasicTimeStep();
249 
250  const double arm_up = 0.65;
251  const double arm_down = 0.8;
252  const double jaw_open = 0.01;
253  const double jaw_closed = -0.0019;
254 
255  Motor* joint_1 = robot->getMotor("joint_1");
256  // Decrease the PID gain from 10 so that we keep a grip on the object.
257  joint_1->setControlPID(9, 0, 0);
258  Motor* joint_2 = robot->getMotor("joint_2");
259  Motor* joint_3 = robot->getMotor("joint_3");
260  Motor* joint_5 = robot->getMotor("joint_5");
261  Motor* joint_6 = robot->getMotor("joint_6");
262  Motor* joint_base_to_jaw_1 = robot->getMotor("joint_base_to_jaw_1");
263  Motor* joint_base_to_jaw_2 = robot->getMotor("joint_base_to_jaw_2");
264  // Increase the strength of the grip.
265  joint_base_to_jaw_1->setControlPID(250, 0, 0);
266  joint_base_to_jaw_2->setControlPID(250, 0, 0);
267 
268  PositionSensor* joint_1_sensor = robot->getPositionSensor("joint_1_sensor");
269  joint_1_sensor->enable(timeStep);
270  PositionSensor* joint_base_to_jaw_1_sensor = robot->getPositionSensor("joint_base_to_jaw_1_sensor");
271  joint_base_to_jaw_1_sensor->enable(timeStep);
272  PositionSensor* joint_base_to_jaw_2_sensor = robot->getPositionSensor("joint_base_to_jaw_2_sensor");
273  joint_base_to_jaw_2_sensor->enable(timeStep);
274 
275  joint_2->setPosition(arm_up);
276  joint_3->setPosition(0.32);
277  joint_5->setPosition(-0.5);
278  joint_6->setPosition(M_PI/2);
279 
280  joint_base_to_jaw_1->setPosition(jaw_open);
281  joint_base_to_jaw_2->setPosition(jaw_open);
282 
283  // Send the setup command.
284  unique_ptr<TCPMessage> msg = make_unique<TCPMessage>("setup");
285  sendMessage(aera_fd, std::move(msg));
286  // Wait for "start"
287  while (true) {
288  while (receiveIsReady(aera_fd) == 0) {}
289  auto in_msg = receiveMessage(aera_fd);
290  if (*in_msg == "start")
291  break;
292 
293  cout << "While waiting for the start command, received unexpected \"" << *in_msg << "\"" << endl;
294  }
295 
296  // Set the initial position.
297  string command = "move";
298  double target_h_position = 20;
299  int command_time = 0;
300 
301  int aera_us = -100;
302  int receive_deadline = MAXINT;
303  while (robot->step(timeStep) != -1) {
304  // aera_time moves at 1/10 the simulation speed.
305  aera_us += timeStep * 100;
306 
307  if (aera_us == 1700*1000 + 65000) {
308  // After grab failure, release and reset the positions of the sphere and cube.
309  joint_1->setPosition((0 + position_offset) * position_factor);
310  joint_6->setPosition(M_PI/2);
311  joint_base_to_jaw_1->setPosition(jaw_open);
312  joint_base_to_jaw_2->setPosition(jaw_open);
313  sphere->resetPhysics();
314  cube->resetPhysics();
315  sphere->getField("rotation")->setSFRotation((const double[]){0, 0, 1, 1.50014});
316  sphere->getField("translation")->setSFVec3f((const double[]){-0.3, -0.025, 0.01});
317  cube->getField("rotation")->setSFRotation((const double[]){0, 0, -1, 1.00257});
318  cube->getField("translation")->setSFVec3f((const double[]){-0.257, -0.161, 0.01});
319  }
320 
321  double h_position = get_position(joint_1_sensor->getValue());
322 
323  // Don't send the state at time 0, but wait for the initial position.
324  if (aera_us > 0 && aera_us % 100000 == 0) {
325  const double* c_translation = cube->getField("translation")->getSFVec3f();
326  double c_offset_x = c_translation[0] - ned_x;
327  double c_offset_y = c_translation[1] - ned_y;
328  double c_position = get_position(atan2(c_offset_x, -c_offset_y));
329 
330  const double* s_translation = sphere->getField("translation")->getSFVec3f();
331  double s_offset_x = s_translation[0] - ned_x;
332  double s_offset_y = s_translation[1] - ned_y;
333  double s_position = get_position(atan2(s_offset_x, -s_offset_y));
334 
335  string holding = "[]";
336  if (fabs(joint_base_to_jaw_1_sensor->getValue() - jaw_closed) < 0.0005 &&
337  fabs(joint_base_to_jaw_2_sensor->getValue() - jaw_closed)) {
338  // The gripper is in the closed position. Check if an object is at the hand position with elevated Z.
339  if (c_position == h_position && c_translation[2] > 0.0103)
340  holding = "c";
341  if (s_position == h_position && s_translation[2] > 0.0103)
342  holding = "s";
343  }
344 
345  // Send the current state.
346  unique_ptr<TCPMessage> msg = make_unique<TCPMessage>(
347  "h position " + to_string(h_position) +
348  "\nc position " + to_string(c_position) +
349  "\ns position " + to_string(s_position) +
350  "\nh holding " + holding);
351  sendMessage(aera_fd, std::move(msg));
352  receive_deadline = aera_us + 65000;
353  }
354 
355  if (aera_us >= receive_deadline) {
356  // We haven't received the command yet, so wait for it.
357  while (receiveIsReady(aera_fd) == 0) {}
358  receive_deadline = MAXINT;
359  }
360 
361  int ready = receiveIsReady(aera_fd);
362  if (ready < 0) {
363  cout << "select() == SOCKET_ERROR error" << endl;
364  break;
365  }
366  if (command == "" && ready == 1) {
367  receive_deadline = MAXINT;
368 
369  auto in_msg = receiveMessage(aera_fd);
370  if (!in_msg)
371  // Already printed the error.
372  break;
373 
374  vector<string> fields = split(*in_msg, ' ');
375  if (fields.size() == 2 && (fields[0] == "grab" || fields[0] == "release") && fields[1] == "h") {
376  command = fields[0];
377  }
378  else if (fields.size() == 3 && fields[0] == "move" && fields[1] == "h") {
379  command = fields[0];
380  double delta = std::stof(fields[2]);
381  if (delta > 20)
382  delta = 20;
383  else if (delta < -20)
384  delta = -20;
385  target_h_position = h_position + delta;
386  }
387  else
388  cout << "ERROR: Unrecognized command " << *in_msg << endl;
389 
390  if (command != "") {
391  // TODO: Get the command time from the message.
392  int frame_start_us = (aera_us / 100000) * 100000;
393  command_time = frame_start_us + 65000;
394  if (command_time < aera_us)
395  // We don't expect this.
396  command_time = aera_us;
397  }
398  }
399 
400  if (command_time == aera_us) {
401  // Execute the command.
402  if (command == "grab") {
403  joint_2->setPosition(arm_down);
404  command = "close_jaw_and_move_up";
405  command_time = command_time + 30000;
406  }
407  else if (command == "close_jaw_and_move_up") {
408  joint_base_to_jaw_1->setPosition(jaw_closed);
409  joint_base_to_jaw_2->setPosition(jaw_closed);
410  command = "move_arm_up";
411  command_time = command_time + 4900;
412  }
413  else if (command == "move_arm_up") {
414  joint_2->setPosition(arm_up);
415  command = "";
416  }
417  else if (command == "release") {
418  if (aera_us == 1500*1000 + 65000) {
419  // Special case: Drop the cube in the same position as the sphere.
420  joint_1->setPosition((-0.3 + position_offset) * position_factor);
421  joint_2->setPosition(0.73);
422  joint_6->setPosition(1);
423  }
424  else
425  joint_2->setPosition(arm_down);
426  command = "open_jaw_and_move_up";
427  command_time = command_time + 30000;
428  }
429  else if (command == "open_jaw_and_move_up") {
430  joint_base_to_jaw_1->setPosition(jaw_open);
431  joint_base_to_jaw_2->setPosition(jaw_open);
432  command = "move_arm_up";
433  command_time = command_time + 4900;
434  }
435  else if (command == "move") {
436  // Do the inverse calculation of h_position;
437  joint_1->setPosition((target_h_position + position_offset) * position_factor);
438  command = "";
439  }
440  }
441  }
442 
443  // Enter here exit cleanup code.
444 
445  delete robot;
446  return 0;
447 }