0%

Guide

install

1
2
3
4
5
6
7
8
9
10
11
12
13
sudo apt-get -y install zsh

cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
/usr/bin/tmux
/bin/zsh
/usr/bin/zsh

chsh -s /usr/bin/zsh

reboot system

1
2
echo $SHELL 
/usr/bin/zsh

install 2(better)

1
2
3
4
5
6
7
8
9
10
sh -c "$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)"
```

### config

#### themes
```bash
git clone https://github.com/fcamblor/oh-my-zsh-agnoster-fcamblor.git
cd oh-my-zsh-agnoster-fcamblor
./install

.zshrc

1
2
3
vim .zshrc

ZSH_THEME="agnoster" # ys avit agnoster

use avit.

.zshrc vs .bashrc

zsh uses env profile ~/.zshrc, not ~/.bashrc.

Reference

History

  • 20190109: created.

Guide

  • pcap: 1.7.4

install

1
2
3
4
5
6
7
8
9
10
sudo apt-get -y install libpcap-dev

locate pcap.so
/usr/lib/x86_64-linux-gnu/libpcap.so
/usr/lib/x86_64-linux-gnu/libpcap.so.0.8
/usr/lib/x86_64-linux-gnu/libpcap.so.1.7.4

locate pcap.h
/usr/include/pcap.h
/usr/include/pcap/pcap.h

pcap-config.cmake

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
66
67
68
69
70
71
72
73
74
# - Try to find libpcap include dirs and libraries
#
# Usage of this module as follows:
#
# find_package(PCAP)
#
# Variables used by this module, they can change the default behaviour and need
# to be set before calling find_package:
#
# PCAP_ROOT_DIR Set this variable to the root installation of
# libpcap if the module has problems finding the
# proper installation path.
#
# Variables defined by this module:
#
# PCAP_FOUND System has libpcap, include and library dirs found
# PCAP_INCLUDE_DIR The libpcap include directories.
# PCAP_LIBRARY The libpcap library (possibly includes a thread
# library e.g. required by pf_ring's libpcap)
# HAVE_PF_RING If a found version of libpcap supports PF_RING

find_path(PCAP_ROOT_DIR
NAMES include/pcap.h
)

find_path(PCAP_INCLUDE_DIR
NAMES pcap.h
HINTS ${PCAP_ROOT_DIR}/include
)

find_library(PCAP_LIBRARY
NAMES pcap
HINTS ${PCAP_ROOT_DIR}/lib
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PCAP DEFAULT_MSG
PCAP_LIBRARY
PCAP_INCLUDE_DIR
)

include(CheckCSourceCompiles)
set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY})
check_c_source_compiles("int main() { return 0; }" PCAP_LINKS_SOLO)
set(CMAKE_REQUIRED_LIBRARIES)

# check if linking against libpcap also needs to link against a thread library
if (NOT PCAP_LINKS_SOLO)
find_package(Threads)
if (THREADS_FOUND)
set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
check_c_source_compiles("int main() { return 0; }" PCAP_NEEDS_THREADS)
set(CMAKE_REQUIRED_LIBRARIES)
endif ()
if (THREADS_FOUND AND PCAP_NEEDS_THREADS)
set(_tmp ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
list(REMOVE_DUPLICATES _tmp)
set(PCAP_LIBRARY ${_tmp}
CACHE STRING "Libraries needed to link against libpcap" FORCE)
else ()
message(FATAL_ERROR "Couldn't determine how to link against libpcap")
endif ()
endif ()

include(CheckFunctionExists)
set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY})
check_function_exists(pcap_get_pfring_id HAVE_PF_RING)
set(CMAKE_REQUIRED_LIBRARIES)

mark_as_advanced(
PCAP_ROOT_DIR
PCAP_INCLUDE_DIR
PCAP_LIBRARY
)

CMakeLists.txt

1
2
3
4
5
6
find_package(PCAP REQUIRED) 
MESSAGE( [Main] " PCAP_INCLUDE_DIR = ${PCAP_INCLUDE_DIR}")
MESSAGE( [Main] " PCAP_LIBRARY = ${PCAP_LIBRARY}")

#[Main] PCAP_INCLUDE_DIR = /usr/include
#[Main] PCAP_LIBRARY = /usr/lib/x86_64-linux-gnu/libpcap.so

Reference

History

  • 20181229: created.

Guide

extern c

相比C语言,C++支持函数重载、类、模版等各种特性,如果C++的符号修饰仍按照C那样直接使用对应名称,虽然可读性很好,却不可避免的会出现各种错乱,于是C++符号修饰别名(mangled name)被设计出来, 用来解决上述问题,然而修饰规则取决于编译器实现,没有统一标准,比如gcc、msvc就各有一套,但好在都提供了相应接口进行解析(demangle),这里有个网站demangler可以在线解析

c++ function: managled names
c function: unmangled names
extern c告诉c++编译器将f_int,f_float视为c函数,使用 unmangled names(修饰名称)

managled/unmangled names

a.cpp

1
2
3
4
5
6
7
8
9
10
void f() {}
void g();

extern "C" {
void ef() {}
void eg();
}

/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }

Compile with GCC 4.8 Linux ELF output:

g++ -c a.cpp

Decompile the symbol table:

readelf -s a.o

output

Num:    Value          Size Type    Bind   Vis      Ndx Name
  8: 0000000000000000     6 FUNC    GLOBAL DEFAULT    1 _Z1fv
  9: 0000000000000006     6 FUNC    GLOBAL DEFAULT    1 ef
 10: 000000000000000c    16 FUNC    GLOBAL DEFAULT    1 _Z1hv
 11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
 12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND eg

managled names: _Z1fv,_Z1hv,_Z1gv for f,g,h
unmangled names: ef,eg

unmanagle them

c++filt _Z1fv
f()

c++filt _Z1hv
h()

c++filt _Z1gv
g()

extern C中错误用法

It becomes obvious that any C++ feature that requires name mangling will not work inside extern C:

error.cpp

1
2
3
4
5
6
7
8
9
10
extern "C" {
// Overloading.
// error: declaration of C function ‘void f(int)’ conflicts with
void f();
void f(int i);

// Templates.
// error: template with C linkage
template <class C> void f(C i) { }
}

compile and generate errors

g++ -c error.cpp 
error.cpp:5:17: error: conflicting declaration of C function ‘void f(int)’
     void f(int i);
                 ^
error.cpp:4:10: note: previous declaration ‘void f()’
     void f();
          ^
error.cpp:9:5: error: template with C linkage
     template <class C> void f(C i) { }
     ^

So you will need extern "C" both when calling:

  • C from C++: tell g++ to expect unmangled symbols produced by gcc
  • C++ from C: tell gcc to use unmangled symbols produced by g++

cpp中使用c方法

code

main.cpp

1
2
3
4
5
6
7
#include <cassert>

#include "c.h"

int main() {
assert(f() == 1);
}

c.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++. */
#ifdef __cplusplus
extern "C" {
#endif

int f();

#ifdef __cplusplus
}
#endif

#endif

c.c

1
2
3
#include "c.h"

int f(void) { return 1; }

OK

compile main.cpp

g++ -c -o main.o -std=c++98 main.cpp

readelf -s main.o
10: 0000000000000000    46 FUNC    GLOBAL DEFAULT    1 main
11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND f
12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __assert_fail

使用c++编译器,extern c告诉c++编译器f是一个c函数,使用unmanagled name f

compile c.c

