GCC Code Coverage Report
Directory: src/catkin/ Exec Total Coverage
File: src/catkin/hardware/blmc_drivers/src/motor_board.cpp Lines: 1 177 0.6 %
Date: 2020-01-13 15:17:31 Branches: 2 167 1.2 %

Line Branch Exec Source
1
/**
2
 * @file motor_board.cpp
3
 * @author Felix Widmaier (felix.widmaier@tuebingen.mpg.de)
4
 * @author Manuel Wuthrich (manuel.wuthrich@gmail.com)
5
 * @author Maximilien Naveau (maximilien.naveau@gmail.com)
6
 * @brief This file implements the classes from
7
 * "blmc_drivers/devices/motor_board.hpp"
8
 * @version 0.1
9
 * @date 2018-11-26
10
 *
11
 * @copyright Copyright (c) 2018
12
 *
13
 */
14
15
#include <blmc_drivers/devices/motor_board.hpp>
16
17
namespace blmc_drivers
18
{
19
20
CanBusMotorBoard::CanBusMotorBoard(
21
        std::shared_ptr<CanBusInterface> can_bus,
22
        const size_t& history_length,
23
        const int& control_timeout_ms):
24
    can_bus_(can_bus),
25
    motors_are_paused_(false),
26
    control_timeout_ms_(control_timeout_ms)
27
{
28
    measurement_  = create_vector_of_pointers<ScalarTimeseries>(
29
                measurement_count,
30
                history_length);
31
    status_       = std::make_shared<StatusTimeseries>(history_length);
32
    control_      = create_vector_of_pointers<ScalarTimeseries>(
33
                control_count,
34
                history_length);
35
    command_      = std::make_shared<CommandTimeseries>(history_length);
36
    sent_control_ = create_vector_of_pointers<ScalarTimeseries>(
37
                control_count,
38
                history_length);
39
    sent_command_ = std::make_shared<CommandTimeseries>(history_length);
40
41
    is_loop_active_ = true;
42
    rt_thread_.create_realtime_thread(&CanBusMotorBoard::loop, this);
43
}
44
45
CanBusMotorBoard::~CanBusMotorBoard()
46
{
47
    is_loop_active_ = false;
48
    rt_thread_.join();
49
    set_command(MotorBoardCommand(MotorBoardCommand::IDs::ENABLE_SYS,
50
                                  MotorBoardCommand::Contents::DISABLE));
51
    send_newest_command();
52
}
53
54
void CanBusMotorBoard::send_if_input_changed()
55
{
56
    // send command if a new one has been set ----------------------------------
57
    if(command_->has_changed_since_tag())
58
    {
59
        send_newest_command();
60
    }
61
62
    // send controls if a new one has been set ---------------------------------
63
    bool controls_have_changed = false;
64
65
    for(auto control : control_)
66
    {
67
        if(control->has_changed_since_tag())
68
            controls_have_changed = true;
69
    }
70
    if(controls_have_changed)
71
    {
72
        send_newest_controls();
73
    }
74
}
75
76
77
void CanBusMotorBoard::wait_until_ready()
78
{
79
    rt_printf("waiting for board and motors to be ready \n");
80
    real_time_tools::ThreadsafeTimeseries<>::Index time_index = status_->newest_timeindex();
81
    bool is_ready = false;
82
    while(!is_ready)
83
    {
84
        MotorBoardStatus status = (*status_)[time_index];
85
        time_index++;
86
87
        is_ready = status.is_ready();
88
    }
89
    rt_printf("board and motors are ready \n");
90
}
91
92
93
bool CanBusMotorBoard::is_ready()
94
{
95
    if(status_->length() == 0)
96
    {
97
        return false;
98
    }
99
    else
100
    {
101
        return status_->newest_element().is_ready();
102
    }
103
}
104
105
void CanBusMotorBoard::pause_motors()
106
{
107
    set_control(0, current_target_0);
108
    set_control(0, current_target_1);
109
    send_newest_controls();
110
111
    set_command(MotorBoardCommand(MotorBoardCommand::IDs::SET_CAN_RECV_TIMEOUT,
112
                                  MotorBoardCommand::Contents::DISABLE));
113
    send_newest_command();
114
115
    motors_are_paused_ = true;
116
}
117
118
void CanBusMotorBoard::disable_can_recv_timeout()
119
{
120
    set_command(MotorBoardCommand(MotorBoardCommand::IDs::SET_CAN_RECV_TIMEOUT,
121
                                  MotorBoardCommand::Contents::DISABLE));
122
    send_newest_command();
123
}
124
125
126
void CanBusMotorBoard::send_newest_controls()
127
{
128
    if(motors_are_paused_)
129
    {
130
        set_command(MotorBoardCommand(
131
                        MotorBoardCommand::IDs::SET_CAN_RECV_TIMEOUT,
132
                        control_timeout_ms_));
133
        send_newest_command();
134
        motors_are_paused_ = false;
135
    }
136
137
    std::array<double, 2> controls;
138
    for(size_t i = 0; i < control_.size(); i++)
139
    {
140
        if(control_[i]->length() == 0)
141
        {
142
            rt_printf("you tried to send control but no control has been set\n");
143
            exit(-1);
144
        }
145
146
        Index timeindex = control_[i]->newest_timeindex();
147
        controls[i] = (*control_[i])[timeindex];
148
        control_[i]->tag(timeindex);
149
150
        sent_control_[i]->append(controls[i]);
151
    }
152
153
    float current_mtr1 = controls[0];
154
    float current_mtr2 = controls[1];
155
156
    uint8_t data[8];
157
    uint32_t q_current1, q_current2;
158
159
    // Convert floats to Q24 values
160
    q_current1 = float_to_q24(current_mtr1);
161
    q_current2 = float_to_q24(current_mtr2);
162
163
    // Motor 1
164
    data[0] = (q_current1 >> 24) & 0xFF;
165
    data[1] = (q_current1 >> 16) & 0xFF;
166
    data[2] = (q_current1 >> 8) & 0xFF;
167
    data[3] =  q_current1 & 0xFF;
168
169
    // Motor 2
170
    data[4] = (q_current2 >> 24) & 0xFF;
171
    data[5] = (q_current2 >> 16) & 0xFF;
172
    data[6] = (q_current2 >> 8) & 0xFF;
173
    data[7] =  q_current2 & 0xFF;
174
175
    CanBusFrame can_frame;
176
    can_frame.id = CanframeIDs::IqRef;
177
    for(size_t i = 0; i < 7; i++)
178
    {
179
        can_frame.data[i] = data[i];
180
    }
181
    can_frame.dlc = 8;
182
183
    can_bus_->set_input_frame(can_frame);
184
    can_bus_->send_if_input_changed();
185
}
186
187
void CanBusMotorBoard::send_newest_command()
188
{
189
    if(command_->length() == 0)
190
    {
191
        rt_printf("you tried to send command but no command has been set\n");
192
        exit(-1);
193
    }
194
195
    Index timeindex = command_->newest_timeindex();
196
    MotorBoardCommand command = (*command_)[timeindex];
197
    command_->tag(timeindex);
198
    sent_command_->append(command);
199
200
    uint32_t id = command.id_;
201
    int32_t content = command.content_;
202
203
    uint8_t data[8];
204
205
    // content
206
    data[0] = (content >> 24) & 0xFF;
207
    data[1] = (content >> 16) & 0xFF;
208
    data[2] = (content >> 8) & 0xFF;
209
    data[3] = content & 0xFF;
210
211
    // command
212
    data[4] = (id >> 24) & 0xFF;
213
    data[5] = (id >> 16) & 0xFF;
214
    data[6] = (id >> 8) & 0xFF;
215
    data[7] = id & 0xFF;
216
217
    CanBusFrame can_frame;
218
    can_frame.id = CanframeIDs::COMMAND_ID;
219
    for(size_t i = 0; i < 8; i++)
220
    {
221
        can_frame.data[i] = data[i];
222
    }
223
    can_frame.dlc = 8;
224
225
    can_bus_->set_input_frame(can_frame);
226
    can_bus_->send_if_input_changed();
227
}
228
229
void CanBusMotorBoard::loop()
230
{
231
    pause_motors();
232
233
    // initialize board --------------------------------------------------------
234
    set_command(MotorBoardCommand(
235
                    MotorBoardCommand::IDs::ENABLE_SYS,
236
                    MotorBoardCommand::Contents::ENABLE));
237
    send_newest_command();
238
239
    set_command(MotorBoardCommand(
240
                    MotorBoardCommand::IDs::SEND_ALL,
241
                    MotorBoardCommand::Contents::ENABLE));
242
    send_newest_command();
243
244
    set_command(MotorBoardCommand(
245
                    MotorBoardCommand::IDs::ENABLE_MTR1,
246
                    MotorBoardCommand::Contents::ENABLE));
247
    send_newest_command();
248
249
    set_command(MotorBoardCommand(
250
                    MotorBoardCommand::IDs::ENABLE_MTR2,
251
                    MotorBoardCommand::Contents::ENABLE));
252
    send_newest_command();
253
254
    // receive data from board in a loop ---------------------------------------
255
    long int timeindex = can_bus_->get_output_frame()->newest_timeindex();
256
    while(is_loop_active_)
257
    {
258
        CanBusFrame can_frame;
259
        Index received_timeindex = timeindex;
260
        can_frame = (*can_bus_->get_output_frame())[received_timeindex];
261
262
        if(received_timeindex != timeindex)
263
        {
264
            rt_printf("did not get the timeindex we expected! "
265
                      "received_timeindex: %d, "
266
                      "desired_timeindex: %d\n",
267
                      int(received_timeindex), int(timeindex));
268
            exit(-1);
269
        }
270
271
        timeindex++;
272
273
        // convert to measurement ------------------------------------------
274
        double measurement_0 = qbytes_to_float(can_frame.data.begin());
275
        double measurement_1 = qbytes_to_float((can_frame.data.begin() + 4));
276
277
278
        switch(can_frame.id)
279
        {
280
        case CanframeIDs::Iq:
281
            measurement_[current_0]->append(measurement_0);
282
            measurement_[current_1]->append(measurement_1);
283
            break;
284
        case CanframeIDs::POS:
285
            // Convert the position unit from the blmc card (kilo-rotations)
286
            // into rad.
287
            measurement_[position_0]->append(measurement_0 * 2 * M_PI);
288
            measurement_[position_1]->append(measurement_1 * 2 * M_PI);
289
            break;
290
        case CanframeIDs::SPEED:
291
            // Convert the speed unit from the blmc card (kilo-rotations-per-minutes)
292
            // into rad/s.
293
            measurement_[velocity_0]->append(measurement_0 * 2 * M_PI * (1000./60.));
294
            measurement_[velocity_1]->append(measurement_1 * 2 * M_PI * (1000./60.));
295
            break;
296
        case CanframeIDs::ADC6:
297
            measurement_[analog_0]->append(measurement_0);
298
            measurement_[analog_1]->append(measurement_1);
299
            break;
300
        case CanframeIDs::ENC_INDEX:
301
        {
302
            // here the interpretation of the message is different,
303
            // we get a motor index and a measurement
304
            uint8_t motor_index = can_frame.data[4];
305
            if(motor_index == 0)
306
            {
307
                measurement_[encoder_index_0]->append(measurement_0 * 2 * M_PI);
308
            }
309
            else if(motor_index == 1)
310
            {
311
                measurement_[encoder_index_1]->append(measurement_0 * 2 * M_PI);
312
            }
313
            else
314
            {
315
                rt_printf("ERROR: Invalid motor number"
316
                          "for encoder index: %d\n", motor_index);
317
                exit(-1);
318
            }
319
            break;
320
        }
321
        case CanframeIDs::STATUSMSG:
322
        {
323
            MotorBoardStatus status;
324
            uint8_t data = can_frame.data[0];
325
            status.system_enabled = data >> 0;
326
            status.motor1_enabled = data >> 1;
327
            status.motor1_ready   = data >> 2;
328
            status.motor2_enabled = data >> 3;
329
            status.motor2_ready   = data >> 4;
330
            status.error_code     = data >> 5;
331
332
            status_->append(status);
333
            break;
334
        }
335
        }
336
337
        //        static int count = 0;
338
        //        if(count % 4000 == 0)
339
        //        {
340
        //            print_status();
341
        //        }
342
        //        count++;
343
    }
344
}
345
346
void CanBusMotorBoard::print_status()
347
{
348
    rt_printf("ouptus ======================================\n");
349
    rt_printf("measurements: -------------------------------\n");
350
    for(size_t i = 0; i < measurement_.size(); i++)
351
    {
352
        rt_printf("%d: ---------------------------------\n", int(i));
353
        if(measurement_[i]->length() > 0)
354
        {
355
            double measurement = measurement_[i]->newest_element();
356
            rt_printf("value %f:\n", measurement);
357
        }
358
    }
359
360
    rt_printf("status: ---------------------------------\n");
361
    if(status_->length() > 0)
362
        status_->newest_element().print();
363
364
    //        rt_printf("inputs ======================================\n");
365
366
    //        for(size_t i = 0; i < control_names.size(); i++)
367
    //        {
368
    //            rt_printf("%s: ---------------------------------\n",
369
    //                                 control_names[i].c_str());
370
    //            if(control_.at(control_names[i])->length() > 0)
371
    //            {
372
    //                double control =
373
    //                        control_.at(control_names[i])->newest_element();
374
    //                rt_printf("value %f:\n", control);
375
    //            }
376
    //        }
377
378
    //        rt_printf("command: ---------------------------------\n");
379
    //        if(command_[command]->length() > 0)
380
    //            command_[command]->newest_element().print();
381
}
382
383

3
} // namespace blmc_drivers