Statistics
| Branch: | Tag: | Revision:

amiro-os / devices / PowerManagement / userthread.cpp @ 6d830173

History | View | Annotate | Download (14.755 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
      current_state = IDLE;
190
      next_state = IDLE;
191
    }
192

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
373
          // set speed
374
          global.robot.setTargetSpeed(kinematic);
375
        }
376

    
377
        break;
378
      }
379
    }
380
  }
381

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

    
386
  return RDY_OK;
387
}
388

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

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

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

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

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

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