Statistics
| Branch: | Tag: | Revision:

amiro-os / devices / PowerManagement / userthread.cpp @ 1d9e5660

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
376
        break;
377
      }
378
    }
379
  }
380

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

    
385
  return RDY_OK;
386
}
387

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

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

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

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

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

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