gcc -c -o c.o -std=c89 c.c

readelf -s c.o 
8: 0000000000000000    11 FUNC    GLOBAL DEFAULT    1 f

使用c编译器,对于c函数生成unmanagled name f

link main.o and c.o

g++ -o main.out main.o c.o
./main.out

OK.

Error

Without extern "C" the link fails with:

main.cpp:6: undefined reference to `f()'

compile main.cpp

g++ -c -o main.o -std=c++98 main.cpp

readelf -s main.o
10: 0000000000000000    46 FUNC    GLOBAL DEFAULT    1 main
11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1fv
12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __assert_fail

因为没有extern c,所以g++编译器将f看做是c++函数,使用managled name _Z1fv

compile c.c

gcc -c -o c.o -std=c89 c.c

readelf -s c.o 
8: 0000000000000000    11 FUNC    GLOBAL DEFAULT    1 f

使用c编译器,对于c函数生成unmanagled name f

link main.o and c.o

g++ -o main.out main.o c.o

main.o: In function `main':
main.cpp:(.text+0x5): undefined reference to `f()'
collect2: error: ld returned 1 exit status

link失败,因为c++编译器需要managled name _Z1fv,然而c编译器生成了unmanagled name f

c中使用cpp方法

code

main.c

1
2
3
4
5
6
7
8
9
#include <assert.h>

#include "cpp.h"

int main(void) {
assert(f_int(1) == 2);
assert(f_float(1.0) == 3);
return 0;
}

cpp.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef CPP_H
#define CPP_H

#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif

int f_int(int i);
int f_float(float i);

#ifdef __cplusplus
}
#endif

#endif

cpp.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "cpp.h"

int f(int i) {
return i + 1;
}

int f(float i) {
return i + 2;
}

int f_int(int i) {
return f(i);
}

int f_float(float i) {
return f(i);
}

OK

compile main.c

gcc -c -o main.o -std=c89 -Wextra main.c

readelf -s main.o
10: 0000000000000000    94 FUNC    GLOBAL DEFAULT    1 main
11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND f_int
12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __assert_fail
13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND f_float

使用c编译器,将f_int,f_float视作c函数,使用unmanagled name f_int,f_float

compile cpp.cpp

g++ -c -o cpp.o -std=c++98 cpp.cpp

readelf -s cpp.o
 9: 0000000000000000    15 FUNC    GLOBAL DEFAULT    1 _Z1fi
10: 000000000000000f    32 FUNC    GLOBAL DEFAULT    1 _Z1ff
11: 000000000000002f    23 FUNC    GLOBAL DEFAULT    1 f_int
12: 0000000000000046    31 FUNC    GLOBAL DEFAULT    1 f_float

使用c++编译器,extern c告诉c++编译器将f_int,f_float视作c函数,使用unmanagled name f_int,f_float

link main.o and cpp.o

g++ -o main.out main.o cpp.o
./main.out

OK.

Error

Without extern “C” it fails with:

main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'

compile main.c

gcc -c -o main.o -std=c89 -Wextra main.c

readelf -s main.o
10: 0000000000000000    94 FUNC    GLOBAL DEFAULT    1 main
11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND f_int
12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __assert_fail
13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND f_float

使用c编译器,将f_int,f_float视作c函数,使用unmanagled name f_int,f_float

compile cpp.cpp

g++ -c -o cpp.o -std=c++98 cpp.cpp

readelf -s cpp.o
 9: 0000000000000000    15 FUNC    GLOBAL DEFAULT    1 _Z1fi
10: 000000000000000f    32 FUNC    GLOBAL DEFAULT    1 _Z1ff
11: 000000000000002f    23 FUNC    GLOBAL DEFAULT    1 _Z5f_inti
12: 0000000000000046    31 FUNC    GLOBAL DEFAULT    1 _Z5f_floatf

使用c++编译器,因为没有extern c,c++编译器将f_int,f_float视作c++函数,使用managled name _Z5f_inti,_Z5f_floatf

link main.o and cpp.o

g++ -o main.out main.o cpp.o

link失败,因为c编译器需要unmanagled name f_int,f_float,然而c++编译器生成了managled name _Z5f_inti,_Z5f_floatf

Reference

History

  • 20181226: created.

Series

Guide

  • protobuf 2.6.1
  • protobuf 3.6.1 latest

old version

install

1
2
3
4
5
6
7
sudo apt-get install libprotobuf-dev

which protoc
/usr/bin/protoc

protoc --version
2.6.1

remove

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#remove exist `protobuf 2.6.1`

sudo apt-get remove libprotobuf-dev
```

### compile

```bash
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-cpp-3.6.1.tar.gz

tar -xvf protobuf-cpp-3.6.1.tar.gz
cd protobuf-3.6.1
./configure --disable-shared CXXFLAGS="-fPIC"
make -j8

tips: we compile static library with --disable-shared CXXFLAGS="-fPIC". 编译动态库dll/so的时候,如果依赖static library(比如profobuf),那么static library编译的时候需要加上-fPIC,否则动态库编译出错。
对于CMake,使用cmake CMAKE_CXX_FLGAS="-fPIC" ..

otherwise, error occurs

Linking CXX shared library ../../../../bin/libcommon.so
/usr/bin/ld: /usr/local/lib/libprotobuf.a(common.o): relocation R_X86_64_32S against `.rodata' can not be used when makinga shared object; recompile with -fPIC
/usr/local/lib/libprotobuf.a: error adding symbols: Bad value

install

1
2
3
4
sudo make install
sudo ldconfig

sudo make uninstall

install path:

  • header: /usr/local/include/google/protobuf
  • lib: /usr/local/lib
  • executable: /usr/local/bin

static libs

1
2
3
ll /usr/local/lib/libproto
libprotobuf.a libprotobuf-lite.a libprotoc.a
libprotobuf.la libprotobuf-lite.la libprotoc.la

By default, make install' will install all the files in /usr/local/bin’, /usr/local/lib' etc. You can specify an installation prefix other than /usr/local’ using --prefix', for instance –prefix=$HOME’.

1
2
3
4
./configure -h

--enable-shared[=PKGS] build shared libraries [default=yes]
--enable-static[=PKGS] build static libraries [default=yes]

Static Linking vs DLL

Static linking is now the default for the Protocol Buffer libraries. Due to issues with Win32’s use of a separate heap for each DLL, as well as binary compatibility issues between different versions of MSVC’s STL library, it is recommended that you use static linkage only.
However, it is possible to build libprotobuf and libprotoc as DLLs if you really want. To do this, do the following:

  • Add an additional flag -Dprotobuf_BUILD_SHARED_LIBS=ON when invoking cmake
  • Follow the same steps as described in the above section.
  • When compiling your project, make sure to #define PROTOBUF_USE_DLLS.

test

1
2
3
4
5
which protoc
/usr/local/bin/protoc

protoc --version
libprotoc 3.6.1

tips install openmpi

1
sudo apt-get install -y  libiomp-dev libopenmpi-dev 

multiple protoc

1
2
3
4
whereis protoc 
protoc: /usr/bin/protoc # 2.6.0
/usr/local/bin/protoc # 3.6.1
/usr/share/man/man1/protoc.1.gz

Example

usage

1
2
3
protoc --cpp_out=. ./point_cloud.proto 
protoc --java_out=./java/ ./proto/helloworld.proto
protoc --go_out=./go/ ./proto/helloworld.proto

CMakeLists.txt

