基于 Python Socket 与 Tkinter 的机器人动作通讯仿真实现

作者:谭贵锋
课程:机器人技术结课作业
项目方向:动作通讯
技术栈:Python、TCP Socket、JSON、Tkinter

摘要

机器人动作通讯是机器人控制系统中的基础环节。控制端需要把“前进、后退、左转、右转、停止”等动作指令发送给机器人端,机器人端再根据指令更新自身运动状态,并把执行结果反馈给控制端。为了便于课程展示和本地运行,本文使用 Python 实现了一个机器人动作通讯仿真系统:服务端模拟机器人本体,客户端模拟上位机控制界面,二者通过 TCP Socket 传输 JSON 格式的动作指令。

项目的重点不是单纯做一个界面,而是把“指令封装、网络发送、服务端解析、状态更新、结果反馈、界面刷新”这一条完整链路跑通。通过该项目,可以较直观地理解机器人动作控制中通信协议和执行逻辑之间的关系。

目录

  • 项目需求与设计目标
  • 系统总体架构
  • 通信协议设计
  • 核心代码实现
  • 运行步骤与效果展示
  • 测试结果
  • 常见问题与解决方法
  • 总结与扩展方向

一、项目需求与设计目标

本项目对应机器人技术课程结课作业中的“动作通讯”方向。根据作业要求,需要完成项目代码、运行截图,并整理 CSDN 博客链接用于提交。

我将项目目标拆成以下几点:

  1. 控制端能够向机器人端发送动作指令。
  2. 机器人端能够解析指令,并更新位置、朝向和状态。
  3. 控制端能够接收机器人端返回的执行结果。
  4. 图形界面能够显示机器人当前坐标、朝向和通信日志。
  5. 项目不依赖实体机器人硬件,保证在普通 Windows 电脑上也可以运行和截图。

最终选择的实现方案是:

  • 使用 TCP Socket 作为通讯方式;
  • 使用 JSON 作为指令和响应的数据格式;
  • 使用 Tkinter 制作图形化控制端;
  • 使用 Python 标准库完成全部功能,不额外安装第三方库。

二、系统总体架构

系统采用客户端/服务端结构,如下所示:

┌──────────────────────┐       TCP Socket        ┌──────────────────────┐
│  Tkinter 控制客户端   │  ───────────────────→  │  机器人模拟服务端     │
│  连接、按钮、日志     │      JSON 动作指令      │  解析、执行、反馈     │
└──────────────────────┘  ←───────────────────  └──────────────────────┘
                         JSON 状态响应

整体执行流程如下:

点击动作按钮
    ↓
客户端生成 JSON 指令
    ↓
通过 TCP 发送到服务端
    ↓
服务端解析 cmd 和 speed
    ↓
更新机器人坐标、朝向、状态
    ↓
服务端返回 JSON 响应
    ↓
客户端刷新 Canvas 图形和通信日志

这种结构和真实机器人控制系统比较接近。真实系统中,客户端可能是上位机、遥控器、手机 App 或调度平台;服务端则可以替换成真实机器人底盘控制程序。

三、通信协议设计

为了让数据结构清晰、便于调试,项目使用 JSON 作为通信格式。每条消息以换行符 \n 结尾,服务端可以按行读取消息,避免 TCP 粘包时不好判断消息边界的问题。

1. 动作指令格式

客户端发送的数据示例:

{"cmd":"forward","speed":1}

字段说明:

字段 类型 说明
cmd 字符串 动作指令名称
speed 整数 速度或步长倍数

支持的动作如下:

指令 含义 对机器人状态的影响
forward 前进 按当前朝向移动
backward 后退 按当前朝向反方向移动
left 左转 朝向逆时针旋转 90 度
right 右转 朝向顺时针旋转 90 度
stop 停止 状态变为空闲,坐标不变

2. 服务端响应格式

服务端处理成功后返回:

{"ok":true,"action":"forward","x":200,"y":140,"direction":"N","status":"moving"}

字段说明:

字段 说明
ok 是否执行成功
action 本次执行的动作
xy 机器人当前位置
direction 当前朝向,取值为 N/E/S/W
status 当前状态,如 movingturningidle

