Statistics
| Branch: | Tag: | Revision:

amiro-os / devices / PowerManagement / userthread.cpp @ 5d138bca

History | View | Annotate | Download (14.7 KB)

1 58fe0e0b Thomas Schöpping
#include "userthread.h"
2
3
#include "global.hpp"
4
#include <array>
5 2f3e64c4 Thomas Schöpping
#include <chprintf.h>
6 61b0791a Thomas Schöpping
#include <cmath>
7 58fe0e0b Thomas Schöpping
8
using namespace amiro;
9
10
extern Global global;
11
12 61b0791a Thomas Schöpping
volatile UserThread::State current_state;
13
volatile UserThread::State next_state;
14
types::kinematic kinematic;
15 58fe0e0b Thomas Schöpping
16 61b0791a Thomas Schöpping
namespace obstacle_avoidance {
17 58fe0e0b Thomas Schöpping
18
uint16_t constexpr proxThresholdLow = 0x0000;
19 b4885314 Thomas Schöpping
uint16_t constexpr proxThresholdHigh = 0x1000;
20 58fe0e0b Thomas Schöpping
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 b4885314 Thomas Schöpping
uint32_t constexpr baseTranslation = 100e3; // 2cm/s
34 58fe0e0b Thomas Schöpping
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 61b0791a Thomas Schöpping
} /* 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 33c91ff2 Thomas Schöpping
uint8_t principal_axis = 1;
70
int8_t axis_direction = -1;
71 61b0791a Thomas Schöpping
72
uint32_t constexpr maxTranslation = 500e3;
73
uint32_t constexpr maxRotation = 3.1415927f * 1000000.0f * 2.0f;
74
75
}
76
77 58fe0e0b Thomas Schöpping
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 61b0791a Thomas Schöpping
  /*
90
   * initialize some variables
91
   */
92
  current_state = IDLE;
93 58fe0e0b Thomas Schöpping
94 61b0791a Thomas Schöpping
  /*
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 58fe0e0b Thomas Schöpping
            }
121 61b0791a Thomas Schöpping
          }
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 58fe0e0b Thomas Schöpping
148
            // set all LEDs to white for one second
149
            for (uint8_t led = 0; led < 8; ++led) {
150 61b0791a Thomas Schöpping
              global.robot.setLightColor(led, Color(Color::WHITE));
151 58fe0e0b Thomas Schöpping
            }
152
            this->sleep(MS2ST(1000));
153
            for (uint8_t led = 0; led < 8; ++led) {
154 61b0791a Thomas Schöpping
              global.robot.setLightColor(led, Color(Color::BLACK));
155 58fe0e0b Thomas Schöpping
            }
156 61b0791a Thomas Schöpping
          }
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 58fe0e0b Thomas Schöpping
        }
164 61b0791a Thomas Schöpping
        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 58fe0e0b Thomas Schöpping
170 61b0791a Thomas Schöpping
            // 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 58fe0e0b Thomas Schöpping
            }
179 61b0791a Thomas Schöpping
          }
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 58fe0e0b Thomas Schöpping
191 61b0791a Thomas Schöpping
    // 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 33c91ff2 Thomas Schöpping
            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 61b0791a Thomas Schöpping
            wii_steering::wiimoteCalib[0] = wiimoteAcc[0];
302 33c91ff2 Thomas Schöpping
            wii_steering::wiimoteCalib[1] = wiimoteAcc[1];
303 61b0791a Thomas Schöpping
            wii_steering::wiimoteCalib[2] = wiimoteAcc[2];
304 33c91ff2 Thomas Schöpping
            wii_steering::wiimoteCalib[wii_steering::principal_axis] += -100.0f * wii_steering::axis_direction;
305 61b0791a Thomas Schöpping
306 33c91ff2 Thomas Schöpping
            // print information
307 61b0791a Thomas Schöpping
            chprintf((BaseSequentialStream*)&global.sercanmux1, "accelerometer calibrated:\n");
308 33c91ff2 Thomas Schöpping
            chprintf((BaseSequentialStream*)&global.sercanmux1, "\tprincipal axis: %c\n", (wii_steering::principal_axis == 0) ? 'X' : (wii_steering::principal_axis == 1) ? 'Y' : 'Z');
309 61b0791a Thomas Schöpping
            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 58fe0e0b Thomas Schöpping
            }
327
328 61b0791a Thomas Schöpping
            // apply deadzone
329
            if (std::fabs(wiimoteAcc[axis]) < wii_steering::deadzone) {
330
              wiimoteAcc[axis] = 0.0f;
331 58fe0e0b Thomas Schöpping
            }
332
333
            /*
334 61b0791a Thomas Schöpping
             * the value is now in (-1 .. -deazone, 0, deadzone .. 1)
335
             * note the gaps [-deadzone .. 0] and [0 .. deadzone]
336 58fe0e0b Thomas Schöpping
             */
337 61b0791a Thomas Schöpping
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 58fe0e0b Thomas Schöpping
            }
344 61b0791a Thomas Schöpping
            wiimoteAcc[axis] *= (1.0f / (1.0f - wii_steering::deadzone));
345
          }
346 58fe0e0b Thomas Schöpping
347 61b0791a Thomas Schöpping
          // only move when A is pressed
348 33c91ff2 Thomas Schöpping
          if (wii_steering::wiimote.getButtons()->A || wii_steering::wiimote.getButtons()->B) {
349 61b0791a Thomas Schöpping
            // set kinematic relaive to maximum speeds
350 33c91ff2 Thomas Schöpping
            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 61b0791a Thomas Schöpping
          } else {
368
            kinematic = {0, 0, 0, 0, 0, 0};
369
          }
370
371
          // set speed
372
          global.robot.setTargetSpeed(kinematic);
373 58fe0e0b Thomas Schöpping
        }
374
375 61b0791a Thomas Schöpping
        break;
376
      }
377 58fe0e0b Thomas Schöpping
    }
378 61b0791a Thomas Schöpping
  }
379
380
  // stop the robot
381
  kinematic = {0, 0, 0, 0, 0, 0};
382
  global.robot.setTargetSpeed(kinematic);
383 58fe0e0b Thomas Schöpping
384
  return RDY_OK;
385
}
386
387 61b0791a Thomas Schöpping
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
}