1
2
3
4
find_package(Protobuf REQUIRED)
#add_definitions( -DPROTOBUF_USE_DLLS ) # KEY STEPS
MESSAGE( [Main] " PROTOBUF_INCLUDE_DIRS = ${PROTOBUF_INCLUDE_DIRS}")
MESSAGE( [Main] " PROTOBUF_LIBRARIES = ${PROTOBUF_LIBRARIES}")

error

1
2
3
4
5
6
7
./node_perception 

[libprotobuf ERROR google/protobuf/descriptor_database.cc:58] File already exists in database: adapter_config.proto
[libprotobuf FATAL google/protobuf/descriptor.cc:1315] CHECK failed: generated_database_->Add(encoded_file_descriptor, size):
terminate called after throwing an instance of 'google::protobuf::FatalException'
what(): CHECK failed: generated_database_->Add(encoded_file_descriptor, size):
Aborted (core dumped)

reasons

The problem happens when you have multiple compiled copies of the same .pb.cc file sharing a single copy of libprotobuf.so. 

`common`模块编译了`adapter_config.pb.cc`,`node_perception`依赖于`common`,同时也要编译`adapter_config.pb.cc`。运行`node_perception`就会报错。

solutions

同一份`adapter_config.pb.cc`只编译到`node_perception`等executable,不要编译到所依赖的`common`模块。

common和node_perception使用static或者dynamic的protobuf,都会遇到同样的问题。

Reference

History

  • 20181219: created.

Guide

create workspace

Let’s create and build a catkin workspace:

mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
catkin_make
source devel/setup.bash 
env | grep ROS

create package

cd ~/catkin_ws/src
catkin_create_pkg beginner_tutorials roscpp rospy std_msgs

tree .
.
├── beginner_tutorials
│   ├── CMakeLists.txt
│   ├── include
│   │   └── beginner_tutorials
│   ├── package.xml
│   └── src
└── CMakeLists.txt -> /opt/ros/kinetic/share/catkin/cmake/toplevel.cmake

4 directories, 3 files

create msg

roscd beginner_tutorials
mkdir msg
echo "int64 num" > msg/Num.msg

vim package.xml

<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

vim CMakeLists.txt

find_package(catkin REQUIRED COMPONENTS
   roscpp
   rospy
   std_msgs
   message_generation
)

catkin_package(
  ...
  CATKIN_DEPENDS message_runtime ...
  ...)


add_message_files(
  FILES
  Num.msg
)

create srv

roscd beginner_tutorials
mkdir srv
roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv

roscp [package_name] [file_to_copy_path] [copy_path]
roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv

vim CMakeLists.txt

add_service_files(
  FILES
  AddTwoInts.srv
)

show msg

rosmsg show beginner_tutorials/Num
int64 num

rosmsg show Num
[beginner_tutorials/Num]:
int64 num

show srv

rossrv show

rossrv show beginner_tutorials/AddTwoInts 

int64 a
int64 b
---
int64 sum


rossrv show AddTwoInts

[beginner_tutorials/AddTwoInts]:
int64 a
int64 b
---
int64 sum

[rospy_tutorials/AddTwoInts]:
int64 a
int64 b
---
int64 sum

Code Example

talker.cpp

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include "ros/ros.h"
#include "std_msgs/String.h"

#include <sstream>

/**
* This tutorial demonstrates simple sending of messages over the ROS system.
*/
int main(int argc, char **argv)
{
/**
* The ros::init() function needs to see argc and argv so that it can perform
* any ROS arguments and name remapping that were provided at the command line.
* For programmatic remappings you can use a different version of init() which takes
* remappings directly, but for most command-line programs, passing argc and argv is
* the easiest way to do it. The third argument to init() is the name of the node.
*
* You must call one of the versions of ros::init() before using any other
* part of the ROS system.
*/
ros::init(argc, argv, "talker");

/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the last
* NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;

/**
* The advertise() function is how you tell ROS that you want to
* publish on a given topic name. This invokes a call to the ROS
* master node, which keeps a registry of who is publishing and who
* is subscribing. After this advertise() call is made, the master
* node will notify anyone who is trying to subscribe to this topic name,
* and they will in turn negotiate a peer-to-peer connection with this
* node. advertise() returns a Publisher object which allows you to
* publish messages on that topic through a call to publish(). Once
* all copies of the returned Publisher object are destroyed, the topic
* will be automatically unadvertised.
*
* The second parameter to advertise() is the size of the message queue
* used for publishing messages. If messages are published more quickly
* than we can send them, the number here specifies how many messages to
* buffer up before throwing some away.
*/
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

ros::Rate loop_rate(10);

/**
* A count of how many messages we have sent. This is used to create
* a unique string for each message.
*/
int count = 0;
while (ros::ok())
{
/**
* This is a message object. You stuff it with data, and then publish it.
*/
std_msgs::String msg;

std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();

ROS_INFO("%s", msg.data.c_str());

/**
* The publish() function is how you send messages. The parameter
* is the message object. The type of this object must agree with the type
* given as a template parameter to the advertise<>() call, as was done
* in the constructor above.
*/
chatter_pub.publish(msg);

ros::spinOnce();

loop_rate.sleep();
++count;
}


return 0;
}

listener.cpp

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
#include "ros/ros.h"
#include "std_msgs/String.h"

/**
* This tutorial demonstrates simple receipt of messages over the ROS system.
*/
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}

int main(int argc, char **argv)
{
/**
* The ros::init() function needs to see argc and argv so that it can perform
* any ROS arguments and name remapping that were provided at the command line.
* For programmatic remappings you can use a different version of init() which takes
* remappings directly, but for most command-line programs, passing argc and argv is
* the easiest way to do it. The third argument to init() is the name of the node.
*
* You must call one of the versions of ros::init() before using any other
* part of the ROS system.
*/
ros::init(argc, argv, "listener");

/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the last
* NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;

/**
* The subscribe() call is how you tell ROS that you want to receive messages
* on a given topic. This invokes a call to the ROS
* master node, which keeps a registry of who is publishing and who
* is subscribing. Messages are passed to a callback function, here
* called chatterCallback. subscribe() returns a Subscriber object that you
* must hold on to until you want to unsubscribe. When all copies of the Subscriber
* object go out of scope, this callback will automatically be unsubscribed from
* this topic.
*
* The second parameter to the subscribe() function is the size of the message
* queue. If messages are arriving faster than they are being processed, this
* is the number of messages that will be buffered up before beginning to throw
* away the oldest ones.
*/
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);

/**
* ros::spin() will enter a loop, pumping callbacks. With this version, all
* callbacks will be called from within this thread (the main one). ros::spin()
* will exit when Ctrl-C is pressed, or the node is shutdown by the master.
*/
ros::spin();

return 0;
}

CMakeLists.txt

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
66
67
68
69
cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)

## Compile as C++11, supported in ROS Kinetic and newer
add_compile_options(-std=c++11)

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)

## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)

## Generate messages in the 'msg' folder
add_message_files(
FILES
Num.msg
)

## Generate services in the 'srv' folder
add_service_files(
FILES
AddTwoInts.srv
)

## Generate actions in the 'action' folder
# add_action_files(
# FILES
# Action1.action
# Action2.action
# )

## Generate added messages and services with any dependencies listed here
generate_messages(
DEPENDENCIES
std_msgs
)

catkin_package(
INCLUDE_DIRS include
LIBRARIES beginner_tutorials
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
include
${catkin_INCLUDE_DIRS}
)

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorials_generate_messages_cpp)

