You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Wi-Fi或蓝牙实现Kotlin与Raspberry Pi 4端Python通信控制机械臂

安卓Kotlin与树莓派Python通信实现机械臂控制方案

两种成熟落地方案可选,优先选Wi-Fi TCP Socket方案,实现简单、延迟低、稳定性好,完全满足机械臂方向控制的需求;蓝牙方案适合无局域网的场景,配置相对繁琐。


方案一:Wi-Fi TCP Socket通信(推荐)

核心逻辑:树莓派作为TCP服务端在固定端口监听,安卓端作为客户端接入同一局域网后主动连接树莓派,通过长连接传输短字符串指令(比如上移传UP、下移传DOWN),解析后直接触发机械臂动作。

树莓派Python端配置

  • 前期准备
    • 确保树莓派和安卓手机连接同一个Wi-Fi,终端执行ifconfig wlan0查看树莓派局域网IP(inet字段后的地址,比如192.168.3.42)
    • 系统防火墙放开你要使用的端口(比如选8888端口,执行sudo ufw allow 8888即可)
    • 不需要额外安装第三方库,直接用Python自带的socket库写服务端:
    import socket
    # 导入你自己写的机械臂控制逻辑,比如舵机控制函数
    # from arm_driver import move_up, move_down, move_left, move_right
    
    LISTEN_IP = '0.0.0.0'  # 监听所有网卡请求
    LISTEN_PORT = 8888
    
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((LISTEN_IP, LISTEN_PORT))
    server.listen(1)  # 仅允许单客户端连接,适配单手机控制场景
    print(f"机械臂控制服务已启动,监听端口{LISTEN_PORT}")
    
    while True:
        conn, client_addr = server.accept()
        print(f"客户端已连接:{client_addr}")
        try:
            while True:
                # 单次最多接收1024字节,足够承载短控制指令
                raw_data = conn.recv(1024).decode('utf-8').strip()
                if not raw_data:
                    break
                print(f"收到指令:{raw_data}")
                # 匹配指令执行对应动作
                if raw_data == "UP":
                    # move_up() 替换成你自己的机械臂上移逻辑
                    print("执行上移动作")
                elif raw_data == "DOWN":
                    # move_down()
                    print("执行下移动作")
                elif raw_data == "LEFT":
                    # move_left()
                    print("执行左移动作")
                elif raw_data == "RIGHT":
                    # move_right()
                    print("执行右移动作")
                # 回执给安卓端,确认指令已执行
                conn.sendall("OK".encode('utf-8'))
        except Exception as e:
            print(f"连接异常:{str(e)}")
        finally:
            conn.close()
            print("客户端断开,等待重连")
    
  • 可选优化:给这个Python脚本配置开机自启,不用每次树莓派重启后手动启动服务。

安卓端Kotlin实现

  • 权限配置:在AndroidManifest.xml中添加网络权限,该权限为普通权限,不需要动态申请:
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
    *别在主线程做任何网络读写操作,安卓4.0以上直接禁止主线程网络请求,会抛异常崩溃,用协程切IO线程处理即可。
  • 封装Socket连接管理类,保持长连接减少每次发指令的连接开销,不要每次点按钮都新建连接:
    import java.io.BufferedReader
    import java.io.BufferedWriter
    import java.io.InputStreamReader
    import java.io.OutputStreamWriter
    import java.net.Socket
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.withContext
    
    object ArmControlSocket {
        private var socket: Socket? = null
        private var outputWriter: BufferedWriter? = null
        private var inputReader: BufferedReader? = null
        // 替换成你自己树莓派的局域网IP和端口
        private const val TARGET_IP = "192.168.3.42"
        private const val TARGET_PORT = 8888
    
        // 进入控制页时调用,建立连接
        suspend fun connect(): Boolean = withContext(Dispatchers.IO) {
            return@withContext try {
                socket = Socket(TARGET_IP, TARGET_PORT).apply {
                    soTimeout = 5000 // 连接/读写超时设为5秒
                }
                outputWriter = BufferedWriter(OutputStreamWriter(socket!!.getOutputStream()))
                inputReader = BufferedReader(InputStreamReader(socket!!.getInputStream()))
                true
            } catch (e: Exception) {
                e.printStackTrace()
                false
            }
        }
    
        // 按钮点击时调用,发送控制指令
        suspend fun sendCmd(command: String): Boolean = withContext(Dispatchers.IO) {
            return@withContext try {
                outputWriter?.apply {
                    write(command)
                    newLine()
                    flush()
                }
                // 等待树莓派执行完成的回执
                val ack = inputReader?.readLine()
                ack == "OK"
            } catch (e: Exception) {
                e.printStackTrace()
                false
            }
        }
    
        // 退出控制页时调用,释放连接
        fun release() {
            try {
                inputReader?.close()
                outputWriter?.close()
                socket?.close()
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                socket = null
                outputWriter = null
                inputReader = null
            }
        }
    }
    
  • 给你已经做好的四个方向按钮绑定点击事件,示例(上移按钮):
    binding.btnMoveUp.setOnClickListener {
        lifecycleScope.launch {
            val sendSuccess = ArmControlSocket.sendCmd("UP")
            if (!sendSuccess) {
                Toast.makeText(this@ControlActivity, "指令发送失败,请检查连接", Toast.LENGTH_SHORT).show()
            }
        }
    }
    // 其余三个按钮同理,分别传入"DOWN"、"LEFT"、"RIGHT"作为指令参数即可
    

方案一注意事项

  • 记得给树莓派在路由器后台绑定静态IP,不然DHCP动态分配的IP每次树莓派重启都会变,安卓端写死的IP会直接连不上
  • 如果长时间不操作连接被路由器自动断开,可以加个简单的心跳逻辑:每隔10秒发个PING指令,树莓派收到后回PONG,保持连接活跃
  • 调试阶段先不要接机械臂,先看树莓派端日志能不能正常收到指令,通信调通了再接硬件动作逻辑,避免误触发

方案二:蓝牙SPP通信(无局域网场景备选)

如果使用场景没有可用Wi-Fi,可以用经典蓝牙的SPP串口协议实现通信,整体逻辑和Socket方案类似,只是传输通道换成蓝牙:

  • 树莓派端先安装蓝牙依赖:sudo apt install pi-bluetooth bluez python3-pybluez,编写蓝牙服务端,使用通用SPP UUID00001101-0000-1000-8000-00805F9B34FB监听连接
  • 安卓端添加蓝牙相关权限,安卓12及以上需要动态申请BLUETOOTH_SCAN、BLUETOOTH_CONNECT权限,先完成和树莓派的蓝牙配对,连接成功后拿到输入输出流,后续发指令的逻辑和Wi-Fi Socket完全一致,直接往输出流写字符串指令即可
    *蓝牙方案延迟比Wi-Fi高、有效连接距离短、配对步骤繁琐,有Wi-Fi的情况下优先选前一种方案。

通用调试技巧

  • 安卓端连不上的时候,先在手机上用网络调试工具telnet树莓派的IP和端口,先确认网络链路通不通,再查代码逻辑
  • 控制指令尽量用短字符串,不要传复杂的JSON或者结构体,降低传输解析出错的概率
  • 可以给指令加个简单的校验位,避免信号干扰导致指令传错触发机械臂误动作

内容的提问来源于stack exchange,提问作者Rares

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 03:06:38