Quantcast
Channel: Sam的技术Blog
Viewing all 158 articles
Browse latest View live

HANDS-FREE学习 

$
0
0
作者:Sam (甄峰)  sam_code@hotmail.com

0.Hands-Free简介:
HANDS FREE是一个面向机器人研究,开发的开源软硬件平台。
从某种意义来说,这个项目是提供一个比较完整的软件架构+一个机器人底盘。开发者可以依托这个软件架构,开发或修改架构内某些模块,并把这个软件架构实施与上位机中,同时Hands Free项目提供的硬件平台则是一个底盘,它则是作为下位机。接受上位机指令,执行指令,并反馈信息(速度/距离)。

HANDS FREE项目中包含机器人导航,SLAM,计算机视觉等模块,并有自己的上层软件和调试系统,它也支持国外的开源项目,如ROS,MPRT, PIXHAWK等。


1. 软件模块介绍
1.1: OpenRE
Open Source Rebot Embedded Library。机器人嵌入式库。它是为机器人构建的,基于STM32系列微处理器的嵌入式开源库。最早,它是Hands Free 项目的Embedded库,使用Windows Keil开发。
当前开发环境变化为:Linux下,Makefile +QTCreator+ARMGCC.
总的来说,它就是为了方便在各种硬件平台下,制作底盘的例子和库。

0_Project: 工程入口,对不同平台的示例。
1_Processor: 与CPU有关的固件库和底层接口函数。
2_Package: 功能包。与底层无关的功能包。涵盖了伺服设备,传感器,输入输出设备包,算法包,通信包。是OpenRE的核心部分之一。
3_OS: OS层面的支持库,涵盖RTOS, GUI,FSTFS, IO等。
4_LIbS: 放置第三方库,如Eigen.
TOOLS: 编译和烧入工具。
HANDS-FREE-OpenRE.pro: QT工程文件,Qt Creator打开编辑。Make来编译。

2_Package中,有Sam感兴趣的几个部分:
imu:IMU包,加速度,陀螺仪,磁力等。(有文档:Hands_Free_IMU_Algorithm.pdf)
motor:电机包。负责所有电机的速度闭环控制。
控制算法,运动学和动力学模型。

这个模块,其实就是Hands Free的核心,它的思路不是要提供一个完整的机器人,只是解决大家实际工作中的一个大问题,无法构建自己的机器人底层,如运动地盘,行走部件等。





1.2:PCB:
各机器人的实体资料。PCB,电路等。
JILONG, STONE等几个机器人。

1.3:Robot:
基于Hands Free底层构建的几个Robot资料。主要是文档。

1.4:ROS_DEMO:
Hands Free平台在ROS下的实现代码,

1.5: PIL:
一个计算机视觉库。


2. 硬件介绍
2.1:主控:
主控为STM32, 外接Sensor有MPU6050, HMC5883L磁力计,MS5611气压计。
2.2:电机驱动:
使用BTS7960B .
2.3: 电源分配模块:


总结:
Hands Free提供了一套硬件,与之匹配的Firmware, 让我们从底层地盘研究中脱离出来。把注意力集中在上位机各模块的开发中去。

同时,它有提供了底层各个库,如对电机控制,IMU等。让大家可以参考这个设计,制作自己的底盘。

为OpenSource,为Hands-Free点赞。






 

HANDS-FREE学习 

$
0
0
作者:Sam (甄峰)  sam_code@hotmail.com


之前介绍过Hands-Free,现在开始介绍如何下载Hands-Free相关ROS Package, 并编译运行之。

0. ROS版本,Cmake版本:
强烈建议使用indigo.   CMake:2.8.12.2或之上
Sam之前使用hydio, cmake 版本也较低, 结果编译时出各类错误。


1.  下载Hands-Free相关ROS Package-----ROS_DEMO
https://github.com/HANDS-FREE是Hands-Free的github仓库,其中https://github.com/HANDS-FREE/ROS_DEMO 是ROS Package.

https://github.com/HANDS-FREE/ROS_DEMO.git

Sam在catkin_ws/src/hands-free目录下,运行:
$sudo git clone https://github.com/HANDS-FREE/ROS_DEMO.git
将ROS_DEMO加入catkin workspace.

2. 运行脚本,安装依赖包:
在catkin_ws/src/hands-free/ROS_DEMO/Documention/中,有个environment_config.sh, 它里面描述了要安装的一些依赖包。
$sudo  apt-get install xxxx

3. 修改main.cpp内容
catkin_ws/src/hands-free/ROS_DEMO/handsfree_hw/src/main.cpp 的第7行,修改为自己的路径。

4. catkin_make


5. 
source catkin_ws/devel/setup.bash

下面就可以运行ROS_DEMO的例子了。






附1:
setup.bash和setup.sh解读:

$(BASH_SOURCE[0]) 
这个用法很关键, 在不同目录下,不同调用方式下,要获取BASH SOURCE File name, 就可以使用它来获取。
例如:
test/build/目录内,有个env.bash文件,
可以在test/build目录内,./env.bash 调用, 也可以对本bash起效的方式:

A:不同调用方式:
. ./env.bash  或:bash  env.bashsource env.bash
都想获取Bash Source 文件。
echo ${BASH_SOURCE[0]}
则以上调用方式,都得到env.bash.

B:不同调用目录:
在 build目录,在test目录,在test/build目录下,分别调用:
得到 build/env.bash
test/build/env.bash
env.bash

所以,很好的得到了bash 文件。


dirname : 取目录名

所以:setup.bash
_CATKIN_SETUP_DIR=$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)
. "$_CATKIN_SETUP_DIR/setup.sh"
含义是,找到这个目录,并运行: . /home/sam/catkin_ws/devel/setup.sh





 

ROS学习 ROS消息

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

1. ROS消息类型和消息文件
ROS语言中立,支持Python, C++, Lisp等。为了支持交叉语言,ROS利用了简单的,语言无关的接口定义语言去描述模块间的消息传递。
它使用这种简化的消息类型描述语言来描述ROS节点发布的数据值,通过这样的描述语言,ROS能够使用多种编程语言生成不同类型消息的源代码。

ROS提供了很多预定义的消息类型,如果我们需要创建一个新的类型,就要把消息的类型定义放在功能包的msg文件夹下(Package介绍时说过)。 在此文件夹下,有用于定义各种消息的文件,它们都以 .msg为扩展名。


2. MSG文件格式
消息类型必须有两个主要部分:字段,常量。
字段定义了消息中传输的数据类型,如:int32, float32, string.
常量用于定义字段的名称。

int32 id
float32 vel
string name 


3. MSG文件的自动解读
没种语言的代码生成器会通过解读MSG文件,生成类似本种语言的目标文件。


4. 创建自定义消息文件:
一些情况下,需要传输特殊类型的消息, 这时,就需要自定义传输数据类型和数据值的文件. ROS会根据这些文件内容自动的为我们创建对应语言(C++, Python....)的代码,以便msg能够被节点使用。
4.1:创建MSG文件
在功能包目录下,创建msg文件夹,并创建一个msg文件:
test_message.msg
内容如下:
int32 width
int32 height
float32 radius

4.2: 配置编译文件
这个部分比较复杂,先记录下来,后面再具体研究:
A: 确保package.xml内容: 

确保在build_depend中包含:generate_messages
在run_depend中包含:message_runtime

B:确保在CMakefiles.txt中:
B1.把message_generation加入到find_package中


B:2.  确保CATKIN_DEPENDS message_runtime被加入到catkin_package中。


B3:确保刚才建立的msg目录内的.msg文件被正确加入:



B4:确保generate_messages()被打开:



