kobuki.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. /*
  2. * Copyright (c) 2006-2021, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2021-05-28 hw630 the first version
  9. */
  10. #include <math.h>
  11. #include <stdio.h>
  12. #include "kobuki.h"
  13. #include "kobuki_serial.h"
  14. #include "kobuki_protocol.h"
  15. #define DBG_SECTION_NAME "kobuki"
  16. #define DBG_LEVEL DBG_LOG
  17. #include <rtdbg.h>
  18. #define M_PI 3.14159265358979323846264338327950288
  19. #define ENCODER_UPPER_WRAP 45875
  20. #define ENCODER_LOWER_WRAP 19660
  21. // b is bias or wheelbase, that indicates the length between the center of the wheels. Fixed at 230 mm.
  22. static float wheelbase = 0.230;
  23. static float left_ticks_per_m = 1./11724.41658029856624751591;
  24. static float right_ticks_per_m = 1./11724.41658029856624751591;
  25. int kobuki_init(kobuki_t robot)
  26. {
  27. rt_err_t result;
  28. result = rt_event_init(&(robot->event), "kobuki_event", RT_IPC_FLAG_FIFO);
  29. robot->connected = 0;
  30. robot->last_sync_tick = 0;
  31. robot->x = 0;
  32. robot->y = 0;
  33. robot->theta = 0;
  34. if (result != RT_EOK)
  35. {
  36. return -2;
  37. }
  38. return kobuki_serial_init();
  39. }
  40. // translation velocity and rotation velocity (cm / s)
  41. void kobuki_set_speed(double tv, double rv)
  42. {
  43. kobuki_base_control_payload_t payload;
  44. payload.header = KOBUKI_BASE_CONTROL_HEADER;
  45. payload.length = KOBUKI_BASE_CONTROL_LENGTH;
  46. // convert to mm;
  47. tv *= 1000;
  48. double b2 = wheelbase * 500;
  49. if (fabs(tv) < 1)
  50. {
  51. // pure rotation
  52. payload.radius = 1;
  53. payload.speed = (int16_t) (rv * b2);
  54. }
  55. else if (fabs(rv) < 1e-3 )
  56. {
  57. // pure translation"
  58. payload.speed = (int16_t) tv;
  59. payload.radius = 0;
  60. }
  61. else {
  62. // translation and rotation
  63. float r = tv / rv;
  64. payload.radius = (int16_t) r;
  65. if (r > 1) {
  66. payload.speed = (int16_t) (tv * (r + b2)/ r);
  67. } else if (r < -1) {
  68. payload.speed = (int16_t) (tv * (r - b2)/ r);
  69. }
  70. }
  71. kobuki_protocol_send_payload( (uint8_t*) (&payload), sizeof(kobuki_base_control_payload_t));
  72. }
  73. // 1 / (f * a), where f is frequency of sound in Hz, and a is 0.00000275
  74. void kobuki_play_sound(uint16_t note, uint8_t duration)
  75. {
  76. kobuki_sound_payload_t payload;
  77. payload.header = KOBUKI_SOUND_HEADER;
  78. payload.length = KOBUKI_SOUND_LENGTH;
  79. payload.note = note;
  80. payload.duration = duration;
  81. kobuki_protocol_send_payload( (uint8_t*) (&payload), sizeof(kobuki_sound_payload_t));
  82. }
  83. void kobuki_play_sound_on()
  84. {
  85. kobuki_play_sound_sequence(KOBUKI_SOUND_SEQUENCE_ON);
  86. }
  87. void kobuki_play_sound_off()
  88. {
  89. kobuki_play_sound_sequence(KOBUKI_SOUND_SEQUENCE_OFF);
  90. }
  91. void kobuki_play_sound_recharge()
  92. {
  93. kobuki_play_sound_sequence(KOBUKI_SOUND_SEQUENCE_RECHARGE);
  94. }
  95. void kobuki_play_sound_button()
  96. {
  97. kobuki_play_sound_sequence(KOBUKI_SOUND_SEQUENCE_BUTTON);
  98. }
  99. void kobuki_play_sound_error()
  100. {
  101. kobuki_play_sound_sequence(KOBUKI_SOUND_SEQUENCE_ERROR);
  102. }
  103. void kobuki_play_sound_cleaning_start()
  104. {
  105. kobuki_play_sound_sequence(KOBUKI_SOUND_SEQUENCE_CLEANING_START);
  106. }
  107. void kobuki_play_sound_cleaning_end()
  108. {
  109. kobuki_play_sound_sequence(KOBUKI_SOUND_SEQUENCE_CLEANING_END);
  110. }
  111. void kobuki_enalbe_3v3()
  112. {
  113. kobuki_set_gpio(KOBUKI_ENABLE_3V3_FLAG);
  114. }
  115. void kobuki_enable_5v()
  116. {
  117. kobuki_set_gpio(KOBUKI_ENABLE_5V_FLAG);
  118. }
  119. void kobuki_enable_12v_5a()
  120. {
  121. kobuki_set_gpio(KOBUKI_ENABLE_12V_5A_FLAG);
  122. }
  123. void kobuki_enable_12v_1_5a()
  124. {
  125. kobuki_set_gpio(KOBUKI_ENABLE_12V_1_5_A_FLAG);
  126. }
  127. void kobuki_set_led1_red()
  128. {
  129. kobuki_set_gpio(KOBUKI_LED1_RED_FLAG);
  130. }
  131. void kobuki_set_led1_green()
  132. {
  133. kobuki_set_gpio(KOBUKI_LED1_GREEN_FLAG);
  134. }
  135. void kobuki_set_led2_red()
  136. {
  137. kobuki_set_gpio(KOBUKI_LED2_RED_FLAG);
  138. }
  139. void kobuki_set_led2_green()
  140. {
  141. kobuki_set_gpio(KOBUKI_LED2_GREEN_FLAG);
  142. }
  143. void kobuki_set_controller_gain(uint32_t kp, uint32_t ki, uint32_t kd)
  144. {
  145. kobuki_set_controller_gain_(1, kp, ki, kd);
  146. }
  147. void kobuki_get_controller_gain()
  148. {
  149. kobuki_get_controller_gain_();
  150. }
  151. void kobuki_reset_controller_gain()
  152. {
  153. kobuki_set_controller_gain_(0, 0, 0, 0);
  154. }
  155. void kobuki_get_hardware_version()
  156. {
  157. kobuki_request_extra(KOBUKI_REQUEST_HARDWARE_VERSION_ID);
  158. }
  159. void kobuki_get_firmware_version()
  160. {
  161. kobuki_request_extra(KOBUKI_REQUEST_FIRMWARE_VERSION_ID);
  162. }
  163. void kobuki_get_uuid()
  164. {
  165. kobuki_request_extra(KOBUKI_REQUEST_UUID);
  166. }
  167. static void kobuki_update_odometry(kobuki_t robot, uint16_t left_encoder, uint16_t right_encoder, uint16_t elapsed_time_ms)
  168. {
  169. double dl, dr;
  170. if( (left_encoder > ENCODER_UPPER_WRAP && robot->left_encoder < ENCODER_LOWER_WRAP) )
  171. {
  172. dl = left_ticks_per_m * ( (left_encoder - 65536) - robot->left_encoder);
  173. }
  174. else if((left_encoder < ENCODER_UPPER_WRAP && robot->left_encoder > ENCODER_LOWER_WRAP))
  175. {
  176. dl = left_ticks_per_m * (left_encoder + (65536 - robot->left_encoder));
  177. }
  178. else
  179. {
  180. dl = left_ticks_per_m * (left_encoder - robot->left_encoder);
  181. }
  182. if( (right_encoder > ENCODER_UPPER_WRAP && robot->right_encoder < ENCODER_LOWER_WRAP) )
  183. {
  184. dr = right_ticks_per_m * ( (right_encoder - 65536) - robot->right_encoder);
  185. }
  186. else if((right_encoder < ENCODER_UPPER_WRAP && robot->right_encoder > ENCODER_LOWER_WRAP))
  187. {
  188. dr = right_ticks_per_m * (right_encoder + (65536 - robot->right_encoder));
  189. }
  190. else
  191. {
  192. dr = right_ticks_per_m * (right_encoder - robot->right_encoder);
  193. }
  194. double dx = 0, dy = 0;
  195. double dtheta = (dr - dl) / wheelbase;
  196. // printf("%d\n", left_encoder - robot->left_encoder);
  197. if (dl != dr)
  198. {
  199. double R = 0.5 * (dr + dl) / dtheta;
  200. dx = R * sin(dtheta);
  201. dy = R * (1 - cos(dtheta));
  202. } else
  203. {
  204. dx = dr;
  205. }
  206. double s = sin(robot->theta);
  207. double c = cos(robot->theta);
  208. double diff_x = c * dx - s * dy;
  209. //ROS_INFO_STREAM("Elapsed " << elapsed_time_ms << " distance " << diff_x);
  210. robot->v_x = diff_x / ((elapsed_time_ms) / 1000.0);
  211. robot->x += diff_x;
  212. robot->y += s * dx + c * dy;
  213. robot->theta += dtheta;
  214. robot->theta = fmod(robot->theta + 4 * M_PI, 2 * M_PI);
  215. if (robot->theta > M_PI)
  216. {
  217. robot->theta -= 2*M_PI;
  218. }
  219. }
  220. static void kobuki_parse_subpaylod(kobuki_t robot, uint8_t* subpayload, uint8_t len)
  221. {
  222. int32_t elapsed_time = 0;
  223. switch(subpayload[0])
  224. {
  225. case KOBUKI_BASIC_SENSOR_DATA_HEADER:
  226. if(robot->last_sync_tick > 0)
  227. {
  228. if( ((kobuki_basic_sensor_data_payload_t*)subpayload)->timestamp > robot->timestamp ) {
  229. elapsed_time = ((kobuki_basic_sensor_data_payload_t*)subpayload)->timestamp - robot->timestamp;
  230. }
  231. else
  232. {
  233. elapsed_time = 65535 - robot->timestamp + ((kobuki_basic_sensor_data_payload_t*)subpayload)->timestamp;
  234. }
  235. kobuki_update_odometry(robot,
  236. ((kobuki_basic_sensor_data_payload_t*)subpayload)->left_encoder,
  237. ((kobuki_basic_sensor_data_payload_t*)subpayload)->right_encoder,
  238. elapsed_time);
  239. }
  240. robot->timestamp = ((kobuki_basic_sensor_data_payload_t*)subpayload)->timestamp;
  241. robot->bumper = ((kobuki_basic_sensor_data_payload_t*)subpayload)->bumper_flag;
  242. robot->left_bumper = ((kobuki_basic_sensor_data_payload_t*)subpayload)->bumper_flag & KOBUKI_LEFT_BUMPER_FLAG ? 1 : 0;
  243. robot->central_bumper = ((kobuki_basic_sensor_data_payload_t*)subpayload)->bumper_flag & KOBUKI_CENTRAL_BUMPER_FLAG ? 1 : 0;
  244. robot->right_bumper = ((kobuki_basic_sensor_data_payload_t*)subpayload)->bumper_flag & KOBUKI_RIGHT_BUMPER_FLAG ? 1 : 0;
  245. robot->wheel_drop = ((kobuki_basic_sensor_data_payload_t*)subpayload)->wheel_drop_flag;
  246. robot->left_wheel_drop = ((kobuki_basic_sensor_data_payload_t*)subpayload)->wheel_drop_flag & KOBUKI_LEFT_WHEEL_DROP_FLAG ? 1 : 0;
  247. robot->right_wheel_drop = ((kobuki_basic_sensor_data_payload_t*)subpayload)->wheel_drop_flag & KOBUKI_RIGHT_WHEEL_DROP_FLAG ? 1 : 0;
  248. robot->cliff = ((kobuki_basic_sensor_data_payload_t*)subpayload)->cliff_flag;
  249. robot->left_cliff = ((kobuki_basic_sensor_data_payload_t*)subpayload)->cliff_flag & KOBUKI_LEFT_CLIFF_FLAG ? 1 : 0;
  250. robot->central_cliff = ((kobuki_basic_sensor_data_payload_t*)subpayload)->cliff_flag & KOBUKI_CENTRAL_CLIFF_FLAG ? 1 : 0;
  251. robot->right_cliff = ((kobuki_basic_sensor_data_payload_t*)subpayload)->cliff_flag & KOBUKI_RIGHT_CLIFF_FLAG ? 1 : 0;
  252. robot->left_encoder = ((kobuki_basic_sensor_data_payload_t*)subpayload)->left_encoder;
  253. robot->right_encoder = ((kobuki_basic_sensor_data_payload_t*)subpayload)->right_encoder;
  254. robot->left_pwm = (int8_t)((kobuki_basic_sensor_data_payload_t*)subpayload)->left_pwm;
  255. robot->right_pwm = (int8_t)((kobuki_basic_sensor_data_payload_t*)subpayload)->right_pwm;
  256. robot->button = ((kobuki_basic_sensor_data_payload_t*)subpayload)->button_flag;
  257. robot->button_0 = ((kobuki_basic_sensor_data_payload_t*)subpayload)->button_flag & KOBUKI_BUTTON_0_FLAG ? 1 : 0;
  258. robot->button_1 = ((kobuki_basic_sensor_data_payload_t*)subpayload)->button_flag & KOBUKI_BUTTON_1_FLAG ? 1 : 0;
  259. robot->button_2 = ((kobuki_basic_sensor_data_payload_t*)subpayload)->button_flag & KOBUKI_BUTTON_2_FLAG ? 1 : 0;
  260. robot->charger = ((kobuki_basic_sensor_data_payload_t*)subpayload)->charger_flag;
  261. robot->charger_discharging = ((kobuki_basic_sensor_data_payload_t*)subpayload)->charger_flag == KOBUKI_DISCHARGING_FLAG ? 1 : 0;
  262. robot->charger_docking_charged = ((kobuki_basic_sensor_data_payload_t*)subpayload)->charger_flag == KOBUKI_DOCKING_CHARGED_FLAG ? 1 : 0;
  263. robot->charger_docking_charging = ((kobuki_basic_sensor_data_payload_t*)subpayload)->charger_flag == KOBUKI_DOCKING_CHARGING_FLAG ? 1 : 0;
  264. robot->charger_adapter_charged = ((kobuki_basic_sensor_data_payload_t*)subpayload)->charger_flag == KOBUKI_ADAPTER_CHARGED_FLAG ? 1 : 0;
  265. robot->charger_adapter_charging = ((kobuki_basic_sensor_data_payload_t*)subpayload)->charger_flag == KOBUKI_ADAPTER_CHARGING_FLAG ? 1 : 0;
  266. robot->wheel_overcurrent = ((kobuki_basic_sensor_data_payload_t*)subpayload)->overcurrent_flag;
  267. robot->left_wheel_overcurrent = ((kobuki_basic_sensor_data_payload_t*)subpayload)->overcurrent_flag & KOBUKI_LEFT_WHEEL_OVERCURRENT_FLAG ? 1 : 0;
  268. robot->right_wheel_overcurrent = ((kobuki_basic_sensor_data_payload_t*)subpayload)->overcurrent_flag & KOBUKI_RIGHT_WHEEL_OVERCURRENT_FLAG ? 1 : 0;
  269. break;
  270. case KOBUKI_DOCKING_IR_HEADER:
  271. robot->docking_ir_left = ((kobuki_docking_ir_payload_t*)subpayload)->left_signal;
  272. robot->docking_ir_center = ((kobuki_docking_ir_payload_t*)subpayload)->central_signal;
  273. robot->docking_ir_right = ((kobuki_docking_ir_payload_t*)subpayload)->right_signal;
  274. robot->docking_ir_far_left = ((kobuki_docking_ir_payload_t*)subpayload)->central_signal & KOBUKI_DOCKING_IR_FAR_LEFT_FLAG ? 1 : 0;
  275. robot->docking_ir_far_center = ((kobuki_docking_ir_payload_t*)subpayload)->central_signal & KOBUKI_DOCKING_IR_FAR_CENTER_FLAG ? 1 : 0;
  276. robot->docking_ir_far_right = ((kobuki_docking_ir_payload_t*)subpayload)->central_signal & KOBUKI_DOCKING_IR_FAR_RIGHT_FLAG ? 1 : 0;
  277. robot->docking_ir_near_left = ((kobuki_docking_ir_payload_t*)subpayload)->central_signal & KOBUKI_DOCKING_IR_NEAR_LEFT_FLAG ? 1 : 0;
  278. robot->docking_ir_near_center = ((kobuki_docking_ir_payload_t*)subpayload)->central_signal & KOBUKI_DOCKING_IR_NEAR_CENTER_FLAG ? 1 : 0;
  279. robot->docking_ir_near_right = ((kobuki_docking_ir_payload_t*)subpayload)->central_signal & KOBUKI_DOCKING_IR_NEAR_RIGHT_FLAG ? 1 : 0;
  280. break;
  281. case KOBUKI_INERTIAL_SENSOR_DATA_HEADER:
  282. robot->inertial_angle = ((kobuki_inertial_sensor_data_payload_t*)subpayload)->angle;
  283. robot->inertial_angle_rate = ((kobuki_inertial_sensor_data_payload_t*)subpayload)->angle_rate;
  284. robot->v_theta = ( (double)(((kobuki_inertial_sensor_data_payload_t*)subpayload)->angle_rate) / 100.0) * (M_PI / 180.0);
  285. break;
  286. case KOBUKI_CLIFF_SENSOR_DATA_HEADER:
  287. robot->left_cliff_adc = ((kobuki_cliff_sensor_data_payload_t*)subpayload)->left_cliff_sensor;
  288. robot->central_cliff_adc = ((kobuki_cliff_sensor_data_payload_t*)subpayload)->central_cliff_sensor;
  289. robot->right_cliff_adc = ((kobuki_cliff_sensor_data_payload_t*)subpayload)->right_cliff_sensor;
  290. break;
  291. case KOBUKI_CURRENT_HEADER:
  292. robot->left_motor_current = ((kobuki_current_payload_t*)subpayload)->left_motor;
  293. robot->right_motor_current = ((kobuki_current_payload_t*)subpayload)->right_motor;
  294. break;
  295. case KOBUKI_HARDWARE_VERSION_HEADER:
  296. robot->harware_version_major = ((kobuki_hardware_version_payload_t*)subpayload)->major;
  297. robot->harware_version_minor = ((kobuki_hardware_version_payload_t*)subpayload)->minor;
  298. robot->harware_version_patch = ((kobuki_hardware_version_payload_t*)subpayload)->pathch;
  299. rt_event_send(&(robot->event), KOBUKI_RECV_HARDWARE_EVENT);
  300. break;
  301. case KOBUKI_FIRMWARE_VERSION_HEADER:
  302. robot->firmware_version_major = ((kobuki_firmware_version_payload_t*)subpayload)->major;
  303. robot->firmware_version_minor = ((kobuki_firmware_version_payload_t*)subpayload)->minor;
  304. robot->firmware_version_patch = ((kobuki_firmware_version_payload_t*)subpayload)->pathch;
  305. rt_event_send(&(robot->event), KOBUKI_RECV_FIRMWARE_EVENT);
  306. break;
  307. case KOBUKI_3D_GYRO_RAW_DATA_HEADER:
  308. // We don't need raw data because angle and angular velocity can be retrieved from Inertial Sensor Data
  309. break;
  310. case KOBUKI_GENERAL_PURPOSE_INPUT_HEADER:
  311. robot->analog_input_0 = ((kobuki_general_purpose_input_payload_t*)subpayload)->analog_input_ch_0;
  312. robot->analog_input_1 = ((kobuki_general_purpose_input_payload_t*)subpayload)->analog_input_ch_1;
  313. robot->analog_input_2 = ((kobuki_general_purpose_input_payload_t*)subpayload)->analog_input_ch_2;
  314. robot->analog_input_3 = ((kobuki_general_purpose_input_payload_t*)subpayload)->analog_input_ch_3;
  315. break;
  316. case KOBUKI_UUID_HEADER:
  317. robot->uuid_0 =((kobuki_uuid_payload_t*)subpayload)->uuid_0;
  318. robot->uuid_1 =((kobuki_uuid_payload_t*)subpayload)->uuid_1;
  319. robot->uuid_2 =((kobuki_uuid_payload_t*)subpayload)->uuid_2;
  320. rt_event_send(&(robot->event), KOBUKI_RECV_UUID_EVENT);
  321. break;
  322. case KOBUKI_CONTROLLER_INFO_HEADER:
  323. robot->kp = ((kobuki_controller_info_payload_t*)subpayload)->p_gain;
  324. robot->ki = ((kobuki_controller_info_payload_t*)subpayload)->i_gain;
  325. robot->kd = ((kobuki_controller_info_payload_t*)subpayload)->d_gain;
  326. rt_event_send(&(robot->event), KOBUKI_RECV_CONTROLLER_INFO_EVENT);
  327. break;
  328. default:
  329. LOG_E("Unknown Payload\n");
  330. break;
  331. }
  332. }
  333. void kobuki_loop(kobuki_t robot)
  334. {
  335. if( (kobuki_get_tick() - robot->last_sync_tick) > KOBUKI_SYNC_TIMEOUT)
  336. {
  337. robot->connected = 0;
  338. }
  339. else
  340. {
  341. robot->connected = 1;
  342. }
  343. int packet_len = kobuki_protocol_loop(robot->packet, KOBUKI_PACKET_BUFFER);
  344. if (packet_len < 0)
  345. {
  346. // buffer overflow
  347. return;
  348. }
  349. else if (packet_len == 0)
  350. {
  351. // invalid checksum
  352. return;
  353. }
  354. else
  355. {
  356. robot->last_sync_tick = kobuki_get_tick();
  357. int i;
  358. for (i = 0; i < packet_len; i += robot->packet[i+1] + 2) {
  359. kobuki_parse_subpaylod(robot, robot->packet + i, robot->packet[i+1]);
  360. }
  361. }
  362. }
  363. void kobuki_close(kobuki_t robot)
  364. {
  365. rt_event_detach(&(robot->event));
  366. kobuki_serial_close();
  367. }