使用的uwb型号为Decawave(已被QORVO公司收购)产的DWM1001C(Nordic Semiconductor nRF52832 SoC),其扩展组件为MDEK1001。其使用官方提供的安卓APP:Decawave DRTLS Manager应用进行定位。且此APP我已在此文章资源中提交,可以进行下载。
1.简单介绍UWB超宽带定位技术
UWB技术是一种使用1GHz以上频率带宽的无线载波通信技术。UWB技术具有系统复杂度低,发射信号功率谱密度低,对信道衰落不敏感,截获能力低,定位精度高等优点,尤其适用于室内等密集多径场所的高速无线接入。
2.使用官方映像刷新DWM1001
( 若你的UWB固件一直闪烁红灯,则代表固件无法正常工作。若你的固件在闪烁红灯同时其他颜色的灯也在闪烁,则代表正常工作可跳过此步骤。)
1.下载板载包,官方烧录软件等
板载包:DWM1001附带预刷写的固件出厂映像。此映像在DWM1001板载包中提供。可从官网资料进行下载:DWM1001C - Qorvohttps://www.qorvo.com/products/p/DWM1001C
烧录软件:https://www.segger.com/downloads/https://www.youtube.com/redirect?event=video_description&redir_token=QUFFLUhqa2dzV3lRazdWOE9CWnVvSHpFdHhEZzNRX2hVZ3xBQ3Jtc0trcXpkeHc2N1FhNjVCTi0tRGc5d3RieTBHZmFKelE4Sm9lSEU0RDZxczA2NzVpWjNaVFJXRWE1YzItbXJ5YW9rUi1DZUVSY25maFZUeGxXZVI2d29yRnRoMGF3ZVRobnZ5WHNWd3FlQm5iR1ZyX3U4TQ&q=https://www.segger.com/downloads/embedded-studio&v=emjC0HYd5gE找到Embedded Studio 与J-Link并下载并选择适配你电脑的版本(大多数均为windows 64位),下载后开始进行安装。(进行安装步骤时尽量按照系统选定的默认安装路径不要更改)
下载成功后,会出现三个APP:SEGGER Embedded Studio 8.22(SES)与J-Flash V8.12与J-Link Commander V8.12。
2.擦除以及烧录等
参考以下两篇博客:
J-Flash工具的使用---擦除、烧录及校验-CSDN博客
J-link J-flash 工程配置及下载_jflash软件安装包下载-CSDN博客
注:在J-flash创建新的文件后,选择版型型号位:Nordic Semiconductor nRF52832_xxAA
且打开的文件为前面官网下载板载包中的DWM1001_PANS_R2.0.hex
3.利用官方提供的DRTLS 安卓APP进行四基站(anchor)一标签(tag)的组网
APP以及官方APP指导手册我已放在文章资源里,可参考。注意:这里四个基站独立供电,一个标签放置在无人车上并通过串口连接主控。
官方YouTube视频教程:
https://www.youtube.com/watch?v=XJ9W7B2eZFw&t=24s
组网完成后效果如下:
点开Grid可看见栅格地图以及基站,标签的实时位置。至此组网完成。
4.通过串口使用DWM1001固件的内置 Shell 命令行模式(CLI)读取tag数据位置数据
讲解一下何为CLI以及如何理解:
DWM1001 的内置 Shell 命令行模式(CLI,Command Line Interface 即命令行接口),可以直接与模块交互,发送命令、获取位置信息、配置参数等。这是 DWM1001 自带的固件(RTLS,Real Time Location System)的一部分。
CLI:Command Line Interface 的缩写,它是一种通过命令行文字交互的方式,和计算机程序沟通的界面。
这是在 Linux 终端中,通过串口连接访问 DWM1001 模块固件的命令行。模块内运行的是 Decawave 官方烧录的固件,并不是你自己编写的程序。
这个 CLI 是直接由 DWM1001 内部 MCU 处理的,和 ROS 等系统没有直接关系,所以需要写代码自动读取这些数据发布到ROS中。
1.将UWB固件连接主控识别串口号与完成串口别名
因为我们要知道连接主控的这个UWB标签固件的串口号是哪一个才好进行串口通信,串口别名是为了固定串口名,防止下次上电串口名被Linux随机分配,同时也方便我们通过别名快速区分不同的UWB固件
识别与别名请参考我的这一篇博客:
2.安装minicom(用以串口通信):
sudo apt install minicom
3.通过minicom与串口/dev/ttyACM0通信,这里以/dev/ttyACM0举个例子。
sudo minicom -D /dev/ttyACM0
进入后会显示Welcome字样,之后一直按住Enter键,会出现@字样,一直到有dwm>提示符字样出现才代表成功。
4.进入dwm>提示符后查看内置命令菜单
输入?,即可看到内置Shell 命令菜单
** Command group: Base **
?: this help
help: this help
quit: quit
** Command group: GPIO **
gc: GPIO clear
gg: GPIO get
gs: GPIO set
gt: GPIO toggle
** Command group: SYS **
f: Show free memory on the heap
reset: Reboot the system
si: System info
ut: Show device uptime
frst: Factory reset
** Command group: SENS **
twi: General purpose TWI read
aid: Read ACC device ID
av: Read ACC values
scs: Stationary config set
scg: Stationary config get
** Command group: LE **
les: Show meas. and pos.
lec: Show meas. and pos. in CSV
lep: Show pos. in CSV
** Command group: UWB **
utpg: Get TxPwr
utps: Set TxPwr
** Command group: UWBMAC **
nmg: Get node mode
nmp: Set UWB mode to passive
nmo: Set UWB mode to off
nma: Set mode to AN
nmi: Set mode to ANI
nmt: Set mode to TN
nmtl: Set mode to TN-LP
nmb: Set mode to BN
la: Show AN list
lb: Show BN list
nis: Set Network ID
nls: Set node label
udi: Show incoming IoT data
uui: Send IoT data
stg: Get stats
stc: Clear stats
** Command group: API **
tlv: Send TLV frame
aurs: Set upd rate
aurg: Get upd rate
apg: Get pos
aps: Set pos
acas: Set anchor config
acts: Set tag config
aks: Set encryption key
akc: Clear encryption key
ans: Set NVM usr data
anc: Clear NVM usr data
ang: Get NVM usr data
** Tips **
Press Enter to repeat the last command
5.让这个UWB模块汇报当前自己坐标
在进入dwm提示符后,输入 lep (location engine position)并按回车。
可以看见POS信息不停的在输出
其POS格式为POS,x,y,z,quality
x, y, z:表示标签当前相对于原点的位置(单位:米)
quality:定位质量指标(通常范围是 0~100,值越高代表定位越可靠)
5. 创建c++节点,实时读取串口数据并发送到ROS2话题中
使用 C++ 节点,读取Tag标签串口 /dev/ttyACM0 数据。
只发布以 POS, 开头的数据行。
发布消息类型为 std_msgs::msg::String 到 ROS2 话题 /dwm1001/tag_pos。
工作空间为 uwbpos_ws。
1.准备 ROS2 工作空间 uwbpos_ws (每一行分开进行)
mkdir -p ~/uwbpos_ws/src
cd ~/uwbpos_ws
colcon build
source install/setup.bash
2.创建功能包 dwm1001_serial_pub
cd ~/uwbpos_ws/src
ros2 pkg create --build-type ament_cmake dwm1001_serial_pub --dependencies rclcpp std_msgs
3.编写 C++ 节点代码
在src/dwm1001_serial_pub/src/下创建dwm_pub.cpp文件并编辑
touch dwm_pub.cpp
gedit dwm_pub.cpp
dwm_pub.cpp节点完整代码如下:
#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/string.hpp>
#include <fstream>
#include <string>
#include <sstream>
class DWMSerialNode : public rclcpp::Node
{
public:
DWMSerialNode() : Node("dwm_serial_node")
{
publisher_ = this->create_publisher<std_msgs::msg::String>("/dwm1001/tag_pos", 10);
// 打开串口
serial_.open("/dev/ttyACM0", std::ios::in);
if (!serial_.is_open()) {
RCLCPP_ERROR(this->get_logger(), "无法打开串口 /dev/ttyACM0");
rclcpp::shutdown();
return;
}
// 创建定时器,周期读取串口数据
timer_ = this->create_wall_timer(
std::chrono::milliseconds(50),
std::bind(&DWMSerialNode::read_serial, this)
);
RCLCPP_INFO(this->get_logger(), "DWM1001串口节点启动,监听 /dev/ttyACM0...");
}
private:
void read_serial()
{
std::string line;
if (std::getline(serial_, line)) {
// 去除回车换行符
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
line.erase(std::remove(line.begin(), line.end(), '\n'), line.end());
if (line.rfind("POS,", 0) == 0) {
auto msg = std_msgs::msg::String();
msg.data = line;
publisher_->publish(msg);
RCLCPP_INFO(this->get_logger(), "发布: %s", line.c_str());
}
}
}
std::ifstream serial_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
rclcpp::TimerBase::SharedPtr timer_;
};
int main(int argc, char *argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<DWMSerialNode>());
rclcpp::shutdown();
return 0;
}
Cmakelists.txt完整内容:
cmake_minimum_required(VERSION 3.8)
project(dwm1001_serial_pub)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
# 添加可执行文件
add_executable(dwm_pub src/dwm_pub.cpp)
ament_target_dependencies(dwm_pub rclcpp std_msgs)
# 安装目标
install(TARGETS
dwm_pub
DESTINATION lib/${PROJECT_NAME}
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
set(ament_cmake_cpplint_FOUND TRUE)
set(ament_cmake_copyright_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
4.构建包:
cd ~/uwbpos_ws
colcon build
source install/setup.bash
5.在工作空间内运行节点:
ros2 run dwm1001_serial_pub dwm_pub
至此tag标签位置已成功移植到ROS2中
若出现串口占用导致Device or resource busy,参考我的这篇博客:
解决串口占用导致Device or resource busy问题-CSDN博客
6.多Tag模式下的UWB的listen(监听)模式设置以及相关说明:
此为附加内容,即你的项目中需要不止一个tag,需要监听并发布所有tag坐标。新增了一个连接小车的固件作为监听者,且串口号为/dev/ttyACM1。listen是用来监听整个DRTLS系统中所有的tag位置坐标的
由前面的命令菜单可知
在dwm>提示符后输入nmp ,即进入Passive 模式。
Passive Mode,用于监听其他 Tag 的位置信息,自己则不发出信息,只监听
在dwm>提示符后输入si,查看此固件具体详细信息
例如输出:dwm> si
[000033.920 INF] sys: fw2 fw_ver=x01030001 cfg_ver=x00010700
[000033.920 INF] uwb0: panid=x87AE addr=xDECA787B04402B8A #网络 ID 和该模块的唯一地址
[000033.930 INF] mode: tn (pasv,twr,np,le) #表示当前 Tag 处于 Passive 模式,并开启了 定位引擎(le)
[000033.930 INF] uwbmac: connected #已成功连接到网络(即已加入你之前搭建的 anchor 网络)
[000033.930 INF] uwbmac: bh disconnected
[000033.940 INF] cfg: sync=0 fwup=0 ble=1 leds=1 le=1 lp=0 stat_det=1 (sens=1) mode=0 upd_rate_norm=1 upd_rate_stat=1 label=DW2B8A
[000033.950 INF] enc: off
[000033.950 INF] ble: addr=F1:A1:D9:1C:E7:41
在dwm>提示符后输入les后,这将显示 所有可见 Tag 的位置 (我的两个tag名字为512F,C985)