Statistics
| Branch: | Tag: | Revision:

amiro-os / devices / PowerManagement / userthread.cpp @ 61b0791a

History | View | Annotate | Download (12.857 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

    
70
uint32_t constexpr maxTranslation = 500e3;
71
uint32_t constexpr maxRotation = 3.1415927f * 1000000.0f * 2.0f;
72

    
73
}
74

    
75
UserThread::UserThread() :
76
  chibios_rt::BaseStaticThread<USER_THREAD_STACK_SIZE>()
77
{
78
}
79

    
80
UserThread::~UserThread()
81
{
82
}
83

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
284
          // calibrate accelerometer offset
285
          if (wii_steering::wiimote.getButtons()->home) {
286
            wii_steering::wiimoteCalib[0] = wiimoteAcc[0];
287
            wii_steering::wiimoteCalib[1] = wiimoteAcc[1] + 100.0f;
288
            wii_steering::wiimoteCalib[2] = wiimoteAcc[2];
289

    
290
            chprintf((BaseSequentialStream*)&global.sercanmux1, "accelerometer calibrated:\n");
291
            chprintf((BaseSequentialStream*)&global.sercanmux1, "\tX = %d\n", (int32_t)wii_steering::wiimoteCalib[0]);
292
            chprintf((BaseSequentialStream*)&global.sercanmux1, "\tY = %d\n", (int32_t)wii_steering::wiimoteCalib[1]);
293
            chprintf((BaseSequentialStream*)&global.sercanmux1, "\tZ = %d\n", (int32_t)wii_steering::wiimoteCalib[2]);
294
          }
295

    
296
          for (uint8_t axis = 0; axis < 3; ++axis) {
297
            // apply calibration values
298
            wiimoteAcc[axis] -= wii_steering::wiimoteCalib[axis];
299

    
300
            // normalize to (-1, 1)
301
            wiimoteAcc[axis] /= 100.0f;
302

    
303
            // limit to 1G
304
            if (wiimoteAcc[axis] > 1.0f) {
305
              wiimoteAcc[axis] = 1.0f;
306
            } else if (wiimoteAcc[axis] < -1.0f) {
307
              wiimoteAcc[axis] = -1.0f;
308
            }
309

    
310
            // apply deadzone
311
            if (std::fabs(wiimoteAcc[axis]) < wii_steering::deadzone) {
312
              wiimoteAcc[axis] = 0.0f;
313
            }
314

    
315
            /*
316
             * the value is now in (-1 .. -deazone, 0, deadzone .. 1)
317
             * note the gaps [-deadzone .. 0] and [0 .. deadzone]
318
             */
319

    
320
            // normalize (deadzone, 1) to (0, 1) and (-1, -deadzone) tpo (-1, 0)
321
            if (wiimoteAcc[axis] > 0) {
322
              wiimoteAcc[axis] -= wii_steering::deadzone;
323
            } else if (wiimoteAcc[axis] < 0){
324
              wiimoteAcc[axis] += wii_steering::deadzone;
325
            }
326
            wiimoteAcc[axis] *= (1.0f / (1.0f - wii_steering::deadzone));
327
          }
328

    
329
          // only move when A is pressed
330
          if (wii_steering::wiimote.getButtons()->A) {
331
            // set kinematic relaive to maximum speeds
332
            kinematic.x = wii_steering::maxTranslation * wiimoteAcc[2];
333
            kinematic.w_z = wii_steering::maxRotation * wiimoteAcc[0] * ((wiimoteAcc[2] < 0.0f) ? 1.0f : -1.0f);
334
          } else {
335
            kinematic = {0, 0, 0, 0, 0, 0};
336
          }
337

    
338
          // set speed
339
          global.robot.setTargetSpeed(kinematic);
340
        }
341

    
342
        break;
343
      }
344
    }
345
  }
346

    
347
  // stop the robot
348
  kinematic = {0, 0, 0, 0, 0, 0};
349
  global.robot.setTargetSpeed(kinematic);
350

    
351
  return RDY_OK;
352
}
353

    
354
void
355
UserThread::setNextState(const UserThread::State state)
356
{
357
  next_state = state;
358
  return;
359
}
360

    
361
UserThread::State
362
UserThread::getCurrenState() const
363
{
364
  return current_state;
365
}
366

    
367
msg_t
368
UserThread::setWiiAddress(const char* address)
369
{
370
  if (strlen(address) != 17) {
371
    return RDY_RESET;
372
  }
373
  else {
374
    strcpy(wii_steering::bt_address, address);
375
    return RDY_OK;
376
  }
377
}
378

    
379
float
380
UserThread::setWiiDeadzone(const float deadzone)
381
{
382
  // check for negative value and limit to zero
383
  float dz = (deadzone < 0.0f) ? 0.0f : deadzone;
384

    
385
  // if value is >1, range is assumed to be (0, 100)
386
  if (dz > 1.0f) {
387
    // limit to 100
388
    if (dz > 100.0f) {
389
      dz = 100.0f;
390
    }
391
    dz /= 100.0f;
392
  }
393

    
394
  // set value and return it
395
  wii_steering::deadzone = dz;
396
  return dz;
397
}
398