树莓派ROS小车
安装树莓派相关ROS环境
安装ubuntu mate系统
下载ubuntu mate20.04镜像



制作并烧录到SD卡里

进入树莓派ubuntu mate系统
把sd卡插入树莓派,随后插电开机。使用micro HDMI线连上显示器。
进行基本的配置,设置用户名密码、网络连接。
更换清华源,不再赘述。
安装network工具
1
| sudo apt install net-tools
|
查看树莓派的ip

将树莓派和主机放在同一局域网,测试ping通,在主机终端输入
1
| ping 10.43.234.55(树莓派ip)
|

这样就是ping通了,可以使用远程连接。
树莓派配置远程连接
ssh连接
直接终端连接:
在ubuntu主机里,下载remmina连接工具。
ping通了之后,终端可以直接连,但是使用remmina工具更方便。
终端输入ssh <树莓派用户名>@<树莓派IP地址>

输入树莓派密码后就可以连接上了。
利于remmina:
这是linux下一个软件能储存信息,方便连接:
点击左上角加好,再输入ip 密码啥的就可以连接了。

利用TigerVNC进行远程桌面连接
先给ubuntu mate换个桌面,自带的mate桌面有点卡。
更换xfce4桌面并卸载自带mate桌面不赘述了(自行搜索)。

个人IP
校园网环境下:
PC有线连接校园网IP:10.43.173.38
树莓派无线连接宿舍校园网IP:10.43.234.55
609wifi环境下:
PC有线连接校园网IP:10.43.173.38
树莓派无线连接宿舍校园网IP:10.43.234.55
sync同步文件的使用
基本用法:
- 路径在左边:意味着将左边同步到右边
- 路径在右边:同理
1 2
| sync <本地路径> <远程连接用户名>@<IP地址>:<远程路径> # 注意空格,路径写全不要用~
|
sync同步命令是一次性的,每次输入都很长,很麻烦,编写一个脚本来实现:
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
| # 树莓派目标目录 REMOTE_DIR=/home/pi/pi_ws/src
echo "===============================" echo " 选择要同步的树莓派" echo "==============================="
# 显示菜单 index=1 for name in "${!PI_HOSTS[@]}"; do echo "$index. $name (${PI_HOSTS[$name]})" names[$index]=$name ((index++)) done
# 用户输入编号 read -p "请输入编号 (1-$((index-1))): " choice
if [[ -z "${names[$choice]}" ]]; then echo "无效的编号,退出。" exit 1 fi
# 获取选择的名称和IP SELECTED_NAME=${names[$choice]} PI_HOST=${PI_HOSTS[$SELECTED_NAME]}
echo "" echo " 已选择: $SELECTED_NAME ($PI_HOST)" echo " 本地目录: $LOCAL_DIR/" echo " 目标目录: $PI_USER@$PI_HOST:$REMOTE_DIR/" echo ""
# 二次确认 read -p "确认执行同步吗?(y/n): " confirm if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then echo "已取消同步。" exit 0 fi
# 检查设备是否在线 ping -c 1 -W 1 "$PI_HOST" &>/dev/null if [[ $? -ne 0 ]]; then echo "无法连接到 $PI_HOST,请检查网络。" exit 1 fi
# Rsync 同步 rsync -avz --progress "${LOCAL_DIR}/" "${PI_USER}@${PI_HOST}:${REMOTE_DIR}/"
echo "同步到 $SELECTED_NAME 完成" # --progress 命令可以中显示文件传输进度条,包括文件大小、已经传输的大小、传输速度和剩余时间
|
- -a———>保留文件属性并递归复制目录
- -v———>显示详细信息
- -z———>压缩数据,加快传输
- –progress———>显示每个文件传输进度
ROS与STM32间的通信
完成hello world!
首先,让STM32F407串口发送一个**”hello world!”**
linux下串口工具可以使用xTools:

cutecom在我的环境下有显示的问题,不使用。
查询串口:

可以通过插拔来查找usb串口通道
linux系统下串口无法使用问题:
- 没有安装usb转ttl的驱动导致问题
- 没有给予usb权限
解决方法:
- 去下载对应芯片的驱动(ch340/ch9102),linux自带了cp210x的驱动
- 使用chmod给对应串口通道施加权限,没有权限无法运行。
1 2 3
| chmod 777 <设备通道> eg: chmod 777 /dev/ttyUSB0
|
ros串口发布节点:
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
| #include "ros/ros.h" #include "std_msgs/String.h" #include "serial/serial.h"
serial::Serial ser; void serial_init() { ser.setPort("/dev/ttyUSB0"); ser.setBaudrate(115200); serial::Timeout to = serial::Timeout::simpleTimeout(100); ser.setTimeout(to);
try { ser.open(); } catch(serial::IOException& e){ ROS_ERROR_STREAM("Unable to open port."); } if (ser.isOpen()) { ROS_INFO("/dev/ttyCH341USB0 is open"); } else { ROS_ERROR("/dev/ttyCH341USB0 open failed."); } } int main(int argc, char **argv) { ros::init(argc, argv, "stm32_Serial_test01"); ros::NodeHandle nh; ros::Publisher stm32_pub = nh.advertise<std_msgs::String>("stm32_data", 10); serial_init(); ros::Rate loop_rate(10); while(ros::ok()) { size_t n = ser.available(); if (n != 0) { uint8_t buffer[1024]; n = ser.read(buffer, n);
std::string received_data(buffer, buffer + n); ROS_INFO_STREAM("Received data: " << received_data); std_msgs::String msg; msg.data = received_data; stm32_pub.publish(msg);
if (received_data.find("hello world!") != std::string::npos) { ROS_INFO("STM32 sent: hello world!"); } } ros::spinOnce(); loop_rate.sleep(); } ser.close(); return 0; }
|
设置stm32串口发送到上位机后,开启此节点,可以看到成功接收。
编写键盘输入节点实现键盘控制小车移动
键盘输入节点
核心是如何读取键盘输入,然后转换成数据发送话题出去
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
| int getch() { struct termios oldt, newt; int ch;
tcgetattr(STDIN_FILENO, &oldt); newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
return ch; } int main(int argc, char** argv) { ros::init(argc, argv, "keyboard_control"); ros::NodeHandle nh;
ros::Publisher cmd_pub = nh.advertise<geometry_msgs::Twist>("cmd_vel", 10);
geometry_msgs::Twist cmd;
std::cout << "\n=== 键盘控制说明 ===\n"; std::cout << "w: 前进 | s: 后退 | a: 左转 | d: 右转\n"; std::cout << "空格: 停止 | q: 退出程序\n"; std::cout << "====================\n";
while (ros::ok()) { int c = getch();
cmd.linear.x = 0.0; cmd.angular.z = 0.0;
if (c == 'w') { cmd.linear.x = 0.5; } else if (c == 's') { cmd.linear.x = -0.5; } else if (c == 'a') { cmd.angular.z = 1.0; } else if (c == 'd') { cmd.angular.z = -1.0; } else if (c == ' ') { cmd.linear.x = 0; cmd.angular.z = 0; } else if (c == 'q') { std::cout << "退出程序\n"; break; }
cmd_pub.publish(cmd);
ROS_INFO("发布速度: linear=%.2f m/s, angular=%.2f rad/s", cmd.linear.x, cmd.angular.z); }
return 0; }
|
编写了这个控制节点后,成功将数据利用话题的方式发布出去。
通信节点
前面写了键盘并且把