add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)

catkin_make

cd ~/catkin_ws
catkin_make 

Base path: /home/kezunlin/workspace/ros/catkin_ws
Source space: /home/kezunlin/workspace/ros/catkin_ws/src
Build space: /home/kezunlin/workspace/ros/catkin_ws/build
Devel space: /home/kezunlin/workspace/ros/catkin_ws/devel
Install space: /home/kezunlin/workspace/ros/catkin_ws/install
####
#### Running command: "make cmake_check_build_system" in "/home/kezunlin/workspace/ros/catkin_ws/build"
####
-- Using CATKIN_DEVEL_PREFIX: /home/kezunlin/workspace/ros/catkin_ws/devel
-- Using CMAKE_PREFIX_PATH: /home/kezunlin/workspace/ros/catkin_ws/devel;/home/kezunlin/catkin_ws/devel;/opt/ros/kinetic
-- This workspace overlays: /home/kezunlin/workspace/ros/catkin_ws/devel;/home/kezunlin/catkin_ws/devel;/opt/ros/kinetic
-- Using PYTHON_EXECUTABLE: /usr/bin/python
-- Using Debian Python package layout
-- Using empy: /usr/bin/empy
-- Using CATKIN_ENABLE_TESTING: ON
-- Call enable_testing()
-- Using CATKIN_TEST_RESULTS_DIR: /home/kezunlin/workspace/ros/catkin_ws/build/test_results
-- Found gtest: gtests will be built
-- Using Python nosetests: /usr/local/bin/nosetests-2.7
-- catkin 0.7.14
-- BUILD_SHARED_LIBS is on
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~  traversing 1 packages in topological order:
-- ~~  - beginner_tutorials
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- +++ processing catkin package: 'beginner_tutorials'
-- ==> add_subdirectory(beginner_tutorials)
-- Using these message generators: gencpp;geneus;genlisp;gennodejs;genpy
-- beginner_tutorials: 1 messages, 1 services
-- Configuring done
-- Generating done
-- Build files have been written to: /home/kezunlin/workspace/ros/catkin_ws/build
####
#### Running command: "make -j8 -l8" in "/home/kezunlin/workspace/ros/catkin_ws/build"
####
[  0%] [  0%] [  0%] [  0%] Built target std_msgs_generate_messages_cpp
Built target std_msgs_generate_messages_py
[  0%] Built target std_msgs_generate_messages_nodejs
Built target std_msgs_generate_messages_lisp
Built target std_msgs_generate_messages_eus
[  0%] [  0%] Built target _beginner_tutorials_generate_messages_check_deps_AddTwoInts
Built target _beginner_tutorials_generate_messages_check_deps_Num
[ 20%] [ 20%] [ 20%] [ 26%] [ 33%] [ 40%] [ 46%] Generating Lisp code from beginner_tutorials/Num.msg
Generating EusLisp code from beginner_tutorials/AddTwoInts.srv
Generating EusLisp code from beginner_tutorials/Num.msg
Generating Javascript code from beginner_tutorials/Num.msg
Generating Javascript code from beginner_tutorials/AddTwoInts.srv
Generating C++ code from beginner_tutorials/Num.msg
[ 53%] Generating C++ code from beginner_tutorials/AddTwoInts.srv
Generating Python from MSG beginner_tutorials/Num
[ 60%] Generating Lisp code from beginner_tutorials/AddTwoInts.srv
[ 66%] Generating Python code from SRV beginner_tutorials/AddTwoInts
[ 66%] Built target beginner_tutorials_generate_messages_nodejs
[ 73%] Built target beginner_tutorials_generate_messages_eus
[ 73%] Built target beginner_tutorials_generate_messages_lisp
[ 80%] [ 86%] Generating Python srv __init__.py for beginner_tutorials
Generating Python msg __init__.py for beginner_tutorials
[ 86%] Built target beginner_tutorials_generate_messages_cpp
Scanning dependencies of target listener
Scanning dependencies of target talker
[ 93%] [100%] Building CXX object beginner_tutorials/CMakeFiles/listener.dir/src/listener.cpp.o
Building CXX object beginner_tutorials/CMakeFiles/talker.dir/src/talker.cpp.o
[100%] Built target beginner_tutorials_generate_messages_py
[100%] Built target beginner_tutorials_generate_messages
Linking CXX executable /home/kezunlin/workspace/ros/catkin_ws/devel/lib/beginner_tutorials/talker
[100%] Built target talker
Linking CXX executable /home/kezunlin/workspace/ros/catkin_ws/devel/lib/beginner_tutorials/listener
[100%] Built target listener

play with talker and listener

roscore 

cd ~/catkin_ws
./devel/lib/beginner_tutorials/talker
./devel/lib/beginner_tutorials/listener

rosrun beginner_tutorials talker

[ INFO] [1545039519.457624559]: hello world 803
[ INFO] [1545039519.557624500]: hello world 804
[ INFO] [1545039519.657595852]: hello world 805
[ INFO] [1545039519.757538635]: hello world 806

rosrun beginner_tutorials listener

[ INFO] [1545039519.458099942]: I heard: [hello world 803]
[ INFO] [1545039519.558116295]: I heard: [hello world 804]
[ INFO] [1545039519.658085716]: I heard: [hello world 805]
[ INFO] [1545039519.757998160]: I heard: [hello world 806]

Quick Ref

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
# install and configure ros environment
## install
sudo sh -c '. /etc/lsb-release && echo "deb [arch=amd64] http://mirrors.ustc.edu.cn/ros/ubuntu/ $DISTRIB_CODENAME main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-key 421C365BD9FF1F717815A3895523BAEEB01FA116

sudo apt-get update
sudo apt-get install ros-kinetic-desktop-full
sudo apt-get install python-rosinstall python-rosinstall-generator python-wstool build-essential

sudo rosdep init
rosdep update

echo "source /opt/ros/kinetic/setup.bash" >> ~/.bashrc
source ~/.bashrc

env | grep ROS

## test
roscore

## create workspace
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
catkin_make
source devel/setup.bash
env | grep ROS

commands

  • rospack = ros + pack(age)
  • roscd = ros + cd
  • rosls = ros + ls

sudo apt-get install ros-kinetic-ros-tutorials

rosls roscpp_tutorials

rospack find roscpp

/opt/ros/kinetic/share/roscpp

roscd roscpp
pwd
/opt/ros/kinetic/share/roscpp

Note that roscd, like other ROS tools, will only find ROS packages that are within the directories listed in your ROS_PACKAGE_PATH.
echo $ROS_PACKAGE_PATH

roscd roscpp/cmake
pwd
/opt/ros/kinetic/share/roscpp/cmake

roscd log
pwd
/home/kezunlin/.ros/log/42e6e804-0199-11e9-93c2-80fa5b47928a

rosls roscpp
cmake msg package.xml rosbuild srv

Creating a ROS Package

package structure

workspace_folder/ – WORKSPACE
src/ – SOURCE SPACE
CMakeLists.txt – ‘Toplevel’ CMake file, provided by catkin
package_1/
CMakeLists.txt – CMakeLists.txt file for package_1
package.xml – Package manifest for package_1

package_n/
CMakeLists.txt – CMakeLists.txt file for package_n
package.xml – Package manifest for package_n

create a catkin package

catkin_create_pkg [depend1] [depend2] [depend3]

cd ~/catkin_ws/src
catkin_create_pkg beginner_tutorials roscpp rospy std_msgs

