基于libpcan库can总线操作的Barrett 机械手控制及腕部六维力传感器驱动
效果:通过can总线直接向机械手控制器发送控制指令,实现机械手闭合控制,同时实现六维力传感器数据实时监测。
环境:ubuntu14.04+ROS indigo+pcan驱动(插入pcan后,能够使用ls /dev/pcan*显示出端口名称)。
[正文]
通过libpcan库操作can总线实现数据发送、读取,按照机械手控制器的数据格式要求,即可实现机械手和六维力传感器的驱动。可以通过两种方法获得机械手和六维力传感器个数据格式:其一是通过在控制机械手同时,外接一根pcan usb到windows下PcanView软件观察总结机械手控制时的数据格式,然后进行模仿,该方法较为直接,但是需要将数据和机械手控制所用的角速度、角度等信息进行对应,因此需要一定的经验。其二是从barrett的节点文件barrett_hand-indigo-devel/bhand_controller/src/bhand_controller/下的bhand_node.py,pyHand_api.py入手,找到其与机械手操作相关的函数或类的数据格式定义。而六维力传感器的数据格式及初始化操作要从MonitorForceTorque.cpp中分析得到。
3.2.1 显式调用libpcan.so 动态链接库实现can总线操作
下面给出一个libcan库的操作例子,本例博文地址:http://blog.csdn.net/ hookie1990/article/details/52629043。
1,添加载入需要的头文件及文件中自己使用的头文件。载入相关的头文件为dlfcn.h,fcntl.h,由于目标使用libpcan函数,所以libpcan.h也要加入。
l dlfcn.h : Linux动态库的显式调用库,包括dlopen(),dlclose()等。
l fcntl.h :fcntl.h定义了很多宏和open,fcntl函数原型,包括close open等关闭文件的系列操作。本文中用到了其打开文件的宏定义,即打开文件的控制模式。
int open(const char*pathname, int oflag, ... /* mode_t mode */);
返回值:成功则返回文件描述符,否则返回 -1
对于open函数来说,第三个参数(...)仅当创建新文件时(即使用了O_CREAT 时)才使用,用于指定文件的访问权限位(access permission bits)。pathname是待打开/创建文件的路径名(如 C:/cpp/a.cpp);oflag用于指定文件的打开/创建模式,这个参数可由以下常量(定义于 fcntl.h)通过逻辑或构成。
O_RDONLY 只读模式
O_WRONLY 只写模式
O_RDWR 读写模式
打开/创建文件时,至少得使用上述三个常量中的一个。以下常量是选用的:
O_APPEND 每次写操作都写入文件的末尾
O_CREAT 如果指定文件不存在,则创建这个文件
O_EXCL 如果要创建的文件已存在,则返回 -1,并且修改errno 的值
O_TRUNC 如果文件存在,并且以只写/读写方式打开,则清空文件全部内容(即将其长度截短为0)
O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端。
O_NONBLOCK 如果路径名指向FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblockingmode)
以下三个常量同样是选用的,它们用于同步输入输出
O_DSYNC 等待物理 I/O 结束后再write。在不影响读取新写入的数据的前提下,不等待文件属性更新。
O_RSYNC read 等待所有写入同一区域的写操作完成后再进行
O_SYNC 等待物理 I/O 结束后再write,包括更新文件属性的 I/O open 返回的文件描述符一定是最小的未被使用的描述符。
2,根据目标使用的函数及其参数形式定义目标指针。
此处暂时使用两个函数,一个CAN_Init,一个LINUX_CAN_Open函数。
下面为libpcan.h头文件中的定义。
1. DWORD CAN_Init(HANDLE hHandle, WORD wBTR0BTR1, int nCANMsgType);
2. HANDLE LINUX_CAN_Open(const char *szDeviceName, int nFlag);
根据上面定义我们声明两个函数指针的类型,方便后文使用
1. //define mapping function according to target function in libpcan.h
2. typedef DWORD (*funCAN_Init_TYPE)(HANDLE hHandle, WORD wBTR0BTR1, int nCANMsgType);
3. typedef HANDLE (*funLINUX_CAN_Open_TYPE)(const char *szDeviceName, int nFlag);
然后用声明的类型定义映射函数名
1. //define function pointer,there is a one-to-one mapping between target function and your defined function
2. funCAN_Init_TYPE fun_CAN_Init;
3. funLINUX_CAN_Open_TYPE funLINUX_CAN_Open;
3,定义文件访问访问的指针,实现文件加载。
1. void *libm_handle = NULL;//define pointer used for file acess of libpcan.so
2. // dlopen 函数还会自动解析共享库中的依赖项。这样,如果您打开了一个依赖于其他共享库的对象,它就会自动加载它们。
3. // 函数返回一个句柄,该句柄用于后续的 API 调用
4. libm_handle = dlopen("libpcan.so", RTLD_LAZY );
5. // 如果返回 NULL 句柄,表示无法找到对象文件,过程结束。否则的话,将会得到对象的一个句柄,可以进一步询问对象
6. if (!libm_handle){
7. // 如果返回 NULL 句柄,通过dlerror方法可以取得无法访问对象的原因
8. printf("Open Error:%s.\n",dlerror());
9. return 0;
10. }
其中用到的dlopen的参数解释如下:
void *dlopen(const char *filename, intflag);
其中flag有:RTLD_LAZYRTLD_NOW RTLD_GLOBAL,其含义分别为:
RTLD_LAZY:在dlopen返回前,对于动态库中存在的未定义的变量(如外部变量extern,也可以是函数)不执行解析,就是不解析这个变量的地址。
RTLD_NOW:与上面不同,他需要在dlopen返回前,解析出每个未定义变量的地址,如果解析不出来,在dlopen会返回NULL,错误为:undefined symbol: xxxx.......
RTLD_GLOBAL:它的含义是它的含义是使得库中的解析的定义变量在随后的随后其它的链接库中变得可以使用。
4,实现动态库函数到目标函数的映射。
1. // 使用 dlsym 函数,尝试解析新打开的对象文件中的符号。您将会得到一个有效的指向该符号的指针,或者是得到一个 NULL 并返回一个错误
2. //one-to-one mapping
3. char *errorInfo;//error information pointer
4. fun_CAN_Init =(funCAN_Init_TYPE)dlsym(libm_handle,"CAN_Init");
5. funLINUX_CAN_Open = (funLINUX_CAN_Open_TYPE)dlsym(libm_handle,"LINUX_CAN_Open");
6. errorInfo = dlerror();// 调用dlerror方法,返回错误信息的同时,内存中的错误信息被清空
7. if (errorInfo != NULL){
8. printf("Dlsym Error:%s.\n",errorInfo);
9. return 0;
10. }
5,定义访问硬件的HANDLE(指针),实现自定义函数对于can总线的访问,访问完成要关闭对象。
此前文中已有定义。
1. #define DEFAULT_NODE "/dev/pcan0"
此处实现如下:
1. HANDLE pcan_handle =NULL;//void *pcan_handle
2.
3. const char *szDevNode = DEFAULT_NODE;//define const pointer point to device name
4. pcan_handle = funLINUX_CAN_Open(szDevNode, O_RDWR | O_NONBLOCK);//use mapping function
5. //judge whether the call is success.if pcan_handle=null,the call would be failed
6. if(pcan_handle){
7. can_set_init();
8. printf("receivetest: %s have been opened\n", szDevNode);
9. }
10. else
11. printf("receivetest: can't open %s\n", szDevNode);
12.
13. // 调用 ELF 对象中的目标函数后,通过调用 dlclose 来关闭对它的访问
14. dlclose(libm_handle);
最终映射使用的函数如下:
funCAN_Init =(funCAN_Init_TYPE) dlsym(libm_handle,"CAN_Init");
funLINUX_CAN_Open =(funLINUX_CAN_Open_TYPE) dlsym(libm_handle,"LINUX_CAN_Open");
funCAN_Close =(funCAN_Close_TYPE) dlsym(libm_handle,"CAN_Close");
funCAN_VersionInfo =(funCAN_VersionInfo_TYPE) dlsym(libm_handle,"CAN_VersionInfo");
funLINUX_CAN_Read =(funLINUX_CAN_Read_TYPE) dlsym(libm_handle,"LINUX_CAN_Read");
funCAN_Status =(funCAN_Status_TYPE) dlsym(libm_handle,"CAN_Status");
funnGetLastError =(funnGetLastError_TYPE) dlsym(libm_handle,"nGetLastError");
funCAN_Write =(funCAN_Write_TYPE) dlsym(libm_handle,"CAN_Write");
3.2.2 机械手位置控制实现
根据我们对于机械手复位的检测,发现在复位过程中上位机软件想机械手发送了一组数据,通过对发送过程的简单模仿,可使Barrett机械手复位。
15. void barett_hand_init()
16. {
17. TPCANMsg test_data;
18. //query the bhand state.
19. for(chari=0;i<=3;i++)
20. {
21. test_data.ID=0xcb+i;
22. test_data.LEN=1;
23. test_data.MSGTYPE=MSGTYPE_STANDARD;
24. test_data.DATA[0]=0x05;
25. funCAN_Write(pcan_handle,&test_data);
26. }
27.
28. for(chari=0;i<=3;i++)
29. {
30. test_data.ID=0xcb+i;
31. test_data.LEN=6;
32. test_data.MSGTYPE=MSGTYPE_STANDARD;
33. test_data.DATA[0]=0xb2;
34. for(charj=1;j<6;j++)test_data.DATA[j]=0;
35. funCAN_Write(pcan_handle,&test_data);
36. }
37.
38. for(chari=0;i<=3;i++)
39. {
40. test_data.ID=0xcb+i;
41. test_data.LEN=6;
42. test_data.MSGTYPE=MSGTYPE_STANDARD;
43. for(charj=0;j<6;j++)test_data.DATA[j]=0;
44. test_data.DATA[0]=0xce;
45. test_data.DATA[2]=0xfa;
46. funCAN_Write(pcan_handle,&test_data);
47. }
48.
49. //query the bhand state.
50. for(chari=0;i<=3;i++)
51. {
52. test_data.ID=0xcb+i;
53. test_data.LEN=1;
54. test_data.MSGTYPE=MSGTYPE_STANDARD;
55. test_data.DATA[0]=0x00;
56. funCAN_Write(pcan_handle,&test_data);
57. }
58. //query the bhand state.
59. for(chari=0;i<=3;i++)
60. {
61. test_data.ID=0xcb+i;
62. test_data.LEN=1;
63. test_data.MSGTYPE=MSGTYPE_STANDARD;
64. test_data.DATA[0]=0x5a;
65. funCAN_Write(pcan_handle,&test_data);
66. }
67. //query the bhand state.
68. for(chari=0;i<=3;i++)
69. {
70. test_data.ID=0xcb+i;
71. test_data.LEN=1;
72. test_data.MSGTYPE=MSGTYPE_STANDARD;
73. test_data.DATA[0]=0x08;
74. funCAN_Write(pcan_handle,&test_data);
75. }
76. //init the bhand.
77. for(chari=0;i<=3;i++)
78. {
79. test_data.ID=0xcb+i;
80. test_data.LEN=4;
81. test_data.MSGTYPE=MSGTYPE_STANDARD;
82. test_data.DATA[0]=0x9d;
83. test_data.DATA[1]=0x00;
84. test_data.DATA[2]=0x0d;
85. test_data.DATA[3]=0x00;
86. funCAN_Write(pcan_handle,&test_data);
87. }
88. sleep(5);
89. ROS_INFO("BHandInit successfully!");
90.
91. }
解析出来对于机械手位置控制的实现,其中参数范围如下式所示,此处还没有转换为角度,后期在sia_bhand_controller中已转换为对应的0-2.5的弧度值。
Position=x*10 0000, 0<=x<=2.5
11. void bhand_position_control(long f1_pos,long f2_pos,longf3_pos,long sp_pos)
12. {
13. TPCANMsg CANMsg;
14.
15.
16. CANMsg.LEN = 6;
17. CANMsg.MSGTYPE =MSGTYPE_STANDARD;
18.
19. CANMsg.DATA[0] = 0xba;
20. CANMsg.DATA[1] = 0;
21. CANMsg.DATA[2] = 0;
22. CANMsg.DATA[3] = 0;
23. CANMsg.DATA[4] = 0;
24. CANMsg.DATA[5] = 0;
25.
26. CANMsg.DATA[2] = f1_pos & 0xff;
27. CANMsg.DATA[3] =(f1_pos>>8) & 0xff;
28. CANMsg.DATA[4] =(f1_pos>>16) & 0xff;
29.
30. CANMsg.ID = 0xcb;
31. funCAN_Write(pcan_handle,&CANMsg);
32. usleep(1000);
33. CANMsg.DATA[2] = f2_pos& 0xff;
34. CANMsg.DATA[3] =(f2_pos>>8) & 0xff;
35. CANMsg.DATA[4] =(f2_pos>>16) & 0xff;
36.
37. CANMsg.ID = 0xcc;
38. funCAN_Write(pcan_handle,&CANMsg);
39. usleep(1000);
40.
41. CANMsg.DATA[2] = f3_pos& 0xff;
42. CANMsg.DATA[3] =(f3_pos>>8) & 0xff;
43. CANMsg.DATA[4] =(f3_pos>>16) & 0xff;
44.
45. CANMsg.ID = 0xcd;
46. funCAN_Write(pcan_handle,&CANMsg);
47. usleep(1000);
48.
49. CANMsg.DATA[2] = sp_pos& 0xff;
50. CANMsg.DATA[3] =(sp_pos>>8) & 0xff;
51. CANMsg.DATA[4] =(sp_pos>>16) & 0xff;
52.
53. CANMsg.ID = 0xce;
54. funCAN_Write(pcan_handle,&CANMsg);
55. usleep(1000);
56. }
由于本例中我们选择对于pcan的处理是非阻塞的模式,因此必须有一个函数处理接收到的无意义数据或者请求。其中的if(m.Msg.ID == 0x50a || m.Msg.ID == 0x50b)判断则是标示该帧头开始数据为六维力传感器的数据。
57. int read_loop(HANDLE h,ros::Publisher pub, bool display_on,boolpublish_on)
58. {
59. TPCANRdMsg m;
60. __u32 status;
61.
62. if (funLINUX_CAN_Read(h,&m)) {
63. //perror("receivetest:LINUX_CAN_Read()");
64. return errno;
65. }
66. if(m.Msg.ID == 0x50a ||m.Msg.ID == 0x50b)
67. {
68. if (display_on)
69. print_message_ex(&m);
70. if (publish_on)
71. publish_forcedata(&m,pub,force_display_on);
72.
73. // check if a CANstatus is pending
74. if (m.Msg.MSGTYPE& MSGTYPE_STATUS) {
75. status =funCAN_Status(h);
76. if ((int)status< 0) {
77. errno =funnGetLastError();
78. perror("receivetest:CAN_Status()");
79. returnerrno;
80. }
81.
82. printf("receivetest: pendingCAN status 0x%04x read.\n",
83. (__u16)status);
84. }
85. }
86. return 0;
87. }
3.2.3 Barrett Hand腕部六维力传感器操作
从MonitorForceTorque.cpp中我们可以查到六维力传感器的初始化步骤:由两个函数完成,wavePuck()和setPropertySlow().完成设备唤醒和启动校准(非初始受力置零)。
88. //wake puck
89. wavePuck(0,8);
90. //tare
91. setPropertySlow(0,FTSENSORID,FTDATAPROPERTY,0,0);
函数的原型如下:
11.int wavePuck(int bus,int id)
12.{
13.
14. TPCANMsg msgOut;
15. DWORD err;
16.
17. //Generate the outbound message
18. msgOut.ID = id;
19. msgOut.MSGTYPE = MSGTYPE_STANDARD;
20. msgOut.LEN = 4;
21. msgOut.DATA[0] = 0x85; // Set Status
22. msgOut.DATA[1] = 0x00;
23. msgOut.DATA[2] = 0x02; // Status = Ready
24. msgOut.DATA[3] = 0x00;
25.
26. //Send the message
27. //err = CAN_Write( &msgOut );
28. err=funCAN_Write(pcan_handle,&msgOut);
29. sleep(1);
30.
31. return(err);
32.}
33.
34.int compile(
35. intproperty /** The property beingcompiled (use the enumerations in btcan.h) */,
36. longlongVal /** The value to set theproperty to */,
37. unsigned char *data /** A pointer to acharacter buffer in which to build the data payload */,
38. int*dataLen /** A pointer to thetotal length of the data payload for this packet */,
39. intisForSafety /** A flag indicatingwhether this packet is destined for the safety circuit or not */)
40.{
41. inti;
42.
43. //Check the property
44. //if(property > PROP_END)
45. //{
46. // syslog(LOG_ERR,"compile(): Invalid property = %d", property);
47. // return(1);
48. //}
49.
50. /*Insert the property */
51. data[0]= property;
52. data[1] = 0; /* To align the values for thetater's DSP */
53.
54. /*Append the value */
55. for(i = 2; i < 6; i++)
56. {
57. data[i] = (char)(longVal &0x000000FF);
58. longVal >>= 8;
59. }
60.
61. /*Record the proper data length */
62. *dataLen = 6; //(dataType[property] &0x0007) + 2;
63.
64. return (0);
65.}
66.
67.int setPropertySlow(int bus, int id, intproperty, int verify, long value)
68.{
69. TPCANMsg msgOut, msgIn;
70. DWORD err;
71. intdataHeader, i;
72. longresponse;
73. //unsigned char data[8];
74. int len;
75.
76. //syslog(LOG_ERR, "About to compilesetProperty, property = %d", property);
77. //Compile 'set' packet
78. err= compile(property, value, msgOut.DATA, &len, 0);
79.
80. //Generate the outbound message
81. msgOut.ID = id;
82. msgOut.MSGTYPE = MSGTYPE_STANDARD;
83. msgOut.LEN = len;
84.
85.
86.
87. //syslog(LOG_ERR, "After compilationdata[0] = %d", data[0]);
88. msgOut.DATA[0] |= 0x80; // Set the 'Set'bit
89.
90. //Send the message
91. //CAN_Write( &msgOut );
92. funCAN_Write(pcan_handle,&msgOut);
93.
94. //Sleep(250);
95.
96. //BUG: This will not verify properties from groups of pucks
97. return(0);
98.}
99.
100.int getPropertyFT(int bus,HANDLE h)
101.
102.{
103.
104.
105. TPCANMsg msgOut, msgInOne, msgInTwo;
106. DWORD err;
107.
108.
109. //Generate the outbound message
110. msgOut.ID = FTSENSORID;
111. msgOut.MSGTYPE = MSGTYPE_STANDARD;
112. msgOut.LEN = 1;
113. msgOut.DATA[0] = FTDATAPROPERTY; // Get theft data
114.
115.}
线程循环检测有效数据即read_loop()所执行的功能,检测到帧头后要进行如下的数据解析,规则是每两个字节表示一个浮点数,以x轴力为例表示一个完整的处理如下:
x_force=mr->Msg.DATA[1];
x_force<<=8;
x_force|=mr->Msg.DATA[0];
force_x_float=x_force/256.0;
得到受力或力矩的数值后便需要根据力和力矩的数值通知机械臂或主控制系统进行进一步的处理,该部分工作由bhand_axis_force_warning()函数实现。完整代码参看src下对应cpp文件或者个人博客。
116.void publish_forcedata(TPCANRdMsg*mr,ros::Publisher axis_force_pub,bool display_on)
117.{
118. short int x_force,y_force,z_force;
119. short int torque_x,torque_y,torque_z;
120.
121.
122. if(0x50a==mr->Msg.ID )
123. {
124. x_force=mr->Msg.DATA[1];
125. x_force<<=8;
126. x_force|=mr->Msg.DATA[0];
127.
128. y_force=mr->Msg.DATA[3];
129. y_force<<=8;
130. y_force|=mr->Msg.DATA[2];
131.
132. z_force=mr->Msg.DATA[5];
133. z_force<<=8;
134. z_force|=mr->Msg.DATA[4];
135. force_x_float=x_force/256.0;
136. force_y_float=y_force/256.0;
137. force_z_float=z_force/256.0;
138. if(display_on) ROS_INFO(" Forcex:%8.4f, y:%8.4f, z:%8.4f",force_x_float,force_y_float,force_z_float);
139. }
140.
141. if(0x50b==mr->Msg.ID )
142. {
143. torque_x=mr->Msg.DATA[1];
144. torque_x<<=8;
145. torque_x|=mr->Msg.DATA[0];
146.
147. torque_y=mr->Msg.DATA[3];
148. torque_y<<=8;
149. torque_y|=mr->Msg.DATA[2];
150.
151. torque_z=mr->Msg.DATA[5];
152. torque_z<<=8;
153. torque_z|=mr->Msg.DATA[4];
154. torque_x_float=torque_x/256.0;
155. torque_y_float=torque_y/256.0;
156. torque_z_float=torque_z/256.0;
157. if(display_on) ROS_INFO("Torquex:%8.4f, y:%8.4f, z:%8.4f",torque_x_float,torque_y_float,torque_z_float);
158. }
159.
160.}
3.3 launch文件及节点数配置
实际参数、节点的配置均由launch文件实现,launch文件可以接收int,string,double类型的数据,如下为一个示例。
161.<launch>
162. <nodepkg="beginner_tutorials" type="bhand_axis_force_limit"name="bhand_axis_force" output="screen">
163. <paramname="dev_name" type="string"value="/dev/pcanusb0" />
164. <paramname="bhand_open_pos" type="int" value="6" />
165. <paramname="bhand_close_pos" type="int" value="14"/>
166. <paramname="force_display_on" type="bool" value="false"/>
167. <paramname="force_threshold" type="double" value="18.0"/>
168. <paramname="torque_threshold" type="double"value="20.0" />
169. <paramname="force_danger" type="double" value="25.0"/>
170. <paramname="torque_danger" type="double" value="40.0"/>
171. <paramname="left_torque_x_threshold" type="double" value="-2.0" />
172. <paramname="right_torque_x_threshold" type="double"value="2.0" />
173. </node>
174.</launch>
节点代码中的接受处理流程与launch文件一一对应,如下:
175. string dev_name;
176. ros::param::get("~dev_name",dev_name);
177. ROS_INFO("PCANDevice:%s",dev_name.c_str());
178.
179. intbhand_open_pos=5;
180. ros::param::get("~bhand_open_pos",bhand_open_pos);
181. ROS_INFO("Bhand open position:%d",bhand_open_pos);
182.
183. intbhand_close_pos=13;
184. ros::param::get("~bhand_close_pos",bhand_close_pos);
185. ROS_INFO("Bhand close position:%d",bhand_close_pos);
186.
187. ros::param::get("~force_display_on",force_display_on);
188. ROS_INFO("ForceDatadisplay:%s",force_display_on==false?"false":"true");
189.
190. ros::param::get("~force_threshold",force_threshold);
191. ROS_INFO("Force threshold :%f",force_threshold);
192. ros::param::get("~torque_threshold",torque_threshold);
193. ROS_INFO("Torque threshold :%f",torque_threshold);
194.
195. ros::param::get("~force_danger",force_danger);
196. ROS_WARN(" Force dangerous threshold:%f",force_danger);
197. ros::param::get("~torque_danger",torque_danger);
198. ROS_WARN("Torque dangerous threshold:%f",torque_danger);
199.
200. ros::param::get("~left_torque_x_threshold",left_torque_x_threshold);
201. ROS_WARN(" left torque x threshold:%f",left_torque_x_threshold);
202. ros::param::get("~right_torque_x_threshold",right_torque_x_threshold);
203. ROS_WARN("right torque x threshold:%f",right_torque_x_threshold);
204.
下附本节完整代码bhand_axis_force_limit.cpp
#include <ros/ros.h>
#include <stdio.h>
#include <dlfcn.h>
#include <libpcan.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include "id_data_msgs/ID_Data.h"//using for notie event
#include "ros/callback_queue.h"
#include "string"using namespace std;
//define mapping function according to target function in libpcan.h
typedef DWORD (*funCAN_Init_TYPE)(HANDLE hHandle, WORD wBTR0BTR1, int nCANMsgType);
typedef HANDLE (*funLINUX_CAN_Open_TYPE)(const char *szDeviceName, int nFlag);
typedef DWORD (*funCAN_VersionInfo_TYPE)(HANDLE hHandle, LPSTR lpszTextBuff);
typedef DWORD (*funCAN_Close_TYPE)(HANDLE hHandle);
typedef DWORD (*funLINUX_CAN_Read_TYPE)(HANDLE hHandle, TPCANRdMsg* pMsgBuff);
typedef DWORD (*funCAN_Status_TYPE)(HANDLE hHandle);
typedef int (*funnGetLastError_TYPE)(void);
typedef DWORD (*funCAN_Write_TYPE)(HANDLE hHandle, TPCANMsg* pMsgBuff);//the target device name
#define DEFAULT_NODE "/dev/pcan0"#define FTSENSORID (8)
#define FTDATAPROPERTY (54)//GLOBALS
void *libm_handle = NULL;//define pointer used for file acess of libpcan.so
HANDLE pcan_handle =NULL;//void *pcan_handle
id_data_msgs::ID_Data axis_force_msg;
bool force_display_on=true;
float force_threshold=4.0;
float torque_threshold=10.0;
double force_danger=15.0;
double torque_danger=30.0;
double left_torque_x_threshold=-1.0;
double right_torque_x_threshold=1.0;//globals
bool close_hand_flag=false;
bool open_hand_flag=false;
float torque_x_float,torque_y_float,torque_z_float;
float force_x_float,force_y_float,force_z_float;void notice_data_clear(id_data_msgs::ID_Data *test);int wavePuck(int bus,int id);
int compile(int property /** The property being compiled (use the enumerations in btcan.h) */,long longVal /** The value to set the property to */,unsigned char *data /** A pointer to a character buffer in which to build the data payload */,int *dataLen /** A pointer to the total length of the data payload for this packet */,int isForSafety /** A flag indicating whether this packet is destined for the safety circuit or not */);
int setPropertySlow(int bus, int id, int property, int verify, long value);//define function pointer,there is a one-to-one mapping between target function and your defined function
funCAN_Init_TYPE funCAN_Init;
funLINUX_CAN_Open_TYPE funLINUX_CAN_Open;
funCAN_VersionInfo_TYPE funCAN_VersionInfo;
funCAN_Close_TYPE funCAN_Close;
funLINUX_CAN_Read_TYPE funLINUX_CAN_Read;
funCAN_Status_TYPE funCAN_Status;
funnGetLastError_TYPE funnGetLastError;
funCAN_Write_TYPE funCAN_Write;class notice_pub_sub
{
public:boost::function<void (const id_data_msgs::ID_Data::ConstPtr&)> notice_pub_sub_msgCallbackFun;notice_pub_sub();void notice_pub_sub_listener();void notice_pub_sub_pulisher(id_data_msgs::ID_Data id_data);void notice_display(id_data_msgs::ID_Data notice_msg,bool set);void notice_sub_spinner(char set);private:ros::NodeHandle notice_handle;ros::Subscriber notice_subscriber;ros::Publisher notice_publisher;ros::SubscribeOptions notice_ops;ros::AsyncSpinner *notice_spinner;ros::CallbackQueue notice_callbackqueue;void notice_msgCallback(const id_data_msgs::ID_Data::ConstPtr ¬ice_msg);
};notice_pub_sub::notice_pub_sub()
{notice_pub_sub_msgCallbackFun=boost::bind(¬ice_pub_sub::notice_msgCallback,this,_1);notice_ops=ros::SubscribeOptions::create<id_data_msgs::ID_Data>("/notice",1,notice_pub_sub_msgCallbackFun,ros::VoidPtr(),¬ice_callbackqueue);notice_subscriber=notice_handle.subscribe(notice_ops);notice_spinner=new ros::AsyncSpinner(1,¬ice_callbackqueue);notice_publisher=notice_handle.advertise<id_data_msgs::ID_Data>("/notice",1);
}void notice_pub_sub::notice_pub_sub_listener()
{}void notice_pub_sub::notice_pub_sub_pulisher(id_data_msgs::ID_Data id_data)
{notice_publisher.publish(id_data);
}void notice_pub_sub::notice_display(id_data_msgs::ID_Data notice_msg,bool set)
{if(set){printf("REC Notice message,ID: %d,Data: ",notice_msg.id);for(char i=0;i<8;i++){printf("%d ",notice_msg.data[i]);if(i==7) printf("\n");}}}
void notice_pub_sub::notice_msgCallback(const id_data_msgs::ID_Data::ConstPtr ¬ice_msg)
{id_data_msgs::ID_Data notice_message;notice_message.id=0;for(char i=0;i<8;i++)notice_message.data[i]=0;notice_message.id=notice_msg->id;for(char i=0;i<8;i++)notice_message.data[i]=notice_msg->data[i];notice_pub_sub::notice_display(notice_message,true);if(notice_message.id==1 && notice_message.data[0]==1)//close flag{close_hand_flag=true;notice_data_clear(¬ice_message);notice_message.id=1;notice_message.data[0]=14;notice_publisher.publish(notice_message);}if(notice_message.id==1 && notice_message.data[0]==0)//open flag{open_hand_flag=true;notice_data_clear(¬ice_message);notice_message.id=1;notice_message.data[0]=14;notice_publisher.publish(notice_message);}}void notice_pub_sub::notice_sub_spinner(char set)
{if(set==1)notice_spinner->start();if(set==0)notice_spinner->stop();
}
//function declaration
int read_loop(HANDLE h,ros::Publisher pub,bool display_on,bool publish_on);
void print_message(TPCANMsg *m);
void print_message_ex(TPCANRdMsg *mr);
void do_exit(void *file,HANDLE h,int error);
void signal_handler(int signal);
void init();
void publish_forcedata(TPCANRdMsg *mr, ros::Publisher axis_force_pub, bool display_on);
void barett_hand_init();void bhand_position_control(long f1_pos,long f2_pos,long f3_pos,long sp_pos);
void bhand_axis_force_warning(notice_pub_sub *notice_test,float force_x,float force_y,float force_z,float torque_x,float torque_y,float torque_z);
//
int sys_count=0;
int main(int argc,char **argv)
{ros::init(argc,argv,"bhand_axis_force");ros::NodeHandle handle_test;init();string dev_name;ros::param::get("~dev_name",dev_name);ROS_INFO("PCAN Device:%s",dev_name.c_str());int bhand_open_pos=5;ros::param::get("~bhand_open_pos",bhand_open_pos);ROS_INFO("Bhand open position :%d",bhand_open_pos);int bhand_close_pos=13;ros::param::get("~bhand_close_pos",bhand_close_pos);ROS_INFO("Bhand close position :%d",bhand_close_pos);ros::param::get("~force_display_on",force_display_on);ROS_INFO("ForceData display:%s",force_display_on==false?"false":"true");ros::param::get("~force_threshold",force_threshold);ROS_INFO("Force threshold :%f",force_threshold);ros::param::get("~torque_threshold",torque_threshold);ROS_INFO("Torque threshold :%f",torque_threshold);ros::param::get("~force_danger",force_danger);ROS_WARN(" Force dangerous threshold :%f",force_danger);ros::param::get("~torque_danger",torque_danger);ROS_WARN("Torque dangerous threshold :%f",torque_danger);ros::param::get("~left_torque_x_threshold",left_torque_x_threshold);ROS_WARN(" left torque x threshold :%f",left_torque_x_threshold);ros::param::get("~right_torque_x_threshold",right_torque_x_threshold);ROS_WARN("right torque x threshold :%f",right_torque_x_threshold);//load libpcan.so using dlopen function,return handle for further uselibm_handle = dlopen("libpcan.so", RTLD_LAZY );if (!libm_handle){printf("Open Error:%s.\n",dlerror());//if file can't be loaded,return null,get reason using dlerror functionreturn 0;}char *errorInfo;//error information pointer//one-to-one mapping using dlsym function,if return null,mapping would be failedfunCAN_Init =(funCAN_Init_TYPE) dlsym(libm_handle,"CAN_Init");funLINUX_CAN_Open =(funLINUX_CAN_Open_TYPE) dlsym(libm_handle,"LINUX_CAN_Open");funCAN_Close =(funCAN_Close_TYPE) dlsym(libm_handle,"CAN_Close");funCAN_VersionInfo =(funCAN_VersionInfo_TYPE) dlsym(libm_handle,"CAN_VersionInfo");funLINUX_CAN_Read =(funLINUX_CAN_Read_TYPE) dlsym(libm_handle,"LINUX_CAN_Read");funCAN_Status =(funCAN_Status_TYPE) dlsym(libm_handle,"CAN_Status");funnGetLastError =(funnGetLastError_TYPE) dlsym(libm_handle,"nGetLastError");funCAN_Write =(funCAN_Write_TYPE) dlsym(libm_handle,"CAN_Write");errorInfo = dlerror();//get error using dlerror function,and clear the error list in memoryif (errorInfo != NULL){printf("Dlsym Error:%s.\n",errorInfo);return 0;}char txt[VERSIONSTRING_LEN]; //store information of can versionunsigned short wBTR0BTR1 = CAN_BAUD_1M; //set the communicate baud rate of can busint nExtended = CAN_INIT_TYPE_ST; //set can message int standard modelconst char *szDevNode = DEFAULT_NODE; //define const pointer point to device nameif(dev_name.c_str() !="")pcan_handle = funLINUX_CAN_Open(dev_name.c_str(), O_RDWR | O_NONBLOCK);else{pcan_handle = funLINUX_CAN_Open(szDevNode, O_RDWR | O_NONBLOCK);//use mapping functiondev_name=DEFAULT_NODE;}//pcan_handle = funLINUX_CAN_Open(szDevNode, O_RDWR | O_NONBLOCK );//use mapping function//judge whether the call is success.if pcan_handle=null,the call would be failedif(pcan_handle){printf("CAN Bus test: %s have been opened\n", szDevNode);errno = funCAN_VersionInfo(pcan_handle, txt);if (!errno)printf("CAN Bus test: driver version = %s\n", txt);else {perror("CAN Bus test: CAN_VersionInfo()");}if (wBTR0BTR1) {errno = funCAN_Init(pcan_handle, wBTR0BTR1, nExtended);if (errno) {perror("CAN Bus test: CAN_Init()");}elseprintf("Device Info: %s; CAN_BAUD_1M; CAN_INIT_TYPE_ST\n", szDevNode);}}elseprintf("CAN Bus test: can't open %s\n", szDevNode);//test of can send databarett_hand_init();bhand_position_control(bhand_open_pos*10000,bhand_open_pos*10000,bhand_open_pos*10000,0);//position: (0--2.5)*10 0000sleep(5);//waiting for bhand ready...ros::NodeHandle axis_force_handle;ros::Publisher axis_force_pub=axis_force_handle.advertise<id_data_msgs::ID_Data>("/notice",1);//advertise axis force to the topic named "/notice"ros::Rate loop_rate(100);//data receive test//wake puckwavePuck(0,8);//taresetPropertySlow(0,FTSENSORID,FTDATAPROPERTY,0,0);TPCANMsg test_data;//query the axis sensor.test_data.ID=0x008;test_data.LEN=1;test_data.MSGTYPE=MSGTYPE_STANDARD;test_data.DATA[0]=0x36;notice_pub_sub notice_test;id_data_msgs::ID_Data notice_data_pub;while(ros::ok()){sys_count++;if(sys_count%20==0)funCAN_Write(pcan_handle,&test_data);//query the axis sensor.read_loop(pcan_handle,axis_force_pub,false,true);bhand_axis_force_warning(¬ice_test,force_x_float,force_y_float,force_z_float,torque_x_float,torque_y_float,torque_z_float);//HANDLE h,ros::Publisher pub, bool display_on,bool publish_onif(close_hand_flag){open_hand_flag=false;bhand_position_control(bhand_close_pos*10000,bhand_close_pos*10000,bhand_close_pos*10000,0);//position: (0-2.5)*10 0000sleep(2);notice_data_clear(¬ice_data_pub);notice_data_pub.id=1;notice_data_pub.data[0]=2;notice_test.notice_pub_sub_pulisher(notice_data_pub);close_hand_flag=false;}if(open_hand_flag){close_hand_flag=false;bhand_position_control((bhand_open_pos-2)*10000,(bhand_open_pos-2)*10000,(bhand_open_pos-2)*10000,0);//position: (0-2.5)*10 0000sleep(2);bhand_position_control(bhand_open_pos*10000,bhand_open_pos*10000,bhand_open_pos*10000,0);//position: (0-2.5)*10 0000sleep(1);notice_data_clear(¬ice_data_pub);notice_data_pub.id=1;notice_data_pub.data[0]=2;notice_test.notice_pub_sub_pulisher(notice_data_pub);open_hand_flag=false;ROS_INFO("Open hand successfully!\n");}notice_test.notice_sub_spinner(1);loop_rate.sleep();}return 0;
}int wavePuck(int bus,int id)
{TPCANMsg msgOut;DWORD err;// Generate the outbound messagemsgOut.ID = id;msgOut.MSGTYPE = MSGTYPE_STANDARD;msgOut.LEN = 4;msgOut.DATA[0] = 0x85; // Set StatusmsgOut.DATA[1] = 0x00;msgOut.DATA[2] = 0x02; // Status = ReadymsgOut.DATA[3] = 0x00;// Send the message//err = CAN_Write( &msgOut );err=funCAN_Write(pcan_handle,&msgOut);sleep(1);return(err);
}int compile(int property /** The property being compiled (use the enumerations in btcan.h) */,long longVal /** The value to set the property to */,unsigned char *data /** A pointer to a character buffer in which to build the data payload */,int *dataLen /** A pointer to the total length of the data payload for this packet */,int isForSafety /** A flag indicating whether this packet is destined for the safety circuit or not */)
{int i;// Check the property//if(property > PROP_END)//{// syslog(LOG_ERR,"compile(): Invalid property = %d", property);// return(1);//}/* Insert the property */data[0] = property;data[1] = 0; /* To align the values for the tater's DSP *//* Append the value */for (i = 2; i < 6; i++){data[i] = (char)(longVal & 0x000000FF);longVal >>= 8;}/* Record the proper data length */*dataLen = 6; //(dataType[property] & 0x0007) + 2;return (0);
}int setPropertySlow(int bus, int id, int property, int verify, long value)
{TPCANMsg msgOut, msgIn;DWORD err;int dataHeader, i;long response;//unsigned char data[8];int len;//syslog(LOG_ERR, "About to compile setProperty, property = %d", property);// Compile 'set' packeterr = compile(property, value, msgOut.DATA, &len, 0);// Generate the outbound messagemsgOut.ID = id;msgOut.MSGTYPE = MSGTYPE_STANDARD;msgOut.LEN = len;//syslog(LOG_ERR, "After compilation data[0] = %d", data[0]);msgOut.DATA[0] |= 0x80; // Set the 'Set' bit// Send the message//CAN_Write( &msgOut );funCAN_Write(pcan_handle,&msgOut);//Sleep(250);// BUG: This will not verify properties from groups of pucksreturn(0);
}int getPropertyFT(int bus,HANDLE h){TPCANMsg msgOut, msgInOne, msgInTwo;DWORD err;// Generate the outbound messagemsgOut.ID = FTSENSORID;msgOut.MSGTYPE = MSGTYPE_STANDARD;msgOut.LEN = 1;msgOut.DATA[0] = FTDATAPROPERTY; // Get the ft data}void barett_hand_init()
{TPCANMsg test_data;//query the bhand state.for(char i=0;i<=3;i++){test_data.ID=0xcb+i;test_data.LEN=1;test_data.MSGTYPE=MSGTYPE_STANDARD;test_data.DATA[0]=0x05;funCAN_Write(pcan_handle,&test_data);}for(char i=0;i<=3;i++){test_data.ID=0xcb+i;test_data.LEN=6;test_data.MSGTYPE=MSGTYPE_STANDARD;test_data.DATA[0]=0xb2;for(char j=1;j<6;j++)test_data.DATA[j]=0;funCAN_Write(pcan_handle,&test_data);}for(char i=0;i<=3;i++){test_data.ID=0xcb+i;test_data.LEN=6;test_data.MSGTYPE=MSGTYPE_STANDARD;for(char j=0;j<6;j++)test_data.DATA[j]=0;test_data.DATA[0]=0xce;test_data.DATA[2]=0xfa;funCAN_Write(pcan_handle,&test_data);}//query the bhand state.for(char i=0;i<=3;i++){test_data.ID=0xcb+i;test_data.LEN=1;test_data.MSGTYPE=MSGTYPE_STANDARD;test_data.DATA[0]=0x00;funCAN_Write(pcan_handle,&test_data);}//query the bhand state.for(char i=0;i<=3;i++){test_data.ID=0xcb+i;test_data.LEN=1;test_data.MSGTYPE=MSGTYPE_STANDARD;test_data.DATA[0]=0x5a;funCAN_Write(pcan_handle,&test_data);}//query the bhand state.for(char i=0;i<=3;i++){test_data.ID=0xcb+i;test_data.LEN=1;test_data.MSGTYPE=MSGTYPE_STANDARD;test_data.DATA[0]=0x08;funCAN_Write(pcan_handle,&test_data);}//init the bhand.for(char i=0;i<=3;i++){test_data.ID=0xcb+i;test_data.LEN=4;test_data.MSGTYPE=MSGTYPE_STANDARD;test_data.DATA[0]=0x9d;test_data.DATA[1]=0x00;test_data.DATA[2]=0x0d;test_data.DATA[3]=0x00;funCAN_Write(pcan_handle,&test_data);}sleep(5);ROS_INFO("BHand Init successfully!");}void bhand_position_control(long f1_pos,long f2_pos,long f3_pos,long sp_pos)
{TPCANMsg CANMsg;CANMsg.LEN = 6;CANMsg.MSGTYPE =MSGTYPE_STANDARD;CANMsg.DATA[0] = 0xba;CANMsg.DATA[1] = 0;CANMsg.DATA[2] = 0;CANMsg.DATA[3] = 0;CANMsg.DATA[4] = 0;CANMsg.DATA[5] = 0;CANMsg.DATA[2] = f1_pos & 0xff;CANMsg.DATA[3] = (f1_pos>>8) & 0xff;CANMsg.DATA[4] = (f1_pos>>16) & 0xff;CANMsg.ID = 0xcb;funCAN_Write(pcan_handle,&CANMsg);usleep(1000);CANMsg.DATA[2] = f2_pos & 0xff;CANMsg.DATA[3] = (f2_pos>>8) & 0xff;CANMsg.DATA[4] = (f2_pos>>16) & 0xff;CANMsg.ID = 0xcc;funCAN_Write(pcan_handle,&CANMsg);usleep(1000);CANMsg.DATA[2] = f3_pos & 0xff;CANMsg.DATA[3] = (f3_pos>>8) & 0xff;CANMsg.DATA[4] = (f3_pos>>16) & 0xff;CANMsg.ID = 0xcd;funCAN_Write(pcan_handle,&CANMsg);usleep(1000);CANMsg.DATA[2] = sp_pos & 0xff;CANMsg.DATA[3] = (sp_pos>>8) & 0xff;CANMsg.DATA[4] = (sp_pos>>16) & 0xff;CANMsg.ID = 0xce;funCAN_Write(pcan_handle,&CANMsg);usleep(1000);
}int read_loop(HANDLE h,ros::Publisher pub, bool display_on,bool publish_on)
{TPCANRdMsg m;__u32 status;if (funLINUX_CAN_Read(h, &m)) {//perror("receivetest: LINUX_CAN_Read()");return errno;}if(m.Msg.ID == 0x50a || m.Msg.ID == 0x50b){if (display_on)print_message_ex(&m);if (publish_on)publish_forcedata(&m,pub,force_display_on);// check if a CAN status is pendingif (m.Msg.MSGTYPE & MSGTYPE_STATUS) {status = funCAN_Status(h);if ((int)status < 0) {errno = funnGetLastError();perror("receivetest: CAN_Status()");return errno;}printf("receivetest: pending CAN status 0x%04x read.\n",(__u16)status);}}return 0;
}void publish_forcedata(TPCANRdMsg *mr,ros::Publisher axis_force_pub,bool display_on)
{short int x_force,y_force,z_force;short int torque_x,torque_y,torque_z;if(0x50a==mr->Msg.ID ){x_force=mr->Msg.DATA[1];x_force<<=8;x_force|=mr->Msg.DATA[0];y_force=mr->Msg.DATA[3];y_force<<=8;y_force|=mr->Msg.DATA[2];z_force=mr->Msg.DATA[5];z_force<<=8;z_force|=mr->Msg.DATA[4];force_x_float=x_force/256.0;force_y_float=y_force/256.0;force_z_float=z_force/256.0;if(display_on) ROS_INFO(" Force x:%8.4f, y:%8.4f, z:%8.4f",force_x_float,force_y_float,force_z_float);}if(0x50b==mr->Msg.ID ){torque_x=mr->Msg.DATA[1];torque_x<<=8;torque_x|=mr->Msg.DATA[0];torque_y=mr->Msg.DATA[3];torque_y<<=8;torque_y|=mr->Msg.DATA[2];torque_z=mr->Msg.DATA[5];torque_z<<=8;torque_z|=mr->Msg.DATA[4];torque_x_float=torque_x/256.0;torque_y_float=torque_y/256.0;torque_z_float=torque_z/256.0;if(display_on) ROS_INFO("Torque x:%8.4f, y:%8.4f, z:%8.4f",torque_x_float,torque_y_float,torque_z_float);}}void bhand_axis_force_warning(notice_pub_sub *notice_test,float force_x,float force_y,float force_z,float torque_x,float torque_y,float torque_z)
{id_data_msgs::ID_Data notice_data_pub;if(fabs(force_x)>force_threshold || fabs(force_y)>force_threshold || fabs(force_z)>force_threshold){notice_data_clear(¬ice_data_pub);notice_data_pub.id=5;notice_data_pub.data[0]=13;//notice_test->notice_pub_sub_pulisher(notice_data_pub);//ROS_WARN(" Force threshhold:%8.4f BHAND COLLISION!",force_threshold);//ROS_INFO(" Force x:%8.4f, y:%8.4f, z:%8.4f",force_x_float,force_y_float,force_z_float);if(torque_x<=left_torque_x_threshold ){notice_data_clear(¬ice_data_pub);notice_data_pub.id=5;notice_data_pub.data[0]=12;//notice_test->notice_pub_sub_pulisher(notice_data_pub);//ROS_WARN("LEFT arm Axis force sensor detect BHAND COLLISION!");usleep(10000);}if(torque_x>=right_torque_x_threshold ){notice_data_clear(¬ice_data_pub);notice_data_pub.id=5;notice_data_pub.data[0]=11;//notice_test->notice_pub_sub_pulisher(notice_data_pub);//ROS_WARN("RIGHT Axis force sensor detect BHAND COLLISION!");usleep(10000);}}if(fabs(torque_x)>torque_threshold || fabs(torque_y)>torque_threshold || fabs(torque_z)>torque_threshold){notice_data_clear(¬ice_data_pub);notice_data_pub.id=5;notice_data_pub.data[0]=13;//notice_test->notice_pub_sub_pulisher(notice_data_pub);//ROS_WARN("Torque threshhold:%8.4f BHAND COLLISION!",torque_threshold);//ROS_INFO("Torque x:%8.4f, y:%8.4f, z:%8.4f",torque_x_float,torque_y_float,torque_z_float);}if(fabs(torque_x)>torque_danger || fabs(torque_y)>torque_danger || fabs(torque_z)>torque_danger || fabs(force_x)>force_danger || fabs(force_y)>force_danger || fabs(force_z)>force_danger){notice_data_clear(¬ice_data_pub);notice_data_pub.id=5;notice_data_pub.data[0]=14;notice_test->notice_pub_sub_pulisher(notice_data_pub);ROS_ERROR("Axis force sensor detect BHAND DANGER!");ROS_INFO(" Force x:%8.4f, y:%8.4f, z:%8.4f",force_x_float,force_y_float,force_z_float);ROS_INFO("Torque x:%8.4f, y:%8.4f, z:%8.4f",torque_x_float,torque_y_float,torque_z_float);}}void print_message(TPCANMsg *m)
{int i;//print RTR, 11 or 29, CAN-Id and datalengthprintf("receivetest: %c %c 0x%08x %1d ",((m->MSGTYPE & MSGTYPE_RTR) ? 'r' : 'm') -((m->MSGTYPE ) ? 0x20 : 0),(m->MSGTYPE & MSGTYPE_EXTENDED) ? 'e' : 's',m->ID,m->LEN);//don't print any telegram contents for remote framesif (!(m->MSGTYPE & MSGTYPE_RTR))for (i = 0; i < m->LEN; i++)printf("%02x ", m->DATA[i]);//printf("%3d ", m->DATA[i]);//decimal format print.printf("\n");
}void print_message_ex(TPCANRdMsg *mr)
{printf("%u.%3u ", mr->dwTime, mr->wUsec);print_message(&mr->Msg);
}// exit handler
void do_exit(void *file,HANDLE h,int error)
{//Must close h handle firstly,then close file using dlcloseif (h) {funCAN_Close(h);}printf("\nCAN Bus test: finished (%d).\n\n", error);//after call the target function in ELF object,close it using dlclosedlclose(file);exit(error);
}// the signal handler for manual break Ctrl-C
void signal_handler(int signal)
{do_exit(libm_handle,pcan_handle,0);
}// what has to be done at program start
void init()
{/* install signal handlers */signal(SIGTERM, signal_handler);signal(SIGINT, signal_handler);
}void notice_data_clear(id_data_msgs::ID_Data *test)
{test->id=0;for(int i=0;i<8;i++) test->data[i]=0;
}
P.S. 文中或代码中有关/notice话题的部分详见博客内该博文http://blog.csdn.net/hookie1990/article/details/54138234
转载于:https://www.cnblogs.com/siahekai/p/11000797.html
基于libpcan库can总线操作的Barrett 机械手控制及腕部六维力传感器驱动相关推荐
- 基于tkinter库txt文件操作的Python界面化图书管理系统
Python图书管理系统 一.简介 该系统主要通过tkinter库实现图书管理系统,该项目包含对文件的操作,对字符串解析的python知识点 二.系统实现功能介绍及使用方法 1.登录功能: 不同用户通 ...
- 大疆开发板A型基于HAL库驱动M3508直流无刷电机及PID控制
1.首先,我们先了解一下大疆开发板A型的资料,官方有提供 官网:RoboMaster 机甲大师赛 芯片型号STM32F427IIH6 2.了解M3508直流无刷电机的资料,官网有提供 3.于是我找到 ...
- 第九章 AT32F403A基于V2库串口 dma接收不定长数据
目录 概述 硬件 DMA 软件 流程 初始化 初始化代码: 中断服务函数: DMA1通道5设置函数:(重新使能通道) DMA1通道4发送函数:(设置dma长度和内存地址) 测试 最后 概述 本文主要是 ...
- 正点原子STM32(基于HAL库)0
目录 开发环境搭建与使用 常用开发工具简介 MDK 安装 仿真器驱动安装 CH340 USB 虚拟串口驱动安装 使用MDK5 编译例程 使用串口下载程序 使用DAP 下载与调试程序 使用DAP 下载程 ...
- 【pythonCAN】基于Kvaser开发CAN总线分析工具
day1 对象: dbc文件(客户提供/矩阵图转) 矩阵图 报文 任务: 解析机器发送的报文 将报文信息与dbc文件进行对比 得出对比结果 day2 总 ...
- 基于libmad库的MP3解码简析
基于libmad库的MP3解码简析 MAD (libmad)是一个开源的高精度 MPEG 音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3). ...
- python输入参数改变图形_Python基于Tensor FLow的图像处理操作详解
本文实例讲述了Python基于Tensor FLow的图像处理操作.分享给大家供大家参考,具体如下: 在对图像进行深度学习时,有时可能图片的数量不足,或者希望网络进行更多的学习,这时可以对现有的图片数 ...
- python加密库_python基于pyDes库实现des加密的方法
本文实例讲述了python基于pyDes库实现des加密的方法.分享给大家供大家参考,具体如下: 下载及简介地址:https://twhiteman.netfirms.com/des.html 如需要 ...
- 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线...
重温.NET下Assembly的加载过程 最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后发现 ...
最新文章
- Silverlight与数据库的三种互操作[源代码]
- WINCE6.0+S3C6410下的DM9000A驱动
- 写给老宅程序员的一些建议
- 第五节 面向对象相关特性
- Scala开发教程(16): 没有“break”和“continue”的日子
- 90-70-010-源码-CUBE查询-源码
- 网络 TCP三次握手及滑动窗口
- [转载] C++学习之异常处理详解
- PCA,ZCA,ICA,白化,稀疏编码和自编码器
- 从个人应用到企业级应用——数字钱包市场调研分析报告
- jrtplib库移植到android上
- 九连环问题c语言程序,九连环游戏的算法源代码
- IOS开发—系统定位
- Python:科赫曲线绘制雪花
- windows查找文件内容和文件内容
- PC端如何使用ITunes无线连接ios手机
- 2020年计算机应用基础试题二,2020年计算机应用基础试题及答案
- 游戏辅助制作核心--植物大战僵尸逆向之阳光生产加速实现满天星(四)1
- mysql 查询去除小数点_SQL查询金额去掉小数点后面的零
- SQL99中的natural join 和 using