07 August 2019

The 10X programmer


Can you call yourself a 10X programmer if you get work done ten times faster than other programmers or can do the work of 10 average programmers?
I don't think so.

There have been some discussions recently about 10X programmers, where some say it's a myth, some say it isn't, and some examine the logic of it. Even a CEO I knew, used to wish for a 10X programmer. He was completely bought into the concept, thinking that such programmers existed at top companies.

Here's my take on it:

Yes, there are programmers who can get work done faster than others.
Yes, there are programmers who are more expert at specific languages and skills than others.


But are any of the above programmers 10X? Nope! 


The measurement of 10X is not done based on how quickly a product was delivered with minimal errors.

10X is measured over a long duration based on:
  • Whether requirements have been understood, documented and verified with the customer.
  • Whether the software architecture and design patterns have been subject to peer-review from experienced people and is as future-proof as possible.
  • Whether the software language and tools have been chosen based on careful evaluation, as per the requirements and anticipated changes.
  • Whether version control, test-cases, bug-trackers, continuous integration and quality checks are performed.
  • Whether the programmer and the team share a good chemistry and fit well into company culture.
Face it. It only makes sense to measure 10X over a span of many years. A programmer who delivers poorly planned code quickly may seem like a hero initially, but when (s)he gets hit by a bus or when more members are added to a team or when the client requests more changes and the software suddenly requires a complete overhaul, that's when you realize that the initial 10X advantage you thought you got (even in a startup), is actually much more costly than if you had invested time in ensuring the bulleted points mentioned above were fulfilled.

I've seen plenty of programmers and companies getting stuck with trying to deliver quickly and then ending up wasting more than double that time making up for errors and unaccounted factors. Moreover, working overtime, trying to be 10X can also get people burnt out with severe health problems; some of which may be irreversible.


Don't go for a 10X programmer. Go for good planning, documentation, execution and teamwork. 
That's where the real 10X is.


01 August 2019

Solving the MokManager mmx64.efi Not Found errors and the missing Mok menu problem

Until I used Ubuntu 16.04, I didn't have much of a problem with UEFI. Just the first time it was confusing and took a while to find the solution, but everything else, like third party drivers and VirtualBox worked without problems.

But when trying to install Ubuntu 16.04.6, the system wouldn't even start. It showed the below message and shutdown.

Unable to find mmx64.efi file:


Failed to open \EFI\BOOT\mmx64.efi - Not Found.
Something has gone seriously wrong.

It sounded like Russel Peter's Dad saying "Somebody's gonna get hurt real bad".

For a long time, searching the internet didn't yield a solution, but some posts mentioned renaming grubx64.efi to mmx64.efi. The trouble with that is, that if you try to rename the file in Ubuntu, it won't work. Ubuntu shows you a totally different filesystem. You have to go to Windows. In Windows, you won't be able to see all the files in the pen drive. It just shows you one folder into which you have to navigate into, find the grubx64.efi file and rename it to mmx64.efi.
Viola! You'll now be able to boot from the pen drive.

Funny thing is, after installing Ubuntu, you have to rename that file on the pen drive back to grubx64.efi (and you have to do it in Windows; not in Ubuntu), or else you won't be able to boot from the pen drive.



Need to set the Mok state after reboot but the screen never shows up:

When installing VirtualBox or the third party drivers in Ubuntu, the screen says that on reboot, you'll have to type a password to enroll mok.


You'll notice VirtualBox generating a private key for Mok

However, when you reboot, you are never presented with any such mok screen.
What you are supposed to do to be able to see the mok screen is this:
  • Go to BIOS. 
  • In the security page, select the option to "specify a trusted key".
  • Navigate to "Ubuntu".



  • Select "mmx64.efi", give it a name like "MokManager" or anything you like, save and exit BIOS and come back to BIOS again.


  • This time in the Boot screen you'll see new boot options. Select the "MokManager" option and move it to the top so that it boots first. Save and exit BIOS.


  • Now when you boot you'll see the Mok menu from which you can enroll the mok key that was generated by Virtual Box or the Ubuntu installer.

That's it. That's the secret of how to do MokManagement. There's no need of disabling UEFI. There's no need of disabling secure boot.

I just wish the OEM people and OS manufacturers came up with a standard which wouldn't require such complicated workarounds.


06 July 2019

Using dd instead of disk and partition cloners like CloneZilla or PartImage

The problem with applications like CloneZilla is that although they are excellent for disk imaging, they have very un-intuitive interfaces, the number of steps they make you go through is huge and they suffer from problems with UEFI when creating a USB version.

Linux's dd command on the other hand, is projected as being very dangerous and people say it's better to use something like CloneZilla just to be safe. But from what I've seen, as long as you type the command properly, dd is actually far more convenient than the other applications.

Here are some useful commands:

To compress and clone entire sda onto a folder on sdb or sdc:

*** Caution: Verify all the commands below before executing them ***

When you plugin the pen drive or external hard disk, it'll get mounted as sdb or sdc. Find out where it got mounted. Mine was in /media/ubuntu/Nav/ and it was in sdc, since sdb was the Ubuntu startup USB. Created a folder named "bkp" in Nav and:
sudo dd bs=1M if=/dev/sda | gzip > /media/ubuntu/Nav/bkp/backup.img.gz
Don't worry. Even though sdc and sdb are mounted on /media, which is the root folder (and hence you'd think it's part of sda), Linux knows that these are mounted filesystems, so it won't consider it part of sda.
Instead of using bs=1M, you could speed it up by checking what your disk buffer size is using sudo hdparm -i /dev/sda, and set that as the bs value. So if the buffer size is 8MB, set bs=8M.

If the disk was not automatically mounted, you can mount it yourself using:
cd mnt/
sudo mkdir bkp
mount /dev/sdc1 /mnt/bkp

Now take a backup
sudo dd bs=8M if=/dev/sda | gzip > /mnt/bkp/backup.img.gz

Restoring sda is done via:
gunzip /mnt/bkp/backup.img.gz | dd of=/dev/sda


Taking a backup of sda means that even unallocated space gets backed up, and that can take very long (3.6 hours for a 500GB HDD). If you don't need a full disk backup and can manage with just the few numbered sda partitions, then just do a backup and restore of sda1, sda2 etc. To do that you'll have to also backup and restore a copy of the partition table as shown below.

You can take a copy of the partition table info using:
sudo sfdisk -d /dev/sda > my_partitions

Now create backups:
sudo -i
dd if=/dev/sda1 of=/media/ubuntu/Nav/bkp/sda1.img bs=8M
dd if=/dev/sda2 of=/media/ubuntu/Nav/bkp/sda2.img bs=8M
dd if=/dev/sda3 of=/media/ubuntu/Nav/bkp/sda3.img bs=8M
dd if=/dev/sda4 of=/media/ubuntu/Nav/bkp/sda4.img bs=8M

Restoring  the partition table info is done via:
sudo sfdisk --force /dev/sda < my_partitions

Restoring backups:
sudo -i
dd if=/media/ubuntu/Nav/bkp/sda1.img of=/dev/sda1 bs=8M
dd if=/media/ubuntu/Nav/bkp/sda2.img of=/dev/sda2 bs=8M
dd if=/media/ubuntu/Nav/bkp/sda3.img of=/dev/sda3 bs=8M
dd if=/media/ubuntu/Nav/bkp/sda4.img of=/dev/sda4 bs=8M



To wipe the first 1MB of any disk:

dd if=/dev/zero of=/dev/sdb bs=1M count=1

Here, the "b" highlighted in bold red should be changed to "a" or "c" etc., based on which disk's first 1MB you want to wipe. The partition information will disappear, so this command is useful when trying to re-purpose any disk. Once the command is run, just use GParted to create the partitions.


To write zeros onto the entire disk:

dd if=/dev/zero of=/dev/sda bs=4096

This is helpful when you want to experiment with whether dd works well for backups. I used this command to fill the disk with zeros, then installed Windows and tried backing up. The 500GB disk with around 20GB of data on the Windows partition got gzip backed up into 12GB. The high compression was because the rest of the disk was full of zeros.


02 July 2019

Ather. My review and first impressions on my first visit and test ride

I had been hoping to visit the Ather center at Indiranagar for a little more than a year. Was passing by today, so dropped in. I'm not a bike expert or enthusiast, so these are just my first impressions. I've also mentioned a lot of the questions I asked them and what answers I received. I haven't yet verified the veracity of their replies.



Took the bike for a test ride and noticed a lot of heads turning when they heard the unique "whoooieeee" sound of the motor :-) The disc brakes can bring the bike to a halt quite fast, and can make the bike skid a little. When this happened, the noise irritated a dog, and it began barking and chasing me. The acceleration from the bike was so good that the dog couldn't keep up. I would've never escaped this easily on my geared bike :-)

The good
  • The bike looks smart from the front and side.
  • Good acceleration.
  • Good quality rubber belts (they say it's reinforced with carbon-fibre and can last 60K to 90K kilometer).
  • Low center of gravity, so could ride it at even 3 kmph.  
  • A reasonably pleasant experience when riding.
  • Good road grip and balance.
  • Reasonable turning radius.
  • Good control.
  • In general, body parts seem to have a reasonably good quality.
  • Reasonably smooth ride. 
  • Hassle-free test ride process, polite, intelligent, helpful and down-to-earth people.
  • Indicator lights switch off automatically when a turn is detected or after a certain number of seconds.
  • Large helmet compartment and pockets for keeping documents etc. below the seat.
  • There don't seem to be parts that would need frequent replacement like in petrol bikes.
  • Headlight seemed bright enough.
  • Alloy wheels and tubeless tyre.
  • The onboard map was a fabulous feature (though I could do without it).
  • The functionality to move in reverse was superb! Not really necessary if you have strong legs, but useful.
  • They show you details of your test ride with some lovely graphics. 

(I'm not entirely sure this was the ride I took, because it certainly didn't feel like I rode for 31 min, and the screen showed that I didn't go on 80 ft road, but I actually did, immediately after the dog-chase).


The bad
  • The rear-wheel portion is kind-of exposed and high, which makes it look ugly.
  • The physical buttons are a bit hard to reach with the thumbs. 
  • When moving on a road hump, the rear suspension clangs, which makes it feel like as though a helmet in the boot is getting jostled around even if there's nothing in the boot.
  • The company knows your exact travel path. Data privacy issue.
  • The handle grip felt too hard and straight. I'm not sure if it was the fact that it was too thin, that made me need to grip it harder or just the hardness, but it felt uncomfortable for long duration use.
  • When the bike is driven with uneven speeds for a while, a fan turns on for cooling. The noise from it can be extremely annoying.
  • Although the bike has an incognito mode, it gets disabled the next day and data about your ride is transmitted to the company server again unless you remember to disable it.
  • When using the key to lock or unlock the bike, the quality of the locking mechanism didn't seem as good as I would've expected from such an expensive bike. There was a certain amount of shakiness or some odd feeling that didn't lend it a perception of excellent quality (although you could say it was good-enough quality). The physical buttons also seemed to be of ordinary quality.
  • Slight chance of the bike being difficult on your back muscles due to the positioning of the handle, and the seat design.
  • At speeds below 20kmph, there can be a bit of a dragging/rough feeling, which is probably because of the rubber belt, but it's tolerable.
  • The headlight is always on. BIS regulation.
  • The seat locking system was unreliable. It should've just been a mechanism where you can push downward and the seat gets locked, but they designed it so that it needs to be dropped from a height of 1 or 2 feet, and I had to drop it multiple times until it got locked. A bit annoying even if practice makes perfect. 
  • If parked under the sun for too long, there's a good chance it could be bad for the battery. Heat is always bad for batteries.
  • The suspension was rather stiff, and they said they couldn't adjust it. It's designed that way to strike a balance between control and comfort.
  • If you aren't covered under insurance, the damage to parts or scratches fixing can cost you a bit more than the average bike.
  • Battery life: Will decrease with time. By the 5th or 6th year it may give only 25km on a single charge (this is what I was told by one person and another person said it'd be more than 25km), but the power of the drive won't be affected.

Questions I asked and responses
  • Units of electricity consumed: Per charge, 3 units. Approx. Rs.20.
  • Charging via solar or inverter: Yes, but only if 1KW and can last for 4.5 hours.
  • Space at footrest for carrying gas cylinder or 20L water: Yes.
  • Doorstep servicing charge if not opting for Rs.8000 or Rs.6000 plan: Rs. 1500 to Rs.1800. 
  • Servicing interval: Every 5000km. Doorstep servicing.
  • Battery cost: Rs.35K to 45K (price is reducing).
  • Battery protection from water: Yes. Can survive complete submerging for 30 min.
  • Riding in water-logged road: Yes. Some dirt may get lodged between wheel area, but will come off after riding a while. Roadside assistance also available.
  • Assistance and servicing radius: Within Bangalore only for now. Plans to setup in Chennai and Hyderabad too.
  • Time for charging: For single phase AC, it takes 4.5hr (at your home) and for 3 phase DC (at Ather charging points), it charges at a rate of giving you 1km range per 1 min of charging.
  • RTO visit when buying bike: No need.
  • Exchange offer for old bike: No. They've considered it, but found it impractical.
  • Charging in apartment basement: Difficult unless you have reserved parking space. Their team can provide a fixed charging point for free for 20m from the electricity meter, but any extra length of wire you'll have to pay.
  • If company shuts down: Unlikely, but if it happens, they are legally obliged to continue offering servicing for 10 years.
  • Lithium availability: At least 10 years or more.
  • Charging stations: They plan to have one every 1.5km by end of one year.
  • Oiling and greasing requirement: No. Will be done during servicing.
  • Theft: Not easy to remove battery. If bike is stolen, when it's switched on, the location will be transmitted. Some parts like the license plate and mirrors can be removed by mischief-mongers.
  • Max weight: 165kg without loss of efficiency.
  • Insurance provider: Go Digit.
  • Cost of insurance: Rs.6000 for 5 years. First year is comprehensive insurance (bumper to bumper). Second year onward, third party insurance only. If you want comprehensive insurance, you'll have to pay Rs.1500.
  • Wait time after placing order: 3 months. You have the option to postpone the delivery/buy date at no extra charge.
  • Lease option: Bike can be taken on lease and returned after the lease period if not satisfied.
  • Portable charger same as fixed charger: Yes. The fixed charger only provides the convenience of being fixed at the parking spot.
  • Not using bike for some time: Bike has a shutdown mode in which it won't draw power from peripherals, and can safely last for around 2 months in this state.
  • Battery explosion possibility: The person said this is unlikely because of the way in which the charging and discharging is managed, but I wasn't fully convinced. In any case, we do carry highly inflammable material in petrol vehicle tanks. Whether batteries are safe or not, time will tell.


My wishlist for electric vehicles like Ather
  • Matte finish body and options for grey shades instead of just white.
  • Separation of vehicle performance data sent to the company and vehicle location data. I don't mind sending performance data, but sending location data is a violation of privacy. Some people just don't seem to understand why that's a problem.
  • Physical buttons that are easier to access with the thumbs.
  • Option to sell old bike like at Maruti True Value.
  • A press-to-lock mechanism for the seat (when closing it after storing something in the under-seat compartment) instead of a drop-to-lock.
  • Switching between eco and sport mode without having to stop the bike.
  • Option to switch off the headlight.
  • Safety mechanisms to prevent injury in case of battery explosion.
  • Charging stations where charging or battery change can be done as fast as we fill petrol vehicles.
  • A less noisy cooling fan. 
  • Drum brakes instead of disc brakes.
  • A covered rear wheel area, instead of leaving it so exposed.
  • Fewer (or zero) power-cuts in Bangalore and more solar electricity. Especially on roof-tops and parking spaces.
This solar parking space was built by Tata Power (as I remember)

Overall, the Ather was a nice bike to ride. Happy that it was designed and manufactured in India. They've got a forum where people share their experiences. Given that there are quite a few points on my wishlist that would remain unfulfilled, the high cost of the vehicle and given the current difficulties in finding charging points and also the person mentioning that they wouldn't be able to make hardware changes (like the handle button position) easily, I'd probably not buy an Ather for a rather long time. If I do decide to buy, the review process is going to be far more detailed than this.


Ather vs. petrol bikes
A somewhat practical analysis of the costs of Ather vs. petrol bikes is done on this website and this website.


One tiny point
They had a few healthy cacti and indoor plants at the experience center. However on entering the loo, I noticed two dead plants. I wonder if that says anything about the culture of communication, care or attention-to-detail. One would of course argue about whether I'd have made any such assumptions if there were no potted plants in the loo. :-)



Have you ever tried an Ather or an electric bike? What was your experience like?






14 June 2019

Do you check the checksum's of downloaded files?

It's usually unnecessary, but always recommended. Let's say you download CloneZilla and they've provided their file for download from a website like SourceForge. There's always a slight chance that some hacker/attacker could've altered the file, added some malware to it and replaced the file on SourceForge.

But you can check whether it's the original file that CloneZilla had uploaded to SourceForge, using the checksum.

I had downloaded clonezilla-live-20190420-disco-amd64.iso which had the MD5 checksum: 981841de868ccc0c927dea9ace9460fa as shown on the CloneZilla website.

Now to verify the file I just opened up a terminal and typed
md5sum clonezilla-live-20190420-disco-amd64.iso.

The output was:
981841de868ccc0c927dea9ace9460fa  clonezilla-live-20190420-disco-amd64.iso

A perfect match! It's that simple.

12 June 2019

Get rid of the Error in appstreamcli: double free or corruption

This is for my personal reference. Immediately after installing Ubuntu 16.04 and trying to update it, this appstreamcli error shows up and it takes a while to search for the solutions to it.

 

The solution is:
sudo apt install --reinstall libappstream3

or

sudo apt-get purge libappstream3

Obtained from here.


10 June 2019

How to remove the Amazon package from Ubuntu 16.04

It's funny that Amazon would want to put up their icon on Ubuntu's Unity launcher or even on Windows.
Just because a user see an Amazon icon somewhere, it's neither going to entice users to click it or give it more advertising. In-fact, it does just the opposite. Annoy people for putting a PUP (Potentially Unwanted Program) into their OS.

To get rid of Amazon on Ubuntu 16.04, just do this:

sudo rm /usr/share/applications/ubuntu-amazon-default.desktop
sudo rm /usr/share/unity-webapps/userscripts/unity-webapps-amazon/Amazon.user.js
sudo rm /usr/share/unity-webapps/userscripts/unity-webapps-amazon/manifest.json

22 May 2019

Pymunk tutorial

If you need to create a 2D physics environment quickly and without bothering about C++ compilation issues, it's better to choose Pymunk over Box2D. The only issue with Pymunk however, is that there's currently only one person (the Pymunk author)  answering questions about Pymunk on StackOverflow.

To get started, install Pymunk and Pygame:
pip install pygame
pip install pymunk


You can also install using pip3 if you use Python 3. Pymunk is used only for doing the 2D physics simulations. It is Pygame that sets up the environment to display the objects created in Pymunk. Instead of Pygame, you can also use Pyglet. Pyglet has features like multiple window support which Pygame doesn't have (yet), but the documentation is in my opinion, in need of improvement.

Do note that although Pymunk has a debug_draw() function to display Pymunk objects, the function is just there for the sake of being there. You are actually supposed to use Pygame's drawing functions to draw Pymunk objects. It's more efficient and supports culling of objects (not needing to draw objects outside screen bounds).


Simple program:


import sys
import pygame
from pygame.locals import * #for key codes etc
from pygame.color import *
import pymunk
import pymunk.pygame_util

fps = 60.0 #frames per second
worldWidth = 1200
worldHeight = 300
#---initializations and setup
pygame.init()
screen = pygame.display.set_mode((worldWidth, worldHeight))
clock = pygame.time.Clock()
space = pymunk.Space()
space.gravity = 0, 900
space.sleep_time_threshold = 1 #The time a group of bodies must remain idle in order to fall asleep
draw_options = pymunk.pygame_util.DrawOptions(screen)
pymunk.pygame_util.positive_y_is_up = False

#---setup line segments in space
x = 10; y = 150; thickness = 2; friction = 1.0
points = [(-100,0),(100,0),(112,12),(125,0),(137,12),(150,0),(162,12),(175,0),(200,0),(250,25),(250,0),(315,0),(320,70),(375,70),(385,0),(425,0),(425,12),(448,12),(448,0),(475,0),(475,12),(485,12),(485,0),(512,0),(512,12),(527,12),(527,0),(590,0),(590,10),(610,10),(610,15),(625,15),(625,25),(646,25),(646,35),(660,35),(660,45),(680,48),(710,-10),(755,5),(755,20),(780,25),(850,25),(825,-20),(865,-20),(895,25),(925,25),(935,0),(980,-75),(1100,-75)];
for i in range(1, len(points)):           
    floor = pymunk.Segment(space.static_body, (x+points[i-1][0], y+points[i-1][1]), (x+points[i][0], y+points[i][1]), thickness)
    floor.friction = friction
    space.add(floor)
   
while True:
    for event in pygame.event.get():
        if event.type == QUIT or event.type == KEYDOWN and (event.key in [K_ESCAPE, K_q]): 
            sys.exit(0)

    #---updations
    space.step(1./fps)#Update the space for the given time step.
    screen.fill(pygame.color.THECOLORS["black"])#clear screen
    space.debug_draw(draw_options)#try using Pygame's drawing functions instead
    pygame.display.flip()#flips the draw buffer to display the objects in current frame
    dt = clock.tick(fps)#creates a delay



Adding ten cars to the space:


You'll notice the cars collide with each other. That's fixed in the next code example below.

import sys
import pygame
from pygame.locals import * #for key codes etc
from pygame.color import *
import pymunk
import pymunk.pygame_util

from pymunk import Vec2d
import random

class Car:
    space = None
    physics = None
    x = 0
    y = 0
    friction = 1.5  
    #---range of values possible for properties of car
    speed_range = range(10, 30, 5)#15
    chWd_range = range(5, 70, 5)#50#chassis width
    chHt_range = range(5, 50, 5)#10#chassis height
    wheel1Radius_range = range(6, 25, 5)#15
    wheel2Radius_range = range(6, 25, 5)#15
    chassisMass_range = range(10, 500, 50)#100
    wheel1Mass_range = range(10, 500, 50)#100
    wheel2Mass_range = range(10, 500, 50)#100 
    #---properties of car   
    speed = 0
    chWd = 0#chassis width
    chHt = 0#chassis height
    wheel1Radius = 0
    wheel2Radius = 0
    chassisMass = 0
    wheel1Mass = 0
    wheel2Mass = 0 
    #---parts to add or remove from space
    chassis_b = None
    chassis_s = None
    wheel1_b = None
    wheel1_s = None
    wheel2_b = None
    wheel2_s = None 
    pin1 = None
    pin2 = None
    pin3 = None
    pin4 = None
    motorJoint1 = None
    motorJoint2 = None
   
    def __init__(self, space, pymunk, xStartPos, yStartPos):
        self.space = space
        self.pymunk = pymunk
        self.x = xStartPos
        self.y = yStartPos
        self.reinitializeWithRandomValues()   
       
    def reinitializeWithRandomValues(self):
        self.speed = random.choice(self.speed_range)
        self.chWd = random.choice(self.chWd_range)
        self.chHt = random.choice(self.chHt_range)
        self.wheel1Radius = random.choice(self.wheel1Radius_range)
        self.wheel2Radius = random.choice(self.wheel2Radius_range)
        self.chassisMass = random.choice(self.chassisMass_range)
        self.wheel1Mass = random.choice(self.wheel1Mass_range)
        self.wheel2Mass = random.choice(self.wheel2Mass_range)    
       
    def setValues(self, v):
        i = 0
        self.speed = v[i]; i = i + 1
        if self.speed < self.speed_range[0] or self.speed > self.speed_range[-1]:
            self.speed = random.choice(self.speed_range)
        self.chWd = v[i]; i = i + 1
        if self.chWd < self.chWd_range[0] or self.chWd > self.chWd_range[-1]:
            self.chWd = random.choice(self.chWd_range)       
        self.chHt = v[i]; i = i + 1
        if self.chHt < self.chHt_range[0] or self.chHt > self.chHt_range[-1]:
            self.chHt = random.choice(self.chHt_range)       
        self.wheel1Radius = v[i]; i = i + 1
        if self.wheel1Radius < self.wheel1Radius_range[0] or self.wheel1Radius > self.wheel1Radius_range[-1]:
            self.wheel1Radius = random.choice(self.wheel1Radius_range)       
        self.wheel2Radius = v[i]; i = i + 1
        if self.wheel2Radius < self.wheel2Radius_range[0] or self.wheel2Radius > self.wheel2Radius_range[-1]:
            self.wheel2Radius = random.choice(self.wheel2Radius_range)       
        self.chassisMass = v[i]; i = i + 1
        if self.chassisMass < self.chassisMass_range[0] or self.chassisMass > self.chassisMass_range[-1]:
            self.chassisMass = random.choice(self.chassisMass_range)       
        self.wheel1Mass = v[i]; i = i + 1
        if self.wheel1Mass < self.wheel1Mass_range[0] or self.wheel1Mass > self.wheel1Mass_range[-1]:
            self.wheel1Mass = random.choice(self.wheel1Mass_range)       
        self.wheel2Mass = v[i]; i = i + 1
        if self.wheel2Mass < self.wheel2Mass_range[0] or self.wheel2Mass > self.wheel2Mass_range[-1]:
            self.wheel2Mass = random.choice(self.wheel2Mass_range)       
   
    def createCar(self):
        chassisXY = Vec2d(self.x, self.y)
        self.fitness = 0
        moment = self.pymunk.moment_for_box(self.chassisMass, (self.chWd, self.chHt))
        self.chassis_b = self.pymunk.Body(self.chassisMass, moment)
        self.chassis_s = self.pymunk.Poly.create_box(self.chassis_b, (self.chWd, self.chHt))
        self.chassis_s.color = 10,150,40
        self.chassis_b.position = chassisXY + (0, 0)
        self.space.add(self.chassis_b, self.chassis_s)          
       
        #---wheel1 (left side wheel)
        #w1 = self.addChassis(chassisXY, -chWd, 0, mass, 5, 30) #special rectangular wheel       
        moment = self.pymunk.moment_for_circle(self.wheel1Mass, 0, self.wheel1Radius)
        self.wheel1_b = self.pymunk.Body(self.wheel1Mass, moment)
        self.wheel1_s = self.pymunk.Circle(self.wheel1_b, self.wheel1Radius)
        self.wheel1_s.friction = self.friction
        self.wheel1_s.color = 180,180,180
        self.wheel1_b.position = chassisXY - (self.chWd, 0)
        self.space.add(self.wheel1_b, self.wheel1_s)         

        #---wheel2 (right side wheel)
        #w1 = self.addChassis(chassisXY, -chWd, 0, mass, 5, 30) #special rectangular wheel       
        moment = self.pymunk.moment_for_circle(self.wheel2Mass, 0, self.wheel2Radius)
        self.wheel2_b = self.pymunk.Body(self.wheel2Mass, moment)
        self.wheel2_s = self.pymunk.Circle(self.wheel2_b, self.wheel2Radius)
        self.wheel2_s.friction = self.friction
        self.wheel2_s.color = 180,180,180
        self.wheel2_b.position = chassisXY - (-self.chWd, 0)
        self.space.add(self.wheel2_b, self.wheel2_s)        
       
        self.pin1 = self.pymunk.PinJoint(self.wheel1_b, self.chassis_b, (0,0), (-self.chWd/2,0))
        self.pin2 = self.pymunk.PinJoint(self.wheel2_b, self.chassis_b, (0,0), (self.chWd/2,0))
        self.pin3 = self.pymunk.PinJoint(self.wheel1_b, self.chassis_b, (0,0), (0,-self.chHt/2))
        self.pin4 = self.pymunk.PinJoint(self.wheel2_b, self.chassis_b, (0,0), (0,-self.chHt/2))
        self.space.add(self.pin1, self.pin2, self.pin3, self.pin4)
        self.motorJoint1 = self.pymunk.SimpleMotor(self.wheel1_b, self.chassis_b, self.speed); self.motorJoint1.max_force = 10000000
        self.motorJoint2 = self.pymunk.SimpleMotor(self.wheel2_b, self.chassis_b, self.speed); self.motorJoint2.max_force = 10000000
        self.space.add(self.motorJoint1, self.motorJoint2)
       
    def stop(self):
        self.motorJoint1.rate(0.0)
        self.motorJoint2.rate(0.0)
       
    def removeCar(self):
        self.space.remove(self.chassis_b)
        self.space.remove(self.chassis_s)
        self.space.remove(self.wheel1_b)
        self.space.remove(self.wheel1_s)
        self.space.remove(self.wheel2_b)
        self.space.remove(self.wheel2_s)
        self.space.remove(self.pin1)
        self.space.remove(self.pin2)
        self.space.remove(self.pin3)
        self.space.remove(self.pin4)
        self.space.remove(self.motorJoint1)
        self.space.remove(self.motorJoint2)
           

fps = 60.0 #frames per second
worldWidth = 1200
worldHeight = 300
#---initializations and setup
pygame.init()
screen = pygame.display.set_mode((worldWidth, worldHeight))
clock = pygame.time.Clock()
space = pymunk.Space()
space.gravity = 0, 900
space.sleep_time_threshold = 1 #The time a group of bodies must remain idle in order to fall asleep
draw_options = pymunk.pygame_util.DrawOptions(screen)
pymunk.pygame_util.positive_y_is_up = False

#---setup line segments in space
x = 10; y = 200; thickness = 2; friction = 1.0
points = [(-100,0),(100,0),(112,12),(125,0),(137,12),(150,0),(162,12),(175,0),(200,0),(250,25),(250,0),(315,0),(320,70),(375,70),(385,0),(425,0),(425,12),(448,12),(448,0),(475,0),(475,12),(485,12),(485,0),(512,0),(512,12),(527,12),(527,0),(590,0),(590,10),(610,10),(610,15),(625,15),(625,25),(646,25),(646,35),(660,35),(660,45),(680,48),(710,-10),(755,5),(755,20),(780,25),(850,25),(825,-20),(865,-20),(895,25),(925,25),(935,0),(980,-75),(1100,-75)];
for i in range(1, len(points)):           
    floor = pymunk.Segment(space.static_body, (x+points[i-1][0], y+points[i-1][1]), (x+points[i][0], y+points[i][1]), thickness)
    floor.friction = friction
    space.add(floor)
#---create cars
xStartPos = 20; yStartPos = 0; numCars = 10; cars = []
for i in range(0, numCars):
    cars.append(Car(space, pymunk, xStartPos, yStartPos))
    cars[i].createCar()

while True:
    for event in pygame.event.get():
        if event.type == QUIT or event.type == KEYDOWN and (event.key in [K_ESCAPE, K_q]): 
            sys.exit(0)

    #---updations
    space.step(1./fps)#Update the space for the given time step.
    screen.fill(pygame.color.THECOLORS["black"])#clear screen
    space.debug_draw(draw_options)#try using Pygame's drawing functions instead
    pygame.display.flip()#flips the draw buffer to display the objects in current frame
    dt = clock.tick(fps)#creates a delay


You'll notice that the cars collide with each other and the wheels of the cars collide with the car's own body too. There's a way to avoid that using ShapeFilters. The shape filter helps categorize objects into "player", "enemy" etc. to help decide which objects should collide and which shouldn't. This is very helpful because if the parts of the body of players collide, it produces very jittery motion.


Cars with ShapeFilters and text display:


import sys
import time
import pygame
from pygame.locals import * #for key codes etc
from pygame.color import *
import pymunk
import pymunk.pygame_util

from pymunk import Vec2d
import random

class Car:
    space = None
    physics = None
    x = 0
    y = 0
    friction = 1.5  
    #---range of values possible for properties of car
    speed_range = range(10, 30, 5)#15
    chWd_range = range(5, 70, 5)#50#chassis width
    chHt_range = range(5, 50, 5)#10#chassis height
    wheel1Radius_range = range(6, 25, 5)#15
    wheel2Radius_range = range(6, 25, 5)#15
    chassisMass_range = range(10, 500, 50)#100
    wheel1Mass_range = range(10, 500, 50)#100
    wheel2Mass_range = range(10, 500, 50)#100 
    #---properties of car   
    speed = 0
    chWd = 0#chassis width
    chHt = 0#chassis height
    wheel1Radius = 0
    wheel2Radius = 0
    chassisMass = 0
    wheel1Mass = 0
    wheel2Mass = 0 
    #---parts to add or remove from space
    chassis_b = None
    chassis_s = None
    wheel1_b = None
    wheel1_s = None
    wheel2_b = None
    wheel2_s = None 
    pin1 = None
    pin2 = None
    pin3 = None
    pin4 = None
    motorJoint1 = None
    motorJoint2 = None
   
    def __init__(self, space, pymunk, xStartPos, yStartPos):
        self.space = space
        self.pymunk = pymunk
        self.x = xStartPos
        self.y = yStartPos
        self.reinitializeWithRandomValues()   
       
    def reinitializeWithRandomValues(self):
        self.speed = random.choice(self.speed_range)
        self.chWd = random.choice(self.chWd_range)
        self.chHt = random.choice(self.chHt_range)
        self.wheel1Radius = random.choice(self.wheel1Radius_range)
        self.wheel2Radius = random.choice(self.wheel2Radius_range)
        self.chassisMass = random.choice(self.chassisMass_range)
        self.wheel1Mass = random.choice(self.wheel1Mass_range)
        self.wheel2Mass = random.choice(self.wheel2Mass_range)    
       
    def setValues(self, v):
        i = 0
        self.speed = v[i]; i = i + 1
        if self.speed < self.speed_range[0] or self.speed > self.speed_range[-1]:
            self.speed = random.choice(self.speed_range)
        self.chWd = v[i]; i = i + 1
        if self.chWd < self.chWd_range[0] or self.chWd > self.chWd_range[-1]:
            self.chWd = random.choice(self.chWd_range)       
        self.chHt = v[i]; i = i + 1
        if self.chHt < self.chHt_range[0] or self.chHt > self.chHt_range[-1]:
            self.chHt = random.choice(self.chHt_range)       
        self.wheel1Radius = v[i]; i = i + 1
        if self.wheel1Radius < self.wheel1Radius_range[0] or self.wheel1Radius > self.wheel1Radius_range[-1]:
            self.wheel1Radius = random.choice(self.wheel1Radius_range)       
        self.wheel2Radius = v[i]; i = i + 1
        if self.wheel2Radius < self.wheel2Radius_range[0] or self.wheel2Radius > self.wheel2Radius_range[-1]:
            self.wheel2Radius = random.choice(self.wheel2Radius_range)       
        self.chassisMass = v[i]; i = i + 1
        if self.chassisMass < self.chassisMass_range[0] or self.chassisMass > self.chassisMass_range[-1]:
            self.chassisMass = random.choice(self.chassisMass_range)       
        self.wheel1Mass = v[i]; i = i + 1
        if self.wheel1Mass < self.wheel1Mass_range[0] or self.wheel1Mass > self.wheel1Mass_range[-1]:
            self.wheel1Mass = random.choice(self.wheel1Mass_range)       
        self.wheel2Mass = v[i]; i = i + 1
        if self.wheel2Mass < self.wheel2Mass_range[0] or self.wheel2Mass > self.wheel2Mass_range[-1]:
            self.wheel2Mass = random.choice(self.wheel2Mass_range)       
   
    def createCar(self, shapeFilter):
        chassisXY = Vec2d(self.x, self.y)
        self.fitness = 0
        moment = self.pymunk.moment_for_box(self.chassisMass, (self.chWd, self.chHt))
        self.chassis_b = self.pymunk.Body(self.chassisMass, moment)
        self.chassis_s = self.pymunk.Poly.create_box(self.chassis_b, (self.chWd, self.chHt))
        self.chassis_s.color = 10,150,40
        self.chassis_b.position = chassisXY + (0, 0)
        self.chassis_s.filter = shapeFilter
        self.space.add(self.chassis_b, self.chassis_s)          
       
        #---wheel1 (left side wheel)      
        moment = self.pymunk.moment_for_circle(self.wheel1Mass, 0, self.wheel1Radius)
        self.wheel1_b = self.pymunk.Body(self.wheel1Mass, moment)
        self.wheel1_s = self.pymunk.Circle(self.wheel1_b, self.wheel1Radius)
        self.wheel1_s.friction = self.friction
        self.wheel1_s.color = 180,180,180
        self.wheel1_b.position = chassisXY - (self.chWd, 0)
        self.wheel1_s.filter = shapeFilter
        self.space.add(self.wheel1_b, self.wheel1_s)         

        #---wheel2 (right side wheel)               
        moment = self.pymunk.moment_for_circle(self.wheel2Mass, 0, self.wheel2Radius)
        self.wheel2_b = self.pymunk.Body(self.wheel2Mass, moment)
        self.wheel2_s = self.pymunk.Circle(self.wheel2_b, self.wheel2Radius)
        self.wheel2_s.friction = self.friction
        self.wheel2_s.color = 180,180,180
        self.wheel2_b.position = chassisXY - (-self.chWd, 0)
        self.wheel2_s.filter = shapeFilter
        self.space.add(self.wheel2_b, self.wheel2_s)        
       
        self.pin1 = self.pymunk.PinJoint(self.wheel1_b, self.chassis_b, (0,0), (-self.chWd/2,0))
        self.pin2 = self.pymunk.PinJoint(self.wheel2_b, self.chassis_b, (0,0), (self.chWd/2,0))
        self.pin3 = self.pymunk.PinJoint(self.wheel1_b, self.chassis_b, (0,0), (0,-self.chHt/2))
        self.pin4 = self.pymunk.PinJoint(self.wheel2_b, self.chassis_b, (0,0), (0,-self.chHt/2))
        self.space.add(self.pin1, self.pin2, self.pin3, self.pin4)
        self.motorJoint1 = self.pymunk.SimpleMotor(self.wheel1_b, self.chassis_b, self.speed); self.motorJoint1.max_force = 10000000
        self.motorJoint2 = self.pymunk.SimpleMotor(self.wheel2_b, self.chassis_b, self.speed); self.motorJoint2.max_force = 10000000
        self.space.add(self.motorJoint1, self.motorJoint2)
       
    def stop(self):
        self.motorJoint1.rate(0.0)
        self.motorJoint2.rate(0.0)
       
    def removeCar(self):
        self.space.remove(self.chassis_b)
        self.space.remove(self.chassis_s)
        self.space.remove(self.wheel1_b)
        self.space.remove(self.wheel1_s)
        self.space.remove(self.wheel2_b)
        self.space.remove(self.wheel2_s)
        self.space.remove(self.pin1)
        self.space.remove(self.pin2)
        self.space.remove(self.pin3)
        self.space.remove(self.pin4)
        self.space.remove(self.motorJoint1)
        self.space.remove(self.motorJoint2)
           

fps = 60.0 #frames per second
worldWidth = 1200
worldHeight = 300
#---initializations and setup
pygame.init()
screen = pygame.display.set_mode((worldWidth, worldHeight))
clock = pygame.time.Clock()
space = pymunk.Space()
space.gravity = 0, 900
space.sleep_time_threshold = 1 #The time a group of bodies must remain idle in order to fall asleep
draw_options = pymunk.pygame_util.DrawOptions(screen)
pymunk.pygame_util.positive_y_is_up = False
font = pygame.font.SysFont("Arial", 20)

#---setup line segments in space
x = 10; y = 200; thickness = 2; friction = 1.0
points = [(-100,0),(100,0),(112,12),(125,0),(137,12),(150,0),(162,12),(175,0),(200,0),(250,25),(250,0),(315,0),(320,70),(375,70),(385,0),(425,0),(425,12),(448,12),(448,0),(475,0),(475,12),(485,12),(485,0),(512,0),(512,12),(527,12),(527,0),(590,0),(590,10),(610,10),(610,15),(625,15),(625,25),(646,25),(646,35),(660,35),(660,45),(680,48),(710,-10),(755,5),(755,20),(780,25),(850,25),(825,-20),(865,-20),(895,25),(925,25),(935,0),(980,-75),(1100,-75)];
for i in range(1, len(points)):           
    floor = pymunk.Segment(space.static_body, (x+points[i-1][0], y+points[i-1][1]), (x+points[i][0], y+points[i][1]), thickness)
    floor.friction = friction
    space.add(floor)
#---create cars
xStartPos = 20; yStartPos = 0; numCars = 10; cars = []
sf = pymunk.ShapeFilter(group=1)
for i in range(0, numCars):
    cars.append(Car(space, pymunk, xStartPos, yStartPos))
    cars[i].createCar(sf)

while True:
    for event in pygame.event.get():
        if event.type == QUIT or event.type == KEYDOWN and (event.key in [K_ESCAPE, K_q]): 
            sys.exit(0)

    #---updations
    space.step(1./fps)#Update the space for the given time step.
    screen.fill(pygame.color.THECOLORS["black"])#clear screen
    space.debug_draw(draw_options)#try using Pygame's drawing functions instead
    screen.blit(font.render("Cars: "+str(numCars)+". Time: "+str(time.time()), 1, THECOLORS["darkgrey"]), (5, 5))
    pygame.display.flip()#flips the draw buffer to display the objects in current frame
    dt = clock.tick(fps)#creates a delay

Hope this gives you an easy-to-understand intro to the awesomeness of Pymunk. I've created a slightly more advanced version of the above code, where the cars try to reach the end of the terrain using learning via an evolutionary algorithm.
I'm not ready to share the code yet, but when I am, I'll be posting the link on this page.

Here's the demo on YouTube:


Few other things:
  • If you are looking to create something like a walking robot, there's an example here
  • I'm relatively new to Python, so forgive me if my code is not "Pythonic" enough :-)
  • You can also look through the examples and test cases in Pymunk to see more examples of how to use various functions.