Created file beginner_tutorials/CMakeLists.txt
Created file beginner_tutorials/package.xml
Created folder beginner_tutorials/include/beginner_tutorials
Created folder beginner_tutorials/src

build a catkin workspace and source setup file

cd ~/catkin_ws/
catkin_make

. ~/catkin_ws/devel/setup.bash

package dependencies

first-order dependencies

rospack depends1 beginner_tutorials
roscpp
rospy
std_msgs

roscd beginner_tutorials
cat package.xml

<package format="2">
...
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>rospy</build_depend>
  <build_depend>std_msgs</build_depend>
...
</package>

Indirect dependencies

rospack depends1 rospy
genpy
roscpp
rosgraph
rosgraph_msgs
roslib
std_msgs

list all dependencies

rospack depends beginner_tutorials

Customizing Your Package

vim package.xml

Building a ROS Package

catkin_make

# In a CMake project
$ mkdir build
$ cd build
$ cmake ..
$ make
$ make install  # (optionally)

# In a catkin workspace
$ catkin_make
$ catkin_make install  # (optionally)

Understanding ROS Nodes

commands

  • roscore = ros+core : master (provides name service for ROS) + rosout (stdout/stderr) + parameter server (parameter server will be introduced later)
  • rosnode = ros+node : ROS tool to get information about a node.
  • rosrun = ros+run : runs a node from a given package.

concepts

Quick Overview of Graph Concepts

  • Nodes: A node is an executable that uses ROS to communicate with other nodes.
  • Messages: ROS data type used when subscribing or publishing to a topic.
  • Topics: Nodes can publish messages to a topic as well as subscribe to a topic to receive messages.
  • Master: Name service for ROS (i.e. helps nodes find each other)
  • rosout: ROS equivalent of stdout/stderr
  • roscore: Master + rosout + parameter server (parameter server will be introduced later)

Client Libraries

  • rospy = python client library
  • roscpp = c++ client library

play with nodes

roscore

rosnode list
/rosout

rosnode info /rosout

--------------------------------------------------------------------------------
Node [/rosout]
Publications: 
 * /rosout_agg [rosgraph_msgs/Log]

Subscriptions: 
 * /rosout [unknown type]

Services: 
 * /rosout/get_loggers
 * /rosout/set_logger_level


contacting node http://ke:40803/ ...
Pid: 31049

run new nodes

rosrun [package_name] [node_name]

rosrun turtlesim turtlesim_node

rosnode list
/rosout
/turtlesim

rosnode info /turtlesim

rename node name

rosrun turtlesim turtlesim_node __name:=my_turtle

rosnode list
/my_turtle
/rosout

Note: If you still see /turtlesim in the list, it might mean that you stopped the node in the terminal using ctrl-C instead of closing the window

ping node

rosnode ping my_turtle
rosnode: node is [/my_turtle]
pinging /my_turtle with a timeout of 3.0s
xmlrpc reply from http://ke:33523/ time=0.274181ms
xmlrpc reply from http://ke:33523/ time=1.040220ms
xmlrpc reply from http://ke:33523/ time=1.013041ms
^Cping average: 0.775814ms

Understanding ROS Topics

roscore
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key

rosrun rqt_graph rqt_graph

rostopic echo /turtle1/cmd_vel

linear:
x: 2.0
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.0

rostopic list -v

Published topics:
 * /turtle1/color_sensor [turtlesim/Color] 1 publisher
 * /turtle1/cmd_vel [geometry_msgs/Twist] 1 publisher
 * /rosout [rosgraph_msgs/Log] 3 publishers
 * /rosout_agg [rosgraph_msgs/Log] 1 publisher
 * /turtle1/pose [turtlesim/Pose] 1 publisher

Subscribed topics:
 * /turtle1/cmd_vel [geometry_msgs/Twist] 1 subscriber
 * /rosout [rosgraph_msgs/Log] 1 subscriber
 * /statistics [rosgraph_msgs/TopicStatistics] 1 subscriber

rostopic type /turtle1/cmd_vel
geometry_msgs/Twist

rosmsg show geometry_msgs/Twist
geometry_msgs/Vector3 linear
float64 x
float64 y
float64 z
geometry_msgs/Vector3 angular
float64 x
float64 y
float64 z

rostopic type /turtle1/cmd_vel | rosmsg show

publish messages

rostopic pub [topic] [msg_type] [args]

rostopic pub /turtle1/cmd_vel geometry_msgs/Twist “linear:
x: 2.0
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 1.8”

rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist – ‘[2.0, 0.0, 0.0]’ ‘[0.0, 0.0, 1.8]’

rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -r 1 – ‘[2.0, 0.0, 0.0]’ ‘[0.0, 0.0, -1.8]’

YAML syntax

rostopic hz /turtle1/pose
$ 60 HZ

rosrun rqt_plot rqt_plot

Understanding ROS Services and Parameters

rosservice

Services are another way that nodes can communicate with each other. Services allow nodes to send a request and receive a response.

rosnode list
/rosout
/rqt_gui_py_node_1614
/teleop_turtle
/turtlesim

rosservice list
/clear
/kill
/reset
/rosout/get_loggers
/rosout/set_logger_level
/rqt_gui_py_node_1614/get_loggers
/rqt_gui_py_node_1614/set_logger_level
/spawn
/teleop_turtle/get_loggers
/teleop_turtle/set_logger_level
/turtle1/set_pen
/turtle1/teleport_absolute
/turtle1/teleport_relative
/turtlesim/get_loggers
/turtlesim/set_logger_level

rosservice type [service]
rosservice call [service] [args]

rosservice type /clear
std_srvs/Empty

rosservice call /clear

clear the background of turtlesim_node

rosservice type /spawn | rossrv show
float32 x
float32 y
float32 theta
string name

string name

rosservice call /spawn 2 2 0.2 “”

This service lets us spawn a new turtle at a given location and orientation. The name field is optional.

rosparam

rosparam list
/background_b
/background_g
/background_r
/rosdistro
/roslaunch/uris/host_ke__35701
/rosversion
/run_id

rosparam set [param_name]
rosparam get [param_name]

rosparam set /background_r 150
rosservice call /clear

rosparam get /
background_b: 255
background_g: 86
background_r: 150
rosdistro: ‘kinetic


