GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/catkin/robots/blmc_robots/src/blmc_joint_module.cpp Lines: 1 183 0.5 %
Date: 2020-04-15 11:50:02 Branches: 2 161 1.2 %

Line Branch Exec Source
1
/**
2
 * @file blmc_joint_module.cpp
3
 * @author Maximilien Naveau (maximilien.naveau@gmail.com)
4
 * @author Manuel Wuthrich
5
 * @license License BSD-3-Clause
6
 * @copyright Copyright (c) 2019, New York University and Max Planck Gesellschaft.
7
 * @date 2019-07-11
8
 */
9
10
#include <cmath>
11
#include "real_time_tools/spinner.hpp"
12
#include "real_time_tools/iostream.hpp"
13
#include "blmc_robots/blmc_joint_module.hpp"
14
15
16
namespace blmc_robots{
17
18
19
BlmcJointModule::BlmcJointModule(std::shared_ptr<blmc_drivers::MotorInterface> motor,
20
                    const double& motor_constant,
21
                    const double& gear_ratio,
22
                    const double& zero_angle,
23
                    const bool& reverse_polarity,
24
                    const double& max_current)
25
{
26
    motor_ = motor;
27
    motor_constant_ = motor_constant;
28
    gear_ratio_ = gear_ratio;
29
    set_zero_angle(zero_angle);
30
    polarity_ = reverse_polarity ? -1.0:1.0;
31
    max_current_ = max_current;
32
33
    position_control_gain_p_ = 0;
34
    position_control_gain_d_ = 0;
35
}
36
37
void BlmcJointModule::set_torque(const double& desired_torque)
38
{
39
    double desired_current = joint_torque_to_motor_current(desired_torque);
40
41
    if(std::fabs(desired_current) > max_current_)
42
    {
43
        std::cout << "something went wrong, it should never happen"
44
                     " that desired_current > "
45
                  << max_current_
46
                  << ". desired_current: "
47
                  << desired_current
48
                  << std::endl;
49
        exit(-1);
50
    }
51
    motor_->set_current_target(polarity_ * desired_current);
52
}
53
54
void BlmcJointModule::set_zero_angle(const double& zero_angle)
55
{
56
    zero_angle_ = zero_angle;
57
}
58
59
void BlmcJointModule::set_joint_polarity(const bool& reverse_polarity)
60
{
61
    polarity_ = reverse_polarity ? -1.0 : 1.0;
62
}
63
void BlmcJointModule::send_torque()
64
{
65
    motor_->send_if_input_changed();
66
}
67
68
double BlmcJointModule::get_max_torque() const
69
{
70
    return motor_current_to_joint_torque(max_current_);
71
}
72
73
double BlmcJointModule::get_sent_torque() const
74
{
75
    auto measurement_history =
76
            motor_->get_sent_current_target();
77
78
    if(measurement_history->length() == 0)
79
    {
80
        return std::numeric_limits<double>::quiet_NaN();
81
    }
82
    return motor_current_to_joint_torque(measurement_history->newest_element());
83
}
84
85
double BlmcJointModule::get_measured_torque() const
86
{
87
    return motor_current_to_joint_torque(get_motor_measurement(mi::current));
88
}
89
90
double BlmcJointModule::get_measured_angle() const
91
{
92
    return get_motor_measurement(mi::position) / gear_ratio_ - zero_angle_;
93
}
94
95
double BlmcJointModule::get_measured_velocity() const
96
{
97
    return get_motor_measurement(mi::velocity) / gear_ratio_;
98
}
99
100
double BlmcJointModule::joint_torque_to_motor_current(double torque) const
101
{
102
    return torque / gear_ratio_ / motor_constant_;
103
}
104
105
double BlmcJointModule::motor_current_to_joint_torque(double current) const
106
{
107
    return current *  gear_ratio_ * motor_constant_;
108
}
109
110
double BlmcJointModule::get_measured_index_angle() const
111
{
112
    return get_motor_measurement(mi::encoder_index) / gear_ratio_;
113
}
114
115
double BlmcJointModule::get_zero_angle() const
116
{
117
    return zero_angle_;
118
}
119
120
double BlmcJointModule::get_motor_measurement(const mi& measurement_id) const
121
{
122
    auto measurement_history =
123
            motor_->get_measurement(measurement_id);
124
125
    if(measurement_history->length() == 0)
126
    {
127
        // rt_printf("get_motor_measurement returns NaN\n");
128
        return std::numeric_limits<double>::quiet_NaN();
129
    }
130
    return polarity_ * measurement_history->newest_element();
131
}
132
133
long int BlmcJointModule::get_motor_measurement_index(const mi& measurement_id) const
134
{
135
    auto measurement_history =
136
            motor_->get_measurement(measurement_id);
137
138
    if(measurement_history->length() == 0)
139
    {
140
        // rt_printf("get_motor_measurement_index returns NaN\n");
141
        return -1;
142
    }
143
    return measurement_history->newest_timeindex();
144
}
145
146
void BlmcJointModule::set_position_control_gains(double kp, double kd)
147
{
148
    position_control_gain_p_ = kp;
149
    position_control_gain_d_ = kd;
150
}
151
152
double BlmcJointModule::execute_position_controller(double target_position_rad) const
153
{
154
    double diff = target_position_rad - get_measured_angle();
155
156
    // simple PD control
157
    double desired_torque = position_control_gain_p_ * diff
158
        - position_control_gain_d_ * get_measured_velocity();
159
160
    // clamp torque
161
    const double max_torque =
162
        motor_current_to_joint_torque(max_current_) * 0.9;
163
    if (desired_torque > max_torque) {
164
        desired_torque = max_torque;
165
    } else if (desired_torque < -max_torque) {
166
        desired_torque = -max_torque;
167
    }
168
169
    return desired_torque;
170
}
171
172
bool BlmcJointModule::calibrate(double& angle_zero_to_index,
173
                                double& index_angle,
174
                                bool mechanical_calibration)
175
{
176
    // save the current position
177
    double starting_position = get_measured_angle();
178
    rt_printf("Starting pose is=%f\n", starting_position);
179
180
    // reset the ouput
181
    index_angle = 0.0;
182
183
    // we reset the internal zero angle.
184
    zero_angle_ = 0.0;
185
186
    long int last_index_time = get_motor_measurement_index(mi::encoder_index);
187
    if(std::isnan(last_index_time)){last_index_time = -1;}
188
    bool reached_next_index = false;
189
    real_time_tools::Spinner spinner;
190
    spinner.set_period(0.001);
191
    rt_printf("Search for the index\n");
192
    while(!reached_next_index)
193
    {
194
      // Small D gain
195
      double k_d = 0.2;
196
      // Small desired velocity
197
      double joint_vel_des = 0.8;
198
      // Velocity controller
199
      double actual_velocity = get_measured_velocity();
200
      double torque = + k_d * (joint_vel_des - actual_velocity);
201
      // rt_printf("error=%f, vel_des=%f, vel=%f, tau=%f\n", joint_vel_des - actual_velocity, joint_vel_des, actual_velocity, torque);
202
      // Send the torque command
203
      set_torque(torque);
204
      send_torque();
205
      // check stop
206
      long int actual_index_time = get_motor_measurement_index(mi::encoder_index);
207
      double actual_index_angle = get_measured_index_angle();
208
209
      reached_next_index = (actual_index_time > last_index_time);
210
      // rt_printf("last_index_time=%ld, actual_index_time=%ld, actual_index_angle=%f\n", last_index_time, actual_index_time, actual_index_angle);
211
      if(reached_next_index)
212
      {
213
          index_angle = actual_index_angle;
214
      }
215
      spinner.spin();
216
    }
217
    // reset the control to zero torque
218
    set_torque(0.0);
219
    send_torque();
220
    spinner.spin();
221
222
    // get the indexes and stuff
223
    if(mechanical_calibration)
224
    {
225
        angle_zero_to_index = index_angle - starting_position;
226
    }
227
    zero_angle_ = index_angle - angle_zero_to_index;
228
229
    rt_printf("Zero angle is=%f\n", zero_angle_);
230
    rt_printf("Zero angle to index angle is=%f\n", angle_zero_to_index);
231
    rt_printf("Index angle is=%f\n", index_angle);
232
233
234
    rt_printf("Position Control\n");
235
    // Go to 0
236
    double init_pose = get_measured_angle();
237
    double final_pose = 0.0;
238
    double final_angle = 0.0;
239
    int traj_time = 2.0 / 0.001;
240
    int counter = 0;
241
    double torque_int = 0.0;
242
    double torque_sat = 0.1; // Nm
243
    bool reached_zero_pose = 0;
244
    while(!reached_zero_pose)
245
    {
246
      // Small P gain
247
      double k_p = 2.5;
248
      // Integrale gain
249
      double k_i = 0.5;
250
      // desired pose
251
      double alpha = 1.0 - (double)((double)counter/(double)traj_time);
252
      double des_angle = alpha * init_pose + (1.0-alpha) * final_pose;
253
      // compute the error
254
      double current_angle = get_measured_angle();
255
      double err = des_angle - current_angle;
256
      // small saturation in intensity
257
      torque_int += k_i * err * 0.001; // 1 ms sampling period
258
      if(torque_int > torque_sat){torque_int = torque_sat;}
259
      if(torque_int < -torque_sat){torque_int = -torque_sat;}
260
      // Position controller
261
      double torque = k_p * err + torque_int ;
262
      // rt_printf("error=%f, torque_int=%f, tau=%f\n", err, torque_int, torque);
263
      // Send the torque command
264
      set_torque(torque);
265
      send_torque();
266
      // check out
267
      reached_zero_pose = (std::fabs(current_angle) <= 1e-2); // nearly 0.1 degree
268
      if(reached_zero_pose)
269
      {
270
        final_angle = -err;
271
      }
272
      spinner.spin();
273
      ++counter;
274
      if(counter > traj_time){counter = traj_time;}
275
    }
276
    rt_printf("Final angle is=%f\n", final_angle);
277
    // reset the control to zero torque
278
    set_torque(0.0);
279
    send_torque();
280
    spinner.spin();
281
282
    // FIXME: always returns true as there is no error checking
283
    return true;
284
}
285
286
287
void BlmcJointModule::init_homing(int joint_id, double search_distance_limit_rad,
288
        double home_offset_rad, double profile_step_size_rad)
289
{
290
    // reset the internal zero angle.
291
    set_zero_angle(0.0);
292
293
    // TODO: would be nice if the joint instance had a `name` or `id` and class
294
    // level instead of storing it here (to make more useful debug prints).
295
    homing_state_.joint_id = joint_id;
296
297
    homing_state_.search_distance_limit_rad = search_distance_limit_rad;
298
    homing_state_.home_offset_rad = home_offset_rad;
299
    homing_state_.profile_step_size_rad = profile_step_size_rad;
300
    homing_state_.last_encoder_index_time_index = get_motor_measurement_index(
301
            mi::encoder_index);
302
    homing_state_.target_position_rad = get_measured_angle();
303
    homing_state_.step_count = 0;
304
305
    homing_state_.status = HomingReturnCode::RUNNING;
306
}
307
308
HomingReturnCode BlmcJointModule::update_homing()
309
{
310
    switch (homing_state_.status) {
311
        case HomingReturnCode::NOT_INITIALIZED:
312
            set_torque(0.0);
313
            send_torque();
314
            rt_printf("[%d] Homing is not initialized.  Abort.\n",
315
                      homing_state_.joint_id);
316
            break;
317
318
        case HomingReturnCode::FAILED:
319
            // when failed, send zero-torque commands
320
            set_torque(0.0);
321
            break;
322
323
        case HomingReturnCode::SUCCEEDED: {
324
            // when succeeded, keep the motor at the home position
325
            double desired_torque = execute_position_controller(
326
                    homing_state_.target_position_rad);
327
328
            set_torque(desired_torque);
329
            break;
330
        }
331
332
        case HomingReturnCode::RUNNING: {
333
334
            // number of steps after which the distance limit is reached
335
            const uint32_t max_step_count =
336
                std::abs(homing_state_.search_distance_limit_rad /
337
                         homing_state_.profile_step_size_rad);
338
339
            // abort if distance limit is reached
340
            if (homing_state_.step_count >= max_step_count) {
341
                set_torque(0.0);
342
                homing_state_.status = HomingReturnCode::FAILED;
343
344
                rt_printf("BlmcJointModule::update_homing(): "
345
                          "ERROR: Failed to find index with joint [%d].\n",
346
                          homing_state_.joint_id);
347
                break;
348
            }
349
350
            // -- EXECUTE ONE STEP
351
352
            homing_state_.step_count++;
353
            homing_state_.target_position_rad +=
354
                homing_state_.profile_step_size_rad;
355
356
#ifdef VERBOSE
357
            const double current_position = get_measured_angle();
358
            if (homing_state_.step_count % 100 == 0) {
359
                rt_printf("[%d] cur: %f,\t des: %f\n", homing_state_.joint_id,
360
                        current_position, homing_state_.target_position_rad);
361
            }
362
#endif
363
364
            // FIXME: add a safety check to stop if following error gets too big.
365
366
            const double desired_torque = execute_position_controller(
367
                    homing_state_.target_position_rad);
368
            set_torque(desired_torque);
369
370
            // Check if new encoder index was observed
371
            const long int actual_index_time = get_motor_measurement_index(
372
                    mi::encoder_index);
373
            if (actual_index_time > homing_state_.last_encoder_index_time_index) {
374
                // -- FINISHED
375
                const double index_angle = get_measured_index_angle();
376
377
                // set the zero angle
378
                set_zero_angle(index_angle + homing_state_.home_offset_rad);
379
380
                // adjust target_position according to the new zero
381
                homing_state_.target_position_rad -= zero_angle_;
382
383
#ifdef VERBOSE
384
                rt_printf("[%d] Zero angle is=%f\n", homing_state_.joint_id,
385
                          zero_angle_);
386
                rt_printf("[%d] Index angle is=%f\n", homing_state_.joint_id,
387
                          index_angle);
388
#endif
389
390
                homing_state_.status = HomingReturnCode::SUCCEEDED;
391
            }
392
393
            break;
394
        }
395
    }
396
397
    return homing_state_.status;
398
}
399
400

3
} // namespace