Sunday, March 31, 2013

First Impressions About AR.Drone 2.0

I got an AR.Drone 2.0 some weeks ago and have been getting in touch with it, mostly studying its basic flight behaviour. My first impressions are mainly positive - AR.Drone is a well-finished product that works out of the box and is versatile and amazingly robust.

Control and Video Recording

The drone acts as a WiFi access point to provide a control link. The controller can be any Android or iOS device, such as a phone or a tablet, which requires a special flight control app. I normally use Galaxy Tab 10.1, which runs Android.

The flight control is very similar to some first-person shooter games for touch devices, with control pads for both thumbs. By pressing with the left thumb, you enable tilt, so that it tilts and moves to that direction as you tilt the controller. There are two control modes - relative and absolute. In the relative mode, tilting forward tilts the drone forward in its forward direction, while in absolute mode the drone uses compass to tilt in the same absolute direction as the device. The right thumb controls altitude and direction. The interface requires some getting used to and I can't say it's great, but it works.

There's a launch/landing button, as well as an emergency shutdown that stops the drone immediately. There's also a button to switch between the main camera and the ground camera. Behind a configuration menu, there are various settings, such as for altitude and tilt limits, and the control mode. The drone can also do flips, which are fun for about one time.

To record video you just tap a button in the control interface. It normally stores the video on the control device, but the drone also has a built-in USB connector to store the video on a USB memory. Using the USB store is usually better, because when using the WiFi connection, it can often lose the connection momentarily (or longer), as can be seen in the following video when the drone almost flies away.


The video is 720p and reasonably good quality, at least on good weather. It's a bit shaky and not quite production quality, but hey, for the price it's quite enough. On snow it's probably more jerky than on a surface with more texture, as the visual stabilizer makes rather erratic corrections.

The biggest issue is that the main camera is leveled horizontally and can not be tilted. So, to get angled shots, you have to fly the drone to tilt it. It is possible to tilt the camera head with a knife and some glue, and get great results with just some 5° tilt.

The video quality can be greatly enhanced by attaching a better camera to the drone. I have a GoPro HD HERO2, so I'll have to try that soon. The GoPro weighs some 100 grams, and the drone can carry it without much effort. However, the rugged camera cover weighs another 100 grams, so it is too heavy to be used. The legs need to be extended to keep the lens away from the ground. And fly carefully.

Indoor and Outdoor Hulls and Build Quality


The drone came with both an indoor and an outdoor protective hull. The purpose of the indoor hull is to protect the rotor blades if and when the drone bumps into a wall, as well as the walls themselves. The blades are incredibly flexible and, amazingly, they have lasted dozens of contacts with various objects. The drone has a safety feature that it makes an instant emergency stop if the blades hit something, which seems to prevent most damage.

It is worth to note that the indoor hull does not help almost at all when colliding against branches, so when the drone hits a branch, it almost always crashes. As the fall distance outdoors is often rather high, typically over two meters, it is usually enough to break the rims of the indoor hull. So, the purpose of the outdoor hull is mainly to preserve the indoor hull from breaking into a hundred pieces.

The motors and the small electronic boards of their speed controllers are completely exposed. Fortunately, even though the drone has crashed in wet snow many times, the electronics haven't short-circuited.

Apparently, the parts do break sometimes, as all the parts are available as spare parts. They are a bit expensive though, and building a drone from the separate parts is more than twice as expensive.

Flight Stability

The drone has a number of sensors to maintain its position, level, and direction:
  • 3-axis accelerometer
  • ultrasonic range finder at the bottom for altitude stability (up to 6 meters)
  • barometric pressure sensor for altitude stability (at 6+ meters)
  • secondary ground camera for visual stabilization
  • 3-axis gyroscope 
  • 3-axis magnetometer (compass)
The magnetometer is especially nice, as coupled with the magnetometer of the controlled device, it allows absolute direction control, so that tilting the controller causes the drone to fly to the same direction.

Unfortunately, even all the sensors are not always sufficient to maintain the position properly. The ground camera is only useful if there's enough light for it (see the problems later) and the ground has enough texture to stabilize on. The ultrasonic sensor doesn't seem to do its work quite properly, and the drone's altitude sometimes changes uncontrollably, for example by climbing to the roof.

