创建ROS包,包名redwall_arm ,通过自定义的消息,将手柄的数据发布 msg/ joycontrol.msg,内容如下,分别对应罗技手柄的按钮和遥杆轴。
int32 button1
int32 button2
int32 button3
int32 button4
int32 button5
int32 button6
int32 button7
int32 button8
float32 axis0
float32 axis1
float32 axis2
float32 axis3
float32 axis4
float32 axis5
有了消息类型,接下来就是手柄数据处理和发送ros节点,命名为logitech_pub.cpp,导师要求主从模式,即两个手柄,当主手柄控制的时候,从手柄无法操作,当只插入了一个手柄的usb时,默认为主手柄。文件开头的宏定义为手柄按钮和摇杆轴的地址,可以通过jstest-gtk查看。
#include "ros/ros.h"
#include "std_msgs/Int32.h"
#include "redwall_arm/joycontrol.h"
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/input.h>
#include <linux/joystick.h>
#include <errno.h>
#include <pthread.h>
#include <math.h>
#define AXES0 0x00
#define AXES1 0x01
#define AXES2 0x02
#define AXES3 0x03
#define AXES4 0x04
#define AXES5 0x05
#define BUTTON1 0x01
#define BUTTON2 0x02
#define BUTTON3 0x03
#define BUTTON4 0x04
#define BUTTON5 0x05
#define BUTTON6 0x06
#define BUTTON7 0x07
#define BUTTON8 0x08
/*手柄模式,单主手柄mode=1,双手柄mode=2*/
int mode;
/* 主手柄 */
int axes0, axes1, axes2, axes3, axes4, axes5;
int button1, button2, button3, button4, button5, button6, button7, button8;
struct js_event js_master;
int js_fd_master;
/* 从手柄 */
int axes0_, axes1_ , axes2_, axes3_, axes4_, axes5_;
int button1_, button2_, button3_, button4_, button5_, button6_, button7_, button8_;
struct js_event js_support;
int js_fd_support;
void * read_js_data_master(void *);
void * read_js_data_support(void *);
using namespace std;
int main(int argc, char **argv)
{
ros::init(argc, argv,"joymsg_pub");
ros::NodeHandle n1;
ros::Publisher number_publisher = n1.advertise<redwall_arm::joycontrol>("/joycontrol_topic",100);
ros::Rate loop_rate(100);
js_fd_master = open("/dev/input/js0", O_RDONLY);
if (js_fd_master < 0)
{
perror("打开js0失败,主手柄不可用");
return -1;
}
else
{
/* 创建线程并使主线程与子线程分离,子线程结束后,资源自动回收 */
pthread_t id0;
pthread_create(&id0,NULL,read_js_data_master,NULL);
pthread_detach(id0);
}
js_fd_support = open("/dev/input/js1", O_RDONLY);
if (js_fd_support < 0)
{
perror("打开js1失败,从手柄不可用");
mode = 1;
}
else
{
mode = 2;
pthread_t id1;
pthread_create(&id1,NULL,read_js_data_support,NULL);
pthread_detach(id1);
}
while (ros::ok())
{
redwall_arm::joycontrol msg;
/* 主从手柄控制 */
if(mode==2)
{
if ((button1==0) && (button2==0) && (button3==0) && (button4==0) && (button5==0) && (button6==0) && (button7==0) && (button8==0) && (axes0==0) && (axes1==0))
{
msg.button1 = button1_;
msg.button2 = button2_;
msg.button3 = button3_;
msg.button4 = button4_;
msg.button5 = button5_;
msg.button6 = button6_;
msg.button7 = button7_;
msg.button8 = button8_;
msg.axis0 = axes0_;
msg.axis1 = axes1_;
msg.axis2 = axes2_;
msg.axis3 = axes3_;
msg.axis4 = axes4_;
msg.axis5 = axes5_;
}
else
{
msg.button1 = button1;
msg.button2 = button2;
msg.button3 = button3;
msg.button4 = button4;
msg.button5 = button5;
msg.button6 = button6;
msg.button7 = button7;
msg.button8 = button8;
msg.axis0 = axes0;
msg.axis1 = axes1;
msg.axis2 = axes2;
msg.axis3 = axes3;
msg.axis4 = axes4;
msg.axis5 = axes5;
}
}
/* 单主手柄 */
else if(mode==1)
{
msg.button1 = button1;
msg.button2 = button2;
msg.button3 = button3;
msg.button4 = button4;
msg.button5 = button5;
msg.button6 = button6;
msg.button7 = button7;
msg.button8 = button8;
msg.axis0 = axes0;
msg.axis1 = axes1;
msg.axis2 = axes2;
msg.axis3 = axes3;
msg.axis4 = axes4;
msg.axis5 = axes5;
}
ROS_INFO("%f %f %f %f %f %f",msg.axis0,msg.axis1,msg.axis2,msg.axis3,msg.axis4,msg.axis5);
/* 发布消息 */
number_publisher.publish(msg);
/* 循环所有的操作 */
ros::spinOnce();
loop_rate.sleep();
}
return 0;
}
void *read_js_data_master(void *)
{
while(1)
{
int len = read(js_fd_master, &js_master, sizeof(struct js_event));
if (len < 0)
{
perror("读取js失败");
return 0 ;
}
int value = js_master.value;
int type = js_master.type;
int number = js_master.number;
if (type == JS_EVENT_BUTTON)
{
if( number == BUTTON1 )
{
button1=value;
}
if( number == BUTTON2 )
{
button2=value;
}
if( number == BUTTON3 )
{
button3=value;
}
if( number == BUTTON4 )
{
button4=value;
}
if( number == BUTTON5 )
{
button5=value;
}
if( number == BUTTON6 )
{
button6=value;
}
if( number == BUTTON7 )
{
button7=value;
}
if( number == BUTTON8 )
{
button8=value;
}
}
else if (type == JS_EVENT_AXIS)
{
if( number == AXES1 )
{
axes1 = value;
}
if( number == AXES0 )
{
axes0 = value ; // 手柄有误差,可用jstest查看
}
if( number == AXES2 )
{
axes2 = value;
}
if( number == AXES3 )
{
axes3 = value ; // 手柄有误差,可用jstest查看
}
if( number == AXES4 )
{
axes4 = value;
}
if( number == AXES5 )
{
axes5 = value ; // 手柄有误差,可用jstest查看
}
}
}
}
void *read_js_data_support(void *)
{
while(1)
{
int len = read(js_fd_support, &js_support, sizeof(struct js_event));
if (len < 0)
{
perror("读取js失败");
return 0 ;
}
int value = js_support.value;
int type = js_support.type;
int number = js_support.number;
if (type == JS_EVENT_BUTTON)
{
if( number == BUTTON1 )
{
button1_=value;
}
if( number == BUTTON2 )
{
button2_=value;
}
if( number == BUTTON3 )
{
button3_=value;
}
if( number == BUTTON4 )
{
button4_=value;
}
if( number == BUTTON5 )
{
button5_=value;
}
if( number == BUTTON6 )
{
button6_=value;
}
if( number == BUTTON7 )
{
button7_=value;
}
if( number == BUTTON8 )
{
button8_=value;
}
}
else if (type == JS_EVENT_AXIS)
{
if( number == AXES1 )
{
axes1_ = value;
}
if( number == AXES0 )
{
axes0_ = value ; // 手柄有误差,可用jstest查看
}
if( number == AXES2 )
{
axes2_ = value;
}
if( number == AXES3 )
{
axes3_ = value ; // 手柄有误差,可用jstest查看
}
if( number == AXES4 )
{
axes4_ = value;
}
if( number == AXES5 )
{
axes5_ = value ; // 手柄有误差,可用jstest查看
}
}
}
}
有了手柄数据,接下需要的是 1 .ROS上位机运行服务器,用于将手柄数据转变成机械臂对应轴的运动和转速。2.运行在beaglebone控制板上的客户端,通过tcp套接字连接上述服务器,接受轴的信息和转速信息,然后转化成pwm信息控制步进电机的运动。
服务器命名为 server_redwall_arm.cpp,功能为创建套接字通信服务器端,等待客户端的连接,一旦有客户端连接,就将手柄通过上述自定义消息发布的消息在回调函数中处理,转换成转速信息,然后通过套接字发送给客户端。
#include <ros/ros.h>
#include <sensor_msgs/JointState.h>
#include <tf/transform_broadcaster.h>
#include "redwall_arm/joycontrol.h"
#include <sstream>
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <signal.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#define PORT 7788
#define MAX_LINE 10
#define MAX_BUF 300
// 运动模式
float lumbar_; // 腰部回转
float big_arm_; // 大手臂
float small_arm_; // 小手臂
float wrist_; // 手腕
float hand_; // 手抓
float finger_; // 末端手指执行器
// 运动速度
int lumbar;
int big_arm;
int small_arm;
int wrist;
int hand;
int finger;
/* 存储的结构体 p2*/
struct vel_data
{
char lumbar_vel[9];
char big_arm_vel[9];
char small_arm_vel[9];
char wrist_vel[9];
char hand_vel[9];
char finger_vel[9];
};
/* 客户端套接字文件描述符和地址,端口 */
typedef struct MySocketInfo{
int socketCon;
char *ipaddr;
uint16_t port;
}_MySocketInfo;
/* 数据收发结构体 */
struct vel_data p2;
char writebuf[sizeof(p2)];
/* 客户端连接所用数据的存储数组 */
struct MySocketInfo arrConSocket[10];
int conClientCount = 0; // 连接的客户端个数
/* 客户端连接所用套接字的存储数组 */
pthread_t arrThrReceiveClient[10];
int thrReceiveClientCount = 0; // 接受数据线程个数
/* 控制信息处理用于运动 */
void joymessageCallback(const redwall_arm::joycontrol::ConstPtr& cmd_)
{
lumbar_ = cmd_->axis4;
big_arm_ = cmd_->axis1;
small_arm_ = cmd_->axis3;
wrist_ = cmd_->axis0;
hand_ = cmd_->axis5;
finger_= cmd_->axis2;
lumbar = int(lumbar_/(32767));
big_arm = int(big_arm_*10/(-32767));
small_arm = int(small_arm_*10/(-32767));
wrist = int(wrist_*10/(32767));
hand = int(hand_/(-32767));
finger = int(finger_*10/(32767));
}
/* SOCKET服务器端处理客户端连接函数 */
void *fun_thrAcceptHandler(void *socketListen)
{
while(1)
{
/* accept函数主要用于服务器端,建立好连接后,它返回的一个新的套接字
* 此后,服务器端即可使用这个新的套接字与该客户端进行通信,而原本的套接字则继续用于监听其他客户端的连接请求。 */
int sockaddr_in_size = sizeof(struct sockaddr_in);
struct sockaddr_in client_addr;
int _socketListen = *((int *)socketListen);
int socketCon = accept(_socketListen, (struct sockaddr *)(&client_addr), (socklen_t *)(&sockaddr_in_size));
if(socketCon < 0){
printf("连接失败\n");
}else{
printf("连接成功 ip: %s:%d\r\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port);
}
printf("连接套接字为:%d\n",socketCon);
/* 开启新的通讯线程,负责同连接上来的客户端进行通讯 */
_MySocketInfo socketInfo; // 用于保存客户端套接字的信息
socketInfo.socketCon = socketCon;
socketInfo.ipaddr = inet_ntoa(client_addr.sin_addr);
socketInfo.port = client_addr.sin_port;
arrConSocket[conClientCount] = socketInfo;
conClientCount++;
printf("连接了%d个用户\n",conClientCount);
//让进程休息1秒
sleep(1);
}
}
/* 判断线程是否被杀死 */
int checkThrIsKill(pthread_t thr)
{
/* 传递的pthread_kill的signal参数为0时,用这个保留的信号测试线程是否存在 */
int res = 1;
int res_kill = pthread_kill(thr,0);
if(res_kill == 0){
res = 0;
}
return res;
}
/* 主函数主要用于消息订阅和套接字通信 */
int main(int argc, char** argv)
{
ros::init(argc, argv, "server_redwall_arm");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("/joycontrol_topic", 100, joymessageCallback); // 订阅imagefeedorder.msg话题,名称为"/img_feedorder
printf("开始socket\n");
/* 创建TCP连接的Socket套接字 */
int socketListen = socket(AF_INET, SOCK_STREAM, 0);
if(socketListen < 0)
{
printf("创建TCP套接字失败\n");
exit(-1);
}
else{
printf("创建套接字成功\n");
}
/* 绑定服务器端口地址信息 */
struct sockaddr_in server_addr; // struct sockaddr_in是已经声明了的结构名
bzero(&server_addr,sizeof(struct sockaddr_in)); // 等价于memset(server_addr,0,sizeof(struct sockaddr_in));清零操作
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY); // 这里地址使用全0,即所有可能的地址
server_addr.sin_port=htons(PORT); // htons一般是转换端口号为整数
if(bind(socketListen, (struct sockaddr *)&server_addr,sizeof(struct sockaddr)) != 0)
{
perror("绑定ip地址,端口号失败\n");
exit(-1);
}
else{
printf("绑定ip地址,端口号成功\n");
}
/* 开始监听相应的端口,最大不超过10个连接 */
if(listen(socketListen, 10) != 0){
printf("开启监听失败\n");
exit(-1);
}else{
printf("开启监听成功\n");
}
/* 创建一个子线程用于接受连接客户端连接 */
pthread_t thrAccept;
pthread_create(&thrAccept,NULL,fun_thrAcceptHandler,&socketListen);
/* 实时发送数据 */
while(ros :: ok())
{
ros::spinOnce();
/***** 获取转速命令信息并保存到结构体p2中 *****/
sprintf(p2.lumbar_vel, "%d", lumbar);
sprintf(p2.big_arm_vel, "%d", big_arm);
sprintf(p2.small_arm_vel, "%d", small_arm);
sprintf(p2.wrist_vel, "%d", wrist);
sprintf(p2.hand_vel, "%d", hand);
sprintf(p2.finger_vel, "%d", finger);
/***** 判断线程存活多少 ******/
for(int i=0;i<thrReceiveClientCount;i++)
{
if(checkThrIsKill(arrThrReceiveClient[i]) == 1)
{
printf("有个线程被杀了\n");
thrReceiveClientCount--;
}
}
// printf("当前有接受数据线程多少个:%d\n",thrReceiveClientCount);
/***** 判断客户端连接和数据发送是否成功 *****/
if(conClientCount <= 0)
{
printf("没有客户端连接\n");
}
else
{
for(int i=0; i<conClientCount; i++)
{
bzero(writebuf,sizeof(writebuf)); // 清零writebuf
memcpy(writebuf,&p2,sizeof(p2)); // 复制p2数据到writebuf
unsigned int sendMsg_len = write(arrConSocket[i].socketCon, writebuf, sizeof(p2)); //返回值:写入文档的字节数(成功);-1(出错)
if(sendMsg_len > 0)
{
// printf("向%s:%d发送成功\n",arrConSocket[i].ipaddr,arrConSocket[i].port);
}else{
printf("向%s:%d发送失败\n",arrConSocket[i].ipaddr,arrConSocket[i].port);
}
}
}
// 测试的时候没加这个延时,导致数据收发不同步
usleep(1000);
}
/* 关闭原始套接字 */
close(socketListen);
return 0;
}
接下来是客户端,由于bbb板子上一共有3组pwm口,一组有两个,但是这两个之间的频率在配置的时候不能相差太大,而且本次控制的机械臂具有5个自由度,加上一个末端舵机控制的执行器,所以用到了两个beaglebone控制板,分别控制三个自由度。两个beaglebone同时连接主机的时候,ip地址都是192.168.7.2会产生冲突,所以修改其中之一ip地址为192.168.0.2。他们分别运行一个客户端如下: client_redwall_arm_0.c
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <math.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#define p813 "/sys/devices/ocp.3/pwm_test_P8_13.12/"
#define p914 "/sys/devices/ocp.3/pwm_test_P9_14.13/"
#define p921 "/sys/devices/ocp.3/pwm_test_P9_21.14/"
#define GPIO_DIR "/sys/class/gpio/"
#define pwm_path "/sys/devices/bone_capemgr.9/slots"
#define PORT 7788
#define ADDR "192.168.0.1"
#define MAX_LINE 10
#define LINE 5
/* p1<--p2*/
struct vel_data
{
char lumbar_vel[9];
char big_arm_vel[9];
char small_arm_vel[9];
char wrist_vel[9];
char hand_vel[9];
char finger_vel[9];
};
struct vel_data p1;
char recvbuf[sizeof(p1)];
int lumbar;
int big_arm;
int small_arm;
int wrist;
int hand;
int finger;
int zero = 0; // 控制电机方向,右转
int one = 1; // 控制电机方向,左转
typedef struct MySocketInfo
{
int socketCon;
unsigned long ipaddr;
unsigned short port;
}_MySocketInfo;
/*SOCKET客户端处理接受数据函数*/
void *fun_thrReceiveHandler(void *socketCon)
{
while(1)
{
/* 保存目标套接字信息 */
int _socketCon = *((int *)socketCon);
bzero(recvbuf, sizeof(p1));
unsigned int buffer_length = read(_socketCon,recvbuf,sizeof(p1));
if(buffer_length == 0){
printf("服务器端异常关闭\n");
exit(-1);
}else if(buffer_length < 0){
printf("接受客户端数据失败\n");
break;
}
recvbuf[buffer_length] = '\0';
/* 将接收到的速度控制信息以p1结构体格式解码*/
memcpy(&p1,recvbuf,sizeof(recvbuf));
/* 把字符串转换成整型 */
lumbar = atoi(p1.lumbar_vel);
big_arm = atoi(p1.big_arm_vel);
small_arm = atoi(p1.small_arm_vel);
wrist = atoi(p1.wrist_vel);
hand = atoi(p1.hand_vel);
finger = atoi(p1.finger_vel);
printf("lumbar=%d big_arm=%d small_arm =%d wrist=%d,hand=%d finger=%d\n",lumbar,big_arm,small_arm,wrist,hand,finger);
}
printf("退出接收服务器数据线程\n");
return NULL;
}
/* 设置gpio函数 */
int set_gpio(void)
{
FILE *stream1 = fopen(GPIO_DIR"gpio44/direction","r+");
/*如果打开文件失败则先加载*/
if(stream1==NULL)
{
stream1=fopen(GPIO_DIR"export","w");
fwrite("44",sizeof(int),2,stream1);
fclose(stream1);
stream1=fopen(GPIO_DIR"gpio44/direction","r+");
}
/* P8.12端口为输出*/
fwrite("out",sizeof(char),3,stream1);
fclose(stream1);
FILE *stream2=fopen(GPIO_DIR"gpio45/direction","r+");
/*如果打开文件失败则打开相应端口*/
if (stream2==NULL)
{
stream2=fopen(GPIO_DIR"export","w");
fwrite("45",sizeof(int),2,stream2);
fclose(stream2);
stream2=fopen(GPIO_DIR"gpio45/direction","r+");
}
/* P8.11端口为输出*/
fwrite("out",sizeof(char),3,stream2);
fclose(stream2);
/* 设置GPIO70-->P8-45为输入模式 */
FILE *stream3 = fopen(GPIO_DIR"gpio26/direction","r+");
if (stream3==NULL)
{
stream3 = fopen(GPIO_DIR"export","w");
fwrite("26",sizeof(int),2,stream3);
fclose(stream3);
stream3 = fopen(GPIO_DIR"gpio26/direction","r+");
}
fwrite("out",sizeof(char),3,stream3);
fclose(stream3);
FILE *stream4= fopen(p921"polarity","r+");
if(!stream4)
{
printf("p921的polarity打开失败\n");
return ;
}
char buf3[7];
/* 舵机不需要方向信号,所以不必写入gpio26,但是必须给定默认polarity=0才行 */
sprintf(buf3,"%d",zero);
fwrite(buf3,sizeof(int),3,stream4);
fclose(stream4);
return 0;
}
/* 加载设备树函数 */
int write_tree(void)
{
int fd = open(pwm_path,O_WRONLY);
if(fd < 0){
printf("打开slots失败\n");
}
write(fd,"am33xx_pwm",10);
write(fd,"bone_pwm_P8_13",14);
write(fd,"bone_pwm_P9_14",14);
write(fd,"bone_pwm_P9_21",14);
close(fd);
return 0;
}
/***************************/
/* p8电机启动函数 */
int wrist_motor_start(int full,int half, int c)
{
FILE *x= fopen(p813"period","r+");
if(!x)
{
printf("p813的period写入失败!\n");
return ;
}
char buf15[7];
sprintf(buf15,"%d",full);
fwrite(buf15,sizeof(int),3,x);
fclose(x);
FILE *y= fopen(p813"duty","r+");
if(!y)
{
printf("p813的duty写入失败!\n");
return ;
}
char buf16[7];
sprintf(buf16,"%d",half);
fwrite(buf16,sizeof(int),3,y);
fclose(y);
FILE *z= fopen(GPIO_DIR"gpio44/value","r+");
if(!z)
{
printf("gpio44的value写入失败\n");
return ;
}
char buf17[7];
sprintf(buf17,"%d",c);
fwrite(buf17,sizeof(int),3,z);
fclose(z);
}
/* p8电机停止函数 */
int wrist_motor_stop(void)
{
/* 停止所有电机 */
int full = 10000000;
FILE *x2=fopen(p813"period","r+"); // 周期
char bufh10[7];
sprintf(bufh10,"%d",full);
fwrite(bufh10,sizeof(int),3,x2);
fclose(x2);
FILE *y2= fopen(p813"duty","r+"); //占空比
char bufh11[7];
sprintf(bufh11,"%d",full);
fwrite(bufh11,sizeof(int),3,y2);
fclose(y2);
return 0;
}
/* p914电机启动函数 */
int hand_motor_start(int full,int half, int c)
{
FILE *x3= fopen(p914"period","r+");
if(!x3)
{
printf("p914的period打开失败\n");
return ;
}
char buf111[7];
sprintf(buf111,"%d",full);
fwrite(buf111,sizeof(int),3,x3);
fclose(x3);
FILE *y3= fopen(p914"duty","r+");
if(!y3)
{
printf("p914的duty打开失败\n");
return ;
}
char buf222[7];
sprintf(buf222,"%d",half);
fwrite(buf222,sizeof(int),3,y3);
fclose(y3);
FILE *z3= fopen(GPIO_DIR"gpio45/value","r+");
if(!z3)
{
printf("gpio45/value打开失败\n");
return ;
}
char buf3[7];
sprintf(buf3,"%d",c);
fwrite(buf3,sizeof(int),3,z3);
fclose(z3);
}
/* p914电机停止函数 */
int hand_motor_stop(void)
{
/* 停止所有电机 */
int full = 1000000;
FILE *x4=fopen(p914"period","r+"); //周期
char bufh12[7];
sprintf(bufh12,"%d",full);
fwrite(bufh12,sizeof(int),3,x4);
fclose(x4);
FILE *y4= fopen(p914"duty","r+"); //占空比
char bufh13[7];
sprintf(bufh13,"%d",full);
fwrite(bufh13,sizeof(int),3,y4);
fclose(y4);
return 0;
}
/* p921电机启动函数 */
int finger_motor_start(int full,int half, int c)
{
FILE *x5= fopen(p921"period","r+");
if(!x5)
{
printf("p921的period打开失败\n");
return ;
}
char buf111[7];
sprintf(buf111,"%d",full);
fwrite(buf111,sizeof(int),3,x5);
fclose(x5);
FILE *y5= fopen(p921"duty","r+");
if(!y5)
{
printf("p921的duty打开失败\n");
return ;
}
char buf222[7];
sprintf(buf222,"%d",half);
fwrite(buf222,sizeof(int),3,y5);
fclose(y5);
}
/* p921电机停止函数 */
int finger_motor_stop(void)
{
/* 停止所有电机 */
int full = 20000000;
FILE *x6=fopen(p921"period","r+"); //周期
char bufh12[7];
sprintf(bufh12,"%d",full);
fwrite(bufh12,sizeof(int),3,x6);
fclose(x6);
FILE *y6= fopen(p921"duty","r+"); //占空比
char bufh13[7];
sprintf(bufh13,"%d",full);
fwrite(bufh13,sizeof(int),3,y6);
fclose(y6);
return 0;
}
/***************************/
void wrist_motor(void)
{
while(1)
{
if(wrist == 0)
{
/* 电机静止,占空比=周期=500000 */
//motor_stop(p813);
wrist_motor_stop();
}
else
{
int full = 10000000 - 10000 * abs(wrist);
int half = full / 2;
int c = wrist > 0 ? 1 : 0; // gpio方向值
//motor_start(full, half, c, p813,"gpio44/value");
wrist_motor_start(full, half, c);
}
}
}
void hand_motor(void)
{
while(1)
{
if(hand == 0)
{
/* 电机静止,占空比=周期=500000 */
//motor_stop(p914);
hand_motor_stop();
}
else
{
int full = 1000000 - 10000 * abs(hand);
int half = full / 2;
int c = hand > 0 ? 1 : 0; // gpio方向值
//motor_start(full, half, c, p914, "gpio45/value");
hand_motor_start(full, half, c);
}
}
}
void finger_motor(void)
{
while(1)
{
if(finger == 0)
{
//motor_stop(p921);
finger_motor_stop();
}
else
{
int full = 20000000;
int half = (100000*finger + 1500000);
int c = zero; // 舵机方向确定为0
//motor_start(full, half, c, p921, "");
finger_motor_start(full, half, c);
}
}
}
/* 主函数完成接受和发送数据 */
int main(int argc, char **argv)
{
/***** 加载设备树 *****/
write_tree();
/***** 配置GPIO用于控制电机转向 *****/
set_gpio();
/***** 开始套接字通信 *****/
printf("开始socket\n");
int socketCon = socket(AF_INET, SOCK_STREAM, 0);
if(socketCon < 0)
{
printf("创建TCP连接套接字失败\n");
exit(-1);
}
/***** 绑定服务器端口地址信息 *****/
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
if(inet_pton(AF_INET, ADDR, &server_addr.sin_addr) !=1)
{
printf("ipv4地址转换失败");
}
/***** 连接服务器 *****/
int res_con = connect(socketCon,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr));
if(res_con != 0)
{
printf("连接服务器失败\n");
exit(-1);
}
printf("连接成功,连接结果为:%d\n",res_con);
/***** 开启新的实时接受数据线程 *****/
pthread_t thrReceive;
pthread_create(&thrReceive,NULL,fun_thrReceiveHandler,&socketCon);
/***** 开启处理电机数据线程 *****/
pthread_t id1;
int ret1=pthread_create(&id1,NULL,(void *) wrist_motor,NULL); // 成功返回0,错误返回错误编号
if(ret1!=0)
{
printf("motor1线程创建失败!\n");
exit(1);
}
/***** 开启处理电机数据线程 *****/
pthread_t id2;
int ret2=pthread_create(&id2,NULL,(void *) hand_motor,NULL); // 成功返回0,错误返回错误编号
if(ret2!=0)
{
printf("motor2线程创建失败!\n");
exit(1);
}
/***** 开启处理电机数据线程 *****/
pthread_t id3;
int ret3=pthread_create(&id3,NULL,(void *) finger_motor,NULL); // 成功返回0,错误返回错误编号
if(ret3!=0)
{
printf("motor3线程创建失败!\n");
exit(1);
}
/***** 主线程阻塞等待子线程 *****/
pthread_detach(id1);
pthread_detach(id2);
pthread_detach(id3);
pthread_join(thrReceive,NULL);
/***** 关闭套接字 *****/
close(socketCon);
return 0;
}
client_redwall_arm_7.c
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <math.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#define p813 "/sys/devices/ocp.3/pwm_test_P8_13.12/"
#define p914 "/sys/devices/ocp.3/pwm_test_P9_14.13/"
#define p921 "/sys/devices/ocp.3/pwm_test_P9_21.14/"
#define GPIO_DIR "/sys/class/gpio/"
#define pwm_path "/sys/devices/bone_capemgr.9/slots"
#define PORT 7788
#define ADDR "192.168.7.1"
#define MAX_LINE 10
#define LINE 5
/* 四个轮子的转速和直线速度 p1<--p2*/
struct vel_data
{
char lumbar_vel[9];
char big_arm_vel[9];
char small_arm_vel[9];
char wrist_vel[9];
char hand_vel[9];
char finger_vel[9];
};
struct vel_data p1;
char recvbuf[sizeof(p1)];
int lumbar;
int big_arm;
int small_arm;
int wrist;
int hand;
int finger;
int zero = 0; // 控制电机方向,右转
int one = 1; // 控制电机方向,左转
typedef struct MySocketInfo
{
int socketCon;
unsigned long ipaddr;
unsigned short port;
}_MySocketInfo;
/*SOCKET客户端处理接受数据函数*/
void *fun_thrReceiveHandler(void *socketCon)
{
while(1)
{
/* 保存目标套接字信息 */
int _socketCon = *((int *)socketCon);
bzero(recvbuf, sizeof(p1));
unsigned int buffer_length = read(_socketCon,recvbuf,sizeof(p1));
if(buffer_length == 0){
printf("服务器端异常关闭\n");
exit(-1);
}else if(buffer_length < 0){
printf("接受客户端数据失败\n");
break;
}
recvbuf[buffer_length] = '\0';
/* 将接收到的速度控制信息以p1结构体格式解码*/
memcpy(&p1,recvbuf,sizeof(recvbuf));
/* 把字符串转换成整型 */
lumbar = atoi(p1.lumbar_vel);
big_arm = atoi(p1.big_arm_vel);
small_arm = atoi(p1.small_arm_vel);
wrist = atoi(p1.wrist_vel);
hand = atoi(p1.hand_vel);
finger = atoi(p1.finger_vel);
printf("lumbar=%d big_arm=%d small_arm =%d wrist=%d,hand=%d finger=%d\n",lumbar,big_arm,small_arm,wrist,hand,finger);
}
printf("退出接收服务器数据线程\n");
return NULL;
}
/* 设置gpio函数 */
int set_gpio(void)
{
FILE *stream1 = fopen(GPIO_DIR"gpio44/direction","r+");
/*如果打开文件失败则先加载*/
if(stream1==NULL)
{
stream1=fopen(GPIO_DIR"export","w");
fwrite("44",sizeof(int),2,stream1);
fclose(stream1);
stream1=fopen(GPIO_DIR"gpio44/direction","r+");
}
/* P8.12端口为输出*/
fwrite("out",sizeof(char),3,stream1);
fclose(stream1);
FILE *stream2=fopen(GPIO_DIR"gpio45/direction","r+");
/*如果打开文件失败则打开相应端口*/
if (stream2==NULL)
{
stream2=fopen(GPIO_DIR"export","w");
fwrite("45",sizeof(int),2,stream2);
fclose(stream2);
stream2=fopen(GPIO_DIR"gpio45/direction","r+");
}
/* P8.11端口为输出*/
fwrite("out",sizeof(char),3,stream2);
fclose(stream2);
/* 设置GPIO70-->P8-45为输入模式 */
FILE *stream3 = fopen(GPIO_DIR"gpio26/direction","r+");
if (stream3==NULL)
{
stream3 = fopen(GPIO_DIR"export","w");
fwrite("26",sizeof(int),2,stream3);
fclose(stream3);
stream3 = fopen(GPIO_DIR"gpio26/direction","r+");
}
fwrite("out",sizeof(char),3,stream3);
fclose(stream3);
return 0;
}
/* 加载设备树函数 */
int write_tree(void)
{
int fd = open(pwm_path,O_WRONLY);
if(fd < 0){
printf("打开slots失败\n");
}
write(fd,"am33xx_pwm",10);
write(fd,"bone_pwm_P8_13",14);
write(fd,"bone_pwm_P9_14",14);
write(fd,"bone_pwm_P9_21",14);
close(fd);
return 0;
}
/***************************/
/* p8电机启动函数 */
int lumbar_motor_start(int full,int half, int c)
{
FILE *x= fopen(p813"period","r+");
if(!x)
{
printf("p813的period写入失败!\n");
return ;
}
char buf15[7];
sprintf(buf15,"%d",full);
fwrite(buf15,sizeof(int),3,x);
fclose(x);
FILE *y= fopen(p813"duty","r+");
if(!y)
{
printf("p813的duty写入失败!\n");
return ;
}
char buf16[7];
sprintf(buf16,"%d",half);
fwrite(buf16,sizeof(int),3,y);
fclose(y);
FILE *z= fopen(GPIO_DIR"gpio44/value","r+");
if(!z)
{
printf("gpio44的value写入失败\n");
return ;
}
char buf17[7];
sprintf(buf17,"%d",c);
fwrite(buf17,sizeof(int),3,z);
fclose(z);
}
/* p8电机停止函数 */
int lumbar_motor_stop(void)
{
/* 停止所有电机 */
int full = 100000;
FILE *x2=fopen(p813"period","r+"); // 周期
char bufh10[7];
sprintf(bufh10,"%d",full);
fwrite(bufh10,sizeof(int),3,x2);
fclose(x2);
FILE *y2= fopen(p813"duty","r+"); //占空比
char bufh11[7];
sprintf(bufh11,"%d",full);
fwrite(bufh11,sizeof(int),3,y2);
fclose(y2);
return 0;
}
/* p914电机启动函数 */
int bigarm_motor_start(int full,int half, int c)
{
FILE *x3= fopen(p914"period","r+");
if(!x3)
{
printf("p914的period打开失败\n");
return ;
}
char buf111[7];
sprintf(buf111,"%d",full);
fwrite(buf111,sizeof(int),3,x3);
fclose(x3);
FILE *y3= fopen(p914"duty","r+");
if(!y3)
{
printf("p914的duty打开失败\n");
return ;
}
char buf222[7];
sprintf(buf222,"%d",half);
fwrite(buf222,sizeof(int),3,y3);
fclose(y3);
FILE *z3= fopen(GPIO_DIR"gpio45/value","r+");
if(!z3)
{
printf("gpio45/value打开失败\n");
return ;
}
char buf3[7];
sprintf(buf3,"%d",c);
fwrite(buf3,sizeof(int),3,z3);
fclose(z3);
}
/* p914电机停止函数 */
int bigarm_motor_stop(void)
{
/* 停止所有电机 */
int full = 5000000;
FILE *x4=fopen(p914"period","r+"); //周期
char bufh12[7];
sprintf(bufh12,"%d",full);
fwrite(bufh12,sizeof(int),3,x4);
fclose(x4);
FILE *y4= fopen(p914"duty","r+"); //占空比
char bufh13[7];
sprintf(bufh13,"%d",full);
fwrite(bufh13,sizeof(int),3,y4);
fclose(y4);
return 0;
}
/* p921电机启动函数 */
int smallarm_motor_start(int full,int half, int c)
{
FILE *x5= fopen(p921"period","r+");
if(!x5)
{
printf("p921的period打开失败\n");
return ;
}
char buf111[7];
sprintf(buf111,"%d",full);
fwrite(buf111,sizeof(int),3,x5);
fclose(x5);
FILE *y5= fopen(p921"duty","r+");
if(!y5)
{
printf("p921的duty打开失败\n");
return ;
}
char buf222[7];
sprintf(buf222,"%d",half);
fwrite(buf222,sizeof(int),3,y5);
fclose(y5);
FILE *z5= fopen(GPIO_DIR"gpio26/value","r+");
if(!z5)
{
printf("gpio26/value打开失败\n");
return ;
}
char buf3[7];
sprintf(buf3,"%d",c);
fwrite(buf3,sizeof(int),3,z5);
fclose(z5);
}
/* p921电机停止函数 */
int smallarm_motor_stop(void)
{
/* 停止所有电机 */
int full = 1000000;
FILE *x6=fopen(p921"period","r+"); //周期
char bufh12[7];
sprintf(bufh12,"%d",full);
fwrite(bufh12,sizeof(int),3,x6);
fclose(x6);
FILE *y6= fopen(p921"duty","r+"); //占空比
char bufh13[7];
sprintf(bufh13,"%d",full);
fwrite(bufh13,sizeof(int),3,y6);
fclose(y6);
return 0;
}
/***************************/
void lumbar_motor(void)
{
while(1)
{
if(lumbar == 0)
{
/* 电机静止,占空比=周期=500000 */
//motor_stop(p813);
lumbar_motor_stop();
}
else
{
int full = 100000;
int half = full/2;
int c = lumbar > 0 ? 1 : 0; // gpio方向值
//motor_start(full,half,c,p813,"gpio44/value");
lumbar_motor_start(full,half,c);
}
}
}
void bigarm_motor(void)
{
while(1)
{
if(big_arm == 0)
{
/* 电机静止,占空比=周期=500000 */
//motor_stop(p914);
bigarm_motor_stop();
}
else
{
int full = 5000000 - 10000 * abs(big_arm);
int half = full / 2;
int c = big_arm > 0 ? 1 : 0; // gpio方向值
//motor_start(full, half, c, p914, "gpio45/value");
bigarm_motor_start(full, half, c);
}
}
}
void smallarm_motor(void)
{
while(1)
{
if(small_arm == 0)
{
/* 电机静止,占空比=周期=500000 */
//motor_stop(p921);
smallarm_motor_stop();
}
else
{
int full = 1000000 - 1000 * abs(small_arm);
int half = full / 2;
int c = small_arm > 0 ? 1 : 0; // gpio方向值
//motor_start(full, half, c, p921, "gpio26/value");
smallarm_motor_start(full, half, c);
}
}
}
/* 主函数完成接受和发送数据 */
int main(int argc, char **argv)
{
/***** 配置GPIO用于控制电机转向 *****/
set_gpio();
/***** 加载设备树 *****/
write_tree();
/***** 开始套接字通信 *****/
printf("开始socket\n");
int socketCon = socket(AF_INET, SOCK_STREAM, 0);
if(socketCon < 0)
{
printf("创建TCP连接套接字失败\n");
exit(-1);
}
/***** 绑定服务器端口地址信息 *****/
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
if(inet_pton(AF_INET, ADDR, &server_addr.sin_addr) !=1)
{
printf("ipv4地址转换失败");
}
/***** 连接服务器 *****/
int res_con = connect(socketCon,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr));
if(res_con != 0)
{
printf("连接服务器失败\n");
exit(-1);
}
printf("连接成功,连接结果为:%d\n",res_con);
/***** 开启新的实时接受数据线程 *****/
pthread_t thrReceive;
pthread_create(&thrReceive,NULL,fun_thrReceiveHandler,&socketCon);
/***** 开启处理电机数据线程 *****/
pthread_t id1;
int ret1=pthread_create(&id1,NULL,(void *) lumbar_motor,NULL); // 成功返回0,错误返回错误编号
if(ret1!=0)
{
printf("motor1线程创建失败!\n");
exit(1);
}
/***** 开启处理电机数据线程 *****/
pthread_t id2;
int ret2=pthread_create(&id2,NULL,(void *) bigarm_motor,NULL); // 成功返回0,错误返回错误编号
if(ret2!=0)
{
printf("motor2线程创建失败!\n");
exit(1);
}
/***** 开启处理电机数据线程 *****/
pthread_t id3;
int ret3=pthread_create(&id3,NULL,(void *) smallarm_motor,NULL); // 成功返回0,错误返回错误编号
if(ret3!=0)
{
printf("motor3线程创建失败!\n");
exit(1);
}
/***** 主线程阻塞等待子线程 *****/
pthread_detach(id1);
pthread_detach(id2);
pthread_detach(id3);
pthread_join(thrReceive,NULL);
/***** 关闭套接字 *****/
close(socketCon);
return 0;
}
两个客户端的内容非常相似,仅仅是控制的关节不一样,所以用不同的pwm周期控制不同型号的电机而已。而且此客户端和服务器的可复用性非常高,套接字通信部分的代码几乎不用更改就可以运用到其他地方,仅仅要修改的只是通信内容的不同,实验室的独立驱动转向模块化小车就是如此。 最后一步,修改CMakeList.txt和package.xml ,然后使用catkin_make -DCATKIN_WHITELIST_PACKAGES=”redwall_arm”编译包,会生成两个可执行文件,也就是上面的Logitech_pub 和server_redwall_arm节点,最后分别将两个客户端发送到板子上,使用 gcc client_redwall_arm.c -lpthread 编译即可。依次运行所有节点,即可通过手柄控制真实世界机械臂的运动。