4.3: 编译生成对应语言头文件:
catkin_make
可以在 catkin_ws/devel/include/test_source/test_message.h
看到这个文件。这就是自动创建的头文件。







 

ROS学习  ROS程序的GDB调试和调试信息

$
0
0
作者:Sam (甄峰)  sam_code@hotmail.com

在使用C++开发ROS程序时,经常需要逐行调试代码。 这时就需要用到GDB了。

之前在Linux上使用GDB, 在嵌入式系统或Android使用过GDB Server。 知道GDB使用中需要注意哪些问题。
1. 编译时,需要加入-g 选项。
2. 不能有任何strip的动作。不能被去掉debug信息。

1. 处理CMakeLists.txt文件:
set (CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS}  -g ")
set (CMAKE_VERBOSE_MAKEFILE ON)

第一句保证了CXXFLAGS被加上了-g
第二局保证了具体的Make语句被显示出来,我们可以实际检查编译选项是否正常。

2. 运行语句:
2.1: 运行ros master
$roscore
2.2: 运行想要Debug的程序:
进入catkin_ws/devel/lib/Package_Name. 可以看到可执行文件。
$gdb ./example2_sam

3. DEBUG :
(gdb) l
可以看到代码。

(gdb) b 20
在20行加入Bread点。

(gdb) r
开始运行。 只有此时,Node才会被建立起来。


3. launch文件启用GDB:



4. CORE文件相关:




5. 调试信息
在调试程序时,可以通过信息记录显示程序的运行状态。但需要这样的信息记录的行为不会影响软件的运行效率,不会和正常的输出混淆。
ROS中,就有满足以上要求,内置于log4cxx之上的API。
与Kernel中printk类似,ROS的调试信息也包含不同的信息级别:

5.1:调试信息宏:
ROS_DEBUG()
ROS_INFO()
ROS_WARN()
ROS_ERROR()
ROS_FATAL()

ROS_DEBUG()和ROS_INFO()通常显示在stdout上。其它的则显示在stderr上。且颜色也不同。

5.2: 信息级别
默认情况下,系统会显示INFO和更高级别的调试信息。
想要调节这个级别,可以在编译时设定,也可以在在执行前使用配制文件调整,也可以在运行中,使用工具动态调节。

方法一:
在代码中调节:
在ROS::init()后,加入代码

方法二:
在CMakeLists.txt 中,添加宏记录去除器。

方法三:








 

ROS学习  外设的加入

$
0
0
作者:Sam (甄峰) sam_code@hotmail.com

这次学习如何将ROS所支持的外设与我们本身的程序结合在一起使用。

例如:已经有底盘,或者使用turtlesim小乌龟程序。 如何使用游戏手柄来控制底盘或小乌龟。如何使用使用激光雷达等。


1. 使用游戏手柄控制Turtlesim小乌龟
1.1:相关包的安装:
在使用游戏手柄前,需要先安装joystick, ros-indigo-joy, ros-indigo-joystick-drivers等包。

1.2:  硬件设别:
把USB插口或USB Dongle插入系统。 察看/dev/input/中是否创建了js0 这个node.  如果建立,说明硬件设备被识别。
此时, cat /dev/input/js0
按下手柄按键,会看到一些乱码。他们是按照input数据格式发送的。这也说明,硬件连接成功了。

1.3: 察看和解析硬件数据:
硬件数据发出了,通过/dev/input/js0 或者 /dev/hidrawX在Linux层面显现出来。
但它的ROS Driver是如何解析这些数据的呢? 又是通过何种途径(Topic)把解析后的数据发送出来的呢?下面就分析这个问题。

1.3.1:
$roscore

先运行 ROS  Package Joy 中的joy_node.来分析它如何把数据发送成何种Topic,里面消息类型是怎样:
$rosrun joy joy_node
[ INFO] [1478056976.955143122]: Opened joystick: /dev/input/js0. deadzone_: 0.050000.
显示正常打开了JoySticks。

察看node:
$rosnode list
/joy_node
/rosout
看到对应Node是/joy_node

察看此node详细信息:
$rosnode info /joy_node
--------------------------------------------------------------------------------
Node [/joy_node]
Publications:
 * /diagnostics [diagnostic_msgs/DiagnosticArray]
 * /joy [sensor_msgs/Joy]
 * /rosout [rosgraph_msgs/Log]

Subscriptions: None

Services:
 * /joy_node/get_loggers
 * /joy_node/set_logger_level


contacting node http://ubuntu:43877/ ...
Pid: 10153
Connections:
 * topic: /rosout
    * to: /rosout
    * direction: outbound
    * transport: TCPROS

可以看到,这个Node发布着/joy Topic 

看Topic具体数据
$rostopic echo /joy
header:
  seq: 388
  stamp:
    secs: 1478057805
    nsecs: 210471365
  frame_id: ''
axes: [-0.0, -0.0, -0.0, -0.0, -0.0, -1.0]
buttons: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
---
header:
  seq: 389
  stamp:
    secs: 1478057805
    nsecs: 274450075
  frame_id: ''
axes: [-0.0, -0.0, -0.0, -0.0, -0.0, -0.0]
buttons: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


看此Topic类型:
rostopic type /joy
sensor_msgs/Joy

看消息具体数据:

rosmsg show sensor_msgs/Joy
std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id
float32[] axes
int32[] buttons

这最终就看出了,Joy_Node是通过/Joy Topic来发布数据,类型为sensor_msgs/Joy.  数据包括Axes和Button数据各几个。


1.4: 分析小乌龟需要什么数据
1.4.1: 运行小乌龟:
$rosrun turtlesim turtlesim_node

1.4.2: 察看Node,小乌龟Node是/turtlesim
$rosnode list
/rosout
/turtlesim

1.4.3:
察看此node具体信息:
$rosnode info /turtlesim
--------------------------------------------------------------------------------
Node [/turtlesim]
Publications:
 * /turtle1/color_sensor [turtlesim/Color]
 * /rosout [rosgraph_msgs/Log]
 * /turtle1/pose [turtlesim/Pose]

Subscriptions:
 * /turtle1/cmd_vel [unknown type]

Services:
 * /turtle1/teleport_absolute
 * /turtlesim/get_loggers
 * /turtlesim/set_logger_level
 * /reset
 * /spawn
 * /clear
 * /turtle1/set_pen
 * /turtle1/teleport_relative
 * /kill


contacting node http://ubuntu:37852/ ...
Pid: 15512
Connections:
 * topic: /rosout
    * to: /rosout
    * direction: outbound
    * transport: TCPROS


1.4.4: 察看Topic Type:
rostopic type /turtle1/cmd_vel
geometry_msgs/Twist


1.4.5: 此Message格式:

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


结合以上信息,就可以得出结论:
当前需要一个Node, 它接收/joy Topic,/Joy Topic中的数据类型是sensor_msgs/Joy,
把其中的数据转换,发送/turtle1/cmd_vel ,数据类型是:geometry_msgs/Twist
就可以把手柄数据和小乌龟所需数据连接起来了。



#include "ros/ros.h"
#include "sensor_msgs/Joy.h"
#include "geometry_msgs/Twist.h"

ros::Publisher pub;
geometry_msgs::Twist msg_twist;


void messageCallback(const sensor_msgs::Joy::ConstPtr& msg);




void messageCallback(const sensor_msgs::Joy::ConstPtr& msg)
{
   
   
    ROS_INFO("Key: %f. %f. %f. %f. %f. %f", msg->axes[0],   msg->axes[1],  msg->axes[2], msg->axes[3], msg->axes[4],msg->axes[5]);
    msg_twist.linear.x = (int) msg->axes[5];
    msg_twist.linear.y = 0;
    msg_twist.linear.z = 0;
   
    msg_twist.angular.x = 0;
    msg_twist.angular.y = 0;
    msg_twist.angular.z = (int) msg->axes[4];
   
    pub.publish(msg_twist);
}



