Statistics
| Branch: | Tag: | Revision:

amiro-os / devices / PowerManagement / userthread.cpp @ 33c91ff2

History | View | Annotate | Download (14.7 KB)

1
#include "userthread.h"
2

    
3
#include "global.hpp"
4
#include <array>
5
#include <chprintf.h>
6
#include <cmath>
7

    
8
using namespace amiro;
9

    
10
extern Global global;
11

    
12
volatile UserThread::State current_state;
13
volatile UserThread::State next_state;
14
types::kinematic kinematic;
15

    
16
namespace obstacle_avoidance {
17

    
18
uint16_t constexpr proxThresholdLow = 0x0000;
19
uint16_t constexpr proxThresholdHigh = 0x1000;
20
uint16_t constexpr proxRange = proxThresholdHigh - proxThresholdLow;
21

    
22
std::array< std::array<float, 2>, 8> constexpr namMatrix = {
23
    /*                              x     w_z */
24
    std::array<float, 2>/* SSW */{ 0.00f,  0.00f},
25
    std::array<float, 2>/* WSW */{ 0.25f, -0.25f},
26
    std::array<float, 2>/* WNW */{-0.75f, -0.50f},
27
    std::array<float, 2>/* NNW */{-0.75f, -1.00f},
28
    std::array<float, 2>/* NNE */{-0.75f,  1.00f},
29
    std::array<float, 2>/* ENE */{-0.75f,  0.50f},
30
    std::array<float, 2>/* ESE */{ 0.25f,  0.25f},
31
    std::array<float, 2>/* SSE */{ 0.00f,  0.00f}
32
};
33
uint32_t constexpr baseTranslation = 100e3; // 2cm/s
34
uint32_t constexpr baseRotation = 1e6; // 1rad/s
35
types::kinematic constexpr defaultKinematic = {
36
    /*  x  [µm/s]   */ baseTranslation,
37
    /*  y  [µm/s]   */ 0,
38
    /*  z  [µm/s]   */ 0,
39
    /* w_x [µrad/s] */ 0,
40
    /* w_y [µrad/s] */ 0,
41
    /* w_z [µrad/s] */ 0
42
};
43

    
44
inline uint8_t ProxId2LedId(const uint8_t proxId) {
45
    return (proxId < 4) ? proxId+4 : proxId-4;
46
}
47

    
48
Color Prox2Color(const float prox) {
49
  float p = 0.0f;
50
  if (prox < 0.5f) {
51
    p = 2.0f * prox;
52
    return Color(0x00, p*0xFF, (1.0f-p)*0xFF);
53
  } else {
54
    p = 2.0f * (prox - 0.5f);
55
    return Color(p*0xFF, (1.0f-p)*0xFF, 0x00);
56
  }
57
}
58

    
59
} /* namespace obstacle_avoidance */
60

    
61
namespace wii_steering {
62

    
63
BluetoothWiimote wiimote(&global.wt12, RX_TX);
64
BluetoothSerial btserial(&global.wt12, RX_TX);
65

    
66
float deadzone;
67
char bt_address[18] = {'\0'};
68
float wiimoteCalib[3] = {0.0f};
69
uint8_t principal_axis = 1;
70
int8_t axis_direction = -1;
71

    
72
uint32_t constexpr maxTranslation = 500e3;
73
uint32_t constexpr maxRotation = 3.1415927f * 1000000.0f * 2.0f;
74

    
75
}
76

    
77
UserThread::UserThread() :
78
  chibios_rt::BaseStaticThread<USER_THREAD_STACK_SIZE>()