I would expect that the next version gets a GPS module for better long-range stability. With that, it could have homing feature, as well as planned flight routes. Most of the more expensive drones already have that.

Flying Away


There's one thing that I learned quite well. The WiFi range is rather limited, just about 40 or so meters, and once it gets out of the range, it won't land immediately. So, it will fly away with the wind (1, 2, etc).

So, when trying it out at night, I lifted it up to some 40 meters...at which point it lost the connection and I couldn't control it any longer. As it didn't have any visual stabilization in the dark, it started slowly drifting away over the treetops into the night, and I soon lost sight of it.

So, a rescue effort was launched, combing the forest in a 10-meter grid. The forest was covered in 30 cm deep snow, so I had a good opportunity to practice skiing with my Altai Hoks.

After many hours of searching on two days, it was finally found in the snow some 200 meters into the forest. The indoor frame was stuck in a tree, and came down later after some windier days. Had the drone been stuck up in a 30 meters high pine tree, it would have been almost impossible to find.

To make the search easier if it ever flies away again, I taped the drone with plenty of reflecting tape so that it shines brightly from over 100 meters away when illuminated with a headlamp.

The main issue, however, is the limited range of the WiFi link. It's specified to be some 50 meters, some claim 80 meters, but it depends on the controller. With my Galaxy Tab 10.1 it seems to be around 40 meters. Some people have used directional antenna to extend the range, some even up to 2.25 km!

Logging In


The fun part about AR.Drone is that its on-board controller is a Linux computer and you can log into over the wireless network with Telnet.

First, I found out the network, then guessed that the device is .1 in the subnet:

magi@vaire:~$ ifconfig
wlan0     Link encap:Ethernet  HWaddr c0:18:85:70:65:48 
          inet addr:192.168.1.3  Bcast:192.168.1.255  Mask:255.255.255.0
...

magi@vaire:~$ telnet 192.168.1.1
Trying 192.168.1.1...
Connected to 192.168.1.1.
Escape character is '^]'.

BusyBox v1.14.0 () built-in shell (ash)
Enter 'help' for a list of built-in commands.

# ls
bin       dev       factory   home      licenses  proc      sbin      tmp       usr
data      etc       firmware  lib       mnt       root      sys       update    var

There are a number of TCP ports open. The port 23 is for Telnet and 21 is for FTP. I assume that it uses FTP for updating the firmware. Then there's a number of ports in range 5551-5559, I suppose those are the control links. The video could be transmitted over UDP to allow losing frames on bad connection instead of piling them up.

# netstat -l -t
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
tcp        0      0 0.0.0.0:5551            0.0.0.0:*               LISTEN      
tcp        0      0 0.0.0.0:5553            0.0.0.0:*               LISTEN      
tcp        0      0 0.0.0.0:5555            0.0.0.0:*               LISTEN      
tcp        0      0 0.0.0.0:5557            0.0.0.0:*               LISTEN      
tcp        0      0 0.0.0.0:21              0.0.0.0:*               LISTEN      
tcp        0      0 0.0.0.0:23              0.0.0.0:*               LISTEN      
tcp        0      0 0.0.0.0:5559            0.0.0.0:*               LISTEN      

There were at least couple of interesting-looking processes:

# ps
...
  806 root     77672 S    /bin/program.elf -360p.slices 0 -live.tcp 
  896 root      1540 S    /bin/parrotauthdaemon 
...

There also seems to be at least one AR-related kernel module:

# cat /proc/modules
ov7670 9943 1 - Live 0xbf083000
soc1040 20292 2 - Live 0xbf079000
omap3_isp 95899 9 - Live 0xbf056000
ar6000 278079 0 - Live 0xbf000000

And various memory devices:

# mount
rootfs on / type rootfs (rw)
ubi1:system on / type ubifs (rw,relatime)
tmp on /tmp type tmpfs (rw,relatime)
proc on /proc type proc (rw,relatime)
dev on /dev type tmpfs (rw,relatime)
devpts on /dev/pts type devpts (rw,relatime,mode=600)
sys on /sys type sysfs (rw,relatime)
none on /proc/bus/usb type usbfs (rw,relatime)
ubi0:factory on /factory type ubifs (ro,relatime)
ubi2:update on /update type ubifs (rw,sync,relatime)
ubi2:data on /data type ubifs (rw,relatime)
The UBIFS is some sort of Flash memory filesystem.

Well, nothing very interesting. The control applications seem to be closed binaries, both in the kernel and user-space, and it's a big question if it is possible to hook custom on-board applications to do something interesting.

The main development APIs are for using over the remote link. I haven't yet looked at those, but will at some point.

Wednesday, March 27, 2013

Navigating URI Fragments in AJAX Apps

I just updated the URI Fragment section in the Book of Vaadin yesterday. The section was one of the most horribly outdated sections in the book. I didn't even include it in the print edition, although it was in the web edition.

To rewrite the section, I had to rewrite the book examples for it. The section now includes a subsection about supporting web crawling in Vaadin applications. It is a tricky topic, as stateful AJAX applications can not be normally crawled simply by loading the page. Google supports a special notation for the purpose, about which you can read in the section.

Anyhow, I'm writing this article mainly to test if Google manages to index the demo pages:
If all goes well, those pages should be indexed in a few days and you should able to google them. It could take less, as Blogger/Blogspot is a Google service.

Update: Now googleable

At least some of the pages can now be found by Google. Google for:
Well, for another example in use, you can google the add-on components in the Vaadin Directory. For example, try googling for:
You should be able to find the #-marked directory page in the results.

Wednesday, January 30, 2013

Analyzing JAR Dependencies

Today I've been analyzing the library dependencies of Vaadin libraries, mainly to find out which are used runtime and which during development, mainly for license purposes.

JarAnalyzer

The JarAnalyzer analyzes the dependencies between a collection of JAR libraries.

After extracting it to somewhere, it came with some BAT files. In Linux, I had to give the parameters on command-line:

$ java -cp ./lib/bcel-5.2.jar:./lib/jakarta-regexp-1.3.jar:./lib:./jaranalyzer-1.2.jar \
    com.kirkk.analyzer.textui.XMLUISummary \
    /ftp/tmp vaadin-7.0.0.rc2.xml

It also supported GraphViz DOT format, to generate a dependency graph:

$ java -cp ./lib/bcel-5.2.jar:./lib/jakarta-regexp-1.3.jar:./lib:./jaranalyzer-1.2.jar \
    com.kirkk.analyzer.textui.DOTSummary \
    /ftp/tmp vaadin-7.0.0.rc2.dot
$ dot -Tpng -o vaadin-7.0.0.rc2.png vaadin-7.0.0.rc2.dot

The problem was that some of the JARs included certain libraries inside them, so the dependencies were not precise enough.

JDepend

JDepend is another dependency analyzer, which analyzes the dependencies between the exact Java packages. It doesn't provide a graph as JarAnalyzer did, but has a rather textual GUI for browsing the package dependency trees.

Again, define the class path on command-line and give the JAR directory as the parameter:

$ java -cp lib/jdepend-2.9.1.jar jdepend.swingui.JDepend /ftp/tmp

The tool was quite useful for understanding some odd dependencies, but gave a bit too much information, as one library typically consists of quite a large number of Java packages.

Maven Dependencies

Listing the Maven dependencies was probably the best way to accomplish the task. Unfortunately, our dependecies were handled by Ivy, so I had to convert those to Maven dependencies. That was simple enough with the ivy:makepom task.

<target name="makepom">
    <ivy:makepom ivyfile="ivy.xml" pomfile="pom.xml">
   <mapping conf="default" scope="compile"/>
   <mapping conf="runtime" scope="runtime"/>
   </ivy:makepom>
</target>

Because of some odd inclusions, it generated duplicate dependencies. Those had to be removed manually.

To produce a textual dependency tree:

$ mvn dependency:tree
com.vaadin:vaadin-all:jar:7.0.0.rc2
+- com.vaadin:vaadin-shared:jar:7.0.0.rc2:compile
+- com.vaadin:vaadin-theme-compiler:jar:7.0.0.rc2:compile
|  +- org.apache.commons:commons-jexl:jar:2.1.1:compile
|  |  \- commons-logging:commons-logging:jar:1.1.1:compile
|  +- org.w3c.css:sac:jar:1.3:compile
|  +- net.sourceforge.cssparser:cssparser:jar:0.9.5:compile
|  \- commons-cli:commons-cli:jar:1.2:compile
+- com.vaadin:vaadin-server:jar:7.0.0.rc2:compile
|  \- org.jsoup:jsoup:jar:1.6.3:compile
+- com.vaadin:vaadin-client:jar:7.0.0.rc2:compile
|  +- javax.validation:validation-api:jar:1.0.0.GA:compile
|  \- javax.validation:validation-api:jar:sources:1.0.0.GA:compile
+- com.vaadin:vaadin-shared-deps:jar:1.0.2:compile
+- com.vaadin:vaadin-client-compiled:jar:7.0.0.rc2:compile
+- com.vaadin:vaadin-themes:jar:7.0.0.rc2:compile
\- com.vaadin:vaadin-client-compiler:jar:7.0.0.rc2:compile
   +- commons-collections:commons-collections:jar:3.1:compile
   +- ant:ant:jar:1.6.5:compile
   +- ant:ant-launcher:jar:1.6.5:compile
   +- org.mortbay.jetty:jetty:jar:6.1.11:compile
   |  \- org.mortbay.jetty:servlet-api-2.5:jar:6.1.11:compile
   +- org.mortbay.jetty:jetty-util:jar:6.1.11:compile
   +- org.jdesktop:swing-worker:jar:1.1:compile
   +- commons-codec:commons-codec:jar:1.3:compile
   +- commons-io:commons-io:jar:1.4:compile
   +- commons-lang:commons-lang:jar:2.6:compile
   \- org.apache.james:apache-mime4j:jar:0.6:compile

Or, as visualized with GraphViz. Using the outputType parameter required a newer version of the Maven Dependency plugin, so it required a full path to the most recent version:

$ mvn org.apache.maven.plugins:maven-dependency-plugin:2.6:tree \
      -DoutputFile=pom.dot -DoutputType=dot
$ dot -Tpng -o pom.png pom.dot

While this was in some way useful, it did not show dependencies included in the JARs, and didn't show all other dependencies either, so it was even less useful than JarAnalyzer display. Oh well.

Tuesday, January 15, 2013

Saving the Galaxy (S2)

This is just one of those days when I saved the galaxy. Well, Samsung Galaxy S II. Last Saturday, I was drinking my tea, when I spilled some on the phone. Nothing happened first, just couple of drops on the surface. Later in the evening it started bleeping strangely and shut down, and didn't wake up.

So, I opened the battery cover and noticed some moisture inside. I put it in the charger. Didn't do any good. Well, it sounded like it started up, but the display was black. The button keys did light up though.

So, my diagnosis was that the tea had caused a short circuit and the display had died.

Water spills have caused a temporary malfunction of my camera and other electronics couple of times before, so it's not always necessarily fatal, as long as you remove the battery quick and put it in a warm place to dry. So, I removed the battery, hoping that it wasn't too late already, and left it to dry on a heater.

However, after couple of nights on the heater, it was still just as dead as it was on Saturday. I assumed that it was bricked.

This was a real disaster. I did have the Android backup functionality enabled, but it only backs up the basic application data. The most important stuff like phone catalog and SMSs are not backed up, which is actually a bit strange. Also, pictures and other such recorded data is not backed up.

Well, the phone appeared to be working even though the display wasn't. Samsung sells a HDMI adapter for the phone, so if I bought one, I might have been able to hook it to a HDMI television and take the necessary backups.

But anyhow, I assumed opening the phone up would not make things much worse, so I started opening it.

Under the battery cover were a number of screws. I opened those and plied the internal cover open as carefully as I could. Well, I did manage to crack it a bit, but it came off nevertheless.

That exposed the motherboard.

Ok, a lot super-tiny surface mount circuitry. There were some five super-tiny connectors. I assume the biggest one was the display connector. So, I took it open and examined the motherboard with a magnifying glass (actually an inverted 7x monocular but anyhow). All was well, except in the display connector, where there was one oddly black pin.

Yeah, that looked bad. Apparently, the liquid had caused it to short circuit with a neighbouring pin and eventual failure. Well, perhaps not even a short circuit, just the accumulation of corrosion (copper oxide or whatever) on the pin because of ion exchange in the liquid medium could have caused it.

So, I took a bottle of CRC, a small screwdriver, and some lens cleaning paper, and wrapped the screwdriver in the paper, wet the paper with CRC, and scrubbed the connector. Closed the phone again, tried it... not any better.

I opened the phone even further, took open all the five tiny connectors on the motherboard, unscrewed couple of more tiny screws, detached the glued power and volume buttons, and managed to get the motherboard off.

Well, I didn't find any more damage there, apparently the one connector pin was all the visible damage. There could have been some internal damage in the display or the motherboard, but that would have been out of my control.

Next morning, I scrubbed the burned connector again even better, also with a bare (tiny) screwdriver. I avoided doing that too much, as chipping copper off the pins could have caused even more short circuits. So, more CRC on the cleaning tissue and scrubbing. Then I closed it and screwed all the tiny screws back, believing that that was my last try.

Then, when I plugged it in the charger, it suddenly started up!!!.

IT WORKS AGAIN!!!

That really saved my day. I hope the event didn't cause any permanent damage and all is well. Hope hope hope.

I'll promise myself that I'll try to find all necessary backup tools to make sure I won't lose anything prescious in the future.

Tuesday, January 8, 2013

Wake Up Android!

Great, I woke up at 1 pm today. I was supposed to wake up at 10 am, but my phone had decided that the snooze time is 3 minutes instead of the 15 minutes I had set it to, so I shut it off...and slept 3 hours more.

The Android clock application apparently forgets the snooze settings every time the phone is shut off and restarted...for some settings. So, it's a bug. It's a built-in app and I haven't updated my phone's OS, so it probably won't be fixed without an OS upgrade. Well, I probably should do one.

Not to say, the built-in alarm clock application is otherwise just great. It can have multiple different alarm profiles that repeat on particular weekdays. For each alarm, it lets you set the time, snooze interval, number of repeats, and also a "smart alarm" that first plays peaceful music, and only after a number of minutes it turns into a annoying buzzer that really forces you to wake up...or press the snooze button.

But when I snooze, I want to snooze longer than the default 3 minutes or otherwise I'll just hate the alarm or shut it down. But it keeps reverting to the default after a restart, so when it annoys me, I turn it off because I'm totally certain that I've properly woken up and it's OK, and in a couple of seconds fall back to sleep.

Yeah, my brain does not work quite rationally when it's just waking up. And I know it all too well.

The Annoying Calendar

Then, the calendar events. I am totally lost if there's no one or no thing that reminds me of them. That's why my Nokia E90 was totally indispensable for me. Its calendar application too had a proper noisy alarm and a snooze function. Unfortunately, the Android calendar hasn't.

I don't know why, but the default Android calendar app, nor any other calendar app for Android, does not have a proper alarm in their calendar. "*BLEEP* *BLEEP*", that's all it says, once. If I've left it in my room or if there's even a bit noise around, I won't hear it. There's a collection of maybe fifteen different sounds, but they all last just couple of seconds and there's no repeat or snooze function, like in the alarm clock.

Apparently it's not just Android, I've been told all iPhone calendars have the same behavior as well.

I've tried several other calendar apps, many rated with 5 stars, and none of them offer any better alarm function.

My software mind starts guessing at this point that the problem is with Google Calendar, as all the apps use that. It probably allows just one alarm at a preset time before the event, and doesn't allow any other settings, such as the snooze settings.

This is not a small problem to me.

I really need a technical solution that actually works. Now. No, make that 10 years ago.

Wednesday, January 2, 2013

Choosing a UAV Robotic Platform for Research

So, I've been planning to look into robotics for a long time, not the least because it's closely related to my research field, evolutionary algorithms and neural networks, especially for control applications. I've done some hardware platform experiments with Arduino, but I haven't had time to do much beyond that yet. But I plan to, at least make basic motor control, PID control, balancing experiments, etc. All that just requires a bit too much tinkering with mechanics and I'm not so interested about that. I want to have the mechanics more or less ready, or at most build them from lego-style components, and concentrate on the control software. I am a software person, after all.

UAVs are, in many respects, easier than walking or wheeled things. You can concentrate on sensors, vision, navigation, and planning, instead of working years on problems like trying to climb over a branch.

The three main platforms that I've found are:

  • Arducopter
  • AscTec Hummingbird/Autopilot
  • Parrot AR.Drone 2.0

Let's look into those.

Arducopter

The Arducopter is quadrocopter with an Arduino-based control board. I'm familiar with Arduino, how to communicate with it, and how to hook sensors to it, so development would be more or less easy. The hardware is highly modifiable and it has a strong community.

The downside is that it is a bit expensive to get started with. A basic kit is around 400 €, so it would be affordable, but the basic kit is very basic. A manual 2.4 GHz remote controller would be highly useful, as well as a remote camera, but they are rather expensive. There would also be a lot of initial work to get it all assembled, configured, and ready to fly.

AscTec Hummingbird

The AscTec Hummingbird has been used in UAV robotics research, most famously by the GRASP laboratory at the University of Pennsylvania, who have used them to create quite amazing flight maneuvers and swarm behaviour.

The AscTec UAVs can be remote controlled with the AscTec SDK, which communicates with the UAV using ZigBee, a commonly used serial radio chip. I've used those with Arduino, they are dumb-easy to use at the simplest. One problem is that there apparently is no on-board API.

While claimed to be the top-of-the-line research UAVs at the moment, they are horribly expensive, reportedly starting from around $4000 for barebones configuration and around 15000 € for a full-featured one, whatever that means. I have not even been able to find a shop that sells them and the manufacturer provides very little details, so it's hard to say much about them. In any case, can't afford one.

Parrot AR.Drone 2.0

The Parrot AR.Drone 2.0 is probably the most well-productized UAV at the moment. It is sold in fully working condition and can be controlled with an Android or iOS device, such as a phone or a tablet, using a WLAN link with the device. The link is specified to have a range of around 50 meters in optimal conditions, so the device is clearly just for close use, especially as it has no built-in autonomous flight.

The AR.Drone includes a 720p video camera through the WLAN link, so you can navigate the UAV using the video link. It also has a separate small video camera pointing directly downward for ground speed measurement.

The vehicle has a gyroscope, accelerometer, magnetometer, barometer, ultrasound sensors, and the two cameras for navigation. That's more than enough for most experimental purposes. No GPS though, so long-distance autonomous navigation is not so easy.

The payload capacity is not even mentioned and the manufacturer even warns that additional payload may affect flight stability. The battery lasts just for some 12 minutes.

Most importantly, the manufacturer provides a remote control API for Linux, Mac, and Windows. This openness has allowed the UAV to have been used for research, which would also be my purpose. Also, the on-board flight controller is a Linux-based embedded computer. It has a 1 GHz ARM processor and 1 GB RAM, plus a DSP for video, and the WLAN, so it's actually much more efficient than a Raspberry. You can log into the system using Telnet, probably even during flight. One big question is, are also the in-controller APIs open, which would be essential for developing autonomous functions that would work without the WLAN control loop.

The price for the AR.Drone 2.0 is just 300 €, plus some 30 € for a spare battery and 40 € for the indoor protective frame. All the parts are sold also as replacement parts, although not very cheap. That could also mean that it breaks very easily...

While apparently marketed foremost as a toy for kids (of rich parents), the AR.Drone appears to have all the features of an excellent research platform. It's just great if they get their main income from the consumer market to bring the prices down.

Conclusions

The AscTec Hummingbird is clearly out of my resources at the moment. The Arducopter would be interesting and probably the most flexible solution. However, as I'm just entering the UAV hobby/research, it's best to buy some cheap entry-level solution that still allows some flexibility. Apparently, the AR.Drone can do all the basic things I need right out of the Box at a great price.

Saturday, November 17, 2012

Avoiding Perl Locale Errors with Remote Git

I've long been troubled with annoying locale warnings that I got with every Git command.

$ git pull
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
        LANGUAGE = (unset),
        LC_ALL = (unset),
        LANG = "fi_FI.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
Current branch master is up to date.

Generating the locales with "locale-gen fi_FI fi_FI.UTF-8", which was suggested by some, didn't help at all.

It seems that the problem occurs with remote SSH repositories, when Git makes an SSH call to the server. Apparently, it runs a shell in the remote host. It forwards language environment variables to the remote host, but when the remote host doesn't have the locale installed, the error occurs.

So, the solutions are:

  1. generate the locales in the remote repository server, or if that is not possible,
  2. change the locale to LANG=C in the local host.

I prefer to have the fi_FI as my system locale, so I wrapped the git calls in the following Bash script:

#!/bin/bash

# This is needed when git makes an SSH call to a remote
# server which doesn't have # all the locales installed
export LANG=C

# Note: the "$@" is important notation to avoid
# splitting arguments at spaces
/usr/bin/git "$@"