Uniapp开发app与ROS2环境Python节点通过蓝牙通信

以下是基于Uniapp开发Android应用与ROS 2 Python节点通过蓝牙通信的完整实现方案:


系统架构

Uniapp(Android) ↔ 蓝牙RFCOMM ↔ ROS 2 Python节点(运行在Linux设备上)

1. ROS 2 Python节点准备

蓝牙服务端代码 (bluetooth_ros_bridge.py)
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
import bluetooth
import json
from threading import Thread

class BluetoothBridge(Node):
    def __init__(self):
        super().__init__('bluetooth_bridge')
        # 初始化蓝牙服务
        self.server_sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
        self.server_sock.bind(("", bluetooth.PORT_ANY))
        self.server_sock.listen(1)
        self.port = self.server_sock.getsockname()[1]
        
        # 广播服务UUID
        bluetooth.advertise_service(
            self.server_sock,
            "ROS2Bridge",
            service_id=bluetooth.SERIAL_PORT_CLASS,
            profiles=[bluetooth.SERIAL_PORT_PROFILE]
        )
        
        self.get_logger().info(f"等待蓝牙连接,端口: {self.port}")
        self.client_sock = None
        self.receive_thread = Thread(target=self.receive_data)
        self.receive_thread.daemon = True
        self.receive_thread.start()

    def receive_data(self):
        try:
            self.client_sock, address = self.server_sock.accept()
            self.get_logger().info(f"已连接: {address}")
            
            while rclpy.ok():
                data = self.client_sock.recv(1024)
                if not data:
                    break
                    
                try:
                    msg = json.loads(data.decode())
                    self.handle_message(msg)
                except json.JSONDecodeError:
                    self.get_logger().warn("收到非JSON数据")

        except Exception as e:
            self.get_logger().error(f"蓝牙错误: {str(e)}")

    def handle_message(self, msg):
        # 处理来自APP的消息
        self.get_logger().info(f"收到消息: {msg}")
        # 示例:发送ROS2主题
        # self.publisher.publish(String(data=msg['command']))
        
        # 回复消息
        response = {"status": "OK", "data": "Received"}
        self.client_sock.send(json.dumps(response).encode())

def main(args=None):
    rclpy.init(args=args)
    node = BluetoothBridge()
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()

if __name__ == '__main__':
    main()
依赖安装
sudo apt install python3-bluez ros-${ROS_DISTRO}-rclpy
pip install pybluez2

2. Uniapp Android端实现

修改 manifest.json
{
  "app-plus": {
    "distribute": {
      "android": {
        "permissions": [
          "android.permission.BLUETOOTH",
          "android.permission.BLUETOOTH_ADMIN",
          "android.permission.ACCESS_FINE_LOCATION"
        ]
      }
    }
  }
}
蓝牙服务封装 (bluetooth-service.js)
const BluetoothDevices = uni.requireNativePlugin('Bluetooth-Devices')

export default {
  data() {
    return {
      connectedDevice: null,
      socket: null
    }
  },
  methods: {
    // 搜索设备
    scanDevices() {
      return new Promise((resolve, reject) => {
        BluetoothDevices.startDiscovery({
          success: (res) => resolve(res.devices),
          fail: (err) => reject(err)
        })
      })
    },

    // 连接ROS2设备
    async connectToROS2(deviceName) {
      const devices = await this.scanDevices()
      const target = devices.find(d => d.name.includes(deviceName))
      
      if (!target) throw new Error('设备未找到')

      this.socket = await new Promise((resolve, reject) => {
        BluetoothDevices.createSocket({
          address: target.address,
          type: 'rfcomm',
          success: resolve,
          fail: reject
        })
      })

      this.connectedDevice = target
      this.setupListeners()
      return target
    },

    // 设置消息监听
    setupListeners() {
      BluetoothDevices.onSocketMessage((res) => {
        try {
          const msg = JSON.parse(res.data)
          this.$emit('message', msg)
        } catch (e) {
          console.error('解析消息失败', e)
        }
      })
    },

    // 发送JSON数据
    sendJson(data) {
      return new Promise((resolve, reject) => {
        BluetoothDevices.writeSocket({
          data: JSON.stringify(data),
          success: resolve,
          fail: reject
        })
      })
    },

    // 断开连接
    disconnect() {
      if (this.socket) {
        BluetoothDevices.closeSocket()
        this.socket = null
      }
    }
  }
}

3. 页面调用示例 (index.vue)

<template>
  <view>
    <button @click="connect">连接ROS2设备</button>
    <button @click="sendTestCommand">发送测试命令</button>
  </view>
</template>

<script>
import BluetoothService from './bluetooth-service.js'

export default {
  mixins: [BluetoothService],
  methods: {
    async connect() {
      try {
        await this.connectToROS2('ROS2_Bluetooth') // 设备名称前缀
        uni.showToast({ title: '连接成功', icon: 'success' })
      } catch (err) {
        uni.showToast({ title: `连接失败: ${err}`, icon: 'none' })
      }
    },

    async sendTestCommand() {
      if (!this.connectedDevice) return
      
      try {
        const response = await this.sendJson({
          command: 'move_forward',
          speed: 0.5,
          duration: 2
        })
        console.log('ROS2响应:', response)
      } catch (err) {
        console.error('发送失败', err)
      }
    }
  }
}
</script>

4. 关键实现细节

自动连接流程
  1. Android端定期扫描蓝牙设备 (startDiscovery)
  2. 识别包含特定前缀的设备名 (如 ROS2_Bluetooth_XX)
  3. 自动建立RFCOMM socket连接
  4. 维护心跳包机制检测连接状态
数据协议设计
{
  "type": "command/respose/event",
  "timestamp": 1630000000,
  "payload": {
    "topic": "/cmd_vel",
    "data": {"linear": 0.1, "angular": 0.0}
  }
}
ROS2消息转换

在Python端需要实现:

def handle_message(self, msg):
    if msg['type'] == 'command':
        # 示例:控制机器人移动
        cmd = Twist()
        cmd.linear.x = msg['payload']['data']['linear']
        self.cmd_pub.publish(cmd)
        
        # 发送响应
        self.send_response({
            "type": "response",
            "status": "executed"
        })

5. 注意事项

  1. Android权限

    • 需要动态申请位置权限(Android 12+要求)
    • pages.json中声明蓝牙权限
  2. 跨平台兼容

    • iOS需要使用不同的原生插件(如uni-bluetooth
    • 建议使用try-catch包裹蓝牙操作
  3. 性能优化

    // 节流消息发送
    import throttle from 'lodash.throttle'
    this.throttledSend = throttle(this.sendJson, 100)
    
  4. 调试工具

    • 使用nRF Connect等蓝牙调试APP测试基础连接
    • ROS2端可以用bluetoothctl查看设备状态

完整项目结构

project/
├── uniapp_project/            # Uniapp工程
│   ├── js_sdk/                # 蓝牙SDK封装
│   └── pages/
├── ros2_ws/                   # ROS2工作空间
│   ├── bluetooth_bridge/      # Python蓝牙桥接包
│   └── launch/                # 启动文件
└── docs/                      # 协议文档

通过以上实现,可以建立稳定的蓝牙通信通道,典型延迟在50-200ms之间,适合传输控制指令和传感器数据。对于大数据量传输建议改用Wi-Fi。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

研创通之逍遥峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值