fracture_robot_interface.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. #include "core/basic_obj_types.hpp"
  2. #include "robot/fracture_robot/fracture_robot_defs.h"
  3. #include "utility/config_utility.hpp"
  4. #include "utility/coro_worker.hpp"
  5. #include "utility/coro_worker_helper_func.hpp"
  6. #include "utility/name_translator.hpp"
  7. #include "utility/singleton_helper.hpp"
  8. #include "utility/variable_helper.hpp"
  9. #include "utility/versatile_buffer2.hpp"
  10. #include <boost/asio/serial_port.hpp>
  11. #include <boost/asio/use_awaitable.hpp>
  12. #include <boost/smart_ptr/scoped_ptr.hpp>
  13. #include <spdlog/spdlog.h>
  14. #include "utility/assert_utility.h"
  15. DEFAULT_TRISTATE_OBJ_DEF(fracture_robot_interface)
  16. namespace sophiar {
  17. using boost::asio::async_read;
  18. using boost::asio::async_write;
  19. using boost::asio::awaitable;
  20. using boost::asio::serial_port;
  21. using boost::asio::use_awaitable;
  22. using boost::scoped_ptr;
  23. struct fracture_robot_interface::impl {
  24. using control_value_type = std::array<int32_t, 6>;
  25. using control_mode_type = fracture_robot_control_mode_type;
  26. struct control_mode_translator_type :
  27. public singleton_helper<control_mode_translator_type>,
  28. public name_translator<control_mode_type> {
  29. void initialize() override {
  30. register_item("direct", control_mode_type::DIRECT);
  31. register_item("cartesian", control_mode_type::CARTESIAN);
  32. }
  33. };
  34. inline static auto control_mode_translator
  35. = control_mode_translator_type::get_instance();
  36. static constexpr auto robot_endian = boost::endian::order::little;
  37. static constexpr auto default_max_velocity = 5.0; // mm/s
  38. static constexpr auto default_controller_gain = 5.0;
  39. static constexpr size_t control_packet_length = 6 * sizeof(int32_t);
  40. static constexpr uint16_t control_packet_tail = 0x7fff;
  41. struct screw_info {
  42. Eigen::Vector3d origin; // Point on the screw
  43. Eigen::Vector3d direction; // Direction of the screw
  44. };
  45. fracture_robot_interface *q_this = nullptr;
  46. screw_info top_screw, side_screw;
  47. double max_velocity = default_max_velocity;
  48. double controller_gain = default_controller_gain;
  49. control_mode_type current_control_mode;
  50. variable_index_type current_control_mode_index = -1;
  51. variable_index_type is_moving_index = -1;
  52. // direct control
  53. variable_index_type target_motor_speed_index = -1;
  54. // cartesian control
  55. variable_index_type current_tcp_pose_index = -1, target_tcp_pose_index = -1;
  56. scoped_ptr<serial_port> conn;
  57. coro_worker::pointer control_worker;
  58. void fake_inverse_kinematics(const transform_obj::value_type &trans,
  59. array6_obj::value_type &pos) const {
  60. static constexpr auto side_z_offset = -60.0; // mm
  61. static constexpr auto top1_y_offset = 440.0;
  62. static constexpr auto top2_y_offset = 590.0;
  63. { // side cross
  64. auto origin = trans * side_screw.origin;
  65. auto direction = trans.linear() * side_screw.direction;
  66. auto t = (side_z_offset - origin.z()) / direction.z();
  67. auto cross = origin + t * direction;
  68. assert(std::abs(cross.z() - side_z_offset) < 1e-3);
  69. pos[0] = cross.x();
  70. pos[3] = cross.y();
  71. }
  72. { // top cross
  73. auto origin = trans * top_screw.origin;
  74. auto direction = trans.linear() * top_screw.direction;
  75. auto t1 = (top1_y_offset - origin.y()) / direction.y();
  76. auto t2 = (top2_y_offset - origin.y()) / direction.y();
  77. auto cross1 = origin + t1 * direction;
  78. auto cross2 = origin + t2 * direction;
  79. assert(std::abs(cross1.y() - top1_y_offset) < 1e-3);
  80. assert(std::abs(cross2.y() - top2_y_offset) < 1e-3);
  81. pos[4] = -cross1.x();
  82. pos[5] = -cross1.z();
  83. pos[2] = -cross2.x();
  84. pos[1] = -cross2.z();
  85. }
  86. }
  87. static void encode_arr(const array6_obj::value_type &vec_val,
  88. control_value_type &arr_val) {
  89. static constexpr int base_freq = 1000000; // Hz
  90. static constexpr auto dis_per_clock = 0.00625; // mm
  91. static constexpr auto minimum_velocity = 0.5; // mm/s, lower than this will cause stop
  92. for (int i = 0; i < 6; ++i) {
  93. if (std::abs(vec_val[i]) < minimum_velocity) {
  94. arr_val[i] = 0; // stop
  95. } else {
  96. auto tar_freq = vec_val[i] / dis_per_clock;
  97. arr_val[i] = static_cast<int32_t>(base_freq / tar_freq);
  98. assert(std::abs(arr_val[i]) < 0x7fff);
  99. if ((arr_val[i] & 0xff) == 0x7f) [[unlikely]] { // make the LSB not 0x7f
  100. ++arr_val[i];
  101. }
  102. }
  103. }
  104. }
  105. void calc_target_velocity(const array6_obj::value_type &cur_pos,
  106. const array6_obj::value_type &tar_pos,
  107. array6_obj::value_type &vec_val) const {
  108. static constexpr auto dis_tolerance = 0.5; // 0.1mm, lower than this will cause stop
  109. double cur_max_vec = 0;
  110. for (int i = 0; i < 6; ++i) {
  111. auto dis_offset = tar_pos[i] - cur_pos[i];
  112. if (std::abs(dis_offset) < dis_tolerance) {
  113. vec_val[i] = 0; // stop
  114. } else {
  115. vec_val[i] = controller_gain * dis_offset;
  116. cur_max_vec = std::max(std::abs(vec_val[i]), cur_max_vec);
  117. }
  118. }
  119. // clamp velocity
  120. if (cur_max_vec > max_velocity) {
  121. auto shrink_factor = max_velocity / cur_max_vec;
  122. for (int i = 0; i < 6; ++i) {
  123. vec_val[i] *= shrink_factor;
  124. }
  125. }
  126. }
  127. awaitable<void> send_control_packet(const array6_obj::value_type &vec_val) {
  128. // encode packet
  129. auto arr_val = control_value_type{};
  130. encode_arr(vec_val, arr_val);
  131. // write packet
  132. bool is_moving = false;
  133. auto mem = static_memory<control_packet_length>{};
  134. auto writer = versatile_writer<robot_endian>(mem);
  135. for (auto item: arr_val) {
  136. writer << item;
  137. if (item != 0) {
  138. is_moving = true;
  139. }
  140. }
  141. // send packet
  142. assert(conn->is_open());
  143. assert(writer.remaining_bytes() == 0);
  144. co_await async_write_memory_to(*conn, mem);
  145. // update moving status
  146. try_update_variable_value<bool_obj>(is_moving_index, is_moving);
  147. co_return;
  148. }
  149. auto create_direct_control_func() {
  150. assert(current_control_mode == control_mode_type::DIRECT);
  151. assert(is_valid_id(target_motor_speed_index));
  152. auto worker_func = [=, this,
  153. motor_speed = VARIABLE_MANUAL_DELEGATE(array6_obj, target_motor_speed_index)]() mutable
  154. -> awaitable<bool> {
  155. co_await motor_speed.coro_wait_update();
  156. if (motor_speed.empty()) co_return true;
  157. co_await send_control_packet(motor_speed->value);
  158. co_return true;
  159. };
  160. return std::move(worker_func);
  161. };
  162. auto create_cartesian_control_func() {
  163. assert(current_control_mode == control_mode_type::CARTESIAN);
  164. assert(is_valid_id(current_tcp_pose_index));
  165. assert(is_valid_id(target_tcp_pose_index));
  166. auto worker_func = [=, this,
  167. current_tcp = VARIABLE_MANUAL_DELEGATE(transform_obj, current_tcp_pose_index),
  168. target_tcp = VARIABLE_MANUAL_DELEGATE(transform_obj, target_tcp_pose_index),
  169. target_pos = array6_obj::value_type{}]() mutable
  170. -> awaitable<bool> {
  171. co_await current_tcp.coro_wait_update();
  172. if (current_tcp.empty()) co_return true;
  173. if (target_tcp.manual_sync()) { // target pose has been updated
  174. fake_inverse_kinematics(target_tcp->value, target_pos);
  175. }
  176. auto current_pos = array6_obj::value_type{};
  177. fake_inverse_kinematics(current_tcp->value, current_pos);
  178. auto vec_val = array6_obj::value_type{};
  179. calc_target_velocity(current_pos, target_pos, vec_val);
  180. co_await send_control_packet(vec_val);
  181. co_return true;
  182. };
  183. return std::move(worker_func);
  184. };
  185. void create_control_worker() {
  186. assert(control_worker == nullptr);
  187. auto exception_handle_func = [](std::exception &) {
  188. // TODO show log
  189. };
  190. auto exit_func = reset_on_exit_func(q_this);
  191. switch (current_control_mode) {
  192. case control_mode_type::DIRECT: {
  193. auto noexcept_wrapper = make_noexcept_func(
  194. create_direct_control_func(), std::move(exception_handle_func));
  195. control_worker = make_infinite_coro_worker(std::move(noexcept_wrapper),
  196. std::move(exit_func));
  197. break;
  198. }
  199. case control_mode_type::CARTESIAN: {
  200. auto noexcept_wrapper = make_noexcept_func(
  201. create_cartesian_control_func(), std::move(exception_handle_func));
  202. control_worker = make_infinite_coro_worker(std::move(noexcept_wrapper),
  203. std::move(exit_func));
  204. break;
  205. }
  206. default: {
  207. assert(false);
  208. return;
  209. }
  210. }
  211. control_worker->run();
  212. // set initial state
  213. try_update_variable_value<bool_obj>(is_moving_index, false);
  214. }
  215. void setup_connection(const nlohmann::json &config) {
  216. assert(conn == nullptr);
  217. conn.reset(new serial_port(*global_context));
  218. // open serial port
  219. auto com_port_name = LOAD_STRING_ITEM("com_port");
  220. conn->open(com_port_name);
  221. assert(conn->is_open());
  222. // config
  223. conn->set_option(serial_port::baud_rate(9600));
  224. conn->set_option(serial_port::stop_bits(serial_port::stop_bits::one));
  225. conn->set_option(serial_port::parity(serial_port::parity::none));
  226. conn->set_option(serial_port::character_size(8));
  227. conn->set_option(serial_port::flow_control(serial_port::flow_control::none));
  228. }
  229. void load_init_config(const nlohmann::json &config) {
  230. assert(config.contains("screw"));
  231. auto &screw_config = config["screw"];
  232. assert(screw_config.contains("top"));
  233. { // top screw
  234. auto &top_screw_config = screw_config["top"];
  235. assert(top_screw_config.contains("origin"));
  236. top_screw.origin = scalarxyz_obj::read_value_from_json_array(top_screw_config["origin"]);
  237. assert(top_screw_config.contains("direction"));
  238. top_screw.direction = scalarxyz_obj::read_value_from_json_array(top_screw_config["direction"]);
  239. }
  240. { // side screw
  241. auto &side_screw_config = screw_config["side"];
  242. assert(side_screw_config.contains("origin"));
  243. side_screw.origin = scalarxyz_obj::read_value_from_json_array(side_screw_config["origin"]);
  244. assert(side_screw_config.contains("direction"));
  245. side_screw.direction = scalarxyz_obj::read_value_from_json_array(side_screw_config["direction"]);
  246. }
  247. }
  248. void load_start_config(const nlohmann::json &config) {
  249. assert(config.contains("input_config"));
  250. { // output config
  251. auto &output_config = config["output_config"];
  252. current_control_mode_index = LOAD_VARIABLE_INDEX2(output_config, u64int_obj, "current_control_mode");
  253. is_moving_index = TRY_LOAD_VARIABLE_INDEX2(output_config, bool_obj, "is_moving");
  254. }
  255. { // input config
  256. // control mode
  257. auto &input_config = config["input_config"];
  258. auto control_mode_str = LOAD_STRING_ITEM2(input_config, "control_mode");
  259. current_control_mode = control_mode_translator->translate(control_mode_str);
  260. UPDATE_VARIABLE_VAL(u64int_obj, current_control_mode_index, (uint8_t) current_control_mode);
  261. // controller parameters
  262. max_velocity = TRY_LOAD_FLOAT_ITEM2(input_config, "max_velocity", default_max_velocity);
  263. controller_gain = TRY_LOAD_FLOAT_ITEM2(input_config, "controller_gain", default_controller_gain);
  264. // control input
  265. switch (current_control_mode) {
  266. case control_mode_type::DIRECT: {
  267. target_motor_speed_index
  268. = LOAD_VARIABLE_INDEX2(input_config, array6_obj, "target_motor_velocity");
  269. break;
  270. }
  271. case control_mode_type::CARTESIAN: {
  272. current_tcp_pose_index = LOAD_VARIABLE_INDEX2(input_config, transform_obj, "current_tcp");
  273. target_tcp_pose_index = LOAD_VARIABLE_INDEX2(input_config, transform_obj, "target_tcp");
  274. break;
  275. }
  276. default: {
  277. assert(false);
  278. return;
  279. }
  280. }
  281. }
  282. }
  283. };
  284. awaitable<bool> fracture_robot_interface::on_init(const nlohmann::json &config) noexcept {
  285. pimpl->load_init_config(config);
  286. try {
  287. pimpl->setup_connection(config);
  288. } catch (std::exception &e) {
  289. SPDLOG_ERROR("Failed to init fracture robot interface: {}", e.what());
  290. co_return false;
  291. }
  292. co_return true;
  293. }
  294. awaitable<bool> fracture_robot_interface::on_start(const nlohmann::json &config) noexcept {
  295. pimpl->load_start_config(config);
  296. pimpl->create_control_worker();
  297. co_return true;
  298. }
  299. awaitable<void> fracture_robot_interface::on_stop() noexcept {
  300. SAFE_RESET_WORKER(pimpl->control_worker)
  301. // update status
  302. try_update_variable_value<bool_obj>(pimpl->is_moving_index, false);
  303. co_return;
  304. }
  305. awaitable<void> fracture_robot_interface::on_reset() noexcept {
  306. pimpl->conn.reset(nullptr);
  307. co_return;
  308. }
  309. fracture_robot_interface::fracture_robot_interface()
  310. : pimpl(std::make_unique<impl>()) {
  311. pimpl->q_this = this;
  312. }
  313. fracture_robot_interface::~fracture_robot_interface() = default;
  314. }