catkin_make 遇到的问题

编译之后,运行时出现这样的结果

重新编译后依然如此,只好删掉build文件夹里对应的文件,再编译后就没问题了

catkin屏蔽掉不想编译的package

workspace/src下有多个package包,有时候只想不编译某个package。可以在不想被编译的package目录下新建了CATKIN_IGNORE 文件,不用写任何东东。返回到workspace目录下后执行catkin_make就不会编译带有CATKIN_IGNORE的package。

如果当src中有一个package中已经有这样的文件(命名为CATKIN_IGNORE的空文件)时,当你指定要编译这个package时: catkin_make --pkg your-pkg-name会报错:

1
Packages "your-pkg-name" not found in the workspace

内存不足

catkin_make之所以有时报错内存不足,有时正常,是因为编译是按拓扑顺序,可能同时编译了move_base和teb_local_planner这两个耗内存大的包,导致报错

.so file truncated

  1. catkin_make中途 ctrl+C 造成
  2. 运行程序时,直接从VMWare关闭虚拟机

新package放入工作空间无法识别

一个新的package放入工作空间后,是不能直接识别的,无论source setup.bash还是catkin_make --pkg都不行,必须先对整个工作空间catkin_make,此时才会识别它

不同工作空间的同名package

要尽量避免这种情况,否则会混乱。此时如果有问题,用echo $ROS_PACKAGE_PATH查看环境变量包含的ROS工作空间

Could not find GTest

执行catkin_make时,有时会报错Could not find GTest,这是由于缺少测试模块,加-DBUILD_TESTING=OFF可以避免测试。

不要移植工作空间,只移植src

把ROS的工作空间转移到另一台电脑上会编译失败,build和devel里有很多文件会构成影响,即使二者的用户名一样也不行。所以每次只能复制src文件夹,放到另一台电脑的工作空间里。

我的一个自定义msg,将整个工作空间移植到另一台电脑,编译没出错,但是echo出错,好像rostopic不被识别一样:
rostopic echo 出错.png

find_package(catkin) 无法识别catkin

使用qt-ros-plugin,不知怎么操作,导致find_package(catkin)无法识别catkin,不能在任何工作空间编译,其他命令都正常。利用whereis catkin找到路径,删除相应文件,然后重装ROS

命令catkin_init_workspace:Initializes a catkin workspace by creating a top-level CMakeLists.txt.
这个命令最好不要用,创建工作空间还是新建文件夹,然后进入执行catkin_make

程序 catkin_make 尚未安装

执行catkin_make时,报错:,执行roscd等命令也提示没有安装,感觉就像是没有装ros一样。解决方法: source /opt/ros/kinetic/setup.bash

编译某个包时缺少头文件

编译禾赛雷达的驱动HesaiLidar_General_ROS,结果报错PandarScan.h: No such file or directory. 然后又catkin_make一次,竟然成功了。这时去devel/include里可以看到缺少的头文件了。 应当是第一次编译时确实缺少,然后接着编译剩下部分,此时就生成头文件了。 再编译第二次,原来需要头文件的地方就编译成功了。
参考: HesaiLidar_General_ROS/issues/24

clock skew detected

clock skew detected.png

出现这个警告是由于时钟不同步导致的,即系统时间比文件修改时间早,换到另一个Linux系统切换可能会出现这个问题。

如果在不同计算机之间传输文件,可能出现这种报警,是由于二者的UTC对时不同。现象是文件的修改时间比当前的系统时间还要晚。可能会导致某些不该编译的文件编译,或者该编译的没有编译。

在受影响的文件夹内执行一次 touch *即可永久修正这一问题。

依次执行下列指令即可

1
2
3
find . -type f | xargs -n 5 touch
catkin_make clean
catkin_make

将所有文件都重新touch一遍,更新到本地系统的时间,再make就没问题了。

The dependency target “nav_msgs_gencpp” of target does not exist

1
2
3
4
5
6
7
8
CMake Warning (dev) at planners/pure_pursuit_local_planner/CMakeLists.txt:57 (add_dependencies):
Policy CMP0046 is not set: Error on non-existent dependency in
add_dependencies. Run "cmake --help-policy CMP0046" for policy details.
Use the cmake_policy command to set the policy and suppress this warning.