如果客户端发送了不支持的指令,服务端会返回错误信息:

{"ok":false,"error":"Unsupported command: jump"}

四、项目文件结构

项目目录如下:

robot_motion_comm/
  __init__.py
  protocol.py      # JSON 通信协议编码和解码
  server.py        # TCP 服务端与机器人状态逻辑
  client_gui.py    # Tkinter 图形化动作控制端
tests/
  test_protocol.py
  test_robot_state.py
screenshots/
  README.md
README.md
csdn_article.md

其中,protocol.py 负责协议层,server.py 负责机器人端逻辑,client_gui.py 负责控制端界面。这样拆分后,每个文件的职责比较清楚,后续如果要换成串口、MQTT 或 ROS2,也可以保留状态更新逻辑,只替换通信部分。

五、核心代码实现

1. 协议编码与解码

protocol.py 中定义了合法动作集合,并提供编码、解码和错误响应函数。

VALID_COMMANDS = {"forward", "backward", "left", "right", "stop"}

发送前先检查动作是否合法:

def make_command(command, speed=1):
    if command not in VALID_COMMANDS:
        raise ProtocolError(f"Unsupported command: {command}")
    if not isinstance(speed, int) or speed < 1:
        raise ProtocolError("Speed must be a positive integer")
    return {"cmd": command, "speed": speed}

编码时使用 json.dumps,并在末尾加上换行符:

def encode_message(payload):
    message = json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
    return f"{message}\n".encode("utf-8")

这里设置 ensure_ascii=False,是为了让中文错误信息也能正常显示。separators=(",", ":") 可以让 JSON 更紧凑,日志中看起来也更清楚。

2. 机器人状态更新

机器人状态用 RobotState 类表示,主要包含坐标、朝向、步长和状态。

@dataclass
class RobotState:
    x: int = 200
    y: int = 160
    direction: str = "N"
    step: int = 20
    status: str = "idle"

动作执行逻辑集中在 apply_command 方法中:

def apply_command(self, payload):
    command = payload.get("cmd")
    speed = int(payload.get("speed", 1))

    if command == "forward":
        self._move(speed)
        self.status = "moving"
    elif command == "backward":
        self._move(-speed)
        self.status = "moving"
    elif command == "left":
        self._turn(-1)
        self.status = "turning"
    elif command == "right":
        self._turn(1)
        self.status = "turning"
    elif command == "stop":
        self.status = "idle"

朝向使用 N/E/S/W 表示。前进时根据当前朝向决定坐标变化,例如朝向为 N 时,y 坐标减少;朝向为 E 时,x 坐标增加。

MOVES = {
    "N": (0, -1),
    "E": (1, 0),
    "S": (0, 1),
    "W": (-1, 0),
}

这种写法比写多个重复的 if 判断更清晰,也方便后续扩展更多方向或动作。

3. TCP 服务端

服务端监听本机 127.0.0.1:9000,接收到客户端连接后,为每个客户端创建一个线程处理通信。

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((host, port))
    server.listen()

服务端读取客户端发来的每一行 JSON 数据:

for line in file:
    payload = decode_message(line)
    response = state.apply_command(payload)
    connection.sendall(encode_message(response))

服务端控制台会打印收到的指令和返回的结果,方便截图和调试。例如:

[通信] 收到 {"cmd":"forward","speed":1} -> 返回 {'ok': True, 'action': 'forward', 'x': 200, 'y': 140, 'direction': 'N', 'status': 'moving'}

4. Tkinter 图形控制端

客户端界面主要包含四部分:

  • 连接设置:主机、端口、速度;
  • 动作按钮:前进、后退、左转、右转、停止;
  • Canvas 仿真区域:显示机器人位置和朝向;
  • 通信日志:显示发送和接收的数据。

按钮点击后,会调用统一的发送方法:

def send_command(self, command):
    payload = make_command(command, self.speed_var.get())
    self.sock.sendall(encode_message(payload))
    response = self._recv_response()

接收到服务端响应后,如果 oktrue,客户端就更新机器人状态,并重新绘制 Canvas。