int main(int argc, char** argv)
{
    ros::init(argc, argv, "x9_turtlesim");
    ros::NodeHandle n_sub;
   
    ros::NodeHandle n_advert;
       
        pub = n_advert.advertise("turtle1/cmd_vel", 1000);
   
    ros::Subscriber sub = n_sub.subscribe("joy", 1000, messageCallback);
    ros::spin();
   
    return 0;
}

 

ROS学习  ROS的Launch文件

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

0. 背景:
rosrun 可以运行node. 但如果某个项目需要N多个Node执行。一句句执行起来,不要遗漏,是件很麻烦的事。
所以提供了一个方式-----Launch文件。ROS中可以把很多的命令以描述的形式写成Launch文件,然后用roslaunch命令执行launch文件。就相当于使用rosrun调用了这些Node。 这个Launch文件,以 .launch为后缀名,放在Package的launch目录内。




1. 使用方法:
roslaunch [package]  [filename.launch]


2. Launch文件解析:
例1:










 

rplidar_ros详解

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

最初接触机器人领域时,就接触到Slamtec(www.slamtec.com)这家公司和他们的产品。购买了他们的早期版本。但之后没有继续下去。最近又开始ROS的相关学习。想通过学习slamtec的RPLidar对三角定位激光雷达有更多的了解。

在ROS wiki 上,有 slamtec的 rplidar的支持包 rplidar_ros. 
可以通过 

1. package分析
首先看package.xml, 它指定包名为:rplidar_ros. 以及一些依赖包。