The dependency target "nav_msgs_gencpp" of target
"pure_pursuit_local_planner" does not exist.
This warning is for project developers. Use -Wno-dev to suppress it.

可能加一句

1
2
3
add_dependencies(${PROJECT_NAME}
${PROJECT_NAME}_gencfg
${PROJECT_NAME}_generate_messages_cpp)


避障后未能继续原路径
abstract Welcome to my blog, enter password to read.
Read more
使用GDB解决算法切换导致的多线程问题 (二)
abstract Welcome to my blog, enter password to read.
Read more
tmux的使用

第一个启动的 Tmux 窗口,编号是0,第二个窗口的编号是1,以此类推。

使用编号区分会话,不太直观,更好的方法是为会话起名 tmux new -s <session-name>

tmux ls命令可以查看当前所有的 Tmux 会话。

tmux attach命令用于重新接入某个已存在的会话。

1
2
3
4
5
# 使用会话编号
tmux attach -t 0

# 使用会话名称
tmux attach -t <session-name>

杀死某个会话

1
2
3
4
5
# 使用会话编号
tmux kill-session -t 0

# 使用会话名称
tmux kill-session -t <session-name>

tmux switch命令用于切换会话

1
2
3
4
5
# 使用会话编号
tmux switch -t 0

# 使用会话名称
tmux switch -t <session-name>


lsof命令解析进程
1
2
3
4
5
6
7
8
9
10
11
12
13
lsof abc.txt             //显示开启文件abc.txt的进程

lsof -c abc //显示abc进程现在打开的文件

lsof -c -p 1234 //列出进程号为1234的进程所打开的文件

lsof +d /usr/local/ //显示目录下被进程开启的文件

lsof +D /usr/local/ //同上,但是会搜索目录下的目录,时间较长

lsof -i //用以显示符合条件的进程情况
lsof -i[46] [protocol][@hostname|hostaddr][:service|port]

使用后发现对文本文件、图片这样的文件不适用,对可执行文件适用。


Quaternion has length close to zero... discarding as navigation goal
abstract Welcome to my blog, enter password to read.
Read more
move_base received signal SIGABRT
abstract Welcome to my blog, enter password to read.
Read more
抛出错误 std__bad_alloc
abstract Welcome to my blog, enter password to read.
Read more
均值平滑处理路径

最简单的平滑算法,处理A星路径。效果如下
原始路径.png
平滑之后的路径.png


均值平滑的使用不宜太多,如图map坐标系的Y轴正向是朝左的,红色路径最左边的点,经过3次均值平滑,肯定会朝右平移,造成平滑后的路径靠近了障碍物

平均采样,获得anchor的index

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 平均分割区间 [start, end] 为num段,num+1 个点,存入sliced
* start and end 为sliced的首尾元素
*/
template <typename T>
void uniform_slice(const T start, const T end, uint32_t num,
std::vector<T>* sliced)
{
if (!sliced || num == 0)
return;
const T delta = (end - start) / num;
sliced->resize(num + 1);
T s = start;
for (uint32_t i = 0; i < num; ++i, s += delta)
{
sliced->at(i) = s;
}
sliced->at(num) = end;
}

