Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpPololu.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * Common features for Pololu Maestro Servo Motor.
32 */
33
34#include <visp3/core/vpConfig.h>
35
36
37#if defined(VISP_HAVE_POLOLU) && defined(VISP_HAVE_THREADS)
38
39#include <chrono>
40#include <thread>
41
42#include <RPMSerialInterface.h>
43
44#include <visp3/robot/vpRobot.h>
45#include <visp3/robot/vpRobotException.h>
46#include <visp3/robot/vpPololu.h>
47
48std::chrono::milliseconds millis(1);
49
51RPMSerialInterface *vpPololu::m_interface = nullptr;
52int vpPololu::m_nb_servo = 0;
53
54vpPololu::vpPololu(bool verbose)
55 : m_channel(0), m_apply_velocity_cmd(false), m_stop_velocity_cmd_thread(false),
56 m_vel_speed(1), m_vel_target_position(0), m_vel_speed_prev(1), m_vel_target_position_prev(0), m_mutex_velocity_cmd(), m_verbose(verbose)
57{ }
58
59vpPololu::vpPololu(const std::string &device, int baudrate, int channel, bool verbose)
60 : m_channel(channel), m_apply_velocity_cmd(false), m_stop_velocity_cmd_thread(false),
61 m_vel_speed(1), m_vel_target_position(0), m_vel_speed_prev(1), m_vel_target_position_prev(0), m_mutex_velocity_cmd(), m_verbose(verbose)
62{
63 connect(device, baudrate, channel);
64}
65
66void vpPololu::connect(const std::string &device, int baudrate, int channel)
67{
68 std::string error_msg;
69 m_channel = channel;
70 m_nb_servo++;
71
72 if (!m_interface) {
73 if (m_verbose) {
74 std::cout << "Creating serial interface '" << device << "' at " << baudrate << " bauds" << std::endl;
75 }
76 m_interface = RPMSerialInterface::createSerialInterface(device, baudrate, &error_msg);
77
78 if (m_interface == nullptr) {
80 "Cannot connect to pololu board with serial port %s at baudrate %d.\n%s",
81 device.c_str(), baudrate, error_msg.c_str()));
82
83 }
84 }
85
86 std::thread t(&vpPololu::VelocityCmdThread, this);
87 t.detach();
88}
89
90
92{
93 m_stop_velocity_cmd_thread = true;
94 if (m_nb_servo == 1) {
95 if (m_verbose) {
96 std::cout << "Deleting serial interface" << std::endl;
97 }
98 delete m_interface;
99 }
100 m_nb_servo--;
101}
102
103void vpPololu::calibrate(unsigned short &pwm_min, unsigned short &pwm_max)
104{
105 bool ret = false;
106 ret = m_interface->setTargetMSSCP(m_channel, 0);
107 if (ret == false) {
108 throw(vpException(vpException::fatalError, "Unable to set servo normalized target at min position"));
109 }
110 std::this_thread::sleep_for(std::chrono::milliseconds(2000));
111 pwm_min = getPwmPosition();
112
113 ret = m_interface->setTargetMSSCP(m_channel, 254);
114 if (ret == false) {
115 throw(vpException(vpException::fatalError, "Unable to set servo normalized target at max position"));
116 }
117 std::this_thread::sleep_for(std::chrono::milliseconds(2000));
118 pwm_max = getPwmPosition();
119}
120
121unsigned short vpPololu::radToPwm(float angle) const
122{
123 float a = m_range_pwm / m_range_angle;
124 float b = m_min_pwm - m_min_angle * a;
125
126 return static_cast<unsigned short>(a * angle + b);
127}
128
130{
131 if (!m_interface->isOpen()) {
132 if (m_verbose) {
133 std::cout << "Serial Communication Failed!\n";
134 }
135 return false;
136 }
137 return true;
138}
139
140short vpPololu::radSToSpeed(float speed_rad_s) const
141{
142 return static_cast<short>((speed_rad_s / 100.f) * (m_range_pwm / m_range_angle));
143}
144
145unsigned short vpPololu::getPwmPosition() const
146{
147 unsigned short position_pwm;
148 bool ret = false;
149 int nb_attempt = 10;
150 int wait_ms = 2;
151 for (int i = 0; i < nb_attempt && ret == false; i++) {
152 ret = m_interface->getPositionCP(m_channel, position_pwm);
153 if (ret == false) {
154 std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms));
155 if (m_verbose) {
156 std::cout << "Failed to get position, new attempt: " << i << std::endl;
157 }
158 }
159 }
160 if (ret == false) {
161 throw(vpException(vpException::fatalError, "Unable to get servo position"));
162 }
163 return position_pwm;
164}
165
167{
168 unsigned short position_pwm = getPwmPosition();
169 float position_angle = pwmToRad(position_pwm);
170
171 return position_angle;
172}
173
174void vpPololu::getRangeAngles(float &minAngle, float &maxAngle) const
175{
176 minAngle = m_min_angle;
177 maxAngle = m_max_angle;
178}
179
180void vpPololu::getRangePwm(unsigned short &min, unsigned short &max)
181{
182 min = m_min_pwm;
183 max = m_max_pwm;
184}
185
186float vpPololu::pwmToRad(unsigned short pwm) const
187{
188 float a = m_range_angle / m_range_pwm;
189 float b = m_min_angle - m_min_pwm * a;
190
191 return (a * pwm + b);
192}
193
194void vpPololu::setAngularPosition(float pos_rad, float vel_rad_s)
195{
196 if ((m_min_angle <= pos_rad) && (pos_rad <= m_max_angle)) {
197 unsigned short pos_pwm = radToPwm(pos_rad);
198 unsigned short pos_speed = static_cast<unsigned short>(std::fabs(radSToSpeed(vel_rad_s)));
199 // Handle the case where pos_speed = 0 which corresponds to the pwm max speed
200 if (pos_speed == 0) {
201 pos_speed = 1;
202 }
203 setPwmPosition(pos_pwm, pos_speed);
204 }
205 else {
207 "Given position: %d is outside of the servo range. You can check the range using the method getRangeAngles()", pos_rad));
208 }
209}
210
211void vpPololu::setPwmPosition(unsigned short pos_pwm, unsigned short speed_pwm)
212{
213 bool ret = false;
214 if ((m_min_pwm <= pos_pwm) && (pos_pwm <= m_max_pwm)) {
215 if (speed_pwm <= 1000) {
216 ret = m_interface->setSpeedCP(m_channel, speed_pwm);
217 if (ret == false) {
218 throw(vpException(vpException::fatalError, "Unable to set servo speed"));
219 }
220 }
221 else {
223 "Given speed (pwm): %d is outside of the servo speed range. range is from 0 to 1000", speed_pwm));
224 }
225 if (m_verbose) {
226 std::cout << "Channel " << m_channel << " set position (pwm): " << pos_pwm << " with speed: " << speed_pwm << std::endl;
227 }
228 ret = m_interface->setTargetCP(m_channel, pos_pwm);
229 if (ret == false) {
230 throw(vpException(vpException::fatalError, "Unable to set servo target position"));
231 }
232 }
233 else {
235 "Given position (pwm): %d is outside servo range. You can check the range using the method getRangePwm()", pos_pwm));
236 }
237}
238
239void vpPololu::setAngularVelocity(float vel_rad_s)
240{
241 short pwm_vel = radSToSpeed(vel_rad_s);
242
243 // Handle the case where vel_rad_s != 0 but pwm_vel == 0. In that case we set a minimal speed
244 if (pwm_vel == 0) {
245 if (vel_rad_s > 0) {
246 pwm_vel = 1;
247 }
248 else if (vel_rad_s < 0) {
249 pwm_vel = -1;
250 }
251 }
252 setPwmVelocity(pwm_vel);
253}
254
255void vpPololu::setPwmVelocity(short pwm_vel)
256{
257 unsigned short vel_speed;
258 unsigned short vel_target_position;
259 if (pwm_vel <= 1000) {
260 vel_speed = static_cast<unsigned short>(std::abs(pwm_vel));
261 if (pwm_vel > 0) {
262 vel_target_position = m_max_pwm;
263 }
264 else if (pwm_vel < 0) {
265 vel_target_position = m_min_pwm;
266 }
267 else { // pwm_vel = 0
268 vel_target_position = getPwmPosition(); // Stay at current position
269 vel_speed = 1; // Set to min speed to keep current position
270 }
271 m_mutex_velocity_cmd.lock();
272 m_vel_speed = vel_speed;
273 m_vel_target_position = vel_target_position;
274 m_apply_velocity_cmd = true;
275 m_mutex_velocity_cmd.unlock();
276 }
277 else {
279 "Given pwm velocity %d is outside of the servo velocity range. Range is from 0 to 1000", pwm_vel));
280 }
281}
282
283float vpPololu::speedToRadS(short speed_pwm) const
284{
285 return (speed_pwm * 100) * (m_range_angle / m_range_pwm);
286}
287
289{
290 if (m_verbose) {
291 std::cout << "Stoping vel cmd channel: " << m_channel << std::endl;
292 }
293
294 m_mutex_velocity_cmd.lock();
295 m_apply_velocity_cmd = false;
296 m_mutex_velocity_cmd.unlock();
297
298 //std::this_thread::sleep_for(10 * millis);
299 unsigned short pos_pwm = getPwmPosition();
300 if (m_verbose) {
301 std::cout << "Stoping channel " << m_channel << " at position " << pos_pwm << std::endl;
302 }
303 setPwmPosition(pos_pwm, 0); // 0 to be as fast as possible to reach pos_pwm
304}
305
306void vpPololu::VelocityCmdThread()
307{
308 if (m_verbose) {
309 std::cout << "Create Velocity command thread" << std::endl;
310 }
311 unsigned short vel_speed;
312 unsigned short vel_target_position;
313 bool apply_velocity_cmd;
314
315 while (!m_stop_velocity_cmd_thread) {
316 m_mutex_velocity_cmd.lock();
317 vel_speed = m_vel_speed;
318 vel_target_position = m_vel_target_position;
319 apply_velocity_cmd = m_apply_velocity_cmd;
320 m_mutex_velocity_cmd.unlock();
321 if (apply_velocity_cmd) {
322 //unsigned short position = getPwmPosition();
323 //if (position != m_vel_target_position) {
324 if (m_vel_speed_prev != vel_speed || m_vel_target_position_prev != vel_target_position) {
325 setPwmPosition(vel_target_position, vel_speed);
326 }
327
328 m_vel_speed_prev = vel_speed;
329 m_vel_target_position_prev = vel_target_position;
330 }
331
332 std::this_thread::sleep_for(20 * millis);
333 }
334}
335END_VISP_NAMESPACE
336#elif !defined(VISP_BUILD_SHARED_LIBS)
337// Work around to avoid warning: libvisp_robot.a(vpPololu.cpp.o) has no symbols
338void dummy_vpPololu() { }
339#endif
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ fatalError
Fatal error.
Definition vpException.h:72
short radSToSpeed(float speed_rad_s) const
Definition vpPololu.cpp:140
unsigned short radToPwm(float angle) const
Definition vpPololu.cpp:121
void getRangePwm(unsigned short &min, unsigned short &max)
Definition vpPololu.cpp:180
unsigned short getPwmPosition() const
Definition vpPololu.cpp:145
void calibrate(unsigned short &pwm_min, unsigned short &pwm_max)
Definition vpPololu.cpp:103
float speedToRadS(short speed) const
Definition vpPololu.cpp:283
void setAngularVelocity(float vel_rad_s)
Definition vpPololu.cpp:239
vpPololu(bool verbose=false)
Definition vpPololu.cpp:54
float pwmToRad(unsigned short pwm) const
Definition vpPololu.cpp:186
virtual ~vpPololu()
Definition vpPololu.cpp:91
void connect(const std::string &device, int baudrate, int channel)
Definition vpPololu.cpp:66
float getAngularPosition() const
Definition vpPololu.cpp:166
void setPwmPosition(unsigned short pos_pwm, unsigned short speed_pwm=0)
Definition vpPololu.cpp:211
void setAngularPosition(float pos_rad, float vel_rad_s=0.f)
Definition vpPololu.cpp:194
bool connected() const
Definition vpPololu.cpp:129
void getRangeAngles(float &minAngle, float &maxAngle) const
Definition vpPololu.cpp:174
void stopVelocityCmd()
Definition vpPololu.cpp:288
void setPwmVelocity(short pwm_vel)
Definition vpPololu.cpp:255
Error that can be emitted by the vpRobot class and its derivatives.
@ constructionError
Error from constructor.
@ positionOutOfRangeError
Position is out of range.