再看CMakeLists.txt:
其中编译了两个可执行程序:
rplidarNodeClient, rplidarNode
rplidarNodeClient是由:src/client.cpp生成的。
rplidarNode则是由:src/node.cpp   sdk/src/arch/linux/*.cpp  sdk/src/hal/*.cpp  sdk/src/*.cpp组成。


2. 代码察看:





 

UD Tegra K1 安装ROS-indigo

$
0
0
作者: Sam (甄峰)   sam_code@hotmail.com

最近想在UD Tegra K1开发板上安装ROS系统。因为TK1开发板,本身就支持安装ubuntu, 所以理论上,一定可以成功安装ROS某个版本。现记录如下。

之前在编译过UD提供的Android Image,并烧入到TK1。所以以为ROS也是类似做法,后来请教过UD的几位朋友,才知道可以直接烧入Ubuntu。并提供了论坛地址,其中详细说明了如何烧入Ubuntu.
http://bbs.uditech.com.cn/


0. 准备工作:
0.1:系统要求:
Linux(Fedora/Ubuntu, 32bit/64bit).

0.2:硬件要求:
TK1开发板,Mirc-USB线。

0.3: 下载TK1 Ubuntu Image:
 百度云:链接: http://pan.baidu.com/s/1nuMb7lR 密码: vsm8 
解压缩后,名为bootloader.




1. TK1安装Ubuntu。

1.1:
从耳机向USB口方向来看。三个BUTTON分别命名为:Button1, Button2, Button3。靠近MicroUSB的LED为LED1,另一个为LED2。
1.2:Linux主机与开发板之间用USB-MicroUSB线连接。(主机板MicroUSB接口在音频耳机接口旁边,靠近中间位置)
1.3: 按住Button1。上电,再按下Button3. 此时开发板进入烧入模式(recovery)。HDMI显示无信号。两个LED均为蓝红双色.
(正常启动时,LED1是红蓝双色,LED2是蓝色)
进入recovery模式.
(说明:1判断系统是否运行:a.观察指示灯显示情况,只有一个指示灯的亮起的情况说明系统没运行,两个指示灯亮起说明系统在运行中,b.或许观察电视输出情况;2 靠近音频输出的那边的那个是recovery按键,靠近串口那边的那个是power按键,中间的按键是reset按键 

1.4: 烧入:
进入bootloader, 在root权限下:
./nvflash --nct nct_jetson.txt  --bct UDI_Hynix_2GB_H5TC4G63AFR_PBA_792MHz.cfg --setbct --configfile flash.cfg  --create --bl fastboot.bin --odmdata 0x7309c008 --go
或:
sudo ./nvflash --nct nct_jetson.txt  --bct UDI_Hynix_2GB_H5TC4G63AFR_PBA_792MHz.cfg --setbct --configfile flash.cfg  --create --bl fastboot.bin --odmdata 0x7309c008 --go

则可以正常烧入。


2. TK1安装ROS:
ROS的安装,和Android 开发环境安装一样,需要下载很多外网包。在国内环境下,常常遇到问题。
所以很多开发者就在国内建立了仓库。让大家可以更好的使用ROS。、

2.1:设置软件源属性:
要允许restricted, universal, multiverse.
Ubuntu Software center --> Edit --> Software Soures.
有时,Edit时,菜单出不来。则可以在:Search your computer and online software中,添加:Software
选中:
Software & Update


确保restricted, universal, multiverse 都被选中:


2.2:设置软件源到sources.list文件中:
我们把EXBOT的ROS源加入sources.list. 这样,就不需要翻墙了。
有两种方法:
2.2.1: 字符模式:
$sudo sh -c 'echo "deb http://ros.exbot.net/rospackage/ros/ubuntu/   $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'

注意:lsb_release -sc 返回的是Ubuntu的codename. 在Sam 14.04中,它就是trusty

2.2.2: 图形界面方式:

在other Software中,Add。
apt line: deb http://ros.exbot.net/rospackage/ros/ubuntu/ trusty main
注意多处空格。

2.3:设置密码:
wget http://packages.ros.org/ros.key


2.4: 安装:
$sudo apt-get update
$sudo apt-get install ros-indigo-desktop


2.5: 设置环境变量:
此时,ROS已经被安装在/opt/ros/indigo中。
但ROS命令也要被加入类似PATH环境变量。
#gedit ~/.bashrc
在最后增加一行:
source /opt/ros/indigo/setup.bash

2.6: 验证:
$roscore
如果正常使用。则说明安装成功。






 

Bash的运行模式和配置文件加载

$
0
0
作者:Sam (甄峰)  sam_code@hotmail.com

0. Bash的运行模式:
运行中的Bash有两种属性(模式)。 A:是否Interactive shell(交互式Shell)。  B:是否Login shell(登陆shell).
当这两项模式排列组合后,就会出现4种运行模式。

1. 登录交互式Shell。
2. 非登录交互式Shell。
3. 登陆非交互式Shell。
4. 非登录非交互式Shell。


1. Bash运行模式具体介绍
1.1:Login Shell
login shell是指用户以非图形化界面或者以ssh登陆到机器上时获得的第一个shell,简单些说就是需要输入用户名和密码的shell。因此通常不管以何种方式登陆机器后用户获得的第一个shell就是login shell。

在下列情况下,我们可以获得一个login shell:

  1. 登录系统时获得的顶层shell,无论是通过本地终端登录,还是通过网络ssh登录。这种情况下获得的login shell是一个交互式shell。
  2. 在终端下使用--login选项调用bash,可以获得一个交互式login shell。
  3. 在脚本中使用--login选项调用bash(比如在shell脚本第一行做如下指定:#!/bin/bash --login),此时得到一个非交互式的login shell。
  4. 使用"su -"切换到指定用户时,获得此用户的login shell。如果不使用"-",则获得non-login shell。
login shell与non-login shell的主要区别在于它们启动时会读取不同的配置文件,从而导致环境不一样。login shell启动时首先读取/etc/profile全局配置,然后依次查找~/.bash_profile、~/.bash_login、~/.profile三个配置文件,并且读取第一个找到的并且可读的文件。login shell退出时读取并执行~/.bash_logout中的命令。

1.2:Interactive Shell (交互Shell): 
interactive shell会有一个输入提示符,并且它的标准输入、输出和错误输出都会显示在控制台上。所以一般来说只要是需要用户交互的,即一个命令一个命令的输入的shell都是interactive shell。而如果无需用户交互,它便是non-interactive shell。
怎样得到一个交互式Shell呢?
没有非选项参数,没有-c,标准输入和标准输出与终端相连的,或者用-i参数启动的Bash实例。
例如在Bash Shell中,使用bash命令启动一个shell. 就是一个交互式(但非Login)Shell。

如何得到一个非交互式Shell:
bash run.sh 这样执行shell脚本的实例, 即为非交互式Shell.它不需要与用户交互,在运行完脚本后,就退出所创建的非交互Shell了。



1.3: 排列组合的讲解
1.3.1: Interactive Login Shell
1. 正常登录字符界面
2. 远程使用ssh等登录。
3. 在终端下使用bash --login 调用bash. 
4. su xxx . 
都会进入Interactive Login Shell.

配置文件的加载:shell首先加载/etc/profile,然后再尝试依次去加载下列三个配置文件之一,一旦找到其中一个便不再接着寻找

  • ~/.bash_profile
  • ~/.bash_login
  • ~/.profile
实际使用例子:在修改完:~/.profile 或 ~/.bashrc后,Sam通常会 bash --login (bash -l) , 调用一个login  bash, 它依次取了新配置的配置文件。

这里就会有疑问:关~/.bashrc什么事?前面不是没提到它么? 但在~/.profile中,可以看到:
.  "$HOME/.bashrc"
在这里把 .bashrc配置加入了。



1.3.2:Non-Interactive + Login Shell:
很少见,
在命令行或脚本中:
bash -l run.sh
-l : Login Shell.
执行脚本,又让这个Shell 成为:Non-Interactive。
配置文件加载与之前类似。


1.3.3: Interactive + Non-Login Shell: 
在命令行或脚本中,运行bash. 此时会打开一个交互式Shell. 但因为没有 -l, 所以是非登录Shell。

会去查找和加载:/etc/bash.bashrc 和  ~/.bashrc.




1.3.4:Non-Interactive + Non-Login Shell:

最后一种模式为非交互非登陆的shell,创建这种shell典型有两种方式:

  • bash script.sh
  • ssh user@remote command
这两种都是创建一个shell,执行完脚本之后便退出,不再需要与用户交互。

对于这种模式而言,它会去寻找环境变量BASH_ENV,将变量的值作为文件名进行查找,如果找到便加载它。
而BASH_EVN 很多多情况下,都是空。所以环境最缺乏。



更为直观的示图

至此,四种模式下配置文件如何加载已经讲完,因为涉及的配置文件有些多,我们再以两个图来更为直观的进行描述:

第一张图来自这篇文章,bash的每种模式会读取其所在列的内容,首先执行A,然后是B,C。而B1,B2和B3表示只会执行第一个存在的文件:

+----------------+--------+-----------+---------------+
|                | login  |interactive|non-interactive|
|                |        |non-login  |non-login      |
+----------------+--------+-----------+---------------+
|/etc/profile    |   A    |           |               |
+----------------+--------+-----------+---------------+
|/etc/bash.bashrc|        |    A      |               |
+----------------+--------+-----------+---------------+
|~/.bashrc       |        |    B      |               |
+----------------+--------+-----------+---------------+
|~/.bash_profile |   B1   |           |               |
+----------------+--------+-----------+---------------+
|~/.bash_login   |   B2   |           |               |
+----------------+--------+-----------+---------------+
|~/.profile      |   B3   |           |               |
+----------------+--------+-----------+---------------+
|BASH_ENV        |        |           |       A       |
+----------------+--------+-----------+---------------+

 

Python module相关知识

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

Sam学习使用Python不久,就Python module的理解有限。因为此原因,在工作中遇到一些疑惑。现把学习过程记录下来。


0. Python Module 的最初认识

模块让你能够有逻辑地组织你的Python代码段。

把相关的代码分配到一个 模块里能让你的代码更好用,更易懂。

模块也是Python对象,具有随机的名字属性用来绑定或引用。

简单地说,模块就是一个保存了Python代码的文件。模块能定义函数,类和变量。模块里也能包含可执行的代码。


Sam学习Python的过程,主要是跟着《Learn PYTHON the Hard WAY》,其中习题40中,明确指出,

A: 模块是包含函数和编来那个的Python文件。

B: 其它程序可以导入这个文件。

C: 然后可以使用  . 操作符访问模块中的函数和变量。


例1:

supports.py:

def apple():

print "I am an Apple!"

tangerine = "Living reflection of a dream"



接下来就可以import这个模块,并用 .操作符调用函数。


import  supports

supports.apple()

print supports.tangerine



1. 模块的导入

模块的导入方式有以下几种: 

A: import

B: from .... import

C: from .... import *


导入模式的异同:

A:import:

import  supports

supports.apple()

print supports.tangerine

在调用函数,变量和类时,需要使用模块名+.操作符调用。


B:from ModuleName import name

from语句让你从模块中导入一个指定的部分到当前命名空间中.

from supports import apple

这个声明不会把整个supports模块导入到当前的命名空间中,它只会将supports里的apple单个引入到执行这个声明的模块的全局符号表。


C: from ModuleName import *

把一个模块的所有内容全都导入到当前的命名空间也是可行的


B和C连个方式,在使用函数,变量和类时,不需要使用模块名+.操作符调用。 而是直接调用。

例如:

from  supports import *

apple()

print tangerine



2. 模块定位:

当使用import ,from... import...导入模块时, Python解析器对模块定位的搜索顺序为:

1. 当前目录。 (即input script所在目录)

2. shell 变量 PYTHONPATH下的每个目录。

3. 默认目录。Python安装目录。 如: /usr/local/lib/python



截至到现在,Python module还是很清晰的。Python由 modulename.py构成。内容放置在modulename.py中。modulename就是文件名。

文件名就是模块名加上后缀.py

导入方式有三种。看来没啥问题。


3. 模块导入的具体行为:

一个模块被导入,那些内容被执行,哪些不被执行呢?这就是模块导入的具体行为定义的:

每个module都包含对象定义和一些语句(statements),这些语句应该是意图要来初始化这个module的,这些语句会在这个module第一次被import的时候执行(多次import只会执行一次,不管是以上两种import的语句中那一种),当这个module被作为一个script来运行的时候也会被执行。


每个module都有自己的private symbol table,当使用第一种import语句import一个module的时候,引入者的local symbol table就加入了这个module,其名字如果没有使用as的话就是被引入的模块名本身。使用第二种import语句这会在引入者的local symbol table中加入具体引用的item,其名称若没使用as则就为item的名称。

这就是为何单纯import时,需要用modulename.item. 而是用from...import时, 直接调用item本身。


例:

fibo.py如下:


1 print ("__name__ :", __name__)
2 def fib(n):
3         a, b = 0, 1
4         result = []
5         print ("in fib():", __name__)
6         while (b<<span style="font-family: 'Courier New' !important;">n):
7                 result.append(b)
8                 a, b = b, a+b                                                                                                             9         print(result)

这个fibo.py就是一个module,它有一个函数定义fib(),和一个语句(statement),第一行的print语句,我们在当前文件目录运行Python Interpreter就可以去引入这个模块,并执行模块中定义的fib()函数。


>>> import fibo
('__name__ :', 'fibo')         #print语句执行
>>> fibo.fib(10)
('in fib() :', 'fibo')         #fib()函数执行
[1, 1, 2, 3, 5, 8]

可以看到,在import的时候,这个module的语句(statements)执行了,所定义的函数并未执行,在通过module名引用module中的函数定义时,函数才被执行。

同样可以在一个script file中引入module,我们在fibo.py所在的目录创建另一个文件calculat.py


1 from fibo import fib                                                                                                                      2 for n in range(10, 50, 5):
3         fib(n)

然后用Python解释器运行calcute.py得到结果。


4. Python标准库:

Python有一个标准库,其中定义了一系列的module,这些module中的一部分是直接集成在Interpreter中的,这些built-in module主要提供了很重要的但是Python语言又没有提供的功能,比如跟system call有关的sys module就集成在所有平台的Python Interpreter中,在Interpreter中集成哪些module是可以配置的,并且随平台不同而有差别。




遇到问题:

Sam在看一些例子代码时,首先遇到了:

import serial

import socket

等语句。

那按照以上认识,就应该有serial.py, socket.py等module存在。但Sam在当前目录,PYTHONPATH,/usr/local/lib/python等目录下均未能发现。这就有了疑问。



在疑问研究中,看到如下用法:

#pry

>>>import serial

>>>print serial.__file__

/usr/lib/python2.7/dist-packages/serial/__init__.pyc


>>>import socket

>>>print socket.__file__

/usr/lib/python2.7/socket.pyc

__file__指向该模块的导入文件名


Sam由此想到,是否除了  *.py, 还有类似 *.pyc的模块名呢?但__init__.py又是什么鬼?这就牵涉出Python中另一个概念---Pakcage。



5. python中的Package

Package是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包等组成的Python的应用环境。

package是一个directory,它包含sub-package或者是module,而module是.py文件,要让Python Interpreter把一个目录作为package,则该目录下必须有__init__.py文件,__init__.py可以为空,当然也可以有对象定义和语句,用来做初始化工作,__init__.py还有个作用就是设置__all__变量。

package本身就可以来作为一个module使用,只是它所包含的sub-module或module可以通过package name用package.module的名称形式去引用,这更有利于组织一系列相关的module,避免module间定义的名称的混乱。

package在实际工程中非常常用,__init__.py也常常不会为空,而会有对象定义和初始化代码来让这个包,也就是这个module,包含其该有的item定义。以后我们会对package做更多了解。




考虑一个在Phone目录下的pots.py文件。这个文件有如下源代码:


#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
def Pots():
   print "I'm Pots Phone"
   

同样地,我们有另外两个保存了不同函数的文件:

  • Phone/Isdn.py 含有函数Isdn()
  • Phone/G3.py 含有函数G3()

现在,在Phone目录下创建file __init__.py:

  • Phone/__init__.py

当你导入Phone时,为了能够使用所有函数,你需要在__init__.py里使用显式的导入语句,如下:


from Pots import Pots
from Isdn import Isdn
from G3 import G3

当你把这些代码添加到__init__.py之后,导入Phone包的时候这些类就全都是可用的了。


#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
# 导入 Phone 包
import Phone
 
Phone.Pots()
Phone.Isdn()
Phone.G3()

以上实例输出结果:


I'm Pots Phone
I'm 3G Phone
I'm ISDN Phone

如上,为了举例,我们只在每个文件里放置了一个函数,但其实你可以放置许多函数。你也可以在这些文件里定义Python的类,然后为这些类建一个包。



通过学习Pakcage,就可以完全解释:/usr/lib/python2.7/dist-packages/serial/__init__.pyc

即serial是一个pakcage.




具体举例分析:

port_publisher.py:


import os

import select

import socket

import sys

import time

import traceback


import serial

import serial.rfc2217

import slerialtools.list_ports


import dbus


其中,serial 就是一个Package.  在serial目录内,包含有__init__.py.














































/


 

ROS知识点之 ActionLib

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

Sam在ROS学习中,遇到知识点actionLib, 觉得特别有用。学习记录之。

0. 基础知识:
actionlib是ROS一个功能包集(actionlib stack).
http://www.ros.org/browse/details.php?distro=kinetic&name=actionlib
http://wiki.ros.org/actionlib

在ROS系统中,向某个Node发送请求,并获取响应。可以通过ROS Srevice机制来满足。但如果执行某个请求的时间很长,(如要求机器人从A点移动到B点)在此期间,用户想要查看执行的进度或者干脆取消这个请求。Service机制就没法满足了,但是actionlib可以满足用户这种需求。
ActionLib栈提供工具,使用户可以创建一些Servers,这些Server用来执行较长时间的任务,并且任务可以被制止。同时ActionLib还提供了一些Client接口,用来给Server发请求。



1.ActionLib工作机制:
1.1:ActionCLlient和ActionServer通过"ROS Action Protocol"沟通。 ROS Action Protocol是ROS 消息方式传递。ActionClient和ActionServer提供一些简单的API,让用户可以发送Goals请求(Client端)以及执行Goals(在Server端)。


1.2:具体传输:


2:Action Specification:Goal, Feedback, Result.
为了能够让Action Client和Action Server通讯,定义了几个概念。Goal, Feedback和Result.

Goal: 代表一个任务。可以被ActionClient发送到ActionServer。比如在MoveBase案例中,它的类型是PoseStamped.包含了机器人应该去的信息。 在激光扫描云台控制案例中,Goal包含扫描的参数,min angle, max angle,speed等。

FeedBack:Server告知Client当前Goal执行过程中的情况。在Move Base案例中,它表示机器人当前姿态。在激光扫描云台控制案例中, 它表明剩余扫描时间。

Result:执行结果。在MoveBase中,结果和机器人pose. 激光扫描云台控制案例中, 给出请求的点云结果。

.action file:
Action 规格的定义,就放在Package内 action目录内的 .action文件中。文件内定义了Goal, FeedBack, Result等规格。
例如:

# Define the goal
uint32 dishwasher_id  # Specify which dishwasher we want to use
---
# Define the result
uint32 total_dishes_cleaned
---
# Define a feedback message
float32 percent_complete


3.实践:
3.1: 创建Package:
在catkin_ws/src目录下:
catkin_create_pkg actionlib_test roscpp std_msgs actionlib actionlib_msgs message_generation
此时,可以看到已经创建了actionlib_test Package。 

3.2:建立自己的action Specification
在Package目录内,创建action目录。并在其中建立Fibonacci.action文件:

#goal definition
int32 order
---
#result definition
int32[] sequence
---
#feedback definition
int32[] sequence

打开package.xml:

actionlib
actionlib_msgs

roscpp
rospy
这是因为在catkin_create 时,指定了依赖这些Package。
添加内容如下:

message_generation

CMakefile.txt:
## Add actions
add_action_files(DIRECTORY action FILES Fibonacci.action )

## Generate messages
generate_messages(DEPENDENCIES std_msgs actionlib_msgs)

$catkin_make clean
$catkin_make

可以看到,在devel/include/actionlib_test/中,创建了头文件。文件中定义了各Specification



 

ROS使用记录

$
0
0
作者:Sam (甄峰)  sam_code@hotmail.com

1. 得到某个ROS Package的软件包名:
Sam在使用ROS时,常有这样一个问题:
如我知道某个ROS Package的名字,以及它的Source地点。如果要拿它的Source Code。可以通过git clone ...
得到它。但如果我只想使用 apt-get install 来安装它。 则常常不知道这个软件包的名字。通常的方法就是bing查。但效率较低。直到某一天发现这个方法。
使用新立得软件管理器
在Ubuntu软件中心,输入:synaptic。安装之。

打开新立得软件包管理器。输入想要安装包的一部分,则软件包的全称就出现了。
例如:
1.想安装ROS Package-----openni-camera. 只需要输入之:就可以看到软件包:ros-indigo-openni-camera.
2.想安装image view. 输入image View,搜索,可以看到:ros-indigo-rqt-image-view.




 

ASUS Xtion在ROS系统下的安装使用

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

ASUS Xtion是华硕使用PrimeSense技术发布的RGBD 3D设备。在机器人导航设备中,除了激光雷达,也可以使用3D摄像头来做环境采集设备。现把实际使用过程和遇到的一些问题记录如下。

问题1. Xtion插入USB口后,未能正常驱动起来。
#lsusb
可以看到设备:1D27:0600  ASUS
#dmesg
Not enough bandwidth for new device state.
怀疑有二。 
A: 记得Linux Kernel在32bit 系统中,确实有个Bug。对多个摄像头插入同一个Hub时,确实会导致带宽不足。
B: XHCI和EHCI的问题。设备也许不支持XHCI。
要解决A,需要重新安装系统到64Bit。工程巨大。所以先测试下B。
进入BISO. xHCI模式关闭掉。 XHCI Hand-off, EHCI  Hand-off 都关闭。重新启动系统。果然能解决问题。看来猜测B是正确的。


软件和驱动安装过程
#apt-get install ros-indigo-openni-launch
#apt-get install ros-indigo-openni-camera

#apt-get install ros-indigo-openni2-launch
#apt-get install ros-indigo-openni2-camera


硬件测试过程:
Sam这里,有两个运行环境:
A:台式机中安装ROS。
B:TK1中安装ROS。
TK1内存较小,无法运行大型的X11程序,例如rviz, rqt等。

A1:本地可以运行Rviz,rqt等:
1. roslaunch openni2_launch openni2.launch
2. rosrun rqt_image_view rqt_image_view
此时,在下来窗口中,可以看到非常多数据源。/camera/depth/image_raw , /camera/rgb/image_raw 分别可以看到深度图像和RGB图像。
如此,则证明驱动,设备,数据均正常。

A2: 本地运行Rviz,并尝试把深度数据转换成雷达数据:
要用到depthimage_to_laserscan 这个Package。

1. roslaunch openni2_launch openni2.launch
2. rosrun depthimage_to_laserscan depthimage_to_laserscan image:=/camera/depth/image_raw 
3. rosrun rviz rviz
在rviz中Fixed Frame选择camera_depth_optical_frame,Add选择By topic,选择LaserScan,在LaserScan中的Color Transformer中选择AxisColor

这里提一下,rviz 可以通过Topic获取ROS消息,并直观反应在可视画面上。如xtion的点云,可见光image, 由depthimage_to_laserscan转换出来的雷达数据(/scan) 等。


B:TK1上采集数据,远端察看:
因为在TK1上无法运行


 

GNU C的零长数组和用法

$
0
0
作者:Sam (甄峰)  sam_code@hotmail.com

在Linux Kernel或一些GNU项目中,可以看到长度设定为0的数组。之前没有深究,这次研究一下。
以下内容大量摘抄网上信息。

标准C和C++不支持0长度数组。如果定义0长数组。编译阶段就会报错。但GNU C支持零长数组,条件是作为结构体的最后一项。

但这一项因为长度为0,所以不占空间。

举例如下:

#include                 //stdio.h
#include          //sys/types.h
#include               //stdint.h
#include     // stdlib.h

typedef struct {
uint8_t subevent;
uint8_t data[0];
} __attribute__ ((packed)) test_event;

int main(int argc, char** argv)
{
int ext_size = 12;
char Letter = 'A';

printf("\nSize is: %d\n", sizeof(test_event));

test_event* event = (test_event*) malloc(sizeof(test_event)+ 9);
for(int i = 0; i < ext_size; i++)
{
event->data[i] = Letter++;
}

printf("\n");
for(int i = 0; i < ext_size; i++)
{
printf("%c\t", event->data[i]);
}

    return 0;
}


可以看到结果是:
Size is: 1
A       B       C       D       E       F       G       H       I       J       K       L 

这个用法主要用于变长Buffer。 结构体大小为1, data[0]不占任何空间。 如果采用另一个方法,比如把data设置为指针,也可以实现此功能。但首先这个执针本身需要占用4bytes.  而且还需要申请新内存再要指针指向它。
这样的话,内存不连续,且要分别管理。
而是用零长度数组。申请时,内存长度为机构体长度+数组长度。内存空间连续。释放时也只需释放一次。


用途 :长度为0的数组的主要用途是为了满足需要变长度 的结构体。

用法 :在一个结构体的最后 ,申明一个长度为0的数组,就可以使得这个结构体是可变长的。对于 编译器来说,此时长度为0的数组并不占用空间,因为数组名本身不占空间,它只是一个偏移量, 数组名这个符号本身代 表了一个不可修改的地址常量 (注意:数组名永远都不会是指针! ),但对于这个数组的大小,我们可以进行动态分配。例如:

typedef struct{
       int len;
       char data[0];
}test_t;

int my_length = 10;

test_t *p_test = (test_t *)malloc(sizeof(test_t) + my_length);
p_test->len = my_length;

......

free(p_test);    

之后对于结构体中的数组可以像一般的数组一样进行访问。




 

BlueZ 5.43 Cross Compile

$
0
0
作者:Sam (甄峰)   sam_code@hotmail.com

之前屡次编译BlueZ(http://blog.sina.com.cn/s/blog_602f87700100zb47.html). 中间有段时间以为不会再编译BlueZ了,其一是Android使用自己的Blue协议栈(BluetDroid)取代了BlueZ。其二是Bluetooth不再是工作重心。没想到搞ROS后,又回到Linux环境,再次需要编译Bluez.  这个令人不快的任务又一次放在面前。


0. Download BlueZ
http://www.bluez.org/

1. X86 Version make
sudo apt-get install systemd
sudo apt-get install libical-dev
sudo apt-get install libudev-dev

$./configure
$make

注意,如果没有 --enable-library,不会编译出动态或静态库的。

$./configure --enable-shared --enable-static --enable-library
$make clean;make

可以看到,库被放在了:
./lib/.libs/libbluetooth.so
./lib/.libs/libbluetooth.a


2. CrossCompile:
2.0: 交叉编译准备:
编译之前,依例看看README:
In order to compile Bluetooth utilities you need following software packages:
        - GCC compiler
        - GLib library
        - D-Bus library
        - udev library (optional)
        - readline (command line clients)
所以在交叉编译中,估计至少要交叉编译GLib和D-BUS。 

2.1:glib编译:
echo glib_cv_uscore=no>>arm-linux.cache  
echo glib_cv_stack_grows=no>>arm-linux.cache
CC=arm-linux-gnueabihf-gcc ./configure --host=arm-linux --cache-file=arm-linux.cache --enable-libmount=no --with-pcre=no

make



 

Fedora20上编译BlueZ-5.43

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

之前把BlueZ 5.43在Fedora20上编译 X86 版本的过程一代而过,其实这里面还是有内容的。这次把它补上。

0. Download : 
http://www.bluez.org/
下载最新版本:当前是Bluez  5.43

1. 开始编译:
1.1:首次尝试:
$ ./configure
configure: error: GLib >= 2.28 is required
使用$rpm -qa |grep glib
glib-1.2.10-40.fc20.x86_64

可以看到,并非没有安装glib, 而是glib版本不够高。当前系统安装版本为1.2.10. 但BlueZ 需要的版本是2.28以上。






1.2: 开始编译Glib:
1.2.1:下载编译glib:
与之前的下载不同(git clone https://github.com/GNOME/glib.git), 这次下载是在:
https://git.gnome.org/browse/glib/
选择直接下载tar.xz文件,或者:
git clone https://git.gnome.org/browse/glib/



$./autogen.sh
报错如下:
You don't have gtk-doc installed, and thus won't be able to generate the documentation.
./autogen.sh: line 14: gtk-doc.make: Permission denied

指出没有安装gtk-doc, 那就安装之。
$sudo yum install gtk-doc


再次运行$./autogen.sh
报错如下:
checking for LIBFFI... no
configure: error: Package requirements (libffi >= 3.0.0) were not met:
No package 'libffi' found

安装之:
$sudo yum install libffi
#sudo yum install libffi-devel


再次运行$./autogen.sh
报错如下:
checking for LIBMOUNT... no
checking libmount/libmount.h usability... no
checking libmount/libmount.h presence... no
checking for libmount/libmount.h... no
configure: error: *** Could not find libmount

sudo yum install libmount
sudo yum install libmount-devel


$./configure
$make; make install

glib编译成功。
可以看到, glib-2.0.pc被放到/usr/local/lib/pkgconfig中去了。所以要告知pkg-config,从哪里找这个pc文件:
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
此后,则不再报有关Glib的错误了。


1.3:安装Dbus, libudev-devel,:
继续编译,进入bluez目录
$./configure
configure: error: D-Bus >= 1.6 is required

同理,需要编译D-Bus.
$sudo yum install dbus
$sudo yum install dbus-devel



之后还有:
sudo yum install libudev-devel
sudo yum install libical-devel
sudo yum install readline-devel



最后:
./configure --enable-library
make

即可成功编译出x86-64版本库。




 

pkg-config在交叉编译过程中的使用探讨

$
0
0
作者:Sam (甄峰)  sam_code@hotmail.com

在研究autoconf时,必然要研究到pkg-config,它非常好用。在x86-64系统下能极大的方便编译。但Sam一直没有找到它在交叉编译时该如何正确的起作用。这次就探讨一下。

1. pkg-config基本作用和用法:
1.1: 背景:
很多软件包会被其它程序使用,在此过程中,需要指定头文件位置,库文件,库文件位置等信息。如果单纯手写如下: -I/usr/include/  -lglib -L/usr/lib/ 其一是容易出错,另外,很多包在yum install, apt-get install后头文件和库文件的位置,和使用源码编译,make;make install 的位置也并不一定一致。再加上还有版本问题。所以就有了pkg-config.
pkg-config主要作用是:
  1. 检查库的版本号。如果所需要的库的版本不满足要求,它会打印出错误信息,避免链接错误版本的库文件。
  2. 获得编译预处理参数,如宏定义,头文件的位置。
  3. 获得链接参数,如库及依赖的其它库的位置,文件名及其它一些连接参数。
  4. 自动加入所依赖的其它库的设置。


1.2:使用方法:
先介绍一个特殊文件 *.pc. 它将描述头文件,库文件所在位置,版本号等具体信息。pkg-config就是通过它得到以上信息的。那它具体放在哪里呢,yum install, apt-get install,make install 通常会把这个文件放到/usr/lib/pkgconfig , /usr/local/lib/pkgconfgig, usr/lib64/pkgconfig等位置。但pkg-config如何知道此文件的位置呢? PKG_CONFIG_PATH
例:
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig/
指明pc文件被放到了哪个目录。

具体使用:

Sam机器上的glib-2.0版本为:Version: 2.51.4(pc文件中所描写)
$pkg-config "glib-2.0 > 2.49.1" --cflags
结果是:
-I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include 

$pkg-config "glib-2.0 > 2.49.1" --libs 
结果是:
-L/usr/local/lib -lglib-2.0 

$pkg-config "glib-2.0 > 2.49.1" --libs --cflags
结果是:
-I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -L/usr/local/lib -lglib-2.0 

但如果版本不对呢?
$pkg-config "glib-2.0 > 2.54.1" --libs --cflags
则显示如下:
Requested 'glib-2.0 > 2.54.1' but version of GLib is 2.51.4



2. 交叉编译处理方式1:




 

autoconf交叉编译细节

$
0
0
作者:Sam (甄峰) sam_code@hotmail.com

使用autoconf 交叉编译时,会遇到非常多异常。Sam一向是强行改过来就好。但也知道这不是正规做法。现在把一些做法记录如下:


问题1:
$ CC=arm-linux-gnueabihf-gcc ./configure --host=arm-linux
报错:
checking for growing stack pointer... configure: error: in `/home/sam/work/current/Source/Robot_ROS/Bluetooth/3rdParty/glib-2.51.0':
configure: error: cannot run test program while cross compiling
不能使用交叉编译器编译测试程序。

首先打开configure,找到growing stack部分。
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for growing stack pointer" >&5
$as_echo_n "checking for growing stack pointer... " >&6; }
if ${glib_cv_stack_grows+:} false; then :
  $as_echo_n "(cached) " >&6
else

        if test "$cross_compiling" = yes; then :
  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot run test program while cross compiling
See \`config.log' for more details" "$LINENO" 5; }

可以看到,如果变量glib_cv_stack_grows 为false. 则正常通过。

所以可以如下操作:
$echo glib_cv_stack_grows=no>>arm-linux.cache
$CC=arm-linux-gnueabihf-gcc ./configure --host=arm-linux --cache-file=arm-linux.cache

问题解决。



 

BLE广播包信息记录

$
0
0
作者:Sam(甄峰)  sam_code@hotmail.com

非常久没有看Bluetooth了。这次又需要在Linux下读取iBeacon广播消息。现在记录一些必要的信息如下:


1. BLE报文结构:


1. 前导

  前导是一个8比特的交替序列。他不是01010101就是10101010,取决于接入地址的第一个比特。

  • 若接入地址的第一个比特为0:01010101
  • 若接入地址的第一个比特为1:10101010

  接收机可以根据前导的无线信号强度来配置自动增益控制。

2 接入地址

  接入地址有两种类型:广播接入地址和数据接入地址。

  • 广播接入地址:固定为0x8E89BED6,在广播、扫描、发起连接时使用。
  • 数据接入地址:随机值,不同的连接有不同的值。在连接建立之后的两个设备间使用。

  对于数据信道,数据接入地址是一个随机值,但需要满足下面几点要求:

     1)  数据接入地址不能超过6个连续的“0”或“1”。

     2)  数据接入地址的值不能与广播接入地址相同。

     3)  数据接入地址的4个字节的值必须互补相同。

     4)  数据接入地址不能有超24次的比特翻转(比特0到1或1到0,称为1次比特翻转)。

     5)  数据接入地址的最后6个比特需要至少两次的比特翻转。

     6)  符合上面条件的有效随机数据接入地址大概有231个。





3. PDU--协议数据单元:



这部分才是BLE 的关键:

广播报文的报头包含4bit广播报文类型、2bit保留位、1bit发送地址类型和1bit接收地址类型。

广播报文类型:

在Linux BlueZ中,它就是:

typedef struct {

uint8_t evt_type;

uint8_t bdaddr_type;

bdaddr_t bdaddr;

uint8_t length;

uint8_t data[0];

} __attribute__ ((packed)) le_advertising_info;

对应解析代码:

static const char *evttype2str(uint8_t type)

{

switch (type) {

case 0x00:

return "ADV_IND - Connectable undirected advertising";

case 0x01:

return "ADV_DIRECT_IND - Connectable directed advertising";

case 0x02:

return "ADV_SCAN_IND - Scannable undirected advertising";

case 0x03:

return "ADV_NONCONN_IND - Non connectable undirected advertising";

case 0x04:

return "SCAN_RSP - Scan Response";

default:

return "Reserved";

}

}



发送地址类型和接收地址类型指示了设备使用公共地址(Public Address)还是随机地址(Random Address)。公共地址和随机地址的长度一样,都包含6个字节共48位。BLE设备至少要拥有这两种地址类型中的一种,当然也可以同时拥有这两种地址类型。


它在BlueZ中:

typedef struct {

uint8_t evt_type;

uint8_t bdaddr_type;

bdaddr_t bdaddr;

uint8_t length;

uint8_t data[0];

 

} __attribute__ ((packed)) le_advertising_info;


static const char *bdaddrtype2str(uint8_t type)

{

switch (type) {

case 0x00:

return "Public";

case 0x01:

return "Random";

default:

return "Reserved";

}

 

}



长度

  • 广播报文:长度域包含6个比特,有效值的范围是6~37。
  •  数据报文:长度域包含5个比特,有效值的范围是0~31。

  广播报文和和数据报文的长度域有所不同,主要原因是:广播报文除了最多31个字节的数据之外,还必须要包含6个字节的广播设备地址。6+31=37,所以需要6比特的长度域。

  再次强调:广播时必须要包含6个字节的广播设备地址。

typedef struct {

uint8_t evt_type;

uint8_t bdaddr_type;

bdaddr_t bdaddr;

uint8_t length;

uint8_t data[0];

 

} __attribute__ ((packed)) le_advertising_info;




                                                         图8:广播和扫描响应的数据格式

  1)  有效数据部分:包含N个AD Structure,每个AD Structure由Length,AD Type和AD Data组成。其中:

  • Length:AD Type和AD Data的长度。
  • AD Type:指示AD Data数据的含义。


  • AD type的定义在程序的“ble_gap.h”头文件中。定义如下:
复制代码
 1 #define BLE_GAP_AD_TYPE_FLAGS                               0x01  2 #define BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE   0x02  3 #define BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE         0x03  4 #define BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE   0x04  5 #define BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_COMPLETE         0x05  6 #define BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE  0x06  7 #define BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE        0x07  8 #define BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME                    0x08  9 #define BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME                 0x09 10 #define BLE_GAP_AD_TYPE_TX_POWER_LEVEL                      0x0A 11 #define BLE_GAP_AD_TYPE_CLASS_OF_DEVICE                     0x0D 12 #define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C               0x0E 13 #define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R         0x0F 14 #define BLE_GAP_AD_TYPE_SECURITY_MANAGER_TK_VALUE           0x10 15 #define BLE_GAP_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS          0x11 16 #define BLE_GAP_AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE     0x12 17 #define BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT       0x14 18 #define BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT      0x15 19 #define BLE_GAP_AD_TYPE_SERVICE_DATA                        0x16 20 #define BLE_GAP_AD_TYPE_PUBLIC_TARGET_ADDRESS               0x17 21 #define BLE_GAP_AD_TYPE_RANDOM_TARGET_ADDRESS               0x18 22 #define BLE_GAP_AD_TYPE_APPEARANCE                          0x19 23 #define BLE_GAP_AD_TYPE_ADVERTISING_INTERVAL                0x1A  24 #define BLE_GAP_AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS         0x1B 25 #define BLE_GAP_AD_TYPE_LE_ROLE                             0x1C 26 #define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C256            0x1D 27 #define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R256      0x1E 28 #define BLE_GAP_AD_TYPE_SERVICE_DATA_32BIT_UUID             0x20 29 #define BLE_GAP_AD_TYPE_SERVICE_DATA_128BIT_UUID            0x21 30 #define BLE_GAP_AD_TYPE_3D_INFORMATION_DATA                 0x3D 31 #define BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA          0xFF 





 

零扩展和符号扩展

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

0.
之前对窄数据类型向宽数据类型转换转换时,符号扩展,零扩展稍有了解,具体什么时候用符号扩展,什么地方用零扩展,并不清楚。所以常用按位与(&) 按位或(|) 等操作来保证扩展部分与编程思路的统一。

这次在使用iBeacon协议时,信号强度的dbm值保存在一个字节中。有时作为原码,有时又要求补码。为了验证,常要printf出来。 这下就要求把零扩展和符号扩展搞清楚。


1. 何时使用零扩展,何时使用符号扩展:
当窄数据类型为:有符号数据类型时,扩展为宽数据类型时,使用符号扩展。
当窄数据类型为:无符号数据类型时,扩展为宽数据类型时,使用零扩展。

例1: 有符号数扩展(符号扩展)
int8_t rssi = 0xBE;
int rssi_4byte = 0;

rssi_4byte = rssi;
解析:
rssi为8位有符号数,则使用符号扩展。
0xBE:   10111110B. 最高位为1。所以使用1扩展到其它3个byte.
rssi_4byte: 11111111 11111111 11111111 10111110B = - 0x42 = -66.

例2:无符号数扩展(零扩展)
uint8_t rssi = 0xBE;
0xBE=10111110B
rssi_4byte: 00000000 00000000 00000000 10111110B = 0xBE = 190


例3:rssi实际使用中遇到的问题:
1个字节保存RSSI:0x9F.
它的含义其实是:1001 1111
最高位:1表示负值。 其实表示:-0x1F. = -31

如果这样处理:
int8_t rssi = 0x9F;
printf("RSSI: [%d] 0x%x\n", rssi, rssi);
此处,因为打印的是%d. 所以会临时扩展为4byte.
又因为是int8_t, 是有符号。所以会符号扩展。
rssi会被扩展为:FFFFFF9F.
所以打印结果是: RSSI [-97], 0xFFFFFF9F
这显然与希望的结果不同。

如果这样处理:
uint8_t rssi= 0x9F;
printf("RSSI: [%d] 0x%x\n", rssi, rssi);
此处,因为打印的是%d. 所以会临时扩展为4byte.
又因为是uint8_t, 是无符号。所以会零扩展。
rssi会被扩展为:0000009F.
打印结果为:RSSI [159]  0x9F.
这显然与希望的结果不同。

仔细分析下来: 0x9F 其实是 RSSI的原码。那如何得到原始值就很清晰了。
最高位是符号位,其它位是绝对值。

int8_t rssi = 0x9F;
if(rssi > 0)
{
    printf("RSSI: [%d] [0x%x]", (int)rssi, (int)rssi);
}
else
{
    printf("RSSI: [-%d] [-0x%x]", (rssi&0x7F), (rssi&0x7F));
}







附1:

原码:

 


如果机器字长为n,那么一个数的原码就是用一个n位的二进制数,其中最高位为符号位:正数为0,负数为1。剩下的n-1位表示概数的绝对值。

例如: X=+101011 , [X]原= 00101011    X=-101011 , [X]原= 10101011 
位数不够的用0补全。

PS:正数的原、反、补码都一样:0的原码跟反码都有两个,因为这里0被分为+0和-0。

反码:

 

知道了什么是原码,那反码就更是张飞吃豆芽——小菜一碟了。知道了原码,那么你只需要具备区分0跟1的能力就可以轻松求出反码,为什么呢?因为反码就是在原码的基础上,符号位不变其他位按位取反(就是0变1,1变0)就可以了。


例如:X=-101011 , [X]原= 10101011 ,[X]反=11010100


补码:


补码也非常的简单就是在反码的基础上按照正常的加法运算加1。


例如:X=-101011 , [X]原= 10101011 ,[X]反=11010100,[X]补=11010101


PS:0的补码是唯一的,如果机器字长为8那么[0]补=00000000。


移码:


移码最简单了,不管正负数,只要将其补码的符号位取反即可。


例如:X=-101011 , [X]原= 10101011 ,[X]反=11010100,[X]补=11010101,[X]移=01010101






 
Viewing all 158 articles
Browse latest View live