补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
vector<Point2D> samplePoints(const vector<Point2D>& input_points, int interval)
{
vector<Point2D> anchor_points;

int sampling_num = std::max(2, static_cast<int>(input_points.size() / interval + 0.5) );

vector<double> anchor_id;
Point2D temp_point;
uniform_slice(0, input_points.size(), sampling_num - 1, &anchor_id);

for(int i=0; i< anchor_id.size(); i++)
{
// cout << anchor_id.at(i) << " ";
temp_point = input_points.at(anchor_id.at(i) );
// cout << "anchor X: " << temp_point.first <<endl;
// cout << "anchor Y: " << temp_point.second <<endl;
anchor_points.push_back(temp_point);
}
return anchor_points;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Check if two points u and v are the same point on XY dimension.
* @param u one point that has member function x() and y().
* @param v one point that has member function x() and y().
* @return sqrt((u.x-v.x)^2 + (u.y-v.y)^2) < epsilon, i.e., the Euclid distance on XY dimension.
*/
template <typename U, typename V>
bool SamePointXY(const U& u, const V& v)
{
static constexpr double kMathEpsilonSqr = 1e-8 * 1e-8;
return (u.x() - v.x()) * (u.x() - v.x()) < kMathEpsilonSqr &&
(u.y() - v.y()) * (u.y() - v.y()) < kMathEpsilonSqr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PathPoint GetWeightedAverageOfTwoPathPoints(const PathPoint& p1,
const PathPoint& p2,
const double w1, const double w2)
{
PathPoint p;
p.set_x(p1.x() * w1 + p2.x() * w2);
p.set_y(p1.y() * w1 + p2.y() * w2);
p.set_z(p1.z() * w1 + p2.z() * w2);
p.set_theta(p1.theta() * w1 + p2.theta() * w2);
p.set_kappa(p1.kappa() * w1 + p2.kappa() * w2);
p.set_dkappa(p1.dkappa() * w1 + p2.dkappa() * w2);
p.set_ddkappa(p1.ddkappa() * w1 + p2.ddkappa() * w2);
p.set_s(p1.s() * w1 + p2.s() * w2);
return p;
}
1
2
3
4
5
6
7
8
9
10
11
12
// Test whether two float or double numbers are equal.
template <typename T>
typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type
IsFloatEqual(T x, T y, int ulp = 2)
{
// the machine epsilon has to be scaled to the magnitude of the values used
// and multiplied by the desired precision in ULPs (units in the last place)
return std::fabs(x - y) <
std::numeric_limits<T>::epsilon() * std::fabs(x + y) * ulp
// unless the result is subnormal
|| std::fabs(x - y) < std::numeric_limits<T>::min();
}

相机点云无法从costmap清除障碍,甚至无法生成障碍

测试的要求: 不能把地面加入costmap,但是稍高于地面的物体能加入。

不断调整 max_obstacle_height, min_obstacle_height, obstacle_range, raytrace_range 四个参数,但是costmap发现生成的障碍总是清除不掉。

换了另一个相机和雷达,发现没有这个问题. 后来发现有时甚至不能生成障碍了。

清除障碍的函数重点是ObservationBuffer::purgeStaleObservations()其中的 observation_list_,它又来自bufferCloud函数中的observation_cloud,后者又来自 global_frame_cloud

相机发布的点云是在相机坐标系,在bufferCloud函数里用pcl_ros::transformPointCloud转换到代价地图的全局坐标系(也就是yaml中指定的global_frame,一般关注的是local costmap。) 得到 global_frame_cloud。然后按如下条件筛选出 observation_cloud

1
2
3
4
5
6
7
8
for (unsigned int i = 0; i < cloud_size; ++i)
{
if (global_frame_cloud.points[i].z <= max_obstacle_height_
&& global_frame_cloud.points[i].z >= min_obstacle_height_)
{
observation_cloud.points[point_count++] = global_frame_cloud.points[i];
}
}

因此 min_obstacle_heightmax_obstacle_height是在代价地图全局坐标系下的值。

bufferCloud函数中加入代码,把observation_cloud发布出来

1
2
3
sensor_msgs::PointCloud2 observation_ros_cloud;
pcl::toROSMsg(observation_cloud, observation_ros_cloud);
observation_cloud_pub.publish( observation_ros_cloud );

在构造函数里加入
1
2
3
ros::NodeHandle n;
n.param("publish_observation_cloud", pub_observation_cloud_, false );
observation_cloud_pub = n.advertise < sensor_msgs::PointCloud2 > ("observation_cloud", 2);

这样可以观察最终生成的障碍来自什么样的点云。比如下面两个点云的对比

最后查啊查啊,终于发现和其他相机的区别不在参数,而在于我之前修改相机驱动时的滤波,滤得太狠了。于是修改驱动,y和z方向的点云不能太少,终于可以清除成功了。

问题的根源在于滤波后的点太少而且稀疏, 导致raytrace机制不能清除障碍。 点云滤波不能直接滤到自己需要的范围,比如即使你实际需要1m的距离,驱动里也不能只给1m,竖直y方向也不能太小,体素滤波的体素不能太大,一般取0.01,这个对点云数量的影响也很大。