Tetrapod Project
motor_interface.cpp
Go to the documentation of this file.
1 /*******************************************************************/
2 /* AUTHOR: Paal Arthur S. Thorseth */
3 /* ORGN: Dept of Eng Cybernetics, NTNU Trondheim */
4 /* FILE: motor_interface.cpp */
5 /* DATE: Apr 23, 2021 */
6 /* */
7 /* Copyright (C) 2021 Paal Arthur S. Thorseth, */
8 /* Adrian B. Ghansah */
9 /* */
10 /* This program is free software: you can redistribute it */
11 /* and/or modify it under the terms of the GNU General */
12 /* Public License as published by the Free Software Foundation, */
13 /* either version 3 of the License, or (at your option) any */
14 /* later version. */
15 /* */
16 /* This program is distributed in the hope that it will be useful, */
17 /* but WITHOUT ANY WARRANTY; without even the implied warranty */
18 /* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */
19 /* See the GNU General Public License for more details. */
20 /* */
21 /* You should have received a copy of the GNU General Public */
22 /* License along with this program. If not, see */
23 /* <https://www.gnu.org/licenses/>. */
24 /* */
25 /*******************************************************************/
26 
28 
29 // Constructor
31  NUM_MOTORS { 12 },
32  serialInterface1("/dev/teensy1"),
33  serialInterface2("/dev/teensy2")
34 {
35 
37 
39 
40  this->InitRos();
41  this->InitRosQueueThreads();
42 }
43 
44 MotorInterface::MotorInterface(const int &_num_motors) :
45  NUM_MOTORS { _num_motors }
46 {
47  // Check whether we should use one or two parts
48  if(_num_motors <= this->MAX_NUM_MOTORS_PER_PORT)
49  {
50  this->num_motors_port_1 = _num_motors;
51 
52  this->serialInterface1.SetPort("/dev/teensy1");
53 
54  this->serialInterface1.SetNumberOfMotors(_num_motors);
55 
57  }
58  else
59  {
61 
62  num_motors_port_2 = _num_motors - num_motors_port_1;
63 
64  this->serialInterface1.SetPort("/dev/teensy1");
65 
66  this->serialInterface1.SetNumberOfMotors(num_motors_port_1);
67 
68  this->serialInterface2.SetPort("/dev/teensy2");
69 
71 
73 
75  }
76 
77  this->InitRos();
78  this->InitRosQueueThreads();
79 
80 }
81 
82 // Destructor
84 {
85  this->rosNode->shutdown();
86 
87  this->rosProcessQueue.clear();
88  this->rosProcessQueue.disable();
89  this->rosProcessQueueThread.join();
90 
91  this->rosPublishQueue.clear();
92  this->rosPublishQueue.disable();
93  this->rosPublishQueueThread.join();
94 }
95 
96 void MotorInterface::SetJointPositions(const std::vector<double> &_pos)
97 {
99  {
101  }
102  else
103  {
104  std::vector<double> commands_port_1(_pos.begin(), _pos.begin() + this->num_motors_port_1);
105  std::vector<double> commands_port_2(_pos.begin() + this->num_motors_port_1, _pos.end());
106 
109  }
110 }
111 
112 void MotorInterface::SetJointVelocities(const std::vector<double> &_vel)
113 {
114  if (this->NUM_MOTORS <= MAX_NUM_MOTORS_PER_PORT)
115  {
117  }
118  else
119  {
120  std::vector<double> commands_port_1(_vel.begin(), _vel.begin() + this->num_motors_port_1);
121  std::vector<double> commands_port_2(_vel.begin() + this->num_motors_port_1, _vel.end());
122 
125  }
126 }
127 
128 void MotorInterface::SetJointTorques(const std::vector<double> &_torque)
129 {
130  if (this->NUM_MOTORS <= MAX_NUM_MOTORS_PER_PORT)
131  {
133  }
134  else
135  {
136  std::vector<double> commands_port_1(_torque.begin(), _torque.begin() + this->num_motors_port_1);
137  std::vector<double> commands_port_2(_torque.begin() + this->num_motors_port_1, _torque.end());
138 
141  }
142 }
143 
145 {
146  if (this->NUM_MOTORS <= MAX_NUM_MOTORS_PER_PORT)
147  {
149  {
150  Eigen::Matrix<Eigen::VectorXd, 3, 1> joint_states = serialInterface1.ReceiveMessage();
151 
152  for (int i = 0; i < num_motors_port_1; i++)
153  {
154  jointStateMsg.position[i] = joint_states(0)(i);
155  jointStateMsg.velocity[i] = joint_states(1)(i);
156  jointStateMsg.effort[i] = joint_states(2)(i);
157  }
158 
159  }
160  }
161  else
162  {
164  {
165  Eigen::Matrix<Eigen::VectorXd, 3, 1> joint_states = serialInterface1.ReceiveMessage();
166 
167  for (int i = 0; i < num_motors_port_1; i++)
168  {
169  jointStateMsg.position[i] = joint_states(0)(i);
170  jointStateMsg.velocity[i] = joint_states(1)(i);
171  jointStateMsg.effort[i] = joint_states(2)(i);
172  }
173 
174  }
175 
177  {
178  Eigen::Matrix<Eigen::VectorXd, 3, 1> joint_states = serialInterface2.ReceiveMessage();
179 
180  for(int i = 0; i < num_motors_port_2; i++)
181  {
182  jointStateMsg.position[num_motors_port_1 + i] = joint_states(0)(i);
183  jointStateMsg.velocity[num_motors_port_1 + i] = joint_states(1)(i);
184  jointStateMsg.effort[num_motors_port_1 + i] = joint_states(2)(i);
185  }
186  }
187  }
188 
189 }
190 
191 // Callback for ROS Contact State messages
192 void MotorInterface::OnJointStateCmdMsg(const sensor_msgs::JointStateConstPtr &_msg)
193 {
194  if ((!_msg->position.empty()) && (_msg->position.size() == this->NUM_MOTORS))
195  {
196  this->SetJointPositions(_msg->position);
197  }
198 
199  else if ((!_msg->velocity.empty()) && (_msg->velocity.size() == this->NUM_MOTORS))
200  {
201  this->SetJointVelocities(_msg->velocity);
202  }
203 
204  else if ((!_msg->effort.empty()) && (_msg->effort.size() == this->NUM_MOTORS))
205  {
206  this->SetJointTorques(_msg->effort);
207  }
208  else
209  {
210  ROS_ERROR("[MotorInterface::OnJointStateCmdMsg] Message failed to match specifications!");
211  }
212 }
213 
214 // Setup thread to process messages
216 {
217  ros::Rate loop_rate(10000);
218 
219  while (this->rosNode->ok())
220  {
221  this->ProcessSerialMessages();
222 
223  loop_rate.sleep();
224  }
225 }
226 
227 // Setup thread to process messages
229 {
230  static const double timeout = 0.0001;
231  while (this->rosNode->ok())
232  {
233  this->rosProcessQueue.callAvailable(ros::WallDuration(timeout));
234  }
235 }
236 
237 // Setup thread to publish messages
239 {
240  static const double timeout = 0.0001;
241  while (this->rosNode->ok())
242  {
243  this->jointStateMsg.header.stamp = ros::Time::now();
244 
245  this->jointStatePub.publish(this->jointStateMsg);
246 
247  this->rosPublishQueue.callAvailable(ros::WallDuration(timeout));
248  }
249 }
250 
251 bool MotorInterface::Shutdown(const std_srvs::Empty::Request &_req,
252  std_srvs::Empty::Response &_res)
253 {
254  this->rosNode->shutdown();
255 
256  return true;
257 }
258 
259 // Initialize ROS
261 {
262  if (!ros::isInitialized())
263  {
264  int argc = 0;
265  char **argv = NULL;
266  ros::init(
267  argc,
268  argv,
269  "motor_interface_node",
270  ros::init_options::NoSigintHandler
271  );
272  }
273 
274  this->rosNode.reset(new ros::NodeHandle("motor_interface_node"));
275 
276  ros::AdvertiseServiceOptions shutdown_aso =
277  ros::AdvertiseServiceOptions::create<std_srvs::Empty>(
278  "/my_robot/motor_interface/shutdown",
279  boost::bind(&MotorInterface::Shutdown, this, _1, _2),
280  ros::VoidPtr(),
281  &this->rosProcessQueue
282  );
283 
284  ros::SubscribeOptions joint_state_cmd_so =
285  ros::SubscribeOptions::create<sensor_msgs::JointState>(
286  "/my_robot/joint_state_cmd",
287  1,
288  boost::bind(&MotorInterface::OnJointStateCmdMsg, this, _1),
289  ros::VoidPtr(),
290  &this->rosProcessQueue
291  );
292 
293 
294  ros::AdvertiseOptions joint_state_ao =
295  ros::AdvertiseOptions::create<sensor_msgs::JointState>(
296  "/my_robot/joint_state",
297  1,
298  ros::SubscriberStatusCallback(),
299  ros::SubscriberStatusCallback(),
300  ros::VoidPtr(),
301  &this->rosPublishQueue
302  );
303 
304  this->shutdownService = this->rosNode->advertiseService(shutdown_aso);
305 
306  this->jointStateCmdSub = this->rosNode->subscribe(joint_state_cmd_so);
307 
308  this->jointStatePub = this->rosNode->advertise(joint_state_ao);
309 
310  this->jointStateMsg.position.resize(this->NUM_MOTORS);
311  this->jointStateMsg.velocity.resize(this->NUM_MOTORS);
312  this->jointStateMsg.effort.resize(this->NUM_MOTORS);
313 }
314 
315 // Initialize ROS Publish and Process Queue Threads
317 {
318  this->serialProcessQueueThread = std::thread(
320  );
321 
322  this->rosPublishQueueThread = std::thread(
323  std::bind(&MotorInterface::RosPublishQueueThread, this)
324  );
325 
326  this->rosProcessQueueThread = std::thread(
327  std::bind(&MotorInterface::RosProcessQueueThread, this)
328  );
329 }
int num_motors_port_2
The number of motors interfaced by the second Teensy.
void InitRosQueueThreads()
The InitRosQueueThreads function is called to initialize the ROS Publish and Process Queue Threads.
std::thread rosPublishQueueThread
Thread running the rosPublishQueue.
std::thread rosProcessQueueThread
Thread running the rosProcessQueue.
void ProcessSerialMessages()
Checks if there are any incomming messages from the serial ports. If there are new replies these are ...
void RosProcessQueueThread()
The RosProcessQueueThread function is a ROS helper function that processes messages.
SerialCommunication serialInterface2
The serial interface used to communicate with the second Teensy.
void SerialProcessQueueThread()
The SerialProcessQueueThread function is a helper function that processes serial messages.
void RosPublishQueueThread()
The RosPublishQueueThread function is a ROS helper function that publish state messages.
const int NUM_MOTORS
The number of motors to interface with.
void SetJointPositions(const std::vector< double > &_pos)
Set the position of the joints.
void OnJointStateCmdMsg(const sensor_msgs::JointStateConstPtr &_msg)
The OnGenCoordMsg function handles an incoming generalized coordinates message from ROS.
virtual ~MotorInterface()
Destructor.
void SetJointTorques(const std::vector< double > &_torque)
Set the torque of the joints.
static constexpr int MAX_NUM_MOTORS_PER_PORT
The maximum numbers of motors to be interfaced with by one Teensy.
ros::CallbackQueue rosPublishQueue
ROS callbackque that helps publish messages.
ros::Publisher jointStatePub
ROS Joint State Publisher.
void InitRos()
The InitRos function is called to initialize ROS.
sensor_msgs::JointState jointStateMsg
Joint State Message.
std::thread serialProcessQueueThread
Thread running the serialProcessQueue.
ros::Subscriber jointStateCmdSub
ROS Joint State Command Subscriber.
ros::ServiceServer shutdownService
ROS Shutdown Service.
MotorInterface()
Default Constructor.
SerialCommunication serialInterface1
The serial interface used to communicate with the first Teensy.
bool Shutdown(const std_srvs::Empty::Request &_req, std_srvs::Empty::Response &_res)
The Shutdown function handles an incoming shutdown service request.
ros::CallbackQueue rosProcessQueue
ROS callbackque that helps process messages.
std::unique_ptr< ros::NodeHandle > rosNode
Node used for ROS transport.
int num_motors_port_1
The number of motors interfaced by the first Teensy.
void SetJointVelocities(const std::vector< double > &_vel)
Set the velocity of the joints.
void SetNumberOfMotors(const int &_number_of_motors)
Set the number of motors for the port after creating the constructor.
void InitLibSerial()
The InitLibSerial function initializes the serial port to the correct input and sets baud rate,...
void SendMessage(const ControlMode &_control_mode, const std::vector< double > &_state)
The SendMessage function handles the sending of joint state command messages to the motors.
bool IsNewDataAvailable()
The IsNewDataAvailable function checks if there is available data on the serial port.
void SetPort(const std::string &_port)
Set the port for the Teensy communication after creating the constructor.
Eigen::Matrix< Eigen::VectorXd, 3, 1 > ReceiveMessage()
The ReceiveMessage function listens for serial messages of incoming joint state data from the motors ...