roslaunch:
uris: {host_ke__35701: ‘http://ke:35701/'}
rosversion: ‘1.12.14


run_id: eb9e9f1e-01a3-11e9-93c2-80fa5b47928a

rosparam get /background_b
255

rosparam dump [file_name] [namespace]
rosparam load [file_name] [namespace]

rosparam dump params.yaml

rosparam load params.yaml copy
rosparam get /copy/background_b

Using rqt_console and roslaunch

rosrun rqt_console rqt_console
rosrun rqt_logger_level rqt_logger_level

2 windows pop up.

logger levels

Fatal
Error
Warn
Info
Debug

roslaunch starts nodes as defined in a launch file.
roslaunch [package] [filename.launch]

cd ~/catkin_ws
source devel/setup.bash
roscd beginner_tutorials

mkdir launch
cd launch

vim turtle.launch

rosnode list
/mimic
/rosout
/turtlesim1/sim
/turtlesim2/sim

rostopic list
/rosout
/rosout_agg
/turtlesim1/turtle1/cmd_vel
/turtlesim1/turtle1/color_sensor
/turtlesim1/turtle1/pose
/turtlesim2/turtle1/cmd_vel
/turtlesim2/turtle1/color_sensor
/turtlesim2/turtle1/pose

rostopic pub /turtlesim1/turtle1/cmd_vel geometry_msgs/Twist -r 1 – ‘[2.0, 0.0, 0.0]’ ‘[0.0, 0.0, -1.8]’

You will see the two turtlesims start moving even though the publish command is only being sent to turtlesim1.

rosed

rosed roscpp

rosed roscpp Logger.msg

Creating a ROS msg and srv

  • msg: msg files are simple text files that describe the fields of a ROS message. They are used to generate source code for messages in different languages.
  • srv: an srv file describes a service. It is composed of two parts: a request and a response.

msg

msgs are just simple text files with a field type and field name per line. The field types you can use are:

int8, int16, int32, int64 (plus uint*)
float32, float64
string
time, duration
other msg files
variable-length array[] and fixed-length array[C]

There is also a special type in ROS: Header, the header contains a timestamp and coordinate frame information that are commonly used in ROS.

Header header
string child_frame_id
geometry_msgs/PoseWithCovariance pose
geometry_msgs/TwistWithCovariance twist

srv

int64 A
int64 B

int64 Sum

A and B are the request, and Sum is the response.

create msg

roscd beginner_tutorials
mkdir msg
echo “int64 num” > msg/Num.msg

vim package.xml

message_generation
message_runtime

vim CMakeLists.txt

find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)

catkin_package(

CATKIN_DEPENDS message_runtime …
…)

add_message_files(
FILES
Num.msg
)

create srv

roscd beginner_tutorials
mkdir srv

roscp [package_name] [file_to_copy_path] [copy_path]
roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv

vim CMakeLists.txt

add_service_files(
FILES
AddTwoInts.srv
)

show msg

rosmsg show beginner_tutorials/Num
int64 num

rosmsg show Num
[beginner_tutorials/Num]:
int64 num

show srv

rossrv show
rossrv show beginner_tutorials/AddTwoInts

int64 a
int64 b
---
int64 sum

rossrv show AddTwoInts

[beginner_tutorials/AddTwoInts]:
int64 a
int64 b
---
int64 sum

[rospy_tutorials/AddTwoInts]:
int64 a
int64 b
---
int64 sum

catkin_make

roscd beginner_tutorials
cd ../..
catkin_make install

review

rospack = ros+pack(age) : provides information related to ROS packages
roscd = ros+cd : changes directory to a ROS package or stack
rosls = ros+ls : lists files in a ROS package
roscp = ros+cp : copies files from/to a ROS package
rosmsg = ros+msg : provides information related to ROS message definitions
rossrv = ros+srv : provides information related to ROS service definitions
catkin_make : makes (compiles) a ROS package
rosmake = ros+make : makes (compiles) a ROS package (if you’re not using a catkin workspace)

Writing a Simple Publisher and Subscriber

src

roscd beginner_tutorials
vim src/talker.cpp
vim src/listener.cpp

build

cd ~/catkin_ws
catkin_make

play with talker and listener

roscore

rosrun beginner_tutorials talker

[ INFO] [1545039519.457624559]: hello world 803
[ INFO] [1545039519.557624500]: hello world 804
[ INFO] [1545039519.657595852]: hello world 805
[ INFO] [1545039519.757538635]: hello world 806

rosrun beginner_tutorials listener

[ INFO] [1545039519.458099942]: I heard: [hello world 803]
[ INFO] [1545039519.558116295]: I heard: [hello world 804]
[ INFO] [1545039519.658085716]: I heard: [hello world 805]
[ INFO] [1545039519.757998160]: I heard: [hello world 806]

Reference

History

  • 20181214: created.

Guide

install

1
sudo apt-get -y install tmux

usage

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
1) 默认创建一个会话,以数字命名。(不推荐)
[root@Centos6 ~]# tmux

2)新建会话,比如新创建一个会话以"ccc"命名
[root@Centos6 ~]# tmux new -s ccc

1) 加上参数-d,表示在后台新建会话
root@bobo:~# tmux new -s shibo -d
root@bobo:~# tmux ls
shibo: 1 windows (created Tue Oct 2 19:22:32 2018) [135x35]

4)查看创建得所有会话
[root@Centos6 ~]# tmux ls
0: 1 windows (created Wed Aug 30 17:58:20 2017) [112x22](attached) #attached表示该会话是当前会话
aaa: 2 windows (created Wed Aug 30 16:54:33 2017) [112x22]
ccc: 1 windows (created Wed Aug 30 17:01:05 2017) [112x22]

5)从终端环境进入会话
[root@Centos6 ~]# tmux attach -t aaa

6)快捷键退出会话:
登到某一个会话后,依次按键ctrl-b + d,这样就会退化该会话,但不会关闭会话;
按键ctrl + d,就会在退出会话的通话也关闭了该会话。

7)关闭会话(销毁会话)
[root@Centos6 ~]# tmux ls
aaa: 2 windows (created Wed Aug 30 16:54:33 2017) [112x22]
bbb: 1 windows (created Wed Aug 30 19:02:09 2017) [112x22]

[root@Centos6 ~]# tmux kill-session -t bbb

[root@Centos6 ~]# tmux ls
aaa: 2 windows (created Wed Aug 30 16:54:33 2017) [112x22]

8)重命名会话
[root@Centos6 ~]# tmux ls
wangshibo: 1 windows (created Sun Sep 30 10:17:00 2018) [136x29] (attached)

[root@Centos6 ~]# tmux rename -t wangshibo kevin

[root@Centos6 ~]# tmux ls
kevin: 1 windows (created Sun Sep 30 10:17:00 2018) [136x29] (attached)

9)发送命令到回话并执行
[root@Centos6 ~]# tmux send -t session_name "command" ENTER

nohup

nohup <command> [argument…] &

使用tmux打开一个终端窗口,可以在窗口里执行一个长时间运行的交互式命令操作,令其一直在后台跑着,并且在按键ctrl-b-d后,可以无感知的退出窗口,
而退出后窗口不会关闭,即窗口里执行的交互命令也不会结束。这比起传统的”nohup command & (然后按ctrl+c)”的方式要好用很多。

1
2
3
4
tmux new -s aaa

sudo apt-get -y update
ctrl+b + d

similar tools

  • screen

Reference

History

  • 20181214: created.

Guide

install

1
2
pip3 install pywin32 pyinstaller
pip3 install --upgrade setuptools

pyinstall -F demo.py error

pyinstaller AttributeError: 'str' object has no attribute 'items'

solution:

1
pip3 install --upgrade setuptools

usage

1
pyinstaller -h 

params:

  • General Options

    -y, –noconfirm
    Replace output directory (default: SPECPATH/dist/SPECNAME)
    without asking for confirmation
    –upx-dir UPX_DIR
    Path to UPX utility (default: search the execution path)

    –clean
    Clean PyInstaller cache and remove temporary files before building.

    –log-level LEVEL
    Amount of detail in build-time console messages. LEVEL may be one of
    TRACE, DEBUG, INFO, WARN, ERROR, CRITICAL (default: INFO).

  • What to generate

    -D, --onedir	
        Create a one-folder bundle containing an executable (default)
    
    -F, --onefile	
        Create a one-file bundled executable.
    
  • What to bundle, where to search

    --add-data <SRC;DEST or SRC:DEST>  
        This option can be used multiple times.
    
    --add-binary <SRC;DEST or SRC:DEST> 
        This option can be used multiple times.
    
    -p DIR, --paths DIR 
        A path to search for imports (like using PYTHONPATH). 
        Multiple paths are allowed, separated by ‘:’, or use 
        this option multiple times
    
    --hidden-import MODULENAME, --hiddenimport MODULENAME 
        Name an import not visible in the code of the script(s). 
        This option can be used multiple times.
    
  • Windows and Mac OS X specific options

    -c, --console, --nowindowed
        Open a console window for standard i/o (default)
    -w, --windowed, --noconsole
        Windows and Mac OS X: do not provide a console window 
        for standard i/o
    

