Driver:包括Action Server
和Joint_state publisher
,接收Moveit下发的数据,反馈机器人状态
Moveit:ActionClient
与Driver的Action Server
对接,Joint_state subscriber
接收Joint_state publisher
反馈信息,MoveIT function
负责任务操作
为了实现客户端和服务器进行通信,ROS定义了一些标准的通信消息,用于规范两者的工作,包括目标(Goal),反馈(Feedback)和结果(Result)。
ros wiki案例:
1 #include <ros/ros.h>
2 #include <actionlib/server/simple_action_server.h>
3 #include <actionlib_tutorials/FibonacciAction.h>
4
5 class FibonacciAction
6 {
7 protected:
8
9 ros::NodeHandle nh_;
10 actionlib::SimpleActionServer<actionlib_tutorials::FibonacciAction> as_; // NodeHandle instance must be created before this line. Otherwise strange error occurs.
11 std::string action_name_;
12 // create messages that are used to published feedback/result
13 actionlib_tutorials::FibonacciFeedback feedback_;
14 actionlib_tutorials::FibonacciResult result_;
15
16 public:
17
18 FibonacciAction(std::string name) :
19 as_(nh_, name, boost::bind(&FibonacciAction::executeCB, this, _1), false),
20 action_name_(name)
21 {
22 as_.start();
23 }
24
25 ~FibonacciAction(void)
26 {
27 }
28
29 void executeCB(const actionlib_tutorials::FibonacciGoalConstPtr &goal)
30 {
31 // helper variables
32 ros::Rate r(1);
33 bool success = true;
34
35 // push_back the seeds for the fibonacci sequence
36 feedback_.sequence.clear();
37 feedback_.sequence.push_back(0);
38 feedback_.sequence.push_back(1);
39
40 // publish info to the console for the user
41 ROS_INFO("%s: Executing, creating fibonacci sequence of order %i with seeds %i, %i", action_name_.c_str(), goal->order, feedback_.sequence[0], feedback_.sequence[1]);
42
43 // start executing the action
44 for(int i=1; i<=goal->order; i++)
45 {
46 // check that preempt has not been requested by the client
47 if (as_.isPreemptRequested() || !ros::ok())
48 {
49 ROS_INFO("%s: Preempted", action_name_.c_str());
50 // set the action state to preempted
51 as_.setPreempted();
52 success = false;
53 break;
54 }
55 feedback_.sequence.push_back(feedback_.sequence[i] + feedback_.sequence[i-1]);
56 // publish the feedback
57 as_.publishFeedback(feedback_);
58 // this sleep is not necessary, the sequence is computed at 1 Hz for demonstration purposes
59 r.sleep();
60 }
61
62 if(success)
63 {
64 result_.sequence = feedback_.sequence;
65 ROS_INFO("%s: Succeeded", action_name_.c_str());
66 // set the action state to succeeded
67 as_.setSucceeded(result_);
68 }
69 }
70
71
72 };
73
74
75 int main(int argc, char** argv)
76 {
77 ros::init(argc, argv, "fibonacci");
78
79 FibonacciAction fibonacci("fibonacci");
80 ros::spin();
81
82 return 0;
83 }
前提:
假设:
add_wayPoint()
trackMove()
改造驱动:
在CMakeLists.exe
和package.xml
中添加相关以来
find_package(catkin REQUIRED COMPONENTS
actionlib
#action的本质是基于消息的,因此,还需要添加moveit的相关消息
moveit_msgs
)
catkin_package(
INCLUDE_DIRS include
CATKIN_DEPENDS actionlib moveit_msgs
)
<build_depend>actionlibbuild_depend>
<build_depend>moveit_msgsbuild_depend>
<exec_depend>actionlibexec_depend>
<exec_depend>moveit_msgsexec_depend>
添加action server头文件
//action服务端的相关定义,请加入到驱动节点的头文件中
#include "actionlib/server/action_server.h"
//action服务端的目标控制句柄定义,与接收的目标相关联后,可以用来实现action的信息反馈等操作
#include "actionlib/server/server_goal_handle.h"
定义server对象
//定义action服务端
actionlib::ActionServer<control_msgs::FollowJointTrajectoryAction> as_;
//定义action服务端目标控制句柄
actionlib::ServerGoalHandle<control_msgs::FollowJointTrajectoryAction> goal_handle_;
//用来反馈action目标的执行情况,客户端由此可以得知服务端是否执行成功了
control_msgs::FollowJointTrajectoryResult result_;
初始化服务端对象
LwrDriver::LwrDriver():
as_(nh, "lwr/follow_joint_trajectory",
boost::bind(&LwrDriver::goalCB, this, _1),
false)
{
as_.start(); //action服务端启动
}
// nh:action所在ros节点的节点句柄
// controllers.yaml中的name和action_ns共同决定了客户端action的名字
// boost::bind(&LwrDriver::goalCB, this,_1)固定用法,不再深究,绑定了LwrDriver::goalCB函数,goalCB()是LwrDriver类成员函数,此处表示其是一个接受不action目标的回调函数。当action客户端请求一个动作时,这个动作被认为是一个目标,传递给action服务端,此时就moveit的action客户端而言,这个目标就是机械臂运动轨迹,服务端接收到目标后,会自动回调goalCB这个函数,而目标(轨迹)会作为参数传递进来。
回调函数goalCB()
//转自:https://blog.csdn.net/lingchen2348/article/details/80379166
void LwrDriver::goalCB( actionlib::ServerGoalHandle<control_msgs::FollowJointTrajectoryAction> gh)
{
// 复制一个副本,防止破坏原数据
actionlib::ActionServer<control_msgs::FollowJointTrajectoryAction>::Goal goal = *gh.getGoal(); //make a copy that we can modify
//将之前定义的句柄与传入的action目标绑定在一起,
goal_handle_ = gh;
//提取正确数据,重新进行映射
reorder_traj_joints(goal.trajectory);
//添加路点函数
Add_waypoint(goal.trajectory);
//执行轨迹函数
if(TrackMove()) //假设轨迹成功后返回值为1,失败时返回值为0
{
//告诉客户端成功了
goal_handle_.setAccepted();
result_.error_code = result_.SUCCESSFUL;
goal_handle_.setSucceeded(result_);
}
}
直接转我伟哥的,他的就是我的/手动滑稽
MyRobot类:
/*
1.主要实现使控制器管理器controller manager(以及控制器管理器内的控制器)能够访问机器人的关节状态以及机器人的命令;当控制器管理器运行时,控制器将读取机器人中的pos,vel和eff变量,并将所需的命令写入cmd变量。
2.确保pos,vel和eff变量始终具有最新的关节状态joint states是你的工作,你还需要确保写入cmd变量的内容由机器人执行。 为此,你可以向机器人类添加read()和write()函数
*/
#include
#include
#include
class MyRobot : public hardware_interface::RobotHW//继承hardware_interface::RobotHW类
{
public:
MyRobot()
{
// connect and register the joint state interface
hardware_interface::JointStateHandle state_handle_a("A", &pos[0], &vel[0], &eff[0]);
jnt_state_interface.registerHandle(state_handle_a);//读状态
hardware_interface::JointStateHandle state_handle_b("B", &pos[1], &vel[1], &eff[1]);
jnt_state_interface.registerHandle(state_handle_b);
registerInterface(&jnt_state_interface);
// connect and register the joint position interface
hardware_interface::JointHandle pos_handle_a(jnt_state_interface.getHandle("A"), &cmd[0]);//写命令
jnt_pos_interface.registerHandle(pos_handle_a);
hardware_interface::JointHandle pos_handle_b(jnt_state_interface.getHandle("B"), &cmd[1]);
jnt_pos_interface.registerHandle(pos_handle_b);
registerInterface(&jnt_pos_interface);
}
private:
hardware_interface::JointStateInterface jnt_state_interface;
hardware_interface::PositionJointInterface jnt_pos_interface;
double cmd[2];
double pos[2];
double vel[2];
double eff[2];
};
main函数
main()
{
MyRobot robot;
controller_manager::ControllerManager cm(&robot);
while (true)
{
robot.read();
cm.update(robot.get_time(), robot.get_period());
robot.write();
sleep();
}
}
controllers.yaml
controller_list:
- name: /dh_arm_controller
action_ns: "follow_joint_trajectory"
type: FollowJointTrajectory
default: true
joints:
- arm_joint_1
- arm_joint_2
- arm_joint_3
- arm_joint_4
joints:
- arm_joint_1
- arm_joint_2
- arm_joint_3
- arm_joint_4
文件级:
├── CMakeLists.txt
├── config
│ └── dh_arm_control.yaml
├── include
│ └── dhrobot_controller
│ └── arm_hardware_interface.h
├── launch
│ └── arm_ros_control.launch
├── package.xml
└── src
└── arm_hardware_interface.cpp
dh_arm_control.yaml
controller_list:
- name: /dh_arm_controller
action_ns: "follow_joint_trajectory"
type: FollowJointTrajectory
default: true
joints:
- arm_joint_1
- arm_joint_2
- arm_joint_3
- arm_joint_4
joints:
- arm_joint_1
- arm_joint_2
- arm_joint_3
- arm_joint_4
#ifndef ARM_HW_INTERFACE
#define ARM_HW_INTERFACE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class ArmHWInterface : public hardware_interface::RobotHW
{
public:
ArmHWInterface();
void read(const dynamixel_msgs::JointStateConstPtr &msg);
void publishCmd();
ros::NodeHandle getnode();
private:
hardware_interface::JointStateInterface jnt_state_interface;
hardware_interface::PositionJointInterface jnt_cmd_interface;
double cmd[5];
double pos[5];
double vel[5];
double eff[5];
controller_manager_msgs::ListControllersRequest list_req;
controller_manager_msgs::ListControllersResponse list_resp;
bool loaded_flag;
ros::NodeHandle _n;
ros::Publisher pub_cmd[5];
std_msgs::Float64 cmd_msg[5];
ros::Time start_time_;
ros::Duration start_dur_;
ros::Subscriber sub_js[5];
ros::ServiceClient client_controller_list;
};
#endif
#include "dhrobot_controller/arm_hardware_interface.h"
boost::mutex Arm_io_mutex;
ArmHWInterface::ArmHWInterface()
{
for(int i=0; i<4; i++)
{
std::string joint_cmd_name="arm_joint_";
std::string joint_state_name="arm_joint_";
std::string joint_num=boost::lexical_cast<std::string>(i+1);
joint_cmd_name.append(joint_num);
joint_state_name.append(joint_num);
joint_cmd_name.append("_controller/command");
joint_state_name.append("_controller/state");
pub_cmd[i]=_n.advertise<std_msgs::Float64>(joint_cmd_name, 1);
sub_js[i]=_n.subscribe(joint_state_name, 1, &ArmHWInterface::read, this);
client_controller_list=_n.serviceClient<controller_manager_msgs::ListControllers>("/controller_manager/list_controllers");
loaded_flag=false;
}
for(int i=0; i<4; i++)
{
std::string joint_name="arm_joint_";
std::string joint_num=boost::lexical_cast<std::string>(i+1);
joint_name.append(joint_num);
hardware_interface::JointStateHandle jnt_state_handle_tmp(joint_name, &pos[i], &vel[i], &eff[i]);
jnt_state_interface.registerHandle(jnt_state_handle_tmp);
}
registerInterface(&jnt_state_interface);
for(int i=0; i<4; i++)
{
std::string joint_name="arm_joint_";
std::string joint_num=boost::lexical_cast<std::string>(i+1);
joint_name.append(joint_num);
hardware_interface::JointHandle jnt_handle_tmp(jnt_state_interface.getHandle(joint_name), &cmd[i]);
jnt_cmd_interface.registerHandle(jnt_handle_tmp);
}
registerInterface(&jnt_cmd_interface);
start_time_=ros::Time::now();
start_dur_.operator +=(ros::Duration(3));
}
void ArmHWInterface::publishCmd()
{
if(ros::Time::now()-start_time_<start_dur_)
return;
for(int i=0; i<4; i++)
{
cmd_msg[i].data=cmd[i];
pub_cmd[i].publish(cmd_msg[i]);
}
}
void ArmHWInterface::read(const dynamixel_msgs::JointStateConstPtr &msg)
{
if(msg->motor_ids.size()<=0)
{
return;
}
if(msg->motor_ids[0]>6 || msg->motor_ids[0]<0)
{
return;
}
int msg_num=msg->motor_ids[0];
double bm=msg->current_pos-pos[msg_num];
boost::mutex::scoped_lock lock(Arm_io_mutex);
pos[msg_num]=msg->current_pos;
if(ros::Time::now()-start_time_>start_dur_)
{
if(bm>=0)
vel[msg_num]=msg->velocity;
else
vel[msg_num]=-1*msg->velocity;
}
else
vel[msg_num]=0;
if(fabs(vel[msg_num])<1.2)
vel[msg_num]=0;
eff[msg_num]=msg->load;
}
ros::NodeHandle ArmHWInterface::getnode()
{
return _n;
}
static void timespecInc(struct timespec &tick, int nsec)
{
int SEC_2_NSEC = 1e+9;
tick.tv_nsec += nsec;
while (tick.tv_nsec >= SEC_2_NSEC)
{
tick.tv_nsec -= SEC_2_NSEC;
++tick.tv_sec;
}
}
void* update_loop(void* threadarg)
{
controller_manager::ControllerManager *c=(controller_manager::ControllerManager *)threadarg;
ros::Rate r(50);
ros::Duration d(0.02);
while(ros::ok())
{
boost::mutex::scoped_lock lock(Arm_io_mutex);
c->update(ros::Time::now(), d);
lock.unlock();
r.sleep();
}
}
int main(int argc, char** argv)
{
ros::init(argc,argv,"Dhrobot Arm_hardware_interface", ros::init_options::AnonymousName);
ArmHWInterface c1;
controller_manager::ControllerManager cm(&c1);
pthread_t tid;
pthread_create(&tid, NULL, update_loop, (void *)&cm);
ROS_INFO("Arm bring up successfully");
// loop
ros::Rate r(50);
while(ros::ok())
{
boost::mutex::scoped_lock lock(Arm_io_mutex);
c1.publishCmd();
lock.unlock();
ros::spinOnce();
r.sleep();
}
return 0;
}
cmake_minimum_required(VERSION 2.8.3)
project(dhrobot_controller)
find_package(catkin REQUIRED COMPONENTS
actionlib
urdf
cmake_modules
control_msgs
control_toolbox
controller_manager
hardware_interface
joint_limits_interface
roscpp
sensor_msgs
std_msgs
trajectory_msgs
transmission_interface
)
catkin_package(
)
include_directories(
include/
${
catkin_INCLUDE_DIRS}
)
add_executable(arm_hardware_interface src/arm_hardware_interface.cpp)
target_link_libraries(arm_hardware_interface ${
catkin_LIBRARIES})
<package>
<name>dhrobot_controllername>
<version>1.0.0version>
<description>The dhrobot_controller packagedescription>
<maintainer email="[email protected]">wxwmaintainer>
<license>BSDlicense>
<buildtool_depend>catkinbuildtool_depend>
<build_depend>actionlibbuild_depend>
<build_depend>urdfbuild_depend>
<build_depend>cmake_modulesbuild_depend>
<build_depend>control_msgsbuild_depend>
<build_depend>control_toolboxbuild_depend>
<build_depend>controller_managerbuild_depend>
<build_depend>hardware_interfacebuild_depend>
<build_depend>joint_limits_interfacebuild_depend>
<build_depend>roscppbuild_depend>
<build_depend>sensor_msgsbuild_depend>
<build_depend>std_msgsbuild_depend>
<build_depend>trajectory_msgsbuild_depend>
<build_depend>transmission_interfacebuild_depend>
<run_depend>actionlibrun_depend>
<run_depend>urdfrun_depend>
<run_depend>cmake_modulesrun_depend>
<run_depend>control_msgsrun_depend>
<run_depend>control_toolboxrun_depend>
<run_depend>controller_managerrun_depend>
<run_depend>hardware_interfacerun_depend>
<run_depend>joint_limits_interfacerun_depend>
<run_depend>roscpprun_depend>
<run_depend>sensor_msgsrun_depend>
<run_depend>std_msgsrun_depend>
<run_depend>trajectory_msgsrun_depend>
<run_depend>transmission_interfacerun_depend>
<export>
export>
package>
<launch>
<param name="robot_description" command="$(find xacro)/xacro --inorder '$(find dhrobot_description)/urdf/dhrobot.urdf.xacro'" />
<node name="arm_hardware_interface" pkg="dhrobot_controller" type="arm_hardware_interface" output="screen"/>
<rosparam file="$(find dhrobot_controller)/config/dh_arm_control.yaml" command="load"/>
<node name="dh_arm_controller_spawner" pkg="controller_manager" type="controller_manager" args="spawn dh_arm_controller" respawn="false" output="screen"/>
launch>
server端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
class FollowJointTrajectoryAction
{
public:
FollowJointTrajectoryAction(std::string name) :
as_(nh_, name, false),
action_name_(name)
{
as_.registerGoalCallback(boost::bind(&FollowJointTrajectoryAction::goalCB, this));
as_.registerPreemptCallback(boost::bind(&FollowJointTrajectoryAction::preemptCB, this));
as_.start();
ROS_INFO("action start");
}
~FollowJointTrajectoryAction(void)
{
}
void goalCB()
{
ROS_INFO("goal is receive");
if(as_.isNewGoalAvailable())
{
}
else
{
ROS_INFO("goal is not available");
}
control_msgs::FollowJointTrajectoryResult result;
result.error_code = 0;
as_.setSucceeded(result);
}
void preemptCB()
{
ROS_INFO("%s: Preempted", action_name_.c_str());
// set the action state to preempted
as_.setPreempted();
}
protected:
ros::NodeHandle nh_;
actionlib::SimpleActionServer<control_msgs::FollowJointTrajectoryAction> as_;
std::string action_name_;
};
joint_state_publish:
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher">
<param name="/use_gui" value="false"/>
<rosparam param="/source_list">[<span style="color:#ff0000;">/move_group/robothand_controller_joint_statesspan>]rosparam>
node>
controller.yaml
controller_list:
- name: left_arm_controller
action_ns: follow_joint_trajectory
type: FollowJointTrajectory
default: true
joints:
- left_shoulder_stevo_to_axis
- left_shoulder_stevo_lift_to_axis
- left_big_arm_up_to_axis
- left_small_arm_up_to_axis
- left_wrist_run_stevo_to_axis
- name: rigth_arm_controller
action_ns: follow_joint_trajectory
type: FollowJointTrajectory
default: true
joints:
- right_shoulder_stevo_to_axis
- right_shoulder_stevo_lift_to_axis
- right_big_arm_up_to_axis
- right_small_arm_up_to_axis
- right_wrist_run_stevo_to_axis
- name: right_gripper_controller
action_ns: follow_joint_trajectory
type: FollowJointTrajectory
default: true
joints:
- right_hand_run_stevo_to_right_hand_run_stevo_axis
- name: left_gripper_controller
action_ns: follow_joint_trajectory
type: FollowJointTrajectory
default: true
joints:
- left_hand_run_stevo_to_left_hand_run_stevo_axis
JointTrajectory msg结构的查看:rosmsg show JointTrajectory
joint.py
from ros_arduino_msgs.srv import *
class Joint:
## @brief Constructs a Joint instance.
##
## @param servoNum The servo number.
##
## @param name The joint name.
def __init__(self, name, servoNum, range):
self.name = name #关节名称
self.servoNum=servoNum #对应的舵机编号
self.range=range #舵机的控制范围,这里是0~180度
self.position = 0.0
self.velocity = 0.0
self.last = rospy.Time.now()
## @brief Set the current position.
def setCurrentPosition(self):
rospy.wait_for_service('/arduino/servo_write')
try:
servo_write=rospy.ServiceProxy('/arduino/servo_write',ServoWrite)
servo_write(self.servoNum,self.position)
except rospy.ServiceException, e:
print "Service call failed: %s"%e
follow_controller.py 主要的驱动
初始化joints列表,同时启动action Server:
def __init__(self, name):
self.name = name
# rates
self.rate = 20.0
# left Arm jonits list
self.left_shoulder_stevo_to_axis=Joint(left_shoulder_stevo_to_axis,6,PI)
self.left_shoulder_stevo_lift_to_axis=Joint(left_shoulder_stevo_lift_to_axis,7,PI)
self.left_big_arm_up_to_axis=Joint(left_big_arm_up_to_axis,8,PI)
self.left_small_arm_up_to_axis=Joint(left_small_arm_up_to_axis,9,PI)
self.left_wrist_run_stevo_to_axis=Joint(left_wrist_run_stevo_to_axis,10,PI)
self.joints=list()
self.joints.append(left_shoulder_stevo_to_axis)
self.joints.append(left_shoulder_stevo_lift_to_axis)
self.joints.append(left_big_arm_up_to_axis)
self.joints.append(left_small_arm_up_to_axis)
self.joints.append(left_wrist_run_stevo_to_axis)
# left hand joint
self.left_hand_run_stevo_to_left_hand_run_stevo_axis=Joint(left_hand_run_stevo_to_left_hand_run_stevo_axis,11,PI)
self.joints.append(left_hand_run_stevo_to_left_hand_run_stevo_axis)
# right Arm jonits
self.right_shoulder_stevo_to_axis=Joint(right_shoulder_stevo_to_axis,0,PI)
self.right_shoulder_stevo_lift_to_axis=Joint(right_shoulder_stevo_lift_to_axis,1,PI)
self.right_big_arm_up_to_axis=Joint(right_big_arm_up_to_axis,2,PI)
self.right_small_arm_up_to_axis=Joint(right_small_arm_up_to_axis,3,PI)
self.right_wrist_run_stevo_to_axis=Joint(right_wrist_run_stevo_to_axis,4,PI)
self.joints.append(right_shoulder_stevo_to_axis)
self.joints.append(right_shoulder_stevo_lift_to_axis)
self.joints.append(right_big_arm_up_to_axis)
self.joints.append(right_small_arm_up_to_axis)
self.joints.append(right_wrist_run_stevo_to_axis)
# left hand joint
self.right_hand_run_stevo_to_right_hand_run_stevo_axis=Joint(right_hand_run_stevo_to_right_hand_run_stevo_axis,5,PI)
self.joints.append(right_hand_run_stevo_to_right_hand_run_stevo_axis)
# set the left arm back to the resting position
rospy.loginfo("set the left arm back to the resting position")
self.left_shoulder_stevo_to_axis.setCurrentPosition(PI/2)
self.left_shoulder_stevo_lift_to_axis.setCurrentPosition(PI/2)
self.left_big_arm_up_to_axis.setCurrentPosition(PI/2)
self.left_small_arm_up_to_axis.setCurrentPosition(PI/2)
self.left_wrist_run_stevo_to_axis.setCurrentPosition(PI/2)
# set the right arm back t
actionCb函数(Action Service的回调函数,收到msg后就会调用该函数)
def actionCb(self, goal):
rospy.loginfo(self.name + ": Action goal recieved.")
traj = goal.trajectory
if not traj.points:#判断收到的消息是否为空
msg = "Trajectory empy."
rospy.logerr(msg)
self.server.set_aborted(text=msg)
return
try:
indexes = [traj.joint_names.index(joint.name) for joint in self.joints]#按照joints列表的顺序对traj的数据进行排序,把排序数据放到indexes中
except ValueError as val:
msg = "Trajectory invalid."
rospy.logerr(msg)
self.server.set_aborted(text=msg)
return
start = traj.header.stamp#当前的时间戳
if start.secs == 0 and start.nsecs == 0:
start = rospy.Time.now()
r = rospy.Rate(self.rate)
for point in traj.points:
desired = [ point.positions[k] for k in indexes ]#期望的控制点
for i in indexes
self.joints[i].position=desired[i]#控制点对应的舵机的位置
self.joints[i].setCurrentPosition()#发送舵机的控制命令
while rospy.Time.now() + rospy.Duration(0.01) < start:#如果当前时间小于舵机这个点预期完成时间,则等待
rospy.sleep(0.01)
rospy.loginfo(self.name + ": Done.")
参考文献: