Intro
that may carry out duties and make selections by replicating or substituting human actions. Robotics is the scientific discipline centered on the design and development of robots. It’s a multidisciplinary mixture of:
- Electrical Engineering for sensors and actuators. Sensors collect knowledge from the surroundings, changing bodily properties into electrical alerts (like our 5 senses). Actuators convert these alerts into bodily actions or actions (like our muscle tissue).
- Mechanical Engineering for the design and motion of the bodily construction.
- Pc Science for the AI software program and algorithms.
At present, ROS (Robot Operating System) is the primary framework for Robotics that handles all of the totally different elements of a robotic (sensors, motors, controllers, cameras…), the place all of the elements trade knowledge in a modular manner. The ROS framework is supposed for actual robotic prototypes, and it may be used with each Python and C++. Given its reputation, there are lots of libraries constructed on prime of ROS, like Gazebo, essentially the most superior 3D simulator.
Since Gazebo is sort of difficult, one can nonetheless be taught the fundamentals of Robotics and construct user-friendly simulations exterior the ROS ecosystem. The principle Python libraries are:
- PyBullet (freshmen) — nice for experimenting with URDF (Unified Robotic Description Format), which is the XML schema for describing robotic our bodies, elements, and geometry.
- Webots (intermediate) — the physics is extra correct, so it may construct extra complicated simulations.
- MuJoCo (superior) — actual world simulator, it’s used for research-grade experiments. OpenAI’s RoboGYM library is constructed on prime of MuJoCo.

Since it is a tutorial for freshmen, I’m going to point out how you can construct easy 3D simulations with PyBullet. I’ll current some helpful Python code that may be simply utilized in different related instances (simply copy, paste, run) and stroll via each line of code with feedback with the intention to replicate this instance.
Setup
PyBullet is essentially the most user-friendly physics simulator for video games, visible results, Robotics, and Reinforcement Studying. You’ll be able to simply set up it with one of many following instructions (if pip fails, conda ought to undoubtedly work):
pip set up pybullet
conda set up -c conda-forge pybullet
You’ll be able to run PyBullet in two principal modes:
p.GUI
→ opens a window and exhibits the simulation in actual time.p.DIRECT
→ no graphics mode, used for scripts.
import pybullet as p
p.join(p.GUI) #or p.join(p.DIRECT)

Since it is a physics simulator, the very first thing to do is ready up gravity:
p.resetSimulation()
p.setGravity(gravX=0, gravY=0, gravZ=-9.8)
Units the worldwide gravity vector for the simulation. “-9.8” on the z-axis means an acceleration of 9.8 m/s^2 downward, identical to on Earth. With out this, your robotic and aircraft would float in zero-gravity, identical to in house.
URDF file
If the robotic was a human physique, the URDF file can be the skeleton that defines its bodily construction. It’s written in XML, and it’s principally the blueprint for the machine, defining what your robotic appears to be like like and the way it strikes.
I’m going to point out how you can create a easy dice, which is the 3D equal of print(“whats up world”).
urdf_string = """"
<robotic identify="cube_urdf">
<hyperlink identify="cube_link">
<visible>
<geometry>
<field measurement="0.5 0.5 0.5"/> <!-- 50 cm dice -->
</geometry>
<materials identify="blue">
<colour rgba="0 0 1 1"/>
</materials>
</visible>
<collision>
<geometry>
<field measurement="0.5 0.5 0.5"/>
</geometry>
</collision>
<inertial>
<mass worth="1.0"/>
<inertia ixx="0.0001" iyy="0.0001" izz="0.0001"
ixy="0.0" ixz="0.0" iyz="0.0"/>
</inertial>
</hyperlink>
</robotic>
"""
You’ll be able to both save the XLM code as a URDF file or use it as a string.
import pybullet as p
import tempfile
## setup
p.join(p.GUI)
p.resetSimulation()
p.setGravity(gravX=0, gravY=0, gravZ=-9.8)
## create a brief URDF file in reminiscence
with tempfile.NamedTemporaryFile(suffix=".urdf", mode="w", delete=False) as f:
f.write(urdf_string)
urdf_file = f.identify
## load URDF
p.loadURDF(fileName=urdf_file, basePosition=[0,0,1])
## render simulation
for _ in vary(240):
p.stepSimulation()

Please word that the loop did run with 240 steps. Why 240? It’s a hard and fast timestep generally utilized in videogame growth for a easy and responsive expertise with out overtaxing the CPU. 60 FPS (frames per second) with 1 body displayed each 1/60 of a second, which implies that a physics step of 1/240 seconds supplies 4 physics updates for each rendered body.
Within the earlier code, I rendered the dice with p.stepSimulation()
. It implies that the simulation is just not in real-time and also you management when the subsequent physics step occurs. Alternatively, you might use time sleep to bind it to the actual world clock.
import time
## render simulation
for _ in vary(240):
p.stepSimulation() #add a physics step (CPU velocity = 0.1 second)
time.sleep(1/240) #decelerate to real-time (240 steps × 1/240 second sleep = 1 second)

Going ahead, the XML code for robots will get rather more difficult. Fortunately, PyBullet comes with a set of preset URDF recordsdata. You’ll be able to simply load the default dice with out creating the XML for it.
import pybullet_data
p.setAdditionalSearchPath(path=pybullet_data.getDataPath())
p.loadURDF(fileName="dice.urdf", basePosition=[0,0,1])
At its core, a URDF file defines two principal issues: hyperlinks and joints. Hyperlinks are the stable a part of the robots (like our bones), and a joint is the connection between two inflexible hyperlinks (like our muscle tissue). With out joints, your robotic can be only a statue, however with joints it turns into a machine with movement and objective.
In a nutshell, each robotic is a tree of hyperlinks linked by joints. Every joint will be fastened (no motion) or rotate (“revolute joint”) and slide (“prismatic joint”). Subsequently, it’s good to know the robotic you’re utilizing.
Let’s make an instance with the well-known robotic R2D2 from Star Wars.

Joints
This time, we’ve to import a aircraft as properly to be able to create a floor for the robotic. And not using a aircraft, objects wouldn’t have a floor to collide with and they’d simply fall indefinitely.
## setup
p.join(p.GUI)
p.resetSimulation()
p.setGravity(gravX=0, gravY=0, gravZ=-9.8)
p.setAdditionalSearchPath(path=pybullet_data.getDataPath())
## load URDF
aircraft = p.loadURDF("aircraft.urdf")
robo = p.loadURDF(fileName="r2d2.urdf", basePosition=[0,0,0.1])
## render simulation
for _ in vary(240):
p.stepSimulation()
time.sleep(1/240)
p.disconnect()

Earlier than utilizing the robotic, we should perceive its elements. PyBullet has standardized the data construction so that each robotic you import shall at all times be outlined by the identical 17 common attributes. Since I simply need to run a script, I’m going to hook up with the physics server with out the graphic interface (p.DIRECT
). The principle operate to research a joint is p.getJointInfo()
.
p.join(p.DIRECT)
dic_info = {
0:"joint Index", #begins at 0
1:"joint Title",
2:"joint Kind", #0=revolute (rotational), 1=prismatic (sliding), 4=fastened
3:"state vectorIndex",
4:"velocity vectorIndex",
5:"flags", #nvm at all times 0
6:"joint Damping",
7:"joint Friction", #coefficient
8:"joint lowerLimit", #min angle
9:"joint upperLimit", #max angle
10:"joint maxForce", #max pressure allowed
11:"joint maxVelocity", #max velocity
12:"hyperlink Title", #youngster hyperlink linked to this joint
13:"joint Axis",
14:"dad or mum FramePos", #place
15:"dad or mum FrameOrn", #orientation
16:"dad or mum Index" #−1 = base
}
for i in vary(p.getNumJoints(bodyUniqueId=robo)):
print(f"--- JOINT {i} ---")
joint_info = p.getJointInfo(bodyUniqueId=robo, jointIndex=i)
print({dic_info[k]:joint_info[k] for ok in dic_info.keys()})

Each joint, regardless of if it’s a wheel, elbow, or gripper, has to show those self same options. The code above exhibits that R2D2 has 15 joints. Let’s analyze the primary one, referred to as “base to right-leg”:
- The joint sort is 4, which suggests it doesn’t transfer. The dad or mum hyperlink is -1, which suggests it’s linked to the base, the foundation a part of the robotic (like our skeleton backbone). For R2D2, the bottom is the primary cylindrical physique, that large blue and white barrel.
- The hyperlink identify is “proper leg”, so we perceive that this joint connects the robotic’s base with the precise leg, but it surely’s not motorized. That’s confirmed by joint axis, joint dumping, and joint friction being all zeros.
- Mum or dad body place and orientation outline the place the precise leg is connected to the bottom.
Hyperlinks
Alternatively, to be able to analyze the hyperlinks (the bodily physique segments), one should loop via the joints to extract the hyperlink identify. Then, you need to use two principal capabilities: p.getDynamicsInfo()
to grasp the bodily properties of the hyperlink, and p.getLinkState()
to know its spatial state.
p.join(p.DIRECT)
for i in vary(p.getNumJoints(robo)):
link_name = p.getJointInfo(robo, i)[12].decode('utf-8') #discipline 12="hyperlink Title"
dyn = p.getDynamicsInfo(robo, i)
pos, orn, *_ = p.getLinkState(robo, i)
dic_info = {"Mass":dyn[0], "Friction":dyn[1], "Place":pos, "Orientation":orn}
print(f"--- LINK {i}: {link_name} ---")
print(dic_info)

Let’s analyze the primary one, the “right-leg”. The mass of 10 kg contributes to gravity and inertia, whereas the friction impacts how a lot it slides when contacting the bottom. The orientation (0.707=sin(45°)/cos(45°)) and place point out that the right-leg hyperlink is a stable piece, barely ahead (5cm), tilted 90° relative to the bottom.
Actions
Let’s take a look at a joint that may really transfer.
For istance, joint 2 is the precise entrance wheel. It’s a sort=0 joint, so it rotates. This joint connects the precise leg (hyperlink index 1) to the precise entrance wheel: base_link → right_leg → right_front_wheel. The joint axis is (0,0,1), so we all know that the wheel spins across the z-axis. The bounds (decrease=0, higher=-1) point out that the wheel can spin infinitely, which is regular for rotors.
We are able to simply transfer this joint. The principle command for controlling actuators in your robotic is p.setJointMotorControl2()
, it permits to regulate the pressure, velocity, and place of a joint. On this case, I need to make the wheel spin, so I’ll apply force to gradually bring the velocity from zero to a target velocity, then maintain it by balancing applied force and resistive forces (i.e. friction, damping, gravity).
p.setJointMotorControl2(bodyUniqueId=robo, jointIndex=2,
controlMode=p.VELOCITY_CONTROL,
targetVelocity=10, pressure=5)
Now, if we apply the identical pressure to all of the 4 wheels, we’ll make our little robotic transfer ahead. From the evaluation carried out earlier, we all know that the joints for the wheel are quantity 2 and three (proper facet), and quantity 6 and seven (left facet).

Contemplating that, I shall first make R2D2 flip round by spinning just one facet, after which apply pressure to each wheel on the similar time to make it transfer ahead.
import pybullet as p
import pybullet_data
import time
## setup
p.join(p.GUI)
p.resetSimulation()
p.setGravity(gravX=0, gravY=0, gravZ=-9.8)
p.setAdditionalSearchPath(path=pybullet_data.getDataPath())
## load URDF
aircraft = p.loadURDF("aircraft.urdf")
robo = p.loadURDF(fileName="r2d2.urdf", basePosition=[0,0,0.1])
## calm down
for _ in vary(240):
p.stepSimulation()
right_wheels = [2,3]
left_wheels = [6,7]
### first flip
for _ in vary(240):
for j in right_wheels:
p.setJointMotorControl2(bodyUniqueId=robo, jointIndex=j,
controlMode=p.VELOCITY_CONTROL,
targetVelocity=-100, pressure=50)
for j in left_wheels:
p.setJointMotorControl2(bodyUniqueId=robo, jointIndex=j,
controlMode=p.VELOCITY_CONTROL,
targetVelocity=100, pressure=50)
p.stepSimulation()
time.sleep(1/240)
### then transfer ahead
for _ in vary(500):
for j in right_wheels + left_wheels:
p.setJointMotorControl2(bodyUniqueId=robo, jointIndex=j,
controlMode=p.VELOCITY_CONTROL,
targetVelocity=-100, pressure=10)
p.stepSimulation()
time.sleep(1/240)
p.disconnect()

Please word that each robotic has a unique mass (weight) so the actions will be totally different primarily based on the simulation physics (i.e. gravity). You’ll be able to play and check out totally different pressure and velocity till you discover the specified end result.

Conclusion
This text has been a tutorial to introduce PyBullet and how you can create very primary 3D simulations for Robotics. We realized how you can import URDF objects and how you can analyze joints and hyperlinks. We additionally performed with the well-known robotic R2D2. New tutorials with extra superior robots will come.
Full code for this text: GitHub
I hope you loved it! Be happy to contact me for questions and suggestions or simply to share your attention-grabbing initiatives.
👉 Let’s Connect 👈

(All photos are by the creator until in any other case famous)