79
{
80
}
81

    
82
UserThread::~UserThread()
83
{
84
}
85

    
86
msg_t
87
UserThread::main()
88
{
89
  /*
90
   * initialize some variables
91
   */
92
  current_state = IDLE;
93

    
94
  /*
95
   * set all LEDs black (off)
96
   */
97
  for (uint8_t led = 0; led < 8; ++led) {
98
    global.robot.setLightColor(led, Color(Color::BLACK));
99
  }
100

    
101
  /*
102
   * thread loop
103
   */
104
  while (!this->shouldTerminate()) {
105
    /*
106
     * handle changes of the state
107
     */
108
    if (next_state != current_state) {
109
      switch (current_state) {
110
        case IDLE:
111
        {
112
          if (next_state == OBSTACLE_AVOIDANCE) {
113
            // set all LEDs to white for one second
114
            for (uint8_t led = 0; led < 8; ++led) {
115
              global.robot.setLightColor(led, Color(Color::WHITE));
116
            }
117
            this->sleep(MS2ST(1000));
118
            for (uint8_t led = 0; led < 8; ++led) {
119
              global.robot.setLightColor(led, Color(Color::BLACK));
120
            }
121
          }
122
          /* if (this->next_state == WII_STEERING) */ else {
123
            // setup bluetooth
124
            wii_steering::wiimote.bluetoothWiimoteListen(wii_steering::bt_address);
125
            wii_steering::btserial.bluetoothSerialListen("ALL");
126

    
127
            // set LEDs: front = green; rear = red; sides = blue
128
            global.robot.setLightColor(constants::LightRing::LED_NNW, Color(Color::GREEN));
129
            global.robot.setLightColor(constants::LightRing::LED_NNE, Color(Color::GREEN));
130
            global.robot.setLightColor(constants::LightRing::LED_SSW, Color(Color::RED));
131
            global.robot.setLightColor(constants::LightRing::LED_SSE, Color(Color::RED));
132
            global.robot.setLightColor(constants::LightRing::LED_WNW, Color(Color::BLUE));
133
            global.robot.setLightColor(constants::LightRing::LED_WSW, Color(Color::BLUE));
134
            global.robot.setLightColor(constants::LightRing::LED_ENE, Color(Color::BLUE));
135
            global.robot.setLightColor(constants::LightRing::LED_ESE, Color(Color::BLUE));
136

    
137
            chprintf((BaseSequentialStream*)&global.sercanmux1, "press buttons '1' and '2' to connect\n");
138
          }
139
          break;
140
        }
141
        case OBSTACLE_AVOIDANCE:
142
        {
143
          if (next_state == IDLE) {
144
            // stop the robot
145
            kinematic = {0, 0, 0, 0, 0, 0};
146
            global.robot.setTargetSpeed(kinematic);
147

    
148
            // set all LEDs to white for one second
149
            for (uint8_t led = 0; led < 8; ++led) {
150
              global.robot.setLightColor(led, Color(Color::WHITE));
151
            }
152
            this->sleep(MS2ST(1000));
153
            for (uint8_t led = 0; led < 8; ++led) {
154
              global.robot.setLightColor(led, Color(Color::BLACK));
155
            }
156
          }
157
          /* if (this->next_state == WII_STEERING) */ else {
158
            // must turn off obstacle avoidance first
159
            chprintf((BaseSequentialStream*)&global.sercanmux1, "ERROR: turn off obstacle avoidance first!\n");
160
            next_state = OBSTACLE_AVOIDANCE;
161
          }
162
          break;
163
        }
164
        case WII_STEERING: {
165
          if (next_state == IDLE) {
166
            // stop the robot
167
            kinematic = {0, 0, 0, 0, 0, 0};
168
            global.robot.setTargetSpeed(kinematic);
169

    
170
            // disconnect from Wiimote controller
171
            wii_steering::wiimote.bluetoothWiimoteDisconnect(wii_steering::bt_address);
172
            wii_steering::btserial.bluetoothSerialStop();
173
            wii_steering::wiimote.bluetoothWiimoteStop();
174

    
175
            // set all LEDs to black
176
            for (uint8_t led = 0; led < 8; ++led) {
177
              global.robot.setLightColor(led, Color(Color::BLACK));
178
            }
179
          }
180
          /* if (this->next_state == OBSTACLE_AVOIDANCE) */ else {
181
            // must turn off wii steering first
182
            chprintf((BaseSequentialStream*)&global.sercanmux1, "ERROR: turn off wii steering first!\n");
183
            next_state = WII_STEERING;
184
          }
185
          break;
186
        }
187
      }
188
      current_state = next_state;
189
    }
190

    
191
    // sleep here so the loop is executed as quickly as possible
192
    this->sleep(CAN::UPDATE_PERIOD);
193

    
194
    /*
195
     * exeute behaviour depending on the current state
196
     */
197
    switch (current_state) {
198
      case IDLE:
199
      {
200
        // read touch sensors
201
        if (global.mpr121.getButtonStatus() == 0x0F) {
202
          next_state = OBSTACLE_AVOIDANCE;
203
        }
204
        break;
205
      }
206
      case OBSTACLE_AVOIDANCE:
207
      {
208
        // read touch sensors
209
        if (global.mpr121.getButtonStatus() == 0x0F) {
210
          next_state = IDLE;
211
          break;
212
        }
213

    
214
        // initialize some variables
215
        uint8_t sensor = 0;
216
        std::array<uint16_t, 8> proximity;
217
        std::array<float, 8> proxNormalized;
218
        float factor_x = 0.0f;
219
        float factor_wz = 0.0f;
220

    
221
        // read proximity values
222
        for (sensor = 0; sensor < 8; ++sensor) {
223
          proximity[sensor] = global.vcnl4020[sensor].getProximityScaledWoOffset();
224
        }
225

    
226
        // normalize proximity values
227
        for (sensor = 0; sensor < 8; ++sensor) {
228
          register uint16_t prox = proximity[sensor];
229
          // limit to high treshold
230
          if (prox > obstacle_avoidance::proxThresholdHigh)
231
              prox = obstacle_avoidance::proxThresholdHigh;
232
          // limit to low threshold
233
          else if (prox < obstacle_avoidance::proxThresholdLow)
234
              prox = obstacle_avoidance::proxThresholdLow;
235
          // apply low threshold
236
          prox -= obstacle_avoidance::proxThresholdLow;
237
          // normalize to [0, 1]
238
          proxNormalized[sensor] = float(prox) / float(obstacle_avoidance::proxRange);
239
        }
240

    
241
        // map the sensor values to the top LEDs
242
        for (sensor = 0; sensor < 8; ++sensor) {
243
          global.robot.setLightColor(obstacle_avoidance::ProxId2LedId(sensor), obstacle_avoidance::Prox2Color(proxNormalized[sensor]));
244
        }
245

    
246
        // evaluate NAM
247
        for (sensor = 0; sensor < 8; ++sensor) {
248
          factor_x += proxNormalized[sensor] * obstacle_avoidance::namMatrix[sensor][0];
249
          factor_wz += proxNormalized[sensor] * obstacle_avoidance::namMatrix[sensor][1];
250
        }
251

    
252
        // set motor commands
253
        kinematic = obstacle_avoidance::defaultKinematic;
254
        kinematic.x += (factor_x * obstacle_avoidance::baseTranslation) + 0.5f;
255
        kinematic.w_z += (factor_wz * obstacle_avoidance::baseRotation) + 0.5f;
256
        global.robot.setTargetSpeed(kinematic);
257

    
258
        break;
259
      }
260
      case WII_STEERING:
261
      {
262
        // if not yet connected to the Wiimote controller
263
        if (!wii_steering::wiimote.bluetoothWiimoteIsConnected()) {
264
          // try to connect
265
          chprintf((BaseSequentialStream*)&global.sercanmux1, "connecting...\n");
266
          wii_steering::wiimote.bluetoothWiimoteConnect(wii_steering::bt_address);
267

    
268
          if (wii_steering::wiimote.bluetoothWiimoteIsConnected()) {
269
            chprintf((BaseSequentialStream*)&global.sercanmux1, "connection established\n");
270
            chprintf((BaseSequentialStream*)&global.sercanmux1, "\n");
271
            chprintf((BaseSequentialStream*)&global.sercanmux1, "Wiimote control:\n");
272
            chprintf((BaseSequentialStream*)&global.sercanmux1, "\tpress 'home' to calibrate\n");
273
            chprintf((BaseSequentialStream*)&global.sercanmux1, "\thold 'A' to steer\n");
274
          }
275
        }
276
        // steer AMiRo using the Wiimote controller like a joystick
277
        else {
278
          // initialize some variables
279
          float wiimoteAcc[3] = {0.0f, 0.0f, 0.0f};
280

    
281
          // get Wiimote accelerometer data
282
          wiimoteAcc[0] = wii_steering::wiimote.getAccelerometer()->x_axis;
283
          wiimoteAcc[1] = wii_steering::wiimote.getAccelerometer()->y_axis;
284
          wiimoteAcc[2] = wii_steering::wiimote.getAccelerometer()->z_axis;
285

    
286
          // calibrate accelerometer offset
287
          if (wii_steering::wiimote.getButtons()->home) {
288
            chprintf((BaseSequentialStream*)&global.sercanmux1, "%f | %f | %f\n", wiimoteAcc[0], wiimoteAcc[1], wiimoteAcc[2]);
289

    
290
            // detect principal axis
291
            if (std::fabs(wiimoteAcc[0]) > std::fabs(wiimoteAcc[1]) && std::fabs(wiimoteAcc[0]) > std::fabs(wiimoteAcc[2])) {
292
              wii_steering::principal_axis = 0;
293
            } else if (std::fabs(wiimoteAcc[1]) > std::fabs(wiimoteAcc[0]) && std::fabs(wiimoteAcc[1]) > std::fabs(wiimoteAcc[2])) {
294
              wii_steering::principal_axis = 1;
295
            } else if (std::fabs(wiimoteAcc[2]) > std::fabs(wiimoteAcc[0]) && std::fabs(wiimoteAcc[2]) > std::fabs(wiimoteAcc[1])) {
296
              wii_steering::principal_axis = 2;
297
            }
298
            wii_steering::axis_direction = (wiimoteAcc[wii_steering::principal_axis] >= 0) ? 1 : -1;
299

    
300
            // get calibration offset
301
            wii_steering::wiimoteCalib[0] = wiimoteAcc[0];
302
            wii_steering::wiimoteCalib[1] = wiimoteAcc[1];
303
            wii_steering::wiimoteCalib[2] = wiimoteAcc[2];
304
            wii_steering::wiimoteCalib[wii_steering::principal_axis] += -100.0f * wii_steering::axis_direction;
305

    
306
            // print information
307
            chprintf((BaseSequentialStream*)&global.sercanmux1, "accelerometer calibrated:\n");
308
            chprintf((BaseSequentialStream*)&global.sercanmux1, "\tprincipal axis: %c\n", (wii_steering::principal_axis == 0) ? 'X' : (wii_steering::principal_axis == 1) ? 'Y' : 'Z');
309
            chprintf((BaseSequentialStream*)&global.sercanmux1, "\tX = %d\n", (int32_t)wii_steering::wiimoteCalib[0]);
310
            chprintf((BaseSequentialStream*)&global.sercanmux1, "\tY = %d\n", (int32_t)wii_steering::wiimoteCalib[1]);
311
            chprintf((BaseSequentialStream*)&global.sercanmux1, "\tZ = %d\n", (int32_t)wii_steering::wiimoteCalib[2]);
312
          }
313

    
314
          for (uint8_t axis = 0; axis < 3; ++axis) {
315
            // apply calibration values
316
            wiimoteAcc[axis] -= wii_steering::wiimoteCalib[axis];
317

    
318
            // normalize to (-1, 1)
319
            wiimoteAcc[axis] /= 100.0f;
320

    
321
            // limit to 1G
322
            if (wiimoteAcc[axis] > 1.0f) {
323
              wiimoteAcc[axis] = 1.0f;
324
            } else if (wiimoteAcc[axis] < -1.0f) {
325
              wiimoteAcc[axis] = -1.0f;
326
            }
327

    
328
            // apply deadzone
329
            if (std::fabs(wiimoteAcc[axis]) < wii_steering::deadzone) {
330
              wiimoteAcc[axis] = 0.0f;
331
            }
332

    
333
            /*
334
             * the value is now in (-1 .. -deazone, 0, deadzone .. 1)
335
             * note the gaps [-deadzone .. 0] and [0 .. deadzone]
336
             */
337

    
338
            // normalize (deadzone, 1) to (0, 1) and (-1, -deadzone) tpo (-1, 0)
339
            if (wiimoteAcc[axis] > 0) {
340
              wiimoteAcc[axis] -= wii_steering::deadzone;
341
            } else if (wiimoteAcc[axis] < 0){
342
              wiimoteAcc[axis] += wii_steering::deadzone;
343
            }
344
            wiimoteAcc[axis] *= (1.0f / (1.0f - wii_steering::deadzone));
345
          }
346

    
347
          // only move when A is pressed
348
          if (wii_steering::wiimote.getButtons()->A || wii_steering::wiimote.getButtons()->B) {
349
            // set kinematic relaive to maximum speeds
350
            switch (wii_steering::principal_axis) {
351
              case 1:
352
                if (wii_steering::axis_direction == -1) {
353
                  kinematic.x = wii_steering::maxTranslation * wiimoteAcc[2];
354
                  kinematic.w_z = wii_steering::maxRotation * wiimoteAcc[0] * ((wiimoteAcc[2] < 0.0f) ? 1.0f : -1.0f);
355
                  break;
356
                }
357
              case 2:
358
                if (wii_steering::axis_direction == 1) {
359
                  kinematic.x = wii_steering::maxTranslation * wiimoteAcc[1];
360
                  kinematic.w_z = wii_steering::maxRotation * wiimoteAcc[0] * ((wiimoteAcc[1] < 0.0f) ? 1.0f : -1.0f);
361
                  break;
362
                }
363
              default:
364
                kinematic = {0, 0, 0, 0, 0, 0};
365
                break;
366
            }
367
          } else {
368
            kinematic = {0, 0, 0, 0, 0, 0};
369
          }
370

    
371
          // set speed
372
          global.robot.setTargetSpeed(kinematic);
373
        }
374

    
375
        break;
376
      }
377
    }
378
  }
379

    
380
  // stop the robot
381
  kinematic = {0, 0, 0, 0, 0, 0};
382
  global.robot.setTargetSpeed(kinematic);
383

    
384
  return RDY_OK;
385
}
386

    
387
void
388
UserThread::setNextState(const UserThread::State state)
389
{
390
  next_state = state;
391
  return;
392
}
393

    
394
UserThread::State
395
UserThread::getCurrenState() const
396
{
397
  return current_state;
398
}
399

    
400
msg_t
401
UserThread::setWiiAddress(const char* address)
402
{
403
  if (strlen(address) != 17) {
404
    return RDY_RESET;
405
  }
406
  else {
407
    strcpy(wii_steering::bt_address, address);
408
    return RDY_OK;
409
  }
410
}
411

    
412
float
413
UserThread::setWiiDeadzone(const float deadzone)
414
{
415
  // check for negative value and limit to zero
416
  float dz = (deadzone < 0.0f) ? 0.0f : deadzone;
417

    
418
  // if value is >1, range is assumed to be (0, 100)
419
  if (dz > 1.0f) {
420
    // limit to 100
421
    if (dz > 100.0f) {
422
      dz = 100.0f;
423
    }
424
    dz /= 100.0f;
425
  }
426

    
427
  // set value and return it
428
  wii_steering::deadzone = dz;
429
  return dz;
430
}
431