can_bus.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * BSD 3-Clause License
3  *
4  * Copyright (c) 2021, Qiayuan Liao
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * * Redistributions of source code must retain the above copyright notice, this
11  * list of conditions and the following disclaimer.
12  *
13  * * Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  *
17  * * Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived from
19  * this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE
25  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *******************************************************************************/
33 
34 //
35 // Created by qiayuan on 12/28/20.
36 //
38 
39 #include <string>
40 #include <ros/ros.h>
42 namespace rm_hw
43 {
44 CanBus::CanBus(const std::string& bus_name, CanDataPtr data_ptr, int thread_priority)
45  : bus_name_(bus_name), data_ptr_(data_ptr)
46 {
47  // Initialize device at can_device, false for no loop back.
48  while (!socket_can_.open(bus_name, boost::bind(&CanBus::frameCallback, this, _1), thread_priority) && ros::ok())
49  ros::Duration(.5).sleep();
50 
51  ROS_INFO("Successfully connected to %s.", bus_name.c_str());
52  // Set up CAN package header
53  rm_frame0_.can_id = 0x200;
54  rm_frame0_.can_dlc = 8;
55  rm_frame1_.can_id = 0x1FF;
56  rm_frame1_.can_dlc = 8;
57 }
58 
59 void CanBus::write()
60 {
61  bool has_write_frame0 = false, has_write_frame1 = false;
62  // safety first
63  std::fill(std::begin(rm_frame0_.data), std::end(rm_frame0_.data), 0);
64  std::fill(std::begin(rm_frame1_.data), std::end(rm_frame1_.data), 0);
65 
66  for (auto& item : *data_ptr_.id2act_data_)
67  {
68  if (item.second.type.find("rm") != std::string::npos)
69  {
70  if (item.second.halted)
71  continue;
72  const ActCoeff& act_coeff = data_ptr_.type2act_coeffs_->find(item.second.type)->second;
73  int id = item.first - 0x201;
74  double cmd =
75  minAbs(act_coeff.effort2act * item.second.exe_effort, act_coeff.max_out); // add max_range to act_data
76  if (-1 < id && id < 4)
77  {
78  rm_frame0_.data[2 * id] = static_cast<uint8_t>(static_cast<int16_t>(cmd) >> 8u);
79  rm_frame0_.data[2 * id + 1] = static_cast<uint8_t>(cmd);
80  has_write_frame0 = true;
81  }
82  else if (3 < id && id < 8)
83  {
84  rm_frame1_.data[2 * (id - 4)] = static_cast<uint8_t>(static_cast<int16_t>(cmd) >> 8u);
85  rm_frame1_.data[2 * (id - 4) + 1] = static_cast<uint8_t>(cmd);
86  has_write_frame1 = true;
87  }
88  }
89  else if (item.second.type.find("cheetah") != std::string::npos)
90  {
91  can_frame frame{};
92  const ActCoeff& act_coeff = data_ptr_.type2act_coeffs_->find(item.second.type)->second;
93  frame.can_id = item.first;
94  frame.can_dlc = 8;
95  uint16_t q_des = static_cast<int>(act_coeff.pos2act * (item.second.cmd_pos - act_coeff.act2pos_offset));
96  uint16_t qd_des = static_cast<int>(act_coeff.vel2act * (item.second.cmd_vel - act_coeff.act2vel_offset));
97  uint16_t kp = 0.;
98  uint16_t kd = 0.;
99  uint16_t tau = static_cast<int>(act_coeff.effort2act * (item.second.exe_effort - act_coeff.act2effort_offset));
100  // TODO(qiayuan) add position vel and effort hardware interface for MIT Cheetah Motor, now we using it as an effort joint.
101  frame.data[0] = q_des >> 8;
102  frame.data[1] = q_des & 0xFF;
103  frame.data[2] = qd_des >> 4;
104  frame.data[3] = ((qd_des & 0xF) << 4) | (kp >> 8);
105  frame.data[4] = kp & 0xFF;
106  frame.data[5] = kd >> 4;
107  frame.data[6] = ((kd & 0xF) << 4) | (tau >> 8);
108  frame.data[7] = tau & 0xff;
109  socket_can_.write(&frame);
110  }
111  }
112 
113  if (has_write_frame0)
114  socket_can_.write(&rm_frame0_);
115  if (has_write_frame1)
116  socket_can_.write(&rm_frame1_);
117 }
118 
119 void CanBus::read(ros::Time time)
120 {
121  std::lock_guard<std::mutex> guard(mutex_);
122 
123  for (auto& imu : *data_ptr_.id2imu_data_)
124  {
125  imu.second.gyro_updated = false;
126  imu.second.accel_updated = false;
127  }
128 
129  for (const auto& frame_stamp : read_buffer_)
130  {
131  can_frame frame = frame_stamp.frame;
132  // Check if robomaster motor
133  if (data_ptr_.id2act_data_->find(frame.can_id) != data_ptr_.id2act_data_->end())
134  {
135  ActData& act_data = data_ptr_.id2act_data_->find(frame.can_id)->second;
136  if ((frame_stamp.stamp - act_data.stamp).toSec() < 0.0005)
137  continue;
138  const ActCoeff& act_coeff = data_ptr_.type2act_coeffs_->find(act_data.type)->second;
139  if (act_data.type.find("rm") != std::string::npos)
140  {
141  act_data.q_raw = (frame.data[0] << 8u) | frame.data[1];
142  act_data.qd_raw = (frame.data[2] << 8u) | frame.data[3];
143  int16_t cur = (frame.data[4] << 8u) | frame.data[5];
144  act_data.temp = frame.data[6];
145 
146  // Multiple circle
147  if (act_data.seq != 0) // not the first receive
148  {
149  if (act_data.q_raw - act_data.q_last > 4096)
150  act_data.q_circle--;
151  else if (act_data.q_raw - act_data.q_last < -4096)
152  act_data.q_circle++;
153  }
154  try
155  { // Duration will be out of dual 32-bit range while motor failure
156  act_data.frequency = 1. / (frame_stamp.stamp - act_data.stamp).toSec();
157  }
158  catch (std::runtime_error& ex)
159  {
160  }
161  act_data.stamp = frame_stamp.stamp;
162  act_data.seq++;
163  act_data.q_last = act_data.q_raw;
164  // Converter raw CAN data to position velocity and effort.
165  act_data.pos =
166  act_coeff.act2pos * static_cast<double>(act_data.q_raw + 8191 * act_data.q_circle) + act_data.offset;
167  act_data.vel = act_coeff.act2vel * static_cast<double>(act_data.qd_raw);
168  act_data.effort = act_coeff.act2effort * static_cast<double>(cur);
169  // Low pass filter
170  act_data.lp_filter->input(act_data.vel, frame_stamp.stamp);
171  act_data.vel = act_data.lp_filter->output();
172  continue;
173  }
174  }
175  // Check MIT Cheetah motor
176  else if (frame.can_id == static_cast<unsigned int>(0x000))
177  {
178  if (data_ptr_.id2act_data_->find(frame.data[0]) != data_ptr_.id2act_data_->end())
179  {
180  ActData& act_data = data_ptr_.id2act_data_->find(frame.data[0])->second;
181  const ActCoeff& act_coeff = data_ptr_.type2act_coeffs_->find(act_data.type)->second;
182  if (act_data.type.find("cheetah") != std::string::npos)
183  { // MIT Cheetah Motor
184  act_data.q_raw = (frame.data[1] << 8) | frame.data[2];
185  uint16_t qd = (frame.data[3] << 4) | (frame.data[4] >> 4);
186  uint16_t cur = ((frame.data[4] & 0xF) << 8) | frame.data[5];
187  // Multiple cycle
188  // NOTE: The raw data range is -4pi~4pi
189  if (act_data.seq != 0) // not the first receive
190  {
191  double pos_new = act_coeff.act2pos * static_cast<double>(act_data.q_raw) + act_coeff.act2pos_offset +
192  static_cast<double>(act_data.q_circle) * 8 * M_PI + act_data.offset;
193  if (pos_new - act_data.pos > 4 * M_PI)
194  act_data.q_circle--;
195  else if (pos_new - act_data.pos < -4 * M_PI)
196  act_data.q_circle++;
197  }
198  try
199  { // Duration will be out of dual 32-bit range while motor failure
200  act_data.frequency = 1. / (frame_stamp.stamp - act_data.stamp).toSec();
201  }
202  catch (std::runtime_error& ex)
203  {
204  }
205  act_data.stamp = frame_stamp.stamp;
206  act_data.seq++;
207  act_data.pos = act_coeff.act2pos * static_cast<double>(act_data.q_raw) + act_coeff.act2pos_offset +
208  static_cast<double>(act_data.q_circle) * 8 * M_PI + act_data.offset;
209  // Converter raw CAN data to position velocity and effort.
210  act_data.vel = act_coeff.act2vel * static_cast<double>(qd) + act_coeff.act2vel_offset;
211  act_data.effort = act_coeff.act2effort * static_cast<double>(cur) + act_coeff.act2effort_offset;
212  // Low pass filter
213  act_data.lp_filter->input(act_data.vel);
214  act_data.vel = act_data.lp_filter->output();
215  continue;
216  }
217  }
218  }
219  else if (data_ptr_.id2imu_data_->find(frame.can_id) != data_ptr_.id2imu_data_->end()) // Check if IMU gyro
220  {
221  ImuData& imu_data = data_ptr_.id2imu_data_->find(frame.can_id)->second;
222  imu_data.gyro_updated = true;
223  imu_data.angular_vel[0] = (((int16_t)((frame.data[1]) << 8) | frame.data[0]) * imu_data.angular_vel_coeff) +
224  imu_data.angular_vel_offset[0];
225  imu_data.angular_vel[1] = (((int16_t)((frame.data[3]) << 8) | frame.data[2]) * imu_data.angular_vel_coeff) +
226  imu_data.angular_vel_offset[1];
227  imu_data.angular_vel[2] = (((int16_t)((frame.data[5]) << 8) | frame.data[4]) * imu_data.angular_vel_coeff) +
228  imu_data.angular_vel_offset[2];
229  imu_data.time_stamp = frame_stamp.stamp;
230  int16_t temp = (int16_t)((frame.data[6] << 3) | (frame.data[7] >> 5));
231  if (temp > 1023)
232  temp -= 2048;
233  imu_data.temperature = temp * imu_data.temp_coeff + imu_data.temp_offset;
234  continue;
235  }
236  else if (data_ptr_.id2imu_data_->find(frame.can_id - 1) != data_ptr_.id2imu_data_->end()) // Check if IMU accel
237  {
238  ImuData& imu_data = data_ptr_.id2imu_data_->find(frame.can_id - 1)->second;
239  imu_data.accel_updated = true;
240  imu_data.linear_acc[0] = ((int16_t)((frame.data[1]) << 8) | frame.data[0]) * imu_data.accel_coeff;
241  imu_data.linear_acc[1] = ((int16_t)((frame.data[3]) << 8) | frame.data[2]) * imu_data.accel_coeff;
242  imu_data.linear_acc[2] = ((int16_t)((frame.data[5]) << 8) | frame.data[4]) * imu_data.accel_coeff;
243  imu_data.time_stamp = frame_stamp.stamp;
244  imu_data.camera_trigger = frame.data[6] & 1;
245  imu_data.enabled_trigger = frame.data[6] & 2;
246  imu_data.imu_filter->update(frame_stamp.stamp, imu_data.linear_acc, imu_data.angular_vel, imu_data.ori,
247  imu_data.linear_acc_cov, imu_data.angular_vel_cov, imu_data.ori_cov,
248  imu_data.temperature, imu_data.camera_trigger && imu_data.enabled_trigger);
249  continue;
250  }
251  else if (data_ptr_.id2tof_data_->find(frame.can_id) != data_ptr_.id2tof_data_->end())
252  {
253  TofData& tof_data = data_ptr_.id2tof_data_->find(frame.can_id)->second;
254  tof_data.distance = ((int16_t)((frame.data[1]) << 8) | frame.data[0]);
255  tof_data.strength = ((int16_t)((frame.data[3]) << 8) | frame.data[2]);
256  continue;
257  }
258  if (frame.can_id != 0x0)
259  ROS_ERROR_STREAM_ONCE("Can not find defined device, id: 0x" << std::hex << frame.can_id
260  << " on bus: " << bus_name_);
261  }
262  read_buffer_.clear();
263 }
264 
265 void CanBus::write(can_frame* frame)
266 {
267  socket_can_.write(frame);
268 }
269 
270 void CanBus::frameCallback(const can_frame& frame)
271 {
272  std::lock_guard<std::mutex> guard(mutex_);
273  CanFrameStamp can_frame_stamp{ .frame = frame, .stamp = ros::Time::now() };
274  read_buffer_.push_back(can_frame_stamp);
275 }
276 
277 } // namespace rm_hw
ros.h
rm_hw::ActCoeff::pos2act
double pos2act
Definition: types.h:111
minAbs
T minAbs(T a, T b)
rm_hw::CanBus::CanBus
CanBus(const std::string &bus_name, CanDataPtr data_ptr, int thread_priority)
Initialize device at can_device, retry if fail. Set up header of CAN frame.
Definition: can_bus.cpp:75
can_bus.h
rm_hw::ActCoeff::act2effort_offset
double act2effort_offset
Definition: types.h:112
ros::ok
ROSCPP_DECL bool ok()
rm_hw::ActCoeff
Definition: types.h:78
math_utilities.h
rm_hw::ActCoeff::vel2act
double vel2act
Definition: types.h:111
rm_hw::ActCoeff::act2pos_offset
double act2pos_offset
Definition: types.h:111
rm_hw::ActCoeff::effort2act
double effort2act
Definition: types.h:111
ros::Time
ros::Duration::sleep
bool sleep() const
rm_hw
Definition: control_loop.h:48
cmd
string cmd
ROS_INFO
#define ROS_INFO(...)
ros::Duration
ROS_ERROR_STREAM_ONCE
#define ROS_ERROR_STREAM_ONCE(args)
rm_hw::ActCoeff::act2vel_offset
double act2vel_offset
Definition: types.h:111
ros::Time::now
static Time now()


rm_hw
Author(s): Qiayuan Liao
autogenerated on Tue May 6 2025 02:23:44