[Plant Simulation Study #05] – Controlling Plant Simulation with an Xbox Controller via Socket Communication

๐Ÿงช [Plant Simulation Study #05] – Controlling Plant Simulation with an Xbox Controller via Socket Communication

๐ŸŽฌ Overview

In this demo, I integrated Plant Simulation with an Xbox game controller using socket communication.
The goal is not just to passively observe simulations but to interactively control simulation objects in real time using an external device.
This makes the simulation experience more dynamic and hands-on for both experimentation and demonstration purposes.

  • Purpose: Test real-time control using an external device
  • Tech Stack: Python + Xbox Controller + Socket Communication + Plant Simulation
  • Simulation Tools Used: Method, SocketClient object in Plant Simulation

๐ŸŽฅ Demo Video
๐Ÿ”— https://youtu.be/wE22Czmx6lM

 


๐Ÿ› ๏ธ Simulation Architecture & Communication Flow

ComponentDescription
Input Device Xbox controller (data received via Python script)
Middle Layer Python socket server processes directional inputs
Simulation Plant Simulation connects to Python server and receives movement commands
Controlled Object MU or 3D robot joints (position and speed controlled in real time)
 


๐Ÿ”ง Key Implementation Details

  1. Python Layer
    Use libraries like pygame or inputs to detect Xbox controller inputs.
    The data is sent via TCP socket as strings to the simulation.
  2. Plant Simulation Side
    Use SocketClient to receive data and update the simulation dynamically.
    Here's how message parsing and joint motion control is implemented:

 
// writes the value to the global variable ’MessageReceived’
if strLen(SocketMessage) = 0
    MessageReceived := to_str(strAscii(SocketMessage)); // byte received
else
    MessageReceived := to_str(SocketMessage); // string received
end
  • Motion Execution (based on socket command)
    Below is the structure for controlling 3D robot joints using the received command:
-- param attribute: string, oldValue, newValue: string
-- param attribute: string, oldValue: string
param newValue: string

switch newValue
case "UP,1"

	if ํ˜„์žฌ๋กœ๋ด‡ = void or ํ˜„์žฌ_์ œ์–ด์ถ• = ""
		messagebox("๋กœ๋ด‡๊ณผ ์ œ์–ด์ถ•์„ ๋‹ค์‹œ ํ™•์ธํ•ด์ฃผ์„ธ์š”.")
		return
	end

	switch ํ˜„์žฌ_์ œ์–ด์ถ•
	case "J1"
		ํ˜„์žฌ๋กœ๋ด‡.J1 := ํ˜„์žฌ๋กœ๋ด‡.J1 + ์ด๋™๊ฐ’
		var J1_num := ํ˜„์žฌ๋กœ๋ด‡.J1

		ํ˜„์žฌ๋กœ๋ด‡._3D.getobject("J1").moveTo(J1_num)
	case "J2"
		ํ˜„์žฌ๋กœ๋ด‡.J2 := ํ˜„์žฌ๋กœ๋ด‡.J2 + ์ด๋™๊ฐ’
		var J2_num := ํ˜„์žฌ๋กœ๋ด‡.J2
		ํ˜„์žฌ๋กœ๋ด‡._3D.getobject("J1").getobject("J2").moveTo(J2_num)
	case "J3"
		ํ˜„์žฌ๋กœ๋ด‡.J3 := ํ˜„์žฌ๋กœ๋ด‡.J3 + ์ด๋™๊ฐ’
		var J3_num := ํ˜„์žฌ๋กœ๋ด‡.J3
		ํ˜„์žฌ๋กœ๋ด‡._3D.getobject("J1").getobject("J2").getobject("J3").moveTo(J3_num)
	case "J4"
		ํ˜„์žฌ๋กœ๋ด‡.J4 := ํ˜„์žฌ๋กœ๋ด‡.J4 + ์ด๋™๊ฐ’
		var J4_num := ํ˜„์žฌ๋กœ๋ด‡.J4
		ํ˜„์žฌ๋กœ๋ด‡._3D.getobject("J1").getobject("J2").getobject("J3").getobject("J4").moveTo(J4_num)
	case "J5"
		ํ˜„์žฌ๋กœ๋ด‡.J5 := ํ˜„์žฌ๋กœ๋ด‡.J5 + ์ด๋™๊ฐ’
		var J5_num := ํ˜„์žฌ๋กœ๋ด‡.J5
		ํ˜„์žฌ๋กœ๋ด‡._3D.getobject("J1").getobject("J2").getobject("J3").getobject("J4").getobject("J5").moveTo(J5_num)
	case "J6"
		ํ˜„์žฌ๋กœ๋ด‡.J6 := ํ˜„์žฌ๋กœ๋ด‡.J6 + ์ด๋™๊ฐ’
		var J6_num := ํ˜„์žฌ๋กœ๋ด‡.J6
		ํ˜„์žฌ๋กœ๋ด‡._3D.getobject("J1").getobject("J2").getobject("J3").getobject("J4").getobject("J5").getobject("J6").moveTo(J6_num)
	end
case "UP,-1"

	if ํ˜„์žฌ๋กœ๋ด‡ = void or ํ˜„์žฌ_์ œ์–ด์ถ• = ""
		messagebox("๋กœ๋ด‡๊ณผ ์ œ์–ด์ถ•์„ ๋‹ค์‹œ ํ™•์ธํ•ด์ฃผ์„ธ์š”.")
		return
	end

	switch ํ˜„์žฌ_์ œ์–ด์ถ•
	case "J1"
		ํ˜„์žฌ๋กœ๋ด‡.J1 := ํ˜„์žฌ๋กœ๋ด‡.J1 - ์ด๋™๊ฐ’
		var J1_num1 := ํ˜„์žฌ๋กœ๋ด‡.J1
		ํ˜„์žฌ๋กœ๋ด‡._3D.getobject("J1").moveTo(J1_num1)
	case "J2"
		ํ˜„์žฌ๋กœ๋ด‡.J2 := ํ˜„์žฌ๋กœ๋ด‡.J2 - ์ด๋™๊ฐ’
		var J2_num1 := ํ˜„์žฌ๋กœ๋ด‡.J2
		ํ˜„์žฌ๋กœ๋ด‡._3D.getobject("J1").getobject("J2").moveTo(J2_num1)
	case "J3"
		ํ˜„์žฌ๋กœ๋ด‡.J3 := ํ˜„์žฌ๋กœ๋ด‡.J3 - ์ด๋™๊ฐ’
		var J3_num1 := ํ˜„์žฌ๋กœ๋ด‡.J3
		ํ˜„์žฌ๋กœ๋ด‡._3D.getobject("J1").getobject("J2").getobject("J3").moveTo(J3_num1)
	case "J4"
		ํ˜„์žฌ๋กœ๋ด‡.J4 := ํ˜„์žฌ๋กœ๋ด‡.J4 - ์ด๋™๊ฐ’
		var J4_num1 := ํ˜„์žฌ๋กœ๋ด‡.J4
		ํ˜„์žฌ๋กœ๋ด‡._3D.getobject("J1").getobject("J2").getobject("J3").getobject("J4").moveTo(J4_num1)
	case "J5"
		ํ˜„์žฌ๋กœ๋ด‡.J5 := ํ˜„์žฌ๋กœ๋ด‡.J5 - ์ด๋™๊ฐ’
		var J5_num1 := ํ˜„์žฌ๋กœ๋ด‡.J5
		ํ˜„์žฌ๋กœ๋ด‡._3D.getobject("J1").getobject("J2").getobject("J3").getobject("J4").getobject("J5").moveTo(J5_num1)
	case "J6"
		ํ˜„์žฌ๋กœ๋ด‡.J6 := ํ˜„์žฌ๋กœ๋ด‡.J6 - ์ด๋™๊ฐ’
		var J6_num1 := ํ˜„์žฌ๋กœ๋ด‡.J6
		ํ˜„์žฌ๋กœ๋ด‡._3D.getobject("J1").getobject("J2").getobject("J3").getobject("J4").getobject("J5").getobject("J6").moveTo(J6_num1)
	end

case "A,1"
	if ํ˜„์žฌ๋กœ๋ด‡.empty = false
	else
		pick.mu.move(ํ˜„์žฌ๋กœ๋ด‡)
	end

case "B,1"
	if ํ˜„์žฌ๋กœ๋ด‡.empty = true
		else
		ํ˜„์žฌ๋กœ๋ด‡.mu.move(place)
	end
case "X,1"
	switch ์ด๋™๊ฐ’
	case 1
		์ด๋™๊ฐ’ := 2
	case 2
		์ด๋™๊ฐ’ := 3
	case 3
		์ด๋™๊ฐ’ := 5
	case 5
		์ด๋™๊ฐ’ := 10
	case 10
		์ด๋™๊ฐ’ := 30
	case 30
		์ด๋™๊ฐ’ := 90
	case 90
		์ด๋™๊ฐ’ := 1
	end
case "Y,1"
	switch ํ˜„์žฌ_์ œ์–ด์ถ•
	case ""
		ํ˜„์žฌ_์ œ์–ด์ถ• := "J1"
	case "J1"
		ํ˜„์žฌ_์ œ์–ด์ถ• := "J2"
	case "J2"
		ํ˜„์žฌ_์ œ์–ด์ถ• := "J3"
	case "J3"
		ํ˜„์žฌ_์ œ์–ด์ถ• := "J4"
	case "J4"
		ํ˜„์žฌ_์ œ์–ด์ถ• := "J5"
	case "J5"
		ํ˜„์žฌ_์ œ์–ด์ถ• := "J6"
	case "J6"
		ํ˜„์žฌ_์ œ์–ด์ถ• := "J1"
	end
end

๐Ÿ“ˆ Experiment Results & Applications

This goes far beyond a simple demo. Possible applications include:

  • Real-time simulation tests integrated with external machines or logistics devices
  • Virtual commissioning of robotic systems
  • Creating interactive simulation models for training or demos
  • Testing digital twins with physical control input

๐Ÿง  Final Thoughts

While the Xbox controller was used in this example, the architecture supports any external device — including sensors, mobile apps, keyboards, or industrial controllers.
This approach shows how Plant Simulation can be used not just as an analysis tool but as a real-time experimentation and feedback platform.
More integrations are coming soon — stay tuned!