Example

use pyd

path related source code

1
2
sys.path.append('./sdk/superdog/')
import superdog # superdog.pyd

pyinstaller commands

1
2
3
pyinstaller -y -D --path="sdk/superdog" demo.py
# pyinstaller -y -D --add-binary './sdk/superdog/superdog.pyd;.'
# --add-binary './sdk/superdog/dog_windows_x64.dll;.' demo.py

generate build and dist folder, plus demo.spec

output

78 INFO: Extending PYTHONPATH with paths
['E:\\git\\python\\helloworld',
 'E:\\git\\python\\helloworld\\sdk\\superdog',
 'E:\\git\\python\\helloworld']

demo.spec

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
# -*- mode: python -*-

block_cipher = None


a = Analysis(['demo.py'],
pathex=['sdk/superdog', 'E:\\git\\python\\helloworld'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='demo',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='demo')

view build/demo/xref-demo.html

superdog found.

Tips:
if we use pyinstaller -y -D demo.py don’t include --path="sdk/superdog",
package will be missing, and error occur when we run executable.

run executable

1
2
3
cd dist/demo
./demo.exe
# OK

all related dlls have been copied to dist/demo/ folder,
eg. cublas64_80.dll,curand64_80.dll,cudart64_80.dll,cudnn64_6.dll

use ctype DLLs

path related source code

1
2
3
4
sys.path.append('./sdk/superdog/')
sys.path.append('./sdk/detect/')
import superdog # superdog.pyd
import detect # detect.py cuda80_detect.dll cuda90_detect.dll

CDLL related source code

1
lib = CDLL("./sdk/detect/cuda80_detect_cpp_dll.dll", RTLD_GLOBAL)

pyinstaller commands

1
pyinstaller -y -D --path="sdk/superdog;sdk/detect" demo.py

warning

93 INFO: Extending PYTHONPATH with paths
['E:\\git\\python\\helloworld',
 'E:\\git\\python\\helloworld\\sdk\\superdog',
 'E:\\git\\python\\helloworld\\sdk\\detect',
 'E:\\git\\python\\helloworld']
  ...
WARNING: Ignoring ./sdk/detect/cuda80_detect_cpp_dll.dll imported
    from E:\git\python\helloworld\sdk\detect\detect.py -
    ctypes imports are only supported using bare filenames

fix

1
2
#lib = CDLL("./sdk/detect/cuda80_detect_cpp_dll.dll", RTLD_GLOBAL)
lib = CDLL("cuda80_detect_cpp_dll.dll", RTLD_GLOBAL)

Tips: for CDLL with pyinstaller, we must use bare filenames in python source.
see here

now we run pyintaller again

1
pyinstaller -y -D --path="sdk/superdog;sdk/detect" demo.py

view build/demo/xref-demo.html

superdog and detect.

run executable

1
2
3
cd dist/demo
./demo.exe
# OK

all related dlls have been copied to dist/demo/ folder,
eg. cublas64_80.dll,curand64_80.dll,cudart64_80.dll,cudnn64_6.dll
and cuda80_detect_cpp_dll.dll

Version

get version

1
python grab_version.py "/path/to/xxx.exe"

C:\Users\zunli\AppData\Local\Programs\Python\Python35\Lib\site-packages
PyInstaller\utils\cliutils\grab_version.py

qq version info

file_version_info.txt

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
# UTF-8
#
# For more details about fixed file info 'ffi' see:
# http://msdn.microsoft.com/en-us/library/ms646997.aspx
VSVersionInfo(
ffi=FixedFileInfo(
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
# Set not needed items to zero 0.
filevers=(9, 0, 4, 23780),
prodvers=(9, 0, 4, 23780),
# Contains a bitmask that specifies the valid bits 'flags'r
mask=0x3f,
# Contains a bitmask that specifies the Boolean attributes of the file.
flags=0x0,
# The operating system for which this file was designed.
# 0x4 - NT and there is no need to change it.
OS=0x40004,
# The general type of file.
# 0x1 - the file is an application.
fileType=0x1,
# The function of the file.
# 0x0 - the function is not defined for this fileType
subtype=0x0,
# Creation date and time stamp.
date=(0, 0)
),
kids=[
StringFileInfo(
[
StringTable(
u'080404b0',
[StringStruct(u'CompanyName', u'Tencent'),
StringStruct(u'FileDescription', u'腾讯QQ'),
StringStruct(u'FileVersion', u'9.0.4.23780'),
StringStruct(u'LegalCopyright', u'Copyright (C) 1999-2018 Tencent. All Rights Reserved'),
StringStruct(u'ProductName', u'腾讯QQ'),
StringStruct(u'ProductVersion', u'9.0.4.23780')])
]),
VarFileInfo([VarStruct(u'Translation', [2052, 1200])])
]
)

set version

1
2
3
4
5
6
python pyinstaller.py \
--version-file=file_version_info.txt \
--icon=ico.ico \
-y -D \
--path="sdk/superdog;sdk/detect" \
demo.py

Advanced

Running PyInstaller with Python optimizations

Tips: be carefull with optimizations.

PyInstaller can be run with Python optimization flags (-O or -OO)
by executing it as a Python module, rather than using the pyinstaller command:

1
python -O -m PyInstaller -y -D --path="sdk/superdog" demo.py

Or, by explicitly setting the PYTHONOPTIMIZE environment variable to a non-zero value:

1
2
3
4
5
# Unix
PYTHONOPTIMIZE=1 pyinstaller myscript.py

# Windows
set PYTHONOPTIMIZE=1 && pyinstaller myscript.py

only import what you need

replace import os with from os import path to reduce final executable size.

Using UPX

see upx

Encrypting Python Bytecode

To encrypt the Python bytecode modules stored in the bundle, pass the --key=key-string argument on the command line.

For this to work, you must have the PyCrypto module installed. The key-string is a string of 16 characters which is used to encrypt each file of Python byte-code before it is stored in the archive inside the executable file.

1
pip install pycrypto

Other similar tools

Reference

History

  • 20181213: created.

Series

Guide

requirements

my system requirements (same as caffe on ubuntu 16.04)

  • ubuntu 16.04
  • GeForce 1060 (6G) sm_61
  • cuda: 9.2
  • cudnn: 7.1.4
  • opencv: 3.3.0
  • caffe: latest

compile

1
2
3
git clone https://github.com/kezunlin/caffe-yolov3.git
cd caffe-yolov3
mkdir build && cd build && cmake-gui ..

caffe-yolov3 is based on caffe with UpsampleLayer and darknet.
tips: edit CMakeLists.txt for caffe.
see CMakeLists.txt

make

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
make -j8
make install

tree install
install
├── bin
│   ├── demo
│   ├── dog.jpg
│   └── libcaffeyolo.so
├── include
│   └── caffeyolo
│   ├── activations.h
│   ├── blas.h
│   ├── box.h
│   ├── cuda.h
│   ├── image.h
│   └── yolo_layer.h
├── lib
│   └── libcaffeyolo.so
└── share
└── cmake
└── caffeyolo
├── caffeyolo-config.cmake
└── caffeyolo-config-noconfig.cmake

7 directories, 12 files

demo

1
2
cd install/bin
./demo

output

num_inputs is 1
num_outputs is 3
I1211 17:13:30.259755 10384 detectnet.cpp:74] Input data layer channels is  3
I1211 17:13:30.259785 10384 detectnet.cpp:75] Input data layer width is  416
I1211 17:13:30.259806 10384 detectnet.cpp:76] Input data layer height is  416
output blob1 shape c= 255, h = 13, w = 13
output blob2 shape c= 255, h = 26, w = 26
output blob3 shape c= 255, h = 52, w = 52
object-detection:  finished processing data operation  (392)ms
object-detection:  finished processing yolov3 network  (135)ms
16: 99%
x = 0.288428,y =  0.660513,w = 0.243282,h =  0.543122
left = 128,right =  314,top = 224,bot =  536
7: 93%
x = 0.756588,y =  0.222789,w = 0.280549,h =  0.147772
left = 473,right =  688,top = 85,bot =  170
1: 99%
x = 0.473371,y =  0.483899,w = 0.517509,h =  0.575438
left = 164,right =  562,top = 112,bot =  444
detectnet-camera:  video device has been un-initialized.
detectnet-camera:  this concludes the test of the video device.

