param::init
Parameter是ROS系统运行所定义的全局变量,它由master节点的parameter server基于XML-RPC负责维护。它是全局可见的,因此可以运行时修改。ROS的namespace使得参数的命名具备非常清晰的层级结构,避免他们之间的冲突
1 | //./src/ros_comm/roscpp/src/libros/param.cpp |
ros::param::set()函数的定义也在文件./src/ros_comm/roscpp/src/libros/param.cpp中,有一系列的重载函数:1
2
3
4
5
6void set(const std::string& key, const XmlRpc::XmlRpcValue& v)
void set(const std::string& key, const std::string& s)
void set(const std::string& key, const char* s)
void set(const std::string& key, double d)
void set(const std::string& key, int i)
void set(const std::string& key, bool b)
以转化为整型的重载函数为例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22void set(const std::string& key, const std::map<std::string, int>& map)
{
setImpl(key, map);
}
// 显然这个函数为模板函数比较合适,适用于多种情况
// 将第二个参数转化成相应了XmlRpcValue类型,然后调用第一个重载函数
template <class T>
void setImpl(const std::string& key, const std::map<std::string, T>& map)
{
// XmlRpcValue starts off as "invalid" and assertStruct turns it into a struct type
XmlRpc::XmlRpcValue xml_value;
xml_value.begin();
// Copy the contents into the XmlRpcValue
for(typename std::map<std::string, T>::const_iterator it = map.begin();
it != map.end(); ++it)
{
xml_value[it->first] = it->second;
}
ros::param::set(key, xml_value);
}
set第一个重载如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24void set(const std::string& key, const XmlRpc::XmlRpcValue& v)
{
std::string mapped_key = ros::names::resolve(key);
XmlRpc::XmlRpcValue params, result, payload;
params[0] = this_node::getName();
params[1] = mapped_key;
params[2] = v;
{
//Lock around the execute to the master in case we get a parameter update on this value between executing on the master and setting the parameter in the g_params list
boost::mutex::scoped_lock lock(g_params_mutex);
if (master::execute("setParam", params, result, payload, true))
{
// Update our cached params list now so that if get() is called immediately after param::set()
// we already have the cached state and our value will be correct
if (g_subscribed_params.find(mapped_key) != g_subscribed_params.end())
{
g_params[mapped_key] = v;
}
invalidateParentParams(mapped_key);
}
}
}
函数master::execute(“setParam”, params, result, payload, true)用于在master(节点管理器)上执行XML-RPC通信机制。1
2
3
4
5
6bool ros::master::execute(const std::string& method,
const XmlRpc::XmlRpcValue & request,
XmlRpc::XmlRpcValue & response,
XmlRpc::XmlRpcValue & payload,
bool wait_for_master
)
- method:要调用的 RPC 方法
- request:The arguments to the RPC call //传递给RPC的参数
- response:[out] The resonse that was received. //接收到的回应
- payload: [out] The payload that was received
- wait_for_master: //是否一直循环等待与master建立连接
函数的源码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66bool execute(const std::string& method, const XmlRpc::XmlRpcValue& request, XmlRpc::XmlRpcValue& response, XmlRpc::XmlRpcValue& payload, bool wait_for_master)
{
ros::WallTime start_time = ros::WallTime::now();
std::string master_host = getHost(); //获取g_host的值
uint32_t master_port = getPort(); //获取g_port的值
//根据 master_host, master_port 获取XMLRPC通信的客户端,这两个一般是根据环境变量所得
XmlRpc::XmlRpcClient *c = XMLRPCManager::instance()->getXMLRPCClient(master_host, master_port, "/");
bool printed = false;
bool slept = false;
bool ok = true;
bool b = false;
do
{
{
boost::mutex::scoped_lock lock(g_xmlrpc_call_mutex);
//c是根据master_host, master_port的值获取XMLRPC通信的客户端指针(XmlRpc::XmlRpcClient *c)
// 循环不断执行execute,以保持和master的通信
b = c->execute(method.c_str(), request, response);
}
ok = !ros::isShuttingDown() && !XMLRPCManager::instance()->isShuttingDown();
if (!b && ok)
{
if (!printed && wait_for_master)
{
ROS_ERROR("[%s] Failed to contact master at [%s:%d]. %s", method.c_str(), master_host.c_str(), master_port, wait_for_master ? "Retrying..." : "");
printed = true;
}
if (!wait_for_master)
{
XMLRPCManager::instance()->releaseXMLRPCClient(c);
return false;
}
if (!g_retry_timeout.isZero() && (ros::WallTime::now() - start_time) >= g_retry_timeout)
{
ROS_ERROR("[%s] Timed out trying to connect to the master after [%f] seconds", method.c_str(), g_retry_timeout.toSec());
XMLRPCManager::instance()->releaseXMLRPCClient(c);
return false;
}
ros::WallDuration(0.05).sleep();
slept = true;
}
else
{
if (!XMLRPCManager::instance()->validateXmlrpcResponse(method, response, payload))
{
XMLRPCManager::instance()->releaseXMLRPCClient(c);
return false;
}
break;
}
// 不断执行循环,除非调用ros::shutdown() 或 XMLRPCManager::shutdown()
ok = !ros::isShuttingDown() && !XMLRPCManager::instance()->isShuttingDown();
} while(ok);
if (ok && slept)
{
ROS_INFO("Connected to master at [%s:%d]", master_host.c_str(), master_port);
}
XMLRPCManager::instance()->releaseXMLRPCClient(c);
return b;
}
函数主要就是基于XML-RPC进行setParam
的远程调用,需要不断判断连接状态
每个节点其实就是个XML-RPC服务端,在运行ros::start()
时执行了XMLRPCManager::instance()->start();——XmlRpcServer::bindAndListen
,在这个函数中又开启了一个线程依次调用:XMLRPCManager::serverThreadFunc
——server_.work(0.1);
,服务端的两个关键函数完成了。
总结
init()其实做很少工作,主要就是解析一下环境和命令行参数,init()不允许参与实际的连接,因而,用户可以手动检测像是master启动没有,有没有按自定义的行为启动这样的事