if response.get("ok"):
    self.robot.update(response)
    self._draw_robot()

在 Canvas 中,机器人主体用圆形表示,朝向用箭头表示。这样截图时能直观看到位置和方向变化,比单纯命令行输出更适合课程作业展示。

六、运行步骤与效果展示

1. 启动服务端

在项目目录执行:

python robot_motion_comm/server.py

服务端启动成功后,会看到:

机器人动作通讯服务端启动:127.0.0.1:9000
等待客户端发送 forward/backward/left/right/stop 指令...

在这里插入图片描述


### 2. 启动客户端

另开一个终端,执行:

```powershell
python robot_motion_comm/client_gui.py

客户端打开后点击“连接”,连接成功后可以点击前进、后退、左转、右转和停止按钮。

建议插入截图:

在这里插入图片描述

3. 动作通讯效果

点击“前进”后,客户端会发送:

{"cmd":"forward","speed":1}

服务端返回:

{"ok":true,"action":"forward","x":200,"y":140,"direction":"N","status":"moving"}

客户端界面中的机器人坐标会发生变化,通信日志也会显示完整的发送和接收内容。

建议插入截图:

在这里插入图片描述

七、测试结果

项目使用 Python 标准库 unittest 编写测试,不需要安装第三方依赖。

测试命令:

python -m unittest discover -s tests -v

本地测试结果:

Ran 11 tests in 0.001s
OK

测试覆盖内容如下:

测试内容 说明
指令编码 验证动作指令能被编码为换行结尾的 JSON
指令解码 验证服务端能从 JSON 中读取指令
非法 JSON 验证错误数据不会导致程序静默失败
非法动作 验证不支持的动作会返回错误响应
前进/后退 验证机器人坐标变化正确
左转/右转 验证机器人朝向变化正确
停止 验证停止后状态变为 idle

测试的意义在于:即使后续修改界面或通信方式,也可以通过测试确认核心动作逻辑没有被破坏。

八、常见问题与解决方法

1. 客户端提示连接失败

原因通常是服务端没有先启动,或者端口填写错误。解决方法:

  1. 先运行 python robot_motion_comm/server.py
  2. 确认客户端主机为 127.0.0.1
  3. 确认端口为 9000

2. 端口被占用

如果服务端提示端口被占用,可以换一个端口启动:

python robot_motion_comm/server.py --port 9010

然后在客户端中把端口也改成 9010

3. 中文显示乱码

代码中 JSON 编码使用了 UTF-8。如果 PowerShell 控制台显示乱码,可以先执行:

chcp 65001

或者主要查看客户端界面中的通信日志。

4. Canvas 中机器人没有明显移动

可以把速度调大到 2 或 3,再点击前进/后退。机器人每次移动的距离由服务端的 step 和客户端发送的 speed 共同决定。

九、项目特点

这个项目有以下几个特点:

  1. 通信链路完整:包含发送、接收、解析、执行和反馈。
  2. 数据格式清楚:使用 JSON 表达动作指令和执行结果。
  3. 展示效果直观:图形界面能看到机器人位置和朝向变化。
  4. 依赖简单:只使用 Python 标准库,便于在普通电脑上运行。
  5. 可扩展性较好:后续可以替换通信层或接入实体机器人。

十、总结与扩展方向

通过本项目,我对机器人动作通讯的基本流程有了更清楚的理解。动作通讯并不只是“点击按钮让机器人动”,中间还包括协议设计、网络传输、异常处理和状态反馈等环节。只有控制端和机器人端对数据格式有一致理解,机器人才能稳定执行动作。

目前项目还是软件仿真版本,但它可以继续扩展:

  • 将 TCP Socket 替换为串口通信,对接 Arduino 或 ESP32;
  • 将动作指令发布为 ROS2 Topic,接入 ROS2 小车仿真;
  • 增加速度、加速度、路径点等更复杂的运动参数;
  • 增加地图边界检测,防止机器人移动到画布外;
  • 增加运行录屏,作为结课作业展示材料。

总体来说,本项目完成了机器人动作通讯的基本闭环,也为后续接入真实机器人硬件提供了一个清晰的通信结构。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