image
dog.jpg

net

input && output

input:

  • data 1,3,416,416

output

  • layer82-conv 1,255,13,13
  • layer94-conv 1,255,26,26
  • layer106-conv 1,255,52,52

python code

1
2
3
4
5
6
7
8
9
10
11
12
13
### Input: the model's output dict
### Output: list of tuples in ((cx1, cy1), (cx2, cy2), cls, prob)
def rects_prepare(output, inp_dim=416, num_classes=80):
prediction = None
# transform prediction coordinates to correspond to pixel location
for key, value in output.items():
# anchor sizes are borrowed from YOLOv3 config file
if key == 'layer82-conv':
anchors = [(116, 90), (156, 198), (373, 326)]
elif key == 'layer94-conv':
anchors = [(30, 61), (62, 45), (59, 119)]
elif key == 'layer106-conv':
anchors = [(10, 13), (16, 30), (33, 23)]

yolov3 model

darknet

files

  • yolov3.weights
  • yolov3.cfg
  • coco.names

caffe-yolov3

files

  • yolov3.caffemodel
  • yolov3.prototxt
  • coco.names
  • yolov3-cpp.prototxt
  • yolov3-trt.prototxt

yolov3-cpp.prototxt

compared with yolov3.prototxt

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
layer {
bottom: "layer82-conv"
bottom: "layer94-conv"
bottom: "layer106-conv"
type: "Yolov3DetectionOutput"
top: "detection_out"
name: "detection_out"
yolov3_detection_output_param {
nms_threshold: 0.45
num_classes: 80
biases: 10
biases: 13
biases: 16
biases: 30
biases: 33
biases: 23
biases: 30
biases: 61
biases: 62
biases: 45
biases: 59
biases: 119
biases: 116
biases: 90
biases: 156
biases: 198
biases: 373
biases: 326
mask: 6
mask: 7
mask: 8
mask: 3
mask: 4
mask: 5
mask: 0
mask: 1
mask: 2
mask_group_num: 3
anchors_scale: 32
anchors_scale: 16
anchors_scale: 8
}
}

Use Yolov3DetectionOutput layer with caffe (Upsample+Yolov3DetectionOutput)

yolov3-trt.prototxt

see yolov3-trt.prototxt
compared with yolov3-cpp.prototxt

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
layer {
bottom: "layer85-conv"
top: "layer86-upsample"
name: "layer86-upsample"
type: "Upsample"
#upsample_param {
# scale: 2
#}
}
...

layer {
bottom: "layer97-conv"
top: "layer98-upsample"
name: "layer98-upsample"
type: "Upsample"
#upsample_param {
# scale: 2
#}
}
...


layer {
bottom: "layer82-conv"
bottom: "layer94-conv"
bottom: "layer106-conv"
top: "yolo-det"
name: "yolo-det"
type: "Yolo"
}

Use YoloLayerPlugin plugin with TensorRT

caffe extension layer

UpsampleLayer

see caffe UpsampleLayer

upsample

Yolov3DetectionOutputLayer

  • proto: caffe.proto
  • yolov3_detection_output_layer.hpp: header
  • yolov3_detection_output_layer.cpp: cpp

yolov3 with tensorrt

Reference

History

  • 20181211: created.

Guide

support framework

cv2.dnn.readNetFromCaffe: deploy.prototxt + iter_140000.caffemodel
cv2.dnn.readNetFromTensorflow: model.pbtxt + model.pb.
cv2.dnn.readNetFromDarknet: yolov3.cfg + yolov3.weights
cv2.dnn.readNetFromTorch: model.t7

readNetFromCaffe

1
2
3
4
5
6
# load our serialized face detector from disk
print("[INFO] loading face detector...")
protoPath = os.path.sep.join([args["detector"], "deploy.prototxt"])
modelPath = os.path.sep.join([args["detector"],
"res10_300x300_ssd_iter_140000.caffemodel"])
net = cv2.dnn.readNetFromCaffe(protoPath, modelPath)

readNetFromTensorflow

1
2
3
4
5
6
7
8
9
10
# derive the paths to the Mask R-CNN weights and model configuration
weightsPath = os.path.sep.join([args["mask_rcnn"],
"frozen_inference_graph.pb"])
configPath = os.path.sep.join([args["mask_rcnn"],
"mask_rcnn_inception_v2_coco_2018_01_28.pbtxt"])

# load our Mask R-CNN trained on the COCO dataset (90 classes)
# from disk
print("[INFO] loading Mask R-CNN from disk...")
net = cv2.dnn.readNetFromTensorflow(weightsPath, configPath)

readNetFromDarknet

1
2
3
4
5
6
7
# derive the paths to the YOLO weights and model configuration
weightsPath = os.path.sep.join([args["yolo"], "yolov3.weights"])
configPath = os.path.sep.join([args["yolo"], "yolov3.cfg"])

# load our YOLO object detector trained on COCO dataset (80 classes)
print("[INFO] loading YOLO from disk...")
net = cv2.dnn.readNetFromDarknet(configPath, weightsPath)

readNetFromTorch

1
2
3
4
5
6
7
# load our serialized face embedding model from disk
print("[INFO] loading face recognizer...")
net = cv2.dnn.readNetFromTorch(args["embedding_model"])

# load the neural style transfer model from disk
print("[INFO] loading style transfer model...")
net = cv2.dnn.readNetFromTorch(args["model"])

Reference

History

  • 20181207: created.

Guide

how to install doxygen

1
2
$ sudo apt-get install doxygen
$ sudo apt-get install graphviz

how to use doxygen

1
$ cd path/to/yourproject

generate a Doxyfile with

1
2
3
$ doxygen -g 

$ ll Doxyfile

generate doc html and latex with

1
doxygen Doxyfile

output

Searching for include files...
Searching for example files...
Searching for images...
Searching for dot files...
Searching for msc files...
Searching for dia files...
Searching for files to exclude
Searching INPUT for files to process...
...

view doxygen result

open html/index.html and view results

doxygen index.html

